linera_wasmer/externals/
function.rs

1#[cfg(feature = "js")]
2use crate::js::externals::function as function_impl;
3#[cfg(feature = "jsc")]
4use crate::jsc::externals::function as function_impl;
5#[cfg(feature = "sys")]
6use crate::sys::externals::function as function_impl;
7
8use crate::exports::{ExportError, Exportable};
9use crate::store::{AsStoreMut, AsStoreRef};
10use crate::vm::{VMExtern, VMExternFunction, VMFuncRef, VMFunctionCallback, VMTrampoline};
11use crate::{
12    Extern, FunctionEnv, FunctionEnvMut, FunctionType, RuntimeError, TypedFunction, Value,
13};
14use wasmer_types::RawValue;
15
16use crate::native_type::WasmTypeList;
17
18/// The `HostFunction` trait represents the set of functions that
19/// can be used as host function. To uphold this statement, it is
20/// necessary for a function to be transformed into a
21/// `VMFunctionCallback`.
22pub trait HostFunction<T, Args, Rets, Kind>
23where
24    Args: WasmTypeList,
25    Rets: WasmTypeList,
26    Kind: HostFunctionKind,
27{
28    /// Get the pointer to the function body.
29    fn function_callback(&self) -> VMFunctionCallback;
30
31    /// Get the pointer to the function call trampoline.
32    fn call_trampoline_address() -> VMTrampoline {
33        // This is not implemented in JS
34        unimplemented!();
35    }
36}
37
38/// Empty trait to specify the kind of `HostFunction`: With or
39/// without an environment.
40///
41/// This trait is never aimed to be used by a user. It is used by
42/// the trait system to automatically generate the appropriate
43/// host functions.
44#[doc(hidden)]
45pub trait HostFunctionKind: private::HostFunctionKindSealed {}
46
47/// An empty struct to help Rust typing to determine
48/// when a `HostFunction` does have an environment.
49pub struct WithEnv;
50
51impl HostFunctionKind for WithEnv {}
52
53/// An empty struct to help Rust typing to determine
54/// when a `HostFunction` does not have an environment.
55pub struct WithoutEnv;
56
57impl HostFunctionKind for WithoutEnv {}
58
59mod private {
60    //! Sealing the HostFunctionKind because it shouldn't be implemented
61    //! by any type outside.
62    //! See:
63    //! <https://rust-lang.github.io/api-guidelines/future-proofing.html#c-sealed>
64    pub trait HostFunctionKindSealed {}
65    impl HostFunctionKindSealed for super::WithEnv {}
66    impl HostFunctionKindSealed for super::WithoutEnv {}
67}
68
69/// A WebAssembly `function` instance.
70///
71/// A function instance is the runtime representation of a function.
72/// It effectively is a closure of the original function (defined in either
73/// the host or the WebAssembly module) over the runtime `Instance` of its
74/// originating `Module`.
75///
76/// The module instance is used to resolve references to other definitions
77/// during execution of the function.
78///
79/// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#function-instances>
80///
81/// # Panics
82/// - Closures (functions with captured environments) are not currently supported
83///   with native functions. Attempting to create a native `Function` with one will
84///   result in a panic.
85///   [Closures as host functions tracking issue](https://github.com/wasmerio/wasmer/issues/1840)
86#[derive(Debug, Clone, PartialEq)]
87#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
88pub struct Function(pub(crate) function_impl::Function);
89
90impl Function {
91    /// Creates a new host `Function` (dynamic) with the provided signature.
92    ///
93    /// If you know the signature of the host function at compile time,
94    /// consider using [`Function::new_typed`] for less runtime overhead.
95    pub fn new<FT, F>(store: &mut impl AsStoreMut, ty: FT, func: F) -> Self
96    where
97        FT: Into<FunctionType>,
98        F: Fn(&[Value]) -> Result<Vec<Value>, RuntimeError> + 'static + Send + Sync,
99    {
100        let env = FunctionEnv::new(&mut store.as_store_mut(), ());
101        let wrapped_func = move |_env: FunctionEnvMut<()>,
102                                 args: &[Value]|
103              -> Result<Vec<Value>, RuntimeError> { func(args) };
104        Self::new_with_env(store, &env, ty, wrapped_func)
105    }
106
107    /// Creates a new host `Function` (dynamic) with the provided signature.
108    ///
109    /// If you know the signature of the host function at compile time,
110    /// consider using [`Function::new_typed_with_env`] for less runtime overhead.
111    ///
112    /// Takes a [`FunctionEnv`] that is passed into func. If that is not required,
113    /// [`Function::new`] might be an option as well.
114    ///
115    /// # Examples
116    ///
117    /// ```
118    /// # use wasmer::{Function, FunctionEnv, FunctionType, Type, Store, Value};
119    /// # let mut store = Store::default();
120    /// # let env = FunctionEnv::new(&mut store, ());
121    /// #
122    /// let signature = FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32]);
123    ///
124    /// let f = Function::new_with_env(&mut store, &env, &signature, |_env, args| {
125    ///     let sum = args[0].unwrap_i32() + args[1].unwrap_i32();
126    ///     Ok(vec![Value::I32(sum)])
127    /// });
128    /// ```
129    ///
130    /// With constant signature:
131    ///
132    /// ```
133    /// # use wasmer::{Function, FunctionEnv, FunctionType, Type, Store, Value};
134    /// # let mut store = Store::default();
135    /// # let env = FunctionEnv::new(&mut store, ());
136    /// #
137    /// const I32_I32_TO_I32: ([Type; 2], [Type; 1]) = ([Type::I32, Type::I32], [Type::I32]);
138    ///
139    /// let f = Function::new_with_env(&mut store, &env, I32_I32_TO_I32, |_env, args| {
140    ///     let sum = args[0].unwrap_i32() + args[1].unwrap_i32();
141    ///     Ok(vec![Value::I32(sum)])
142    /// });
143    /// ```
144    pub fn new_with_env<FT, F, T: 'static>(
145        store: &mut impl AsStoreMut,
146        env: &FunctionEnv<T>,
147        ty: FT,
148        func: F,
149    ) -> Self
150    where
151        FT: Into<FunctionType>,
152        F: Fn(FunctionEnvMut<T>, &[Value]) -> Result<Vec<Value>, RuntimeError>
153            + 'static
154            + Send
155            + Sync,
156    {
157        Self(function_impl::Function::new_with_env(store, env, ty, func))
158    }
159
160    /// Creates a new host `Function` from a native function.
161    pub fn new_typed<F, Args, Rets>(store: &mut impl AsStoreMut, func: F) -> Self
162    where
163        F: HostFunction<(), Args, Rets, WithoutEnv> + 'static + Send + Sync,
164        Args: WasmTypeList,
165        Rets: WasmTypeList,
166    {
167        Self(function_impl::Function::new_typed(store, func))
168    }
169
170    /// Creates a new host `Function` with an environment from a typed function.
171    ///
172    /// The function signature is automatically retrieved using the
173    /// Rust typing system.
174    ///
175    /// # Example
176    ///
177    /// ```
178    /// # use wasmer::{Store, Function, FunctionEnv, FunctionEnvMut};
179    /// # let mut store = Store::default();
180    /// # let env = FunctionEnv::new(&mut store, ());
181    /// #
182    /// fn sum(_env: FunctionEnvMut<()>, a: i32, b: i32) -> i32 {
183    ///     a + b
184    /// }
185    ///
186    /// let f = Function::new_typed_with_env(&mut store, &env, sum);
187    /// ```
188    pub fn new_typed_with_env<T: 'static, F, Args, Rets>(
189        store: &mut impl AsStoreMut,
190        env: &FunctionEnv<T>,
191        func: F,
192    ) -> Self
193    where
194        F: HostFunction<T, Args, Rets, WithEnv> + 'static + Send + Sync,
195        Args: WasmTypeList,
196        Rets: WasmTypeList,
197    {
198        Self(function_impl::Function::new_typed_with_env(
199            store, env, func,
200        ))
201    }
202
203    /// Returns the [`FunctionType`] of the `Function`.
204    ///
205    /// # Example
206    ///
207    /// ```
208    /// # use wasmer::{Function, FunctionEnv, FunctionEnvMut, Store, Type};
209    /// # let mut store = Store::default();
210    /// # let env = FunctionEnv::new(&mut store, ());
211    /// #
212    /// fn sum(_env: FunctionEnvMut<()>, a: i32, b: i32) -> i32 {
213    ///     a + b
214    /// }
215    ///
216    /// let f = Function::new_typed_with_env(&mut store, &env, sum);
217    ///
218    /// assert_eq!(f.ty(&mut store).params(), vec![Type::I32, Type::I32]);
219    /// assert_eq!(f.ty(&mut store).results(), vec![Type::I32]);
220    /// ```
221    pub fn ty(&self, store: &impl AsStoreRef) -> FunctionType {
222        self.0.ty(store)
223    }
224
225    /// Returns the number of parameters that this function takes.
226    ///
227    /// # Example
228    ///
229    /// ```
230    /// # use wasmer::{Function, FunctionEnv, FunctionEnvMut, Store, Type};
231    /// # let mut store = Store::default();
232    /// # let env = FunctionEnv::new(&mut store, ());
233    /// #
234    /// fn sum(_env: FunctionEnvMut<()>, a: i32, b: i32) -> i32 {
235    ///     a + b
236    /// }
237    ///
238    /// let f = Function::new_typed_with_env(&mut store, &env, sum);
239    ///
240    /// assert_eq!(f.param_arity(&mut store), 2);
241    /// ```
242    pub fn param_arity(&self, store: &impl AsStoreRef) -> usize {
243        self.ty(store).params().len()
244    }
245
246    /// Returns the number of results this function produces.
247    ///
248    /// # Example
249    ///
250    /// ```
251    /// # use wasmer::{Function, FunctionEnv, FunctionEnvMut, Store, Type};
252    /// # let mut store = Store::default();
253    /// # let env = FunctionEnv::new(&mut store, ());
254    /// #
255    /// fn sum(_env: FunctionEnvMut<()>, a: i32, b: i32) -> i32 {
256    ///     a + b
257    /// }
258    ///
259    /// let f = Function::new_typed_with_env(&mut store, &env, sum);
260    ///
261    /// assert_eq!(f.result_arity(&mut store), 1);
262    /// ```
263    pub fn result_arity(&self, store: &impl AsStoreRef) -> usize {
264        self.ty(store).results().len()
265    }
266
267    /// Call the `Function` function.
268    ///
269    /// Depending on where the Function is defined, it will call it.
270    /// 1. If the function is defined inside a WebAssembly, it will call the trampoline
271    ///    for the function signature.
272    /// 2. If the function is defined in the host (in a native way), it will
273    ///    call the trampoline.
274    ///
275    /// # Examples
276    ///
277    /// ```
278    /// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, Value};
279    /// # use wasmer::FunctionEnv;
280    /// # let mut store = Store::default();
281    /// # let env = FunctionEnv::new(&mut store, ());
282    /// # let wasm_bytes = wat2wasm(r#"
283    /// # (module
284    /// #   (func (export "sum") (param $x i32) (param $y i32) (result i32)
285    /// #     local.get $x
286    /// #     local.get $y
287    /// #     i32.add
288    /// #   ))
289    /// # "#.as_bytes()).unwrap();
290    /// # let module = Module::new(&store, wasm_bytes).unwrap();
291    /// # let import_object = imports! {};
292    /// # let instance = Instance::new(&mut store, &module, &import_object).unwrap();
293    /// #
294    /// let sum = instance.exports.get_function("sum").unwrap();
295    ///
296    /// assert_eq!(sum.call(&mut store, &[Value::I32(1), Value::I32(2)]).unwrap().to_vec(), vec![Value::I32(3)]);
297    /// ```
298    pub fn call(
299        &self,
300        store: &mut impl AsStoreMut,
301        params: &[Value],
302    ) -> Result<Box<[Value]>, RuntimeError> {
303        self.0.call(store, params)
304    }
305
306    #[doc(hidden)]
307    #[allow(missing_docs)]
308    pub fn call_raw(
309        &self,
310        store: &mut impl AsStoreMut,
311        params: Vec<RawValue>,
312    ) -> Result<Box<[Value]>, RuntimeError> {
313        self.0.call_raw(store, params)
314    }
315
316    pub(crate) fn vm_funcref(&self, store: &impl AsStoreRef) -> VMFuncRef {
317        self.0.vm_funcref(store)
318    }
319
320    pub(crate) unsafe fn from_vm_funcref(store: &mut impl AsStoreMut, funcref: VMFuncRef) -> Self {
321        Self(function_impl::Function::from_vm_funcref(store, funcref))
322    }
323
324    /// Transform this WebAssembly function into a typed function.
325    /// See [`TypedFunction`] to learn more.
326    ///
327    /// # Examples
328    ///
329    /// ```
330    /// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, TypedFunction, Value};
331    /// # use wasmer::FunctionEnv;
332    /// # let mut store = Store::default();
333    /// # let env = FunctionEnv::new(&mut store, ());
334    /// # let wasm_bytes = wat2wasm(r#"
335    /// # (module
336    /// #   (func (export "sum") (param $x i32) (param $y i32) (result i32)
337    /// #     local.get $x
338    /// #     local.get $y
339    /// #     i32.add
340    /// #   ))
341    /// # "#.as_bytes()).unwrap();
342    /// # let module = Module::new(&store, wasm_bytes).unwrap();
343    /// # let import_object = imports! {};
344    /// # let instance = Instance::new(&mut store, &module, &import_object).unwrap();
345    /// #
346    /// let sum = instance.exports.get_function("sum").unwrap();
347    /// let sum_typed: TypedFunction<(i32, i32), i32> = sum.typed(&mut store).unwrap();
348    ///
349    /// assert_eq!(sum_typed.call(&mut store, 1, 2).unwrap(), 3);
350    /// ```
351    ///
352    /// # Errors
353    ///
354    /// If the `Args` generic parameter does not match the exported function
355    /// an error will be raised:
356    ///
357    /// ```should_panic
358    /// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, TypedFunction, Value};
359    /// # use wasmer::FunctionEnv;
360    /// # let mut store = Store::default();
361    /// # let env = FunctionEnv::new(&mut store, ());
362    /// # let wasm_bytes = wat2wasm(r#"
363    /// # (module
364    /// #   (func (export "sum") (param $x i32) (param $y i32) (result i32)
365    /// #     local.get $x
366    /// #     local.get $y
367    /// #     i32.add
368    /// #   ))
369    /// # "#.as_bytes()).unwrap();
370    /// # let module = Module::new(&store, wasm_bytes).unwrap();
371    /// # let import_object = imports! {};
372    /// # let instance = Instance::new(&mut store, &module, &import_object).unwrap();
373    /// #
374    /// let sum = instance.exports.get_function("sum").unwrap();
375    ///
376    /// // This results in an error: `RuntimeError`
377    /// let sum_typed : TypedFunction<(i64, i64), i32> = sum.typed(&mut store).unwrap();
378    /// ```
379    ///
380    /// If the `Rets` generic parameter does not match the exported function
381    /// an error will be raised:
382    ///
383    /// ```should_panic
384    /// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, TypedFunction, Value};
385    /// # use wasmer::FunctionEnv;
386    /// # let mut store = Store::default();
387    /// # let env = FunctionEnv::new(&mut store, ());
388    /// # let wasm_bytes = wat2wasm(r#"
389    /// # (module
390    /// #   (func (export "sum") (param $x i32) (param $y i32) (result i32)
391    /// #     local.get $x
392    /// #     local.get $y
393    /// #     i32.add
394    /// #   ))
395    /// # "#.as_bytes()).unwrap();
396    /// # let module = Module::new(&store, wasm_bytes).unwrap();
397    /// # let import_object = imports! {};
398    /// # let instance = Instance::new(&mut store, &module, &import_object).unwrap();
399    /// #
400    /// let sum = instance.exports.get_function("sum").unwrap();
401    ///
402    /// // This results in an error: `RuntimeError`
403    /// let sum_typed: TypedFunction<(i32, i32), i64> = sum.typed(&mut store).unwrap();
404    /// ```
405    pub fn typed<Args, Rets>(
406        &self,
407        store: &impl AsStoreRef,
408    ) -> Result<TypedFunction<Args, Rets>, RuntimeError>
409    where
410        Args: WasmTypeList,
411        Rets: WasmTypeList,
412    {
413        let ty = self.ty(store);
414
415        // type check
416        {
417            let expected = ty.params();
418            let given = Args::wasm_types();
419
420            if expected != given {
421                return Err(RuntimeError::new(format!(
422                    "given types (`{:?}`) for the function arguments don't match the actual types (`{:?}`)",
423                    given,
424                    expected,
425                )));
426            }
427        }
428
429        {
430            let expected = ty.results();
431            let given = Rets::wasm_types();
432
433            if expected != given {
434                // todo: error result types don't match
435                return Err(RuntimeError::new(format!(
436                    "given types (`{:?}`) for the function results don't match the actual types (`{:?}`)",
437                    given,
438                    expected,
439                )));
440            }
441        }
442
443        Ok(TypedFunction::new(store, self.clone()))
444    }
445
446    pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExternFunction) -> Self {
447        Self(function_impl::Function::from_vm_extern(store, vm_extern))
448    }
449
450    /// Checks whether this `Function` can be used with the given store.
451    pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool {
452        self.0.is_from_store(store)
453    }
454
455    pub(crate) fn to_vm_extern(&self) -> VMExtern {
456        self.0.to_vm_extern()
457    }
458}
459
460impl std::cmp::Eq for Function {}
461
462impl<'a> Exportable<'a> for Function {
463    fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> {
464        match _extern {
465            Extern::Function(func) => Ok(func),
466            _ => Err(ExportError::IncompatibleType),
467        }
468    }
469}