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