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

1use crate::backend::sys::engine::NativeEngineExt;
2use crate::{
3    FromToNativeWasmType, Function, NativeWasmTypeInto, RuntimeError, StoreContext, TypedFunction,
4    Value, WasmTypeList,
5    store::{AsStoreMut, AsStoreRef},
6};
7#[cfg(feature = "experimental-async")]
8use crate::{StoreAsync, store::AsStoreAsync};
9use std::future::Future;
10use wasmer_types::{FunctionType, RawValue, Type};
11
12macro_rules! impl_native_traits {
13    (  $( $x:ident ),* ) => {
14        #[allow(unused_parens, non_snake_case)]
15        impl<$( $x , )* Rets> TypedFunction<( $( $x ),* ), Rets>
16        where
17            $( $x: FromToNativeWasmType, )*
18            Rets: WasmTypeList,
19        {
20            /// Call the typed func and return results.
21            #[allow(unused_mut)]
22            #[allow(clippy::too_many_arguments)]
23            pub fn call_sys(&self, store: &mut impl AsStoreMut, $( $x: $x, )* ) -> Result<Rets, RuntimeError> {
24                let anyfunc = unsafe {
25                    *self.func.as_sys()
26                        .handle
27                        .get(store.as_store_ref().objects().as_sys())
28                        .anyfunc
29                        .as_ptr()
30                        .as_ref()
31                };
32                // Ensure all parameters come from the same context.
33                if $(!FromToNativeWasmType::is_from_store(&$x, store) ||)* false {
34                    return Err(RuntimeError::new(
35                        "cross-`Store` values are not supported",
36                    ));
37                }
38                // TODO: when `const fn` related features mature more, we can declare a single array
39                // of the correct size here.
40                let mut params_list = [ $( $x.to_native().into_raw(store) ),* ];
41                let mut rets_list_array = Rets::empty_array();
42                let rets_list: &mut [RawValue] = rets_list_array.as_mut();
43                let using_rets_array;
44                let args_rets: &mut [RawValue] = if params_list.len() > rets_list.len() {
45                    using_rets_array = false;
46                    params_list.as_mut()
47                } else {
48                    using_rets_array = true;
49                    for (i, &arg) in params_list.iter().enumerate() {
50                        rets_list[i] = arg;
51                    }
52                    rets_list.as_mut()
53                };
54
55                let store_id = store.objects_mut().id();
56                // Install the store into the store context
57                let store_install_guard = unsafe {
58                    StoreContext::ensure_installed(store.as_store_mut().inner as *mut _)
59                };
60
61                let mut r;
62                loop {
63                    let storeref = store.as_store_ref();
64                    let config = storeref.engine().tunables().vmconfig();
65                    r = unsafe {
66                        // Safety: This is the intended use-case for StoreContext::pause, as
67                        // documented in the function's doc comments.
68                        let pause_guard = StoreContext::pause(store_id);
69                        wasmer_vm::wasmer_call_trampoline(
70                            store.as_store_ref().signal_handler(),
71                            config,
72                            anyfunc.vmctx,
73                            anyfunc.call_trampoline,
74                            anyfunc.func_ptr,
75                            args_rets.as_mut_ptr() as *mut u8,
76                        )
77                    };
78                    let store_mut = store.as_store_mut();
79                    if let Some(callback) = store_mut.inner.on_called.take() {
80                        match callback(store_mut) {
81                            Ok(wasmer_types::OnCalledAction::InvokeAgain) => { continue; }
82                            Ok(wasmer_types::OnCalledAction::Finish) => { break; }
83                            Ok(wasmer_types::OnCalledAction::Trap(trap)) => { return Err(RuntimeError::user(trap)) },
84                            Err(trap) => { return Err(RuntimeError::user(trap)) },
85                        }
86                    }
87                    break;
88                }
89
90                drop(store_install_guard);
91
92                r?;
93
94                let num_rets = rets_list.len();
95                if !using_rets_array && num_rets > 0 {
96                    let src_pointer = params_list.as_ptr();
97                    let rets_list = &mut rets_list_array.as_mut()[0] as *mut RawValue;
98                    unsafe {
99                        // TODO: we can probably remove this copy by doing some clever `transmute`s.
100                        // we know it's not overlapping because `using_rets_array` is false
101                        std::ptr::copy_nonoverlapping(src_pointer,
102                                                        rets_list,
103                                                        num_rets);
104                    }
105                }
106                Ok(unsafe { Rets::from_array(store, rets_list_array) })
107                // TODO: When the Host ABI and Wasm ABI are the same, we could do this instead:
108                // but we can't currently detect whether that's safe.
109                //
110                // let results = unsafe {
111                //     wasmer_vm::catch_traps_with_result(self.vmctx, || {
112                //         let f = std::mem::transmute::<_, unsafe extern "C" fn( *mut VMContext, $( $x, )*) -> Rets::CStruct>(self.address());
113                //         // We always pass the vmctx
114                //         f( self.vmctx, $( $x, )* )
115                //     }).map_err(RuntimeError::from_trap)?
116                // };
117                // Ok(Rets::from_c_struct(results))
118            }
119
120            /// Call the typed func asynchronously.
121            #[allow(unused_mut)]
122            #[allow(clippy::too_many_arguments)]
123            #[cfg(feature = "experimental-async")]
124            pub(crate) fn call_async_sys(
125                func: Function,
126                store: StoreAsync,
127                $( $x: $x, )*
128            ) -> impl Future<Output = Result<Rets, RuntimeError>> + 'static
129            where
130                $( $x: FromToNativeWasmType + 'static, )*
131            {
132                async move {
133                    let mut write = store.write_lock().await;
134                    let func_ty = func.ty(&mut write);
135                    let mut params_raw = [ $( $x.to_native().into_raw(&mut write) ),* ];
136                    let mut params_values = Vec::with_capacity(params_raw.len());
137                    {
138                        for (raw, ty) in params_raw.iter().zip(func_ty.params()) {
139                            unsafe {
140                                params_values.push(Value::from_raw(&mut write, *ty, *raw));
141                            }
142                        }
143                    }
144                    drop(write);
145
146                    let results = func.call_async(&store, params_values).await?;
147                    let mut write = store.write_lock().await;
148                    convert_results::<Rets>(&mut write, func_ty, &results)
149                }
150            }
151
152            #[doc(hidden)]
153            #[allow(missing_docs)]
154            #[allow(unused_mut)]
155            #[allow(clippy::too_many_arguments)]
156            pub fn call_raw_sys(&self, store: &mut impl AsStoreMut, mut params_list: Vec<RawValue> ) -> Result<Rets, RuntimeError> {
157                let anyfunc = unsafe {
158                    *self.func.as_sys()
159                        .handle
160                        .get(store.as_store_ref().objects().as_sys())
161                        .anyfunc
162                        .as_ptr()
163                        .as_ref()
164                };
165                // TODO: when `const fn` related features mature more, we can declare a single array
166                // of the correct size here.
167                let mut rets_list_array = Rets::empty_array();
168                let rets_list: &mut [RawValue] = rets_list_array.as_mut();
169                let using_rets_array;
170                let args_rets: &mut [RawValue] = if params_list.len() > rets_list.len() {
171                    using_rets_array = false;
172                    params_list.as_mut()
173                } else {
174                    using_rets_array = true;
175                    for (i, &arg) in params_list.iter().enumerate() {
176                        rets_list[i] = arg;
177                    }
178                    rets_list.as_mut()
179                };
180
181                let store_id = store.objects_mut().id();
182                // Install the store into the store context
183                let store_install_guard = unsafe {
184                    StoreContext::ensure_installed(store.as_store_mut().inner as *mut _)
185                };
186
187                let mut r;
188                loop {
189                    let storeref = store.as_store_ref();
190                    let config = storeref.engine().tunables().vmconfig();
191                    r = unsafe {
192                        // Safety: This is the intended use-case for StoreContext::pause, as
193                        // documented in the function's doc comments.
194                        let pause_guard = StoreContext::pause(store_id);
195                        wasmer_vm::wasmer_call_trampoline(
196                            store.as_store_ref().signal_handler(),
197                            config,
198                            anyfunc.vmctx,
199                            anyfunc.call_trampoline,
200                            anyfunc.func_ptr,
201                            args_rets.as_mut_ptr() as *mut u8,
202                        )
203                    };
204                    let store_mut = store.as_store_mut();
205                    if let Some(callback) = store_mut.inner.on_called.take() {
206                        // TODO: OnCalledAction is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451
207                        match callback(store_mut) {
208                            Ok(wasmer_types::OnCalledAction::InvokeAgain) => { continue; }
209                            Ok(wasmer_types::OnCalledAction::Finish) => { break; }
210                            Ok(wasmer_types::OnCalledAction::Trap(trap)) => { return Err(RuntimeError::user(trap)) },
211                            Err(trap) => { return Err(RuntimeError::user(trap)) },
212                        }
213                    }
214                    break;
215                }
216
217                drop(store_install_guard);
218
219                r?;
220
221                let num_rets = rets_list.len();
222                if !using_rets_array && num_rets > 0 {
223                    let src_pointer = params_list.as_ptr();
224                    let rets_list = &mut rets_list_array.as_mut()[0] as *mut RawValue;
225                    unsafe {
226                        // TODO: we can probably remove this copy by doing some clever `transmute`s.
227                        // we know it's not overlapping because `using_rets_array` is false
228                        std::ptr::copy_nonoverlapping(src_pointer,
229                                                        rets_list,
230                                                        num_rets);
231                    }
232                }
233                Ok(unsafe { Rets::from_array(store, rets_list_array) })
234                // TODO: When the Host ABI and Wasm ABI are the same, we could do this instead:
235                // but we can't currently detect whether that's safe.
236                //
237                // let results = unsafe {
238                //     wasmer_vm::catch_traps_with_result(self.vmctx, || {
239                //         let f = std::mem::transmute::<_, unsafe extern "C" fn( *mut VMContext, $( $x, )*) -> Rets::CStruct>(self.address());
240                //         // We always pass the vmctx
241                //         f( self.vmctx, $( $x, )* )
242                //     }).map_err(RuntimeError::from_trap)?
243                // };
244                // Ok(Rets::from_c_struct(results))
245            }
246        }
247    };
248}
249
250impl_native_traits!();
251impl_native_traits!(A1);
252impl_native_traits!(A1, A2);
253impl_native_traits!(A1, A2, A3);
254impl_native_traits!(A1, A2, A3, A4);
255impl_native_traits!(A1, A2, A3, A4, A5);
256impl_native_traits!(A1, A2, A3, A4, A5, A6);
257impl_native_traits!(A1, A2, A3, A4, A5, A6, A7);
258impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8);
259impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9);
260impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10);
261impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11);
262impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12);
263impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13);
264impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14);
265impl_native_traits!(
266    A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15
267);
268impl_native_traits!(
269    A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16
270);
271impl_native_traits!(
272    A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17
273);
274impl_native_traits!(
275    A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18
276);
277impl_native_traits!(
278    A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19
279);
280impl_native_traits!(
281    A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20
282);
283
284fn convert_results<Rets>(
285    store: &mut impl AsStoreMut,
286    ty: FunctionType,
287    results: &[Value],
288) -> Result<Rets, RuntimeError>
289where
290    Rets: WasmTypeList,
291{
292    if results.len() != ty.results().len() {
293        return Err(RuntimeError::new("result arity mismatch"));
294    }
295    let mut raw_array = Rets::empty_array();
296    for ((slot, value_ty), value) in raw_array
297        .as_mut()
298        .iter_mut()
299        .zip(ty.results().iter())
300        .zip(results.iter())
301    {
302        debug_assert_eq!(value.ty(), *value_ty);
303        *slot = value.as_raw(store);
304    }
305    unsafe { Ok(Rets::from_array(store, raw_array)) }
306}