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}