marine_wasm_backend_traits/
function.rs

1/*
2 * Copyright 2023 Fluence Labs Limited
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use crate::AsContextMut;
18use crate::FuncSig;
19use crate::impl_for_each_function_signature;
20use crate::RuntimeResult;
21use crate::WasmBackend;
22use crate::WValue;
23
24use futures::future::BoxFuture;
25
26/// A host function ready to be used as an import for instantiating a module.
27/// As it is only a handle to an object in `Store`, cloning is cheap.
28pub trait HostFunction<WB: WasmBackend>: Send + Sync + Clone {
29    /// Creates a new function with dynamic signature.
30    /// The signature check is performed at runtime.
31    fn new<F>(store: &mut impl AsContextMut<WB>, sig: FuncSig, func: F) -> Self
32    where
33        F: for<'c> Fn(&[WValue]) -> anyhow::Result<Vec<WValue>> + Sync + Send + 'static;
34
35    /// Creates a new function with dynamic signature that needs a context.
36    fn new_with_caller<F>(store: &mut impl AsContextMut<WB>, sig: FuncSig, func: F) -> Self
37    where
38        F: for<'c> Fn(
39                <WB as WasmBackend>::ImportCallContext<'c>,
40                &[WValue],
41            ) -> anyhow::Result<Vec<WValue>>
42            + Sync
43            + Send
44            + 'static;
45
46    /// Creates a new function with dynamic signature that needs a context.
47    fn new_with_caller_async<F>(store: &mut impl AsContextMut<WB>, sig: FuncSig, func: F) -> Self
48    where
49        F: for<'c> Fn(
50                <WB as WasmBackend>::ImportCallContext<'c>,
51                &'c [WValue],
52            ) -> BoxFuture<'c, anyhow::Result<Vec<WValue>>>
53            + Sync
54            + Send
55            + 'static;
56
57    /// Creates a new function with dynamic signature that needs a context.
58    fn new_async<F>(store: &mut impl AsContextMut<WB>, sig: FuncSig, func: F) -> Self
59    where
60        F: for<'c> Fn(&'c [WValue]) -> BoxFuture<'c, anyhow::Result<Vec<WValue>>>
61            + Sync
62            + Send
63            + 'static;
64
65    /// Creates a new function with static signature.
66    /// Requires less runtime checks when called.
67    fn new_typed<Params, Results, Env>(
68        store: &mut impl AsContextMut<WB>,
69        func: impl IntoFunc<WB, Params, Results, Env>,
70    ) -> Self;
71
72    /// Returns the signature of the function.
73    /// The signature is constructed each time this function is called, so
74    /// it is not recommended to use this function extensively.
75    fn signature(&self, store: &mut impl AsContextMut<WB>) -> FuncSig;
76}
77
78/// A Wasm function handle, it can be either a function from a host or an export from an `Instance`.
79/// As it is only a handle to an object in `Store`, cloning is cheap
80pub trait ExportFunction<WB: WasmBackend>: Send + Sync + Clone {
81    /// Returns the signature of the function.
82    /// The signature is constructed each time this function is called, so
83    /// it is not recommended to use this function extensively.
84    fn signature(&self, store: &mut impl AsContextMut<WB>) -> FuncSig;
85
86    /// Calls the wasm function.
87    /// # Panics:
88    ///     If given a store different from the one that stores the function.
89    /// # Errors:
90    ///     See `RuntimeError` documentation.
91    fn call_async<'args>(
92        &'args self,
93        store: &'args mut impl AsContextMut<WB>,
94        args: &'args [WValue],
95    ) -> BoxFuture<'args, RuntimeResult<Vec<WValue>>>;
96}
97
98/// A helper trait for creating a function with a static signature.
99/// Should not be implemented by users.
100/// Implemented for all functions that meet the following criteria:
101///     * implement Send + Sync + 'static
102///     * take or not take ImportCallContext as first parameter
103///     * take from 0 to 16 i32 parameters
104///     * return () or i32
105pub trait IntoFunc<WB: WasmBackend, Params, Results, Env> {
106    fn into_func(self, ctx: &mut impl AsContextMut<WB>) -> <WB as WasmBackend>::HostFunction;
107}
108
109/// An indicator of using ImportCallContext argument.
110pub struct WithEnv {}
111
112/// An indicator of using ImportCallContext argument.
113pub struct WithoutEnv {}
114
115#[macro_export]
116macro_rules! replace_with {
117    ($from:ident -> $to:ident) => {
118        $to
119    };
120}
121
122macro_rules! impl_into_func {
123    ($num:tt $($args:ident)*) => (paste::paste!{
124        #[allow(non_snake_case)]
125        impl<WB, F> IntoFunc<WB, ($(replace_with!($args -> i32),)*), (), WithoutEnv> for F
126        where
127            WB: WasmBackend,
128            F: Fn($(replace_with!($args -> i32),)*) + Send + Sync + 'static,
129        {
130            fn into_func(self, ctx: &mut impl AsContextMut<WB>) -> <WB as WasmBackend>::HostFunction {
131                <WB as WasmBackend>::HostFunction:: [< new_typed_ $num >] (ctx.as_context_mut(), self)
132            }
133        }
134
135        #[allow(non_snake_case)]
136        impl<WB, F> IntoFunc<WB, ($(replace_with!($args -> i32),)*), (), WithEnv> for F
137        where
138            WB: WasmBackend,
139            F: Fn(<WB as WasmBackend>::ImportCallContext<'_>, $(replace_with!($args -> i32),)*) + Send + Sync + 'static,
140        {
141            fn into_func(self, ctx: &mut impl AsContextMut<WB>) -> <WB as WasmBackend>::HostFunction {
142                <WB as WasmBackend>::HostFunction:: [< new_typed_with_env_ $num >] (ctx.as_context_mut(), self)
143            }
144        }
145
146        #[allow(non_snake_case)]
147        impl<WB, F> IntoFunc<WB, ($(replace_with!($args -> i32),)*), i32, WithoutEnv> for F
148        where
149            WB: WasmBackend,
150            F: Fn($(replace_with!($args -> i32),)*) -> i32 + Send + Sync + 'static,
151        {
152            fn into_func(self, ctx: &mut impl AsContextMut<WB>) -> <WB as WasmBackend>::HostFunction {
153                <WB as WasmBackend>::HostFunction:: [< new_typed_ $num _r >] (ctx.as_context_mut(), self)
154            }
155        }
156
157        #[allow(non_snake_case)]
158        impl<WB, F> IntoFunc<WB, ($(replace_with!($args -> i32),)*), i32, WithEnv> for F
159        where
160            WB: WasmBackend,
161            F: Fn(<WB as WasmBackend>::ImportCallContext<'_>, $(replace_with!($args -> i32),)*) -> i32 + Send + Sync + 'static,
162        {
163            fn into_func(self, ctx: &mut impl AsContextMut<WB>) -> <WB as WasmBackend>::HostFunction {
164                <WB as WasmBackend>::HostFunction:: [< new_typed_with_env_ $num _r >] (ctx.as_context_mut(), self)
165            }
166        }
167    });
168}
169
170impl_for_each_function_signature!(impl_into_func);
171
172macro_rules! declare_func_construction {
173    ($num:tt $($args:ident)*) => (paste::paste!{
174        #[allow(non_snake_case)]
175        fn [< new_typed_ $num >]<F>(ctx: <WB as WasmBackend>::ContextMut<'_>, func: F) -> <WB as WasmBackend>::HostFunction
176            where F: Fn($(replace_with!($args -> i32),)*) + Send + Sync + 'static
177        {
178            let func = move |_: <WB as WasmBackend>::ImportCallContext<'_>, $($args,)*| { func($($args,)*)};
179            Self:: [< new_typed_with_env_ $num >] (ctx, func)
180        }
181
182        #[allow(non_snake_case)]
183        fn [< new_typed_with_env_ $num >]<F>(ctx: <WB as WasmBackend>::ContextMut<'_>, func: F) -> <WB as WasmBackend>::HostFunction
184            where F: Fn(<WB as WasmBackend>::ImportCallContext<'_>, $(replace_with!($args -> i32),)*) + Send + Sync + 'static;
185
186        #[allow(non_snake_case)]
187        fn [< new_typed_ $num _r>]<F>(ctx: <WB as WasmBackend>::ContextMut<'_>, func: F) -> <WB as WasmBackend>::HostFunction
188            where F: Fn($(replace_with!($args -> i32),)*) -> i32 + Send + Sync + 'static
189        {
190            let func = move |_: <WB as WasmBackend>::ImportCallContext<'_>, $($args,)*| -> i32 { func($($args,)*)};
191            Self:: [< new_typed_with_env_ $num _r >] (ctx, func)
192        }
193
194        #[allow(non_snake_case)]
195        fn [< new_typed_with_env_ $num _r>]<F>(ctx: <WB as WasmBackend>::ContextMut<'_>, func: F) -> <WB as WasmBackend>::HostFunction
196            where F: Fn(<WB as WasmBackend>::ImportCallContext<'_>, $(replace_with!($args -> i32),)*) -> i32 + Send + Sync + 'static;
197    });
198}
199
200pub trait FuncConstructor<WB: WasmBackend> {
201    impl_for_each_function_signature!(declare_func_construction);
202}