1use std::pin::Pin;
2
3use wasmer_types::{FunctionType, RawValue};
4
5#[cfg(feature = "experimental-async")]
6use crate::{AsStoreAsync, AsyncFunctionEnvMut, entities::function::async_host::AsyncHostFunction};
7use crate::{
8 AsStoreMut, AsStoreRef, ExportError, Exportable, Extern, FunctionEnv, FunctionEnvMut,
9 HostFunction, StoreMut, StoreRef, TypedFunction, Value, WasmTypeList, WithEnv, WithoutEnv,
10 error::RuntimeError,
11 macros::backend::{gen_rt_ty, match_rt},
12 vm::{VMExtern, VMExternFunction, VMFuncRef},
13};
14
15gen_rt_ty!(Function
33 @cfg feature = "artifact-size" => derive(loupe::MemoryUsage)
34 @derives Debug, Clone, PartialEq, Eq
35);
36
37impl BackendFunction {
38 #[inline]
43 pub fn new<FT, F>(store: &mut impl AsStoreMut, ty: FT, func: F) -> Self
44 where
45 FT: Into<FunctionType>,
46 F: Fn(&[Value]) -> Result<Vec<Value>, RuntimeError> + 'static + Send + Sync,
47 {
48 let env = FunctionEnv::new(&mut store.as_store_mut(), ());
49 let wrapped_func = move |_env: FunctionEnvMut<()>,
50 args: &[Value]|
51 -> Result<Vec<Value>, RuntimeError> { func(args) };
52 Self::new_with_env(store, &env, ty, wrapped_func)
53 }
54
55 #[inline]
93 pub fn new_with_env<FT, F, T: Send + 'static>(
94 store: &mut impl AsStoreMut,
95 env: &FunctionEnv<T>,
96 ty: FT,
97 func: F,
98 ) -> Self
99 where
100 FT: Into<FunctionType>,
101 F: Fn(FunctionEnvMut<T>, &[Value]) -> Result<Vec<Value>, RuntimeError>
102 + 'static
103 + Send
104 + Sync,
105 {
106 match &store.as_store_mut().inner.store {
107 #[cfg(feature = "sys")]
108 crate::BackendStore::Sys(_) => Self::Sys(
109 crate::backend::sys::entities::function::Function::new_with_env(
110 store, env, ty, func,
111 ),
112 ),
113 #[cfg(feature = "wamr")]
114 crate::BackendStore::Wamr(_) => Self::Wamr(
115 crate::backend::wamr::entities::function::Function::new_with_env(
116 store, env, ty, func,
117 ),
118 ),
119 #[cfg(feature = "wasmi")]
120 crate::BackendStore::Wasmi(_) => Self::Wasmi(
121 crate::backend::wasmi::entities::function::Function::new_with_env(
122 store, env, ty, func,
123 ),
124 ),
125 #[cfg(feature = "v8")]
126 crate::BackendStore::V8(_) => Self::V8(
127 crate::backend::v8::entities::function::Function::new_with_env(
128 store, env, ty, func,
129 ),
130 ),
131 #[cfg(feature = "js")]
132 crate::BackendStore::Js(_) => Self::Js(
133 crate::backend::js::entities::function::Function::new_with_env(
134 store, env, ty, func,
135 ),
136 ),
137 #[cfg(feature = "jsc")]
138 crate::BackendStore::Jsc(_) => Self::Jsc(
139 crate::backend::jsc::entities::function::Function::new_with_env(
140 store, env, ty, func,
141 ),
142 ),
143 }
144 }
145
146 #[inline]
148 pub fn new_typed<F, Args, Rets>(store: &mut impl AsStoreMut, func: F) -> Self
149 where
150 F: HostFunction<(), Args, Rets, WithoutEnv> + 'static + Send + Sync,
151 Args: WasmTypeList,
152 Rets: WasmTypeList,
153 {
154 match &store.as_store_mut().inner.store {
155 #[cfg(feature = "sys")]
156 crate::BackendStore::Sys(_) => {
157 Self::Sys(crate::backend::sys::entities::function::Function::new_typed(store, func))
158 }
159 #[cfg(feature = "wamr")]
160 crate::BackendStore::Wamr(_) => Self::Wamr(
161 crate::backend::wamr::entities::function::Function::new_typed(store, func),
162 ),
163
164 #[cfg(feature = "wasmi")]
165 crate::BackendStore::Wasmi(_) => Self::Wasmi(
166 crate::backend::wasmi::entities::function::Function::new_typed(store, func),
167 ),
168 #[cfg(feature = "v8")]
169 crate::BackendStore::V8(_) => Self::V8(
170 crate::backend::v8::entities::function::Function::new_typed(store, func),
171 ),
172 #[cfg(feature = "js")]
173 crate::BackendStore::Js(_) => Self::Js(
174 crate::backend::js::entities::function::Function::new_typed(store, func),
175 ),
176
177 #[cfg(feature = "jsc")]
178 crate::BackendStore::Jsc(_) => {
179 Self::Jsc(crate::backend::jsc::entities::function::Function::new_typed(store, func))
180 }
181 }
182 }
183
184 #[inline]
203 pub fn new_typed_with_env<T: Send + 'static, F, Args, Rets>(
204 store: &mut impl AsStoreMut,
205 env: &FunctionEnv<T>,
206 func: F,
207 ) -> Self
208 where
209 F: HostFunction<T, Args, Rets, WithEnv> + 'static + Send + Sync,
210 Args: WasmTypeList,
211 Rets: WasmTypeList,
212 {
213 match &store.as_store_mut().inner.store {
214 #[cfg(feature = "sys")]
215 crate::BackendStore::Sys(s) => Self::Sys(
216 crate::backend::sys::entities::function::Function::new_typed_with_env(
217 store, env, func,
218 ),
219 ),
220 #[cfg(feature = "wamr")]
221 crate::BackendStore::Wamr(s) => Self::Wamr(
222 crate::backend::wamr::entities::function::Function::new_typed_with_env(
223 store, env, func,
224 ),
225 ),
226
227 #[cfg(feature = "wasmi")]
228 crate::BackendStore::Wasmi(s) => Self::Wasmi(
229 crate::backend::wasmi::entities::function::Function::new_typed_with_env(
230 store, env, func,
231 ),
232 ),
233 #[cfg(feature = "v8")]
234 crate::BackendStore::V8(s) => Self::V8(
235 crate::backend::v8::entities::function::Function::new_typed_with_env(
236 store, env, func,
237 ),
238 ),
239 #[cfg(feature = "js")]
240 crate::BackendStore::Js(s) => Self::Js(
241 crate::backend::js::entities::function::Function::new_typed_with_env(
242 store, env, func,
243 ),
244 ),
245 #[cfg(feature = "jsc")]
246 crate::BackendStore::Jsc(s) => Self::Jsc(
247 crate::backend::jsc::entities::function::Function::new_typed_with_env(
248 store, env, func,
249 ),
250 ),
251 }
252 }
253
254 #[inline]
267 #[cfg(feature = "experimental-async")]
268 pub fn new_async<FT, F, Fut>(store: &mut impl AsStoreMut, ty: FT, func: F) -> Self
269 where
270 FT: Into<FunctionType>,
271 F: Fn(&[Value]) -> Fut + 'static,
272 Fut: Future<Output = Result<Vec<Value>, RuntimeError>> + 'static,
273 {
274 match &store.as_store_mut().inner.store {
275 #[cfg(feature = "sys")]
276 crate::BackendStore::Sys(_) => Self::Sys(
277 crate::backend::sys::entities::function::Function::new_async(store, ty, func),
278 ),
279 #[cfg(feature = "wamr")]
280 crate::BackendStore::Wamr(_) => unsupported_async_backend("wamr"),
281 #[cfg(feature = "wasmi")]
282 crate::BackendStore::Wasmi(_) => unsupported_async_backend("wasmi"),
283 #[cfg(feature = "v8")]
284 crate::BackendStore::V8(_) => unsupported_async_backend("v8"),
285 #[cfg(feature = "js")]
286 crate::BackendStore::Js(_) => unsupported_async_backend("js"),
287 #[cfg(feature = "jsc")]
288 crate::BackendStore::Jsc(_) => unsupported_async_backend("jsc"),
289 }
290 }
291
292 #[inline]
301 #[cfg(feature = "experimental-async")]
302 pub fn new_with_env_async<FT, F, Fut, T: 'static>(
303 store: &mut impl AsStoreMut,
304 env: &FunctionEnv<T>,
305 ty: FT,
306 func: F,
307 ) -> Self
308 where
309 FT: Into<FunctionType>,
310 F: Fn(AsyncFunctionEnvMut<T>, &[Value]) -> Fut + 'static,
311 Fut: Future<Output = Result<Vec<Value>, RuntimeError>> + 'static,
312 {
313 match &store.as_store_mut().inner.store {
314 #[cfg(feature = "sys")]
315 crate::BackendStore::Sys(_) => Self::Sys(
316 crate::backend::sys::entities::function::Function::new_with_env_async(
317 store, env, ty, func,
318 ),
319 ),
320 #[cfg(feature = "wamr")]
321 crate::BackendStore::Wamr(_) => unsupported_async_backend("wamr"),
322 #[cfg(feature = "wasmi")]
323 crate::BackendStore::Wasmi(_) => unsupported_async_backend("wasmi"),
324 #[cfg(feature = "v8")]
325 crate::BackendStore::V8(_) => unsupported_async_backend("v8"),
326 #[cfg(feature = "js")]
327 crate::BackendStore::Js(_) => unsupported_async_backend("js"),
328 #[cfg(feature = "jsc")]
329 crate::BackendStore::Jsc(_) => unsupported_async_backend("jsc"),
330 }
331 }
332
333 #[inline]
338 #[cfg(feature = "experimental-async")]
339 pub fn new_typed_async<F, Args, Rets>(store: &mut impl AsStoreMut, func: F) -> Self
340 where
341 F: AsyncHostFunction<(), Args, Rets, WithoutEnv> + 'static,
342 Args: WasmTypeList + 'static,
343 Rets: WasmTypeList + 'static,
344 {
345 match &store.as_store_mut().inner.store {
346 #[cfg(feature = "sys")]
347 crate::BackendStore::Sys(_) => Self::Sys(
348 crate::backend::sys::entities::function::Function::new_typed_async(store, func),
349 ),
350 #[cfg(feature = "wamr")]
351 crate::BackendStore::Wamr(_) => unsupported_async_backend("wamr"),
352 #[cfg(feature = "wasmi")]
353 crate::BackendStore::Wasmi(_) => unsupported_async_backend("wasmi"),
354 #[cfg(feature = "v8")]
355 crate::BackendStore::V8(_) => unsupported_async_backend("v8"),
356 #[cfg(feature = "js")]
357 crate::BackendStore::Js(_) => unsupported_async_backend("js"),
358 #[cfg(feature = "jsc")]
359 crate::BackendStore::Jsc(_) => unsupported_async_backend("jsc"),
360 }
361 }
362
363 #[inline]
365 #[cfg(feature = "experimental-async")]
366 pub fn new_typed_with_env_async<T: 'static, F, Args, Rets>(
367 store: &mut impl AsStoreMut,
368 env: &FunctionEnv<T>,
369 func: F,
370 ) -> Self
371 where
372 F: AsyncHostFunction<T, Args, Rets, WithEnv> + 'static,
373 Args: WasmTypeList + 'static,
374 Rets: WasmTypeList + 'static,
375 {
376 match &store.as_store_mut().inner.store {
377 #[cfg(feature = "sys")]
378 crate::BackendStore::Sys(_) => Self::Sys(
379 crate::backend::sys::entities::function::Function::new_typed_with_env_async(
380 store, env, func,
381 ),
382 ),
383 #[cfg(feature = "wamr")]
384 crate::BackendStore::Wamr(_) => unsupported_async_backend("wamr"),
385 #[cfg(feature = "wasmi")]
386 crate::BackendStore::Wasmi(_) => unsupported_async_backend("wasmi"),
387 #[cfg(feature = "v8")]
388 crate::BackendStore::V8(_) => unsupported_async_backend("v8"),
389 #[cfg(feature = "js")]
390 crate::BackendStore::Js(_) => unsupported_async_backend("js"),
391 #[cfg(feature = "jsc")]
392 crate::BackendStore::Jsc(_) => unsupported_async_backend("jsc"),
393 }
394 }
395
396 #[inline]
415 pub fn ty(&self, store: &impl AsStoreRef) -> FunctionType {
416 match_rt!(on self => f {
417 f.ty(store)
418 })
419 }
420
421 #[inline]
439 pub fn param_arity(&self, store: &impl AsStoreRef) -> usize {
440 self.ty(store).params().len()
441 }
442
443 #[inline]
461 pub fn result_arity(&self, store: &impl AsStoreRef) -> usize {
462 self.ty(store).params().len()
463 }
464
465 #[inline]
497 pub fn call(
498 &self,
499 store: &mut impl AsStoreMut,
500 params: &[Value],
501 ) -> Result<Box<[Value]>, RuntimeError> {
502 match_rt!(on self => f {
503 f.call(store, params)
504 })
505 }
506
507 #[doc(hidden)]
508 #[allow(missing_docs)]
509 #[inline]
510 pub fn call_raw(
511 &self,
512 store: &mut impl AsStoreMut,
513 params: Vec<RawValue>,
514 ) -> Result<Box<[Value]>, RuntimeError> {
515 match_rt!(on self => f {
516 f.call_raw(store, params)
517 })
518 }
519
520 #[cfg(feature = "experimental-async")]
521 #[allow(clippy::type_complexity)]
522 pub fn call_async(
523 &self,
524 store: &impl AsStoreAsync,
525 params: Vec<Value>,
526 ) -> Pin<Box<dyn Future<Output = Result<Box<[Value]>, RuntimeError>> + 'static>> {
527 match self {
528 #[cfg(feature = "sys")]
529 Self::Sys(f) => f.call_async(store, params),
530 #[cfg(feature = "wamr")]
531 Self::Wamr(_) => unsupported_async_future(),
532 #[cfg(feature = "wasmi")]
533 Self::Wasmi(_) => unsupported_async_future(),
534 #[cfg(feature = "v8")]
535 Self::V8(_) => unsupported_async_future(),
536 #[cfg(feature = "js")]
537 Self::Js(_) => unsupported_async_future(),
538 #[cfg(feature = "jsc")]
539 Self::Jsc(_) => unsupported_async_future(),
540 }
541 }
542
543 #[inline]
544 pub(crate) fn vm_funcref(&self, store: &impl AsStoreRef) -> VMFuncRef {
545 match self {
546 #[cfg(feature = "sys")]
547 Self::Sys(f) => VMFuncRef::Sys(f.vm_funcref(store)),
548 #[cfg(feature = "wamr")]
549 Self::Wamr(f) => VMFuncRef::Wamr(f.vm_funcref(store)),
550 #[cfg(feature = "wasmi")]
551 Self::Wasmi(f) => VMFuncRef::Wasmi(f.vm_funcref(store)),
552 #[cfg(feature = "v8")]
553 Self::V8(f) => VMFuncRef::V8(f.vm_funcref(store)),
554 #[cfg(feature = "js")]
555 Self::Js(f) => VMFuncRef::Js(f.vm_funcref(store)),
556 #[cfg(feature = "jsc")]
557 Self::Jsc(f) => VMFuncRef::Jsc(f.vm_funcref(store)),
558 }
559 }
560
561 #[inline]
562 pub(crate) unsafe fn from_vm_funcref(store: &mut impl AsStoreMut, funcref: VMFuncRef) -> Self {
563 match &store.as_store_mut().inner.store {
564 #[cfg(feature = "sys")]
565 crate::BackendStore::Sys(s) => Self::Sys(unsafe {
566 crate::backend::sys::entities::function::Function::from_vm_funcref(
567 store,
568 funcref.into_sys(),
569 )
570 }),
571 #[cfg(feature = "wamr")]
572 crate::BackendStore::Wamr(s) => Self::Wamr(unsafe {
573 crate::backend::wamr::entities::function::Function::from_vm_funcref(
574 store,
575 funcref.into_wamr(),
576 )
577 }),
578 #[cfg(feature = "wasmi")]
579 crate::BackendStore::Wasmi(s) => Self::Wasmi(unsafe {
580 crate::backend::wasmi::entities::function::Function::from_vm_funcref(
581 store,
582 funcref.into_wasmi(),
583 )
584 }),
585 #[cfg(feature = "v8")]
586 crate::BackendStore::V8(s) => Self::V8(unsafe {
587 crate::backend::v8::entities::function::Function::from_vm_funcref(
588 store,
589 funcref.into_v8(),
590 )
591 }),
592 #[cfg(feature = "js")]
593 crate::BackendStore::Js(s) => Self::Js(unsafe {
594 crate::backend::js::entities::function::Function::from_vm_funcref(
595 store,
596 funcref.into_js(),
597 )
598 }),
599 #[cfg(feature = "jsc")]
600 crate::BackendStore::Jsc(s) => Self::Jsc(unsafe {
601 crate::backend::jsc::entities::function::Function::from_vm_funcref(
602 store,
603 funcref.into_jsc(),
604 )
605 }),
606 }
607 }
608
609 #[inline]
691 pub fn typed<Args, Rets>(
692 &self,
693 store: &impl AsStoreRef,
694 ) -> Result<TypedFunction<Args, Rets>, RuntimeError>
695 where
696 Args: WasmTypeList,
697 Rets: WasmTypeList,
698 {
699 let ty = self.ty(store);
700
701 {
703 let expected = ty.params();
704 let given = Args::wasm_types();
705
706 if expected != given {
707 return Err(RuntimeError::new(format!(
708 "given types (`{given:?}`) for the function arguments don't match the actual types (`{expected:?}`)",
709 )));
710 }
711 }
712
713 {
714 let expected = ty.results();
715 let given = Rets::wasm_types();
716
717 if expected != given {
718 return Err(RuntimeError::new(format!(
720 "given types (`{given:?}`) for the function results don't match the actual types (`{expected:?}`)",
721 )));
722 }
723 }
724
725 Ok(TypedFunction::new(store, super::Function(self.clone())))
726 }
727
728 pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExternFunction) -> Self {
729 match &store.as_store_mut().inner.store {
730 #[cfg(feature = "sys")]
731 crate::BackendStore::Sys(_) => Self::Sys(
732 crate::backend::sys::entities::function::Function::from_vm_extern(store, vm_extern),
733 ),
734 #[cfg(feature = "wamr")]
735 crate::BackendStore::Wamr(_) => Self::Wamr(
736 crate::backend::wamr::entities::function::Function::from_vm_extern(
737 store, vm_extern,
738 ),
739 ),
740 #[cfg(feature = "wasmi")]
741 crate::BackendStore::Wasmi(_) => Self::Wasmi(
742 crate::backend::wasmi::entities::function::Function::from_vm_extern(
743 store, vm_extern,
744 ),
745 ),
746 #[cfg(feature = "v8")]
747 crate::BackendStore::V8(_) => Self::V8(
748 crate::backend::v8::entities::function::Function::from_vm_extern(store, vm_extern),
749 ),
750 #[cfg(feature = "js")]
751 crate::BackendStore::Js(_) => Self::Js(
752 crate::backend::js::entities::function::Function::from_vm_extern(store, vm_extern),
753 ),
754 #[cfg(feature = "jsc")]
755 crate::BackendStore::Jsc(_) => Self::Jsc(
756 crate::backend::jsc::entities::function::Function::from_vm_extern(store, vm_extern),
757 ),
758 }
759 }
760
761 #[inline]
763 pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool {
764 match_rt!(on self => f {
765 f.is_from_store(store)
766 })
767 }
768
769 #[inline]
770 pub(crate) fn to_vm_extern(&self) -> VMExtern {
771 match_rt!(on self => f {
772 f.to_vm_extern()
773 })
774 }
775}
776
777#[cold]
778fn unsupported_async_backend(backend: &str) -> ! {
779 panic!(
780 "async host functions are only supported with the `sys` backend (attempted on {backend})"
781 )
782}
783
784#[allow(clippy::type_complexity)]
785pub(super) fn unsupported_async_future<'a>()
786-> Pin<Box<dyn Future<Output = Result<Box<[Value]>, RuntimeError>> + 'a>> {
787 Box::pin(async {
788 Err(RuntimeError::new(
789 "async calls are only supported with the `sys` backend",
790 ))
791 })
792}
793
794impl<'a> Exportable<'a> for BackendFunction {
795 fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> {
796 match _extern {
797 Extern::Function(func) => Ok(&func.0),
798 _ => Err(ExportError::IncompatibleType),
799 }
800 }
801}