Skip to main content

wasmer/backend/sys/entities/function/
mod.rs

1//! Data types, functions and traits for `sys` runtime's `Function` implementation.
2
3pub(crate) mod env;
4pub(crate) mod typed;
5
6#[cfg(feature = "experimental-async")]
7use crate::{
8    AsStoreAsync, AsyncFunctionEnvMut, BackendAsyncFunctionEnvMut, StoreAsync,
9    entities::function::async_host::{AsyncFunctionEnv, AsyncHostFunction},
10    sys::{
11        async_runtime::{AsyncRuntimeError, block_on_host_future, call_function_async},
12        function::env::AsyncFunctionEnvMutStore,
13    },
14};
15use crate::{
16    BackendFunction, FunctionEnv, FunctionEnvMut, FunctionType, HostFunction, RuntimeError,
17    StoreContext, StoreInner, Value, WithEnv, WithoutEnv,
18    backend::sys::{engine::NativeEngineExt, vm::VMFunctionCallback},
19    entities::store::{AsStoreMut, AsStoreRef, StoreMut},
20    utils::{FromToNativeWasmType, IntoResult, NativeWasmTypeInto, WasmTypeList},
21    vm::{VMExtern, VMExternFunction},
22};
23use std::panic::{self, AssertUnwindSafe};
24use std::{
25    cell::UnsafeCell, cmp::max, error::Error, ffi::c_void, future::Future, marker::PhantomData,
26    pin::Pin, sync::Arc,
27};
28use wasmer_types::{NativeWasmType, RawValue, StoreId};
29use wasmer_vm::{
30    MaybeInstanceOwned, StoreHandle, Trap, TrapCode, VMCallerCheckedAnyfunc, VMContext,
31    VMDynamicFunctionContext, VMFuncRef, VMFunction, VMFunctionBody, VMFunctionContext,
32    VMFunctionKind, VMTrampoline, on_host_stack, raise_lib_trap, raise_user_trap, resume_panic,
33    wasmer_call_trampoline,
34};
35
36#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
37#[derive(Debug, Clone, PartialEq, Eq)]
38/// A WebAssembly `function` instance, in the `sys` runtime.
39pub struct Function {
40    pub(crate) handle: StoreHandle<VMFunction>,
41}
42
43impl From<StoreHandle<VMFunction>> for Function {
44    fn from(handle: StoreHandle<VMFunction>) -> Self {
45        Self { handle }
46    }
47}
48
49impl Function {
50    pub(crate) fn new_with_env<FT, F, T: Send + 'static>(
51        store: &mut impl AsStoreMut,
52        env: &FunctionEnv<T>,
53        ty: FT,
54        func: F,
55    ) -> Self
56    where
57        FT: Into<FunctionType>,
58        F: Fn(FunctionEnvMut<T>, &[Value]) -> Result<Vec<Value>, RuntimeError>
59            + 'static
60            + Send
61            + Sync,
62    {
63        let function_type = ty.into();
64        let func_ty = function_type.clone();
65        let func_env = env.clone().into_sys();
66        let store_id = store.objects_mut().id();
67        let wrapper = move |values_vec: *mut RawValue| -> HostCallOutcome {
68            unsafe {
69                let mut store_wrapper = unsafe { StoreContext::get_current(store_id) };
70                let mut store_mut = store_wrapper.as_mut();
71                let mut args = Vec::with_capacity(func_ty.params().len());
72
73                for (i, ty) in func_ty.params().iter().enumerate() {
74                    args.push(Value::from_raw(
75                        &mut store_mut,
76                        *ty,
77                        values_vec.add(i).read_unaligned(),
78                    ));
79                }
80                let env = env::FunctionEnvMut {
81                    store_mut,
82                    func_env: func_env.clone(),
83                }
84                .into();
85                let sig = func_ty.clone();
86                let result = func(env, &args);
87                HostCallOutcome::Ready {
88                    func_ty: sig,
89                    result,
90                }
91            }
92        };
93        let mut host_data = Box::new(VMDynamicFunctionContext {
94            address: std::ptr::null(),
95            ctx: DynamicFunction {
96                func: wrapper,
97                store_id,
98            },
99        });
100        host_data.address = host_data.ctx.func_body_ptr();
101
102        // We don't yet have the address with the Wasm ABI signature.
103        // The engine linker will replace the address with one pointing to a
104        // generated dynamic trampoline.
105        let func_ptr = std::ptr::null() as VMFunctionCallback;
106        let type_index = store
107            .as_store_ref()
108            .engine()
109            .as_sys()
110            .register_signature(&function_type);
111        let vmctx = VMFunctionContext {
112            host_env: host_data.as_ref() as *const _ as *mut c_void,
113        };
114        let call_trampoline = host_data.ctx.call_trampoline_address();
115        let anyfunc = VMCallerCheckedAnyfunc {
116            func_ptr,
117            type_index,
118            vmctx,
119            call_trampoline,
120        };
121
122        let vm_function = VMFunction {
123            anyfunc: MaybeInstanceOwned::Host(Box::new(UnsafeCell::new(anyfunc))),
124            kind: VMFunctionKind::Dynamic,
125            signature: function_type,
126            host_data,
127        };
128        Self {
129            handle: StoreHandle::new(store.objects_mut().as_sys_mut(), vm_function),
130        }
131    }
132
133    #[cfg(feature = "experimental-async")]
134    pub(crate) fn new_async<FT, F, Fut>(store: &mut impl AsStoreMut, ty: FT, func: F) -> Self
135    where
136        FT: Into<FunctionType>,
137        F: Fn(&[Value]) -> Fut + 'static,
138        Fut: Future<Output = Result<Vec<Value>, RuntimeError>> + 'static,
139    {
140        let env = FunctionEnv::new(store, ());
141        let wrapped = move |_env: AsyncFunctionEnvMut<()>, values: &[Value]| func(values);
142        Self::new_with_env_async(store, &env, ty, wrapped)
143    }
144
145    #[cfg(feature = "experimental-async")]
146    pub(crate) fn new_with_env_async<FT, F, Fut, T: 'static>(
147        store: &mut impl AsStoreMut,
148        env: &FunctionEnv<T>,
149        ty: FT,
150        func: F,
151    ) -> Self
152    where
153        FT: Into<FunctionType>,
154        F: Fn(AsyncFunctionEnvMut<T>, &[Value]) -> Fut + 'static,
155        Fut: Future<Output = Result<Vec<Value>, RuntimeError>> + 'static,
156    {
157        let function_type = ty.into();
158        let func_ty = function_type.clone();
159        let func_env = env.clone().into_sys();
160        let store_id = store.objects_mut().id();
161        let wrapper = move |values_vec: *mut RawValue| -> HostCallOutcome {
162            unsafe {
163                let mut context = StoreContext::try_get_current_async(store_id);
164                let mut store_mut = match &mut context {
165                    crate::GetStoreAsyncGuardResult::Ok(wrapper) => StoreMut {
166                        inner: wrapper.guard.as_mut().unwrap(),
167                    },
168                    crate::GetStoreAsyncGuardResult::NotAsync(ptr) => ptr.as_mut(),
169                    crate::GetStoreAsyncGuardResult::NotInstalled => {
170                        panic!("No store context installed on this thread")
171                    }
172                };
173                let id = store_mut.as_store_ref().objects().id();
174                let mut args = Vec::with_capacity(func_ty.params().len());
175
176                for (i, ty) in func_ty.params().iter().enumerate() {
177                    args.push(Value::from_raw(
178                        &mut store_mut,
179                        *ty,
180                        values_vec.add(i).read_unaligned(),
181                    ));
182                }
183                let store_async = match context {
184                    crate::GetStoreAsyncGuardResult::Ok(wrapper) => {
185                        AsyncFunctionEnvMutStore::Async(StoreAsync {
186                            id,
187                            inner: crate::LocalRwLockWriteGuard::lock_handle(
188                                wrapper.guard.as_mut().unwrap(),
189                            ),
190                        })
191                    }
192                    crate::GetStoreAsyncGuardResult::NotAsync(ptr) => {
193                        AsyncFunctionEnvMutStore::Sync(ptr)
194                    }
195                    crate::GetStoreAsyncGuardResult::NotInstalled => unreachable!(),
196                };
197                let env = crate::AsyncFunctionEnvMut(crate::BackendAsyncFunctionEnvMut::Sys(
198                    env::AsyncFunctionEnvMut {
199                        store: store_async,
200                        func_env: func_env.clone(),
201                    },
202                ));
203                let sig = func_ty.clone();
204                let future = func(env, &args);
205                HostCallOutcome::Future {
206                    func_ty: sig,
207                    future: Box::pin(future),
208                }
209            }
210        };
211        let mut host_data = Box::new(VMDynamicFunctionContext {
212            address: std::ptr::null(),
213            ctx: DynamicFunction {
214                func: wrapper,
215                store_id,
216            },
217        });
218        host_data.address = host_data.ctx.func_body_ptr();
219
220        let func_ptr = std::ptr::null() as VMFunctionCallback;
221        let type_index = store
222            .as_store_ref()
223            .engine()
224            .as_sys()
225            .register_signature(&function_type);
226        let vmctx = VMFunctionContext {
227            host_env: host_data.as_ref() as *const _ as *mut c_void,
228        };
229        let call_trampoline = host_data.ctx.call_trampoline_address();
230        let anyfunc = VMCallerCheckedAnyfunc {
231            func_ptr,
232            type_index,
233            vmctx,
234            call_trampoline,
235        };
236
237        let vm_function = VMFunction {
238            anyfunc: MaybeInstanceOwned::Host(Box::new(UnsafeCell::new(anyfunc))),
239            kind: VMFunctionKind::Dynamic,
240            signature: function_type,
241            host_data,
242        };
243        Self {
244            handle: StoreHandle::new(store.objects_mut().as_sys_mut(), vm_function),
245        }
246    }
247
248    /// Creates a new host `Function` from a native function.
249    pub(crate) fn new_typed<F, Args, Rets>(store: &mut impl AsStoreMut, func: F) -> Self
250    where
251        F: HostFunction<(), Args, Rets, WithoutEnv> + 'static + Send + Sync,
252        Args: WasmTypeList,
253        Rets: WasmTypeList,
254    {
255        let env = FunctionEnv::new(store, ());
256        let func_ptr = func.function_callback_sys().into_sys();
257        let host_data = Box::new(StaticFunction {
258            store_id: store.objects_mut().id(),
259            env,
260            func,
261        });
262        let function_type = FunctionType::new(Args::wasm_types(), Rets::wasm_types());
263
264        let type_index = store
265            .as_store_ref()
266            .engine()
267            .as_sys()
268            .register_signature(&function_type);
269        let vmctx = VMFunctionContext {
270            host_env: host_data.as_ref() as *const _ as *mut c_void,
271        };
272        let call_trampoline =
273            <F as HostFunction<(), Args, Rets, WithoutEnv>>::call_trampoline_address().into_sys();
274        let anyfunc = VMCallerCheckedAnyfunc {
275            func_ptr,
276            type_index,
277            vmctx,
278            call_trampoline,
279        };
280
281        let vm_function = VMFunction {
282            anyfunc: MaybeInstanceOwned::Host(Box::new(UnsafeCell::new(anyfunc))),
283            kind: VMFunctionKind::Static,
284            signature: function_type,
285            host_data,
286        };
287        Self {
288            handle: StoreHandle::new(store.objects_mut().as_sys_mut(), vm_function),
289        }
290    }
291
292    #[cfg(feature = "experimental-async")]
293    pub(crate) fn new_typed_async<F, Args, Rets>(store: &mut impl AsStoreMut, func: F) -> Self
294    where
295        Args: WasmTypeList + 'static,
296        Rets: WasmTypeList + 'static,
297        F: AsyncHostFunction<(), Args, Rets, WithoutEnv> + 'static,
298    {
299        let env = FunctionEnv::new(store, ());
300        let signature = FunctionType::new(Args::wasm_types(), Rets::wasm_types());
301        let args_sig = Arc::new(signature.clone());
302        let results_sig = Arc::new(signature.clone());
303        let func = Arc::new(func);
304        Self::new_with_env_async(
305            store,
306            &env,
307            signature,
308            move |mut env_mut,
309                  values|
310                  -> Pin<Box<dyn Future<Output = Result<Vec<Value>, RuntimeError>>>> {
311                let sys_env = match env_mut.0 {
312                    BackendAsyncFunctionEnvMut::Sys(ref mut sys_env) => sys_env,
313                    _ => panic!("Not a sys backend"),
314                };
315                let mut store_mut_wrapper =
316                    unsafe { StoreContext::get_current(sys_env.store_id()) };
317                let mut store_mut = store_mut_wrapper.as_mut();
318                let args_sig = args_sig.clone();
319                let results_sig = results_sig.clone();
320                let func = func.clone();
321                let args =
322                    match typed_args_from_values::<Args>(&mut store_mut, args_sig.as_ref(), values)
323                    {
324                        Ok(args) => args,
325                        Err(err) => return Box::pin(async { Err(err) }),
326                    };
327                drop(store_mut_wrapper);
328                let future = func.as_ref().call_async(AsyncFunctionEnv::new(), args);
329                Box::pin(async move {
330                    let typed_result = future.await?;
331                    let mut store_mut = env_mut.write().await;
332                    typed_results_to_values::<Rets>(
333                        &mut store_mut.as_store_mut(),
334                        results_sig.as_ref(),
335                        typed_result,
336                    )
337                })
338            },
339        )
340    }
341
342    #[cfg(feature = "experimental-async")]
343    pub(crate) fn new_typed_with_env_async<T, F, Args, Rets>(
344        store: &mut impl AsStoreMut,
345        env: &FunctionEnv<T>,
346        func: F,
347    ) -> Self
348    where
349        T: 'static,
350        F: AsyncHostFunction<T, Args, Rets, WithEnv> + 'static,
351        Args: WasmTypeList + 'static,
352        Rets: WasmTypeList + 'static,
353    {
354        let signature = FunctionType::new(Args::wasm_types(), Rets::wasm_types());
355        let args_sig = Arc::new(signature.clone());
356        let results_sig = Arc::new(signature.clone());
357        let func = Arc::new(func);
358        Self::new_with_env_async(
359            store,
360            env,
361            signature,
362            move |mut env_mut,
363                  values|
364                  -> Pin<Box<dyn Future<Output = Result<Vec<Value>, RuntimeError>>>> {
365                let sys_env = match env_mut.0 {
366                    BackendAsyncFunctionEnvMut::Sys(ref mut sys_env) => sys_env,
367                    _ => panic!("Not a sys backend"),
368                };
369                let mut store_mut_wrapper =
370                    unsafe { StoreContext::get_current(sys_env.store_id()) };
371                let mut store_mut = store_mut_wrapper.as_mut();
372                let args_sig = args_sig.clone();
373                let results_sig = results_sig.clone();
374                let func = func.clone();
375                let args =
376                    match typed_args_from_values::<Args>(&mut store_mut, args_sig.as_ref(), values)
377                    {
378                        Ok(args) => args,
379                        Err(err) => return Box::pin(async { Err(err) }),
380                    };
381                drop(store_mut_wrapper);
382                let env_mut_clone = env_mut.as_mut();
383                let future = func
384                    .as_ref()
385                    .call_async(AsyncFunctionEnv::with_env(env_mut), args);
386                Box::pin(async move {
387                    let typed_result = future.await?;
388                    let mut store_mut = env_mut_clone.write().await;
389                    typed_results_to_values::<Rets>(
390                        &mut store_mut.as_store_mut(),
391                        results_sig.as_ref(),
392                        typed_result,
393                    )
394                })
395            },
396        )
397    }
398
399    pub(crate) fn new_typed_with_env<T: Send + 'static, F, Args, Rets>(
400        store: &mut impl AsStoreMut,
401        env: &FunctionEnv<T>,
402        func: F,
403    ) -> Self
404    where
405        F: HostFunction<T, Args, Rets, WithEnv> + 'static + Send + Sync,
406        Args: WasmTypeList,
407        Rets: WasmTypeList,
408    {
409        let func_ptr = func.function_callback_sys().into_sys();
410        let host_data = Box::new(StaticFunction {
411            store_id: store.objects_mut().id(),
412            env: env.as_sys().clone().into(),
413            func,
414        });
415        let function_type = FunctionType::new(Args::wasm_types(), Rets::wasm_types());
416
417        let type_index = store
418            .as_store_ref()
419            .engine()
420            .as_sys()
421            .register_signature(&function_type);
422        let vmctx = VMFunctionContext {
423            host_env: host_data.as_ref() as *const _ as *mut c_void,
424        };
425        let call_trampoline =
426            <F as HostFunction<T, Args, Rets, WithEnv>>::call_trampoline_address().into_sys();
427        let anyfunc = VMCallerCheckedAnyfunc {
428            func_ptr,
429            type_index,
430            vmctx,
431            call_trampoline,
432        };
433
434        let vm_function = VMFunction {
435            anyfunc: MaybeInstanceOwned::Host(Box::new(UnsafeCell::new(anyfunc))),
436            kind: VMFunctionKind::Static,
437            signature: function_type,
438            host_data,
439        };
440        Self {
441            handle: StoreHandle::new(store.objects_mut().as_sys_mut(), vm_function),
442        }
443    }
444
445    pub(crate) fn ty(&self, store: &impl AsStoreRef) -> FunctionType {
446        self.handle
447            .get(store.as_store_ref().objects().as_sys())
448            .signature
449            .clone()
450    }
451
452    fn call_wasm(
453        &self,
454        store: &mut impl AsStoreMut,
455        trampoline: VMTrampoline,
456        params: &[Value],
457        results: &mut [Value],
458    ) -> Result<(), RuntimeError> {
459        let format_types_for_error_message = |items: &[Value]| {
460            items
461                .iter()
462                .map(|param| param.ty().to_string())
463                .collect::<Vec<String>>()
464                .join(", ")
465        };
466        // TODO: Avoid cloning the signature here, it's expensive.
467        let signature = self.ty(store);
468        if signature.params().len() != params.len() {
469            return Err(RuntimeError::new(format!(
470                "Parameters of type [{}] did not match signature {}",
471                format_types_for_error_message(params),
472                &signature
473            )));
474        }
475        if signature.results().len() != results.len() {
476            return Err(RuntimeError::new(format!(
477                "Results of type [{}] did not match signature {}",
478                format_types_for_error_message(results),
479                &signature,
480            )));
481        }
482
483        let mut values_vec = vec![RawValue { i32: 0 }; max(params.len(), results.len())];
484
485        // Store the argument values into `values_vec`.
486        let param_tys = signature.params().iter();
487        for ((arg, slot), ty) in params.iter().zip(&mut values_vec).zip(param_tys) {
488            if arg.ty() != *ty {
489                let param_types = format_types_for_error_message(params);
490                return Err(RuntimeError::new(format!(
491                    "Parameters of type [{}] did not match signature {}",
492                    param_types, &signature,
493                )));
494            }
495            if !arg.is_from_store(store) {
496                return Err(RuntimeError::new("cross-`Store` values are not supported"));
497            }
498            *slot = arg.as_raw(store);
499        }
500
501        // Invoke the call
502        self.call_wasm_raw(store, trampoline, values_vec, results)?;
503        Ok(())
504    }
505
506    fn call_wasm_raw(
507        &self,
508        store: &mut impl AsStoreMut,
509        trampoline: VMTrampoline,
510        mut params: Vec<RawValue>,
511        results: &mut [Value],
512    ) -> Result<(), RuntimeError> {
513        // Call the trampoline.
514        let result = {
515            let store_id = store.objects_mut().id();
516            // Safety: the store context is uninstalled before we return, and the
517            // store mut is valid for the duration of the call.
518            let store_install_guard =
519                unsafe { StoreContext::ensure_installed(store.as_store_mut().inner as *mut _) };
520
521            let mut r;
522            // TODO: This loop is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451
523            loop {
524                let storeref = store.as_store_ref();
525                let vm_function = self.handle.get(storeref.objects().as_sys());
526                let config = storeref.engine().tunables().vmconfig();
527                let signal_handler = storeref.signal_handler();
528                r = unsafe {
529                    // Safety: This is the intended use-case for StoreContext::pause, as
530                    // documented in the function's doc comments.
531                    let pause_guard = StoreContext::pause(store_id);
532                    wasmer_call_trampoline(
533                        signal_handler,
534                        config,
535                        vm_function.anyfunc.as_ptr().as_ref().vmctx,
536                        trampoline,
537                        vm_function.anyfunc.as_ptr().as_ref().func_ptr,
538                        params.as_mut_ptr() as *mut u8,
539                    )
540                };
541                let store_mut = store.as_store_mut();
542                if let Some(callback) = store_mut.inner.on_called.take() {
543                    match callback(store_mut) {
544                        Ok(wasmer_types::OnCalledAction::InvokeAgain) => {
545                            continue;
546                        }
547                        Ok(wasmer_types::OnCalledAction::Finish) => {
548                            break;
549                        }
550                        Ok(wasmer_types::OnCalledAction::Trap(trap)) => {
551                            return Err(RuntimeError::user(trap));
552                        }
553                        Err(trap) => return Err(RuntimeError::user(trap)),
554                    }
555                }
556                break;
557            }
558
559            drop(store_install_guard);
560
561            r
562        };
563
564        if let Err(error) = result {
565            return Err(error.into());
566        }
567
568        // Load the return values out of `values_vec`.
569        let signature = self.ty(store);
570        for (index, &value_type) in signature.results().iter().enumerate() {
571            unsafe {
572                results[index] = Value::from_raw(store, value_type, params[index]);
573            }
574        }
575
576        Ok(())
577    }
578
579    pub(crate) fn result_arity(&self, store: &impl AsStoreRef) -> usize {
580        self.ty(store).results().len()
581    }
582
583    pub(crate) fn call(
584        &self,
585        store: &mut impl AsStoreMut,
586        params: &[Value],
587    ) -> Result<Box<[Value]>, RuntimeError> {
588        let trampoline = unsafe {
589            self.handle
590                .get(store.objects_mut().as_sys())
591                .anyfunc
592                .as_ptr()
593                .as_ref()
594                .call_trampoline
595        };
596        let mut results = vec![Value::null(); self.result_arity(store)];
597        self.call_wasm(store, trampoline, params, &mut results)?;
598        Ok(results.into_boxed_slice())
599    }
600
601    #[cfg(feature = "experimental-async")]
602    #[allow(clippy::type_complexity)]
603    pub(crate) fn call_async(
604        &self,
605        store: &impl AsStoreAsync,
606        params: Vec<Value>,
607    ) -> Pin<Box<dyn Future<Output = Result<Box<[Value]>, RuntimeError>> + 'static>> {
608        let function = self.clone();
609        let store = store.store();
610        Box::pin(call_function_async(function, store, params))
611    }
612
613    #[doc(hidden)]
614    #[allow(missing_docs)]
615    pub(crate) fn call_raw(
616        &self,
617        store: &mut impl AsStoreMut,
618        params: Vec<RawValue>,
619    ) -> Result<Box<[Value]>, RuntimeError> {
620        let trampoline = unsafe {
621            self.handle
622                .get(store.objects_mut().as_sys())
623                .anyfunc
624                .as_ptr()
625                .as_ref()
626                .call_trampoline
627        };
628        let mut results = vec![Value::null(); self.result_arity(store)];
629        self.call_wasm_raw(store, trampoline, params, &mut results)?;
630        Ok(results.into_boxed_slice())
631    }
632
633    pub(crate) fn vm_funcref(&self, store: &impl AsStoreRef) -> VMFuncRef {
634        let vm_function = self.handle.get(store.as_store_ref().objects().as_sys());
635        if vm_function.kind == VMFunctionKind::Dynamic {
636            panic!("dynamic functions cannot be used in tables or as funcrefs");
637        }
638        VMFuncRef(vm_function.anyfunc.as_ptr())
639    }
640
641    pub(crate) unsafe fn from_vm_funcref(store: &mut impl AsStoreMut, funcref: VMFuncRef) -> Self {
642        let signature = {
643            let anyfunc = unsafe { funcref.0.as_ref() };
644            store
645                .as_store_mut()
646                .engine()
647                .as_sys()
648                .lookup_signature(anyfunc.type_index)
649                .expect("Signature not found in store")
650        };
651        let vm_function = VMFunction {
652            anyfunc: MaybeInstanceOwned::Instance(funcref.0),
653            signature,
654            // All functions in tables are already Static (as dynamic functions
655            // are converted to use the trampolines with static signatures).
656            kind: wasmer_vm::VMFunctionKind::Static,
657            host_data: Box::new(()),
658        };
659        Self {
660            handle: StoreHandle::new(store.objects_mut().as_sys_mut(), vm_function),
661        }
662    }
663
664    pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExternFunction) -> Self {
665        Self {
666            handle: unsafe {
667                StoreHandle::from_internal(store.objects_mut().id(), vm_extern.into_sys())
668            },
669        }
670    }
671
672    /// Checks whether this `Function` can be used with the given store.
673    pub(crate) fn is_from_store(&self, store: &impl AsStoreRef) -> bool {
674        self.handle.store_id() == store.as_store_ref().objects().id()
675    }
676
677    pub(crate) fn to_vm_extern(&self) -> VMExtern {
678        VMExtern::Sys(wasmer_vm::VMExtern::Function(self.handle.internal_handle()))
679    }
680}
681
682// We want to keep as much logic as possible on the host stack,
683// since the WASM stack may be out of memory. In that scenario,
684// throwing exceptions won't work since libunwind requires
685// considerable stack space to do its magic, but everything else
686// should work.
687enum InvocationResult<T, E> {
688    Success(T),
689    Exception(crate::Exception),
690    Trap(Box<E>),
691    YieldOutsideAsyncContext,
692}
693
694fn to_invocation_result<T, E>(result: Result<T, E>) -> InvocationResult<T, E>
695where
696    E: Error + 'static,
697{
698    match result {
699        Ok(value) => InvocationResult::Success(value),
700        Err(trap) => {
701            let dyn_err_ref = &trap as &dyn Error;
702            if let Some(runtime_error) = dyn_err_ref.downcast_ref::<RuntimeError>()
703                && let Some(exception) = runtime_error.to_exception()
704            {
705                return InvocationResult::Exception(exception);
706            }
707            InvocationResult::Trap(Box::new(trap))
708        }
709    }
710}
711
712fn write_dynamic_results(
713    store_id: StoreId,
714    func_ty: &FunctionType,
715    returns: Vec<Value>,
716    values_vec: *mut RawValue,
717) -> Result<(), RuntimeError> {
718    let mut store_wrapper = unsafe { StoreContext::get_current(store_id) };
719    let mut store = store_wrapper.as_mut();
720    let return_types = returns.iter().map(|ret| ret.ty());
721    if return_types.ne(func_ty.results().iter().copied()) {
722        return Err(RuntimeError::new(format!(
723            "Dynamic function returned wrong signature. Expected {:?} but got {:?}",
724            func_ty.results(),
725            returns.iter().map(|ret| ret.ty())
726        )));
727    }
728    for (i, ret) in returns.iter().enumerate() {
729        unsafe {
730            values_vec.add(i).write_unaligned(ret.as_raw(&store));
731        }
732    }
733    Ok(())
734}
735
736fn finalize_dynamic_call(
737    store_id: StoreId,
738    func_ty: FunctionType,
739    values_vec: *mut RawValue,
740    result: Result<Vec<Value>, RuntimeError>,
741) -> Result<(), RuntimeError> {
742    match result {
743        Ok(values) => write_dynamic_results(store_id, &func_ty, values, values_vec),
744        Err(err) => Err(err),
745    }
746}
747
748fn typed_args_from_values<Args>(
749    store: &mut StoreMut,
750    func_ty: &FunctionType,
751    values: &[Value],
752) -> Result<Args, RuntimeError>
753where
754    Args: WasmTypeList,
755{
756    if values.len() != func_ty.params().len() {
757        return Err(RuntimeError::new(
758            "typed host function received wrong number of parameters",
759        ));
760    }
761    let mut raw_array = Args::empty_array();
762    for ((slot, value), expected_ty) in raw_array
763        .as_mut()
764        .iter_mut()
765        .zip(values.iter())
766        .zip(func_ty.params().iter())
767    {
768        debug_assert_eq!(
769            value.ty(),
770            *expected_ty,
771            "wasm should only call host functions with matching signatures"
772        );
773        *slot = value.as_raw(store);
774    }
775    unsafe { Ok(Args::from_array(store, raw_array)) }
776}
777
778fn typed_results_to_values<Rets>(
779    store: &mut StoreMut,
780    func_ty: &FunctionType,
781    rets: Rets,
782) -> Result<Vec<Value>, RuntimeError>
783where
784    Rets: WasmTypeList,
785{
786    let mut raw_array = unsafe { rets.into_array(store) };
787    let mut values = Vec::with_capacity(func_ty.results().len());
788    for (raw, ty) in raw_array.as_mut().iter().zip(func_ty.results().iter()) {
789        unsafe {
790            values.push(Value::from_raw(store, *ty, *raw));
791        }
792    }
793    Ok(values)
794}
795
796pub(crate) enum HostCallOutcome {
797    Ready {
798        func_ty: FunctionType,
799        result: Result<Vec<Value>, RuntimeError>,
800    },
801    #[cfg(feature = "experimental-async")]
802    Future {
803        func_ty: FunctionType,
804        future: Pin<Box<dyn Future<Output = Result<Vec<Value>, RuntimeError>>>>,
805    },
806}
807
808/// Host state for a dynamic function.
809pub(crate) struct DynamicFunction<F> {
810    func: F,
811    store_id: StoreId,
812}
813
814impl<F> DynamicFunction<F>
815where
816    F: Fn(*mut RawValue) -> HostCallOutcome + 'static,
817{
818    // This function wraps our func, to make it compatible with the
819    // reverse trampoline signature
820    unsafe extern "C-unwind" fn func_wrapper(
821        this: &mut VMDynamicFunctionContext<Self>,
822        values_vec: *mut RawValue,
823    ) {
824        let result = on_host_stack(|| {
825            panic::catch_unwind(AssertUnwindSafe(|| match (this.ctx.func)(values_vec) {
826                HostCallOutcome::Ready { func_ty, result } => to_invocation_result(
827                    finalize_dynamic_call(this.ctx.store_id, func_ty, values_vec, result),
828                ),
829                #[cfg(feature = "experimental-async")]
830                HostCallOutcome::Future { func_ty, future } => {
831                    let awaited = block_on_host_future(future);
832                    let result = match awaited {
833                        Ok(value) => Ok(value),
834                        Err(AsyncRuntimeError::RuntimeError(e)) => Err(e),
835                        Err(AsyncRuntimeError::YieldOutsideAsyncContext) => {
836                            return InvocationResult::YieldOutsideAsyncContext;
837                        }
838                    };
839                    to_invocation_result(finalize_dynamic_call(
840                        this.ctx.store_id,
841                        func_ty,
842                        values_vec,
843                        result,
844                    ))
845                }
846            }))
847        });
848
849        // IMPORTANT: DO NOT ALLOCATE ON THE STACK,
850        // AS WE ARE IN THE WASM STACK, NOT ON THE HOST ONE.
851        // See: https://github.com/wasmerio/wasmer/pull/5700
852        match result {
853            Ok(InvocationResult::Success(())) => {}
854            Ok(InvocationResult::Exception(exception)) => unsafe {
855                // Note: can't acquire a proper ref-counted context ref here, since we can switch
856                // away from the WASM stack at any time.
857                // Safety: The pointer is only used for the duration of the call to `throw`.
858                let mut store_wrapper = StoreContext::get_current_transient(this.ctx.store_id);
859                let mut store = store_wrapper.as_mut().unwrap();
860                wasmer_vm::libcalls::throw(
861                    store.objects.as_sys(),
862                    exception.vm_exceptionref().as_sys().to_u32_exnref(),
863                )
864            },
865            Ok(InvocationResult::Trap(trap)) => unsafe { raise_user_trap(trap) },
866            Ok(InvocationResult::YieldOutsideAsyncContext) => unsafe {
867                raise_lib_trap(Trap::lib(TrapCode::YieldOutsideAsyncContext))
868            },
869            Err(panic) => unsafe { resume_panic(panic) },
870        }
871    }
872
873    fn func_body_ptr(&self) -> VMFunctionCallback {
874        Self::func_wrapper as VMFunctionCallback
875    }
876
877    fn call_trampoline_address(&self) -> VMTrampoline {
878        Self::call_trampoline
879    }
880
881    unsafe extern "C" fn call_trampoline(
882        vmctx: *mut VMContext,
883        _body: VMFunctionCallback,
884        args: *mut RawValue,
885    ) {
886        // The VMFunctionCallback is null here: it is only filled in later
887        // by the engine linker.
888        unsafe {
889            let dynamic_function = &mut *(vmctx as *mut VMDynamicFunctionContext<Self>);
890            Self::func_wrapper(dynamic_function, args);
891        }
892    }
893}
894
895/// Represents a low-level Wasm static host function. See
896/// [`crate::Function::new_typed`] and
897/// [`crate::Function::new_typed_with_env`] to learn more.
898pub(crate) struct StaticFunction<F, T> {
899    pub(crate) store_id: StoreId,
900    pub(crate) env: FunctionEnv<T>,
901    pub(crate) func: F,
902}
903
904impl crate::Function {
905    /// Consume [`self`] into [`crate::backend::sys::function::Function`].
906    pub fn into_sys(self) -> crate::backend::sys::function::Function {
907        match self.0 {
908            BackendFunction::Sys(s) => s,
909            _ => panic!("Not a `sys` function!"),
910        }
911    }
912
913    /// Convert a reference to [`self`] into a reference to [`crate::backend::sys::function::Function`].
914    pub fn as_sys(&self) -> &crate::backend::sys::function::Function {
915        match self.0 {
916            BackendFunction::Sys(ref s) => s,
917            _ => panic!("Not a `sys` function!"),
918        }
919    }
920
921    /// Convert a mutable reference to [`self`] into a mutable reference [`crate::backend::sys::function::Function`].
922    pub fn as_sys_mut(&mut self) -> &mut crate::backend::sys::function::Function {
923        match self.0 {
924            BackendFunction::Sys(ref mut s) => s,
925            _ => panic!("Not a `sys` function!"),
926        }
927    }
928}
929
930macro_rules! impl_host_function {
931    ([$c_struct_representation:ident] $c_struct_name:ident, $( $x:ident ),* ) => {
932        paste::paste! {
933        #[allow(non_snake_case)]
934        pub(crate) fn [<gen_fn_callback_ $c_struct_name:lower _no_env>]
935            <$( $x: FromToNativeWasmType, )* Rets: WasmTypeList, RetsAsResult: IntoResult<Rets>, Func: Fn($( $x , )*) -> RetsAsResult + 'static>
936            (this: &Func) -> crate::backend::sys::vm::VMFunctionCallback {
937            /// This is a function that wraps the real host
938            /// function. Its address will be used inside the
939            /// runtime.
940            unsafe extern "C-unwind" fn func_wrapper<$( $x, )* Rets, RetsAsResult, Func>( env: &StaticFunction<Func, ()>, $( $x: <$x::Native as NativeWasmType>::Abi, )* ) -> Rets::CStruct
941            where
942                $( $x: FromToNativeWasmType, )*
943                Rets: WasmTypeList,
944                RetsAsResult: IntoResult<Rets>,
945                Func: Fn($( $x , )*) -> RetsAsResult + 'static,
946            {
947                let result = on_host_stack(|| {
948                    panic::catch_unwind(AssertUnwindSafe(|| {
949                        let mut store_wrapper = unsafe { StoreContext::get_current(env.store_id) };
950                        let mut store = store_wrapper.as_mut();
951                        $(
952                            let $x = unsafe {
953                                FromToNativeWasmType::from_native(NativeWasmTypeInto::from_abi(&mut store, $x))
954                            };
955                        )*
956                        to_invocation_result((env.func)($($x),* ).into_result())
957                    }))
958                });
959
960                // IMPORTANT: DO NOT ALLOCATE ON THE STACK,
961                // AS WE ARE IN THE WASM STACK, NOT ON THE HOST ONE.
962                // See: https://github.com/wasmerio/wasmer/pull/5700
963                match result {
964                    Ok(InvocationResult::Success(result)) => unsafe {
965                        // Note: can't acquire a proper ref-counted context ref here, since we can switch
966                        // away from the WASM stack at any time.
967                        // Safety: The pointer is only used for the duration of the call to `into_c_struct`.
968                        let mut store_wrapper = StoreContext::get_current_transient(env.store_id);
969                        let mut store = store_wrapper.as_mut().unwrap();
970                        return result.into_c_struct(store);
971                    },
972                    Ok(InvocationResult::Exception(exception)) => unsafe {
973                        // Note: can't acquire a proper ref-counted context ref here, since we can switch
974                        // away from the WASM stack at any time.
975                        // Safety: The pointer is only used for the duration of the call to `throw`.
976                        let mut store_wrapper = StoreContext::get_current_transient(env.store_id);
977                        let mut store = store_wrapper.as_mut().unwrap();
978                        wasmer_vm::libcalls::throw(
979                            store.objects.as_sys(),
980                            exception.vm_exceptionref().as_sys().to_u32_exnref()
981                        )
982                    }
983                    Ok(InvocationResult::Trap(trap)) => unsafe { raise_user_trap(trap) },
984                    Ok(InvocationResult::YieldOutsideAsyncContext) => unsafe {
985                        raise_lib_trap(Trap::lib(TrapCode::YieldOutsideAsyncContext))
986                    },
987                    Err(panic) => unsafe { resume_panic(panic) },
988                }
989            }
990
991            func_wrapper::< $( $x, )* Rets, RetsAsResult, Func > as _
992
993        }
994
995        #[allow(non_snake_case)]
996        pub(crate) fn [<gen_call_trampoline_address_ $c_struct_name:lower _no_env>]
997            <$( $x: FromToNativeWasmType, )* Rets: WasmTypeList>
998            () -> crate::backend::sys::vm::VMTrampoline {
999
1000            unsafe extern "C" fn call_trampoline<$( $x: FromToNativeWasmType, )* Rets: WasmTypeList>
1001            (
1002                vmctx: *mut crate::backend::sys::vm::VMContext,
1003                body: crate::backend::sys::vm::VMFunctionCallback,
1004                args: *mut RawValue,
1005            ) {
1006                let mut _n = 0;
1007
1008                unsafe {
1009                    let body: unsafe extern "C" fn(vmctx: *mut crate::backend::sys::vm::VMContext, $( $x: <$x::Native as NativeWasmType>::Abi, )*) -> Rets::CStruct = std::mem::transmute(body);
1010                    $(
1011                        let $x = *args.add(_n).cast();
1012                        _n += 1;
1013                    )*
1014                    let results = body(vmctx, $( $x ),*);
1015                    Rets::write_c_struct_to_ptr(results, args);
1016                }
1017            }
1018
1019            call_trampoline::<$( $x, )* Rets> as _
1020
1021        }
1022
1023        #[allow(non_snake_case)]
1024        pub(crate) fn [<gen_fn_callback_ $c_struct_name:lower>]
1025            <$( $x: FromToNativeWasmType, )* Rets: WasmTypeList, RetsAsResult: IntoResult<Rets>, T: Send + 'static,  Func: Fn(FunctionEnvMut<T>, $( $x , )*) -> RetsAsResult + 'static>
1026            (this: &Func) -> crate::backend::sys::vm::VMFunctionCallback {
1027            /// This is a function that wraps the real host
1028            /// function. Its address will be used inside the
1029            /// runtime.
1030            unsafe extern "C-unwind" fn func_wrapper<T: Send + 'static, $( $x, )* Rets, RetsAsResult, Func>( env: &StaticFunction<Func, T>, $( $x: <$x::Native as NativeWasmType>::Abi, )* ) -> Rets::CStruct
1031                where
1032                $( $x: FromToNativeWasmType, )*
1033                Rets: WasmTypeList,
1034                RetsAsResult: IntoResult<Rets>,
1035                Func: Fn(FunctionEnvMut<T>, $( $x , )*) -> RetsAsResult + 'static,
1036            {
1037                let result = wasmer_vm::on_host_stack(|| {
1038                    panic::catch_unwind(AssertUnwindSafe(|| {
1039                        let mut store_wrapper = unsafe { StoreContext::get_current(env.store_id) };
1040                        let mut store = store_wrapper.as_mut();
1041                        $(
1042                            let $x = unsafe {
1043                                FromToNativeWasmType::from_native(NativeWasmTypeInto::from_abi(&mut store, $x))
1044                            };
1045                        )*
1046                        let f_env = crate::backend::sys::function::env::FunctionEnvMut {
1047                            store_mut: store,
1048                            func_env: env.env.as_sys().clone(),
1049                        }.into();
1050                        to_invocation_result((env.func)(f_env, $($x),* ).into_result())
1051                    }))
1052                });
1053
1054                // IMPORTANT: DO NOT ALLOCATE ON THE STACK,
1055                // AS WE ARE IN THE WASM STACK, NOT ON THE HOST ONE.
1056                // See: https://github.com/wasmerio/wasmer/pull/5700
1057                match result {
1058                    Ok(InvocationResult::Success(result)) => unsafe {
1059                        // Note: can't acquire a proper ref-counted context ref here, since we can switch
1060                        // away from the WASM stack at any time.
1061                        // Safety: The pointer is only used for the duration of the call to `into_c_struct`.
1062                        let mut store_wrapper = StoreContext::get_current_transient(env.store_id);
1063                        let mut store = store_wrapper.as_mut().unwrap();
1064                        return result.into_c_struct(store);
1065                    },
1066                    Ok(InvocationResult::Exception(exception)) => unsafe {
1067                        // Note: can't acquire a proper ref-counted context ref here, since we can switch
1068                        // away from the WASM stack at any time.
1069                        // Safety: The pointer is only used for the duration of the call to `throw`.
1070                        let mut store_wrapper = StoreContext::get_current_transient(env.store_id);
1071                        let mut store = store_wrapper.as_mut().unwrap();
1072                        wasmer_vm::libcalls::throw(
1073                            store.objects.as_sys(),
1074                            exception.vm_exceptionref().as_sys().to_u32_exnref()
1075                        )
1076                    }
1077                    Ok(InvocationResult::Trap(trap)) => unsafe { raise_user_trap(trap) },
1078                    Ok(InvocationResult::YieldOutsideAsyncContext) => unsafe {
1079                        raise_lib_trap(Trap::lib(TrapCode::YieldOutsideAsyncContext))
1080                    },
1081                    Err(panic) => unsafe { resume_panic(panic) },
1082                }
1083            }
1084            func_wrapper::< T, $( $x, )* Rets, RetsAsResult, Func > as _
1085        }
1086
1087        #[allow(non_snake_case)]
1088        pub(crate) fn [<gen_call_trampoline_address_ $c_struct_name:lower>]
1089            <$( $x: FromToNativeWasmType, )* Rets: WasmTypeList>
1090            () -> crate::backend::sys::vm::VMTrampoline {
1091
1092            unsafe extern "C" fn call_trampoline<$( $x: FromToNativeWasmType, )* Rets: WasmTypeList>(
1093                  vmctx: *mut crate::backend::sys::vm::VMContext,
1094                  body: crate::backend::sys::vm::VMFunctionCallback,
1095                  args: *mut RawValue,
1096            ) {
1097                unsafe {
1098                    let body: unsafe extern "C" fn(vmctx: *mut crate::backend::sys::vm::VMContext, $( $x: <$x::Native as NativeWasmType>::Abi, )*) -> Rets::CStruct = std::mem::transmute(body);
1099                    let mut _n = 0;
1100                    $(
1101                    let $x = *args.add(_n).cast();
1102                    _n += 1;
1103                    )*
1104
1105                    let results = body(vmctx, $( $x ),*);
1106
1107                    Rets::write_c_struct_to_ptr(results, args);
1108                }
1109            }
1110
1111            call_trampoline::<$( $x, )* Rets> as _
1112        }
1113    }};
1114}
1115
1116// Here we go! Let's generate all the C struct, `WasmTypeList`
1117// implementations and `HostFunction` implementations.
1118impl_host_function!([C] S0,);
1119impl_host_function!([transparent] S1, A1);
1120impl_host_function!([C] S2, A1, A2);
1121impl_host_function!([C] S3, A1, A2, A3);
1122impl_host_function!([C] S4, A1, A2, A3, A4);
1123impl_host_function!([C] S5, A1, A2, A3, A4, A5);
1124impl_host_function!([C] S6, A1, A2, A3, A4, A5, A6);
1125impl_host_function!([C] S7, A1, A2, A3, A4, A5, A6, A7);
1126impl_host_function!([C] S8, A1, A2, A3, A4, A5, A6, A7, A8);
1127impl_host_function!([C] S9, A1, A2, A3, A4, A5, A6, A7, A8, A9);
1128impl_host_function!([C] S10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10);
1129impl_host_function!([C] S11, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11);
1130impl_host_function!([C] S12, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12);
1131impl_host_function!([C] S13, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13);
1132impl_host_function!([C] S14, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14);
1133impl_host_function!([C] S15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15);
1134impl_host_function!([C] S16, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16);
1135impl_host_function!([C] S17, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17);
1136impl_host_function!([C] S18, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18);
1137impl_host_function!([C] S19, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19);
1138impl_host_function!([C] S20, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20);
1139impl_host_function!([C] S21, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21);
1140impl_host_function!([C] S22, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22);
1141impl_host_function!([C] S23, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23);
1142impl_host_function!([C] S24, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24);
1143impl_host_function!([C] S25, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25);
1144impl_host_function!([C] S26, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26);