Skip to main content

wasmedge_sdk/async/
vm.rs

1//! Defines WasmEdge Vm struct.
2use crate::{
3    error::{VmError, WasmEdgeError},
4    vm::SyncInst,
5    Instance, Module, Store, WasmEdgeResult, WasmValue,
6};
7use sys::{r#async::fiber::AsyncState, AsInstance};
8use wasmedge_sys as sys;
9
10use super::import::ImportObject;
11
12pub trait AsyncInst: AsInstance {}
13
14impl<T: Send> AsyncInst for ImportObject<T> {}
15impl<T: Send + SyncInst> AsyncInst for T {}
16
17/// A [Vm] defines a virtual environment for managing WebAssembly programs.
18///
19/// # Example
20///
21/// The example below presents how to register a module as named module in a Vm instance and run a target wasm function.
22///
23/// ```rust
24/// // If the version of rust used is less than v1.63, please uncomment the follow attribute.
25/// // #![feature(explicit_generic_args_with_impl_trait)]
26/// #[cfg(not(feature = "async"))]
27/// use wasmedge_sdk::{params, VmBuilder, WasmVal, wat2wasm, ValType, NeverType};
28///
29/// #[cfg_attr(test, test)]
30/// fn main() -> Result<(), Box<dyn std::error::Error>> {
31///     #[cfg(not(feature = "async"))]
32///     {
33///         // create a Vm context
34///         let vm = VmBuilder::new().build()?;
35///
36///         // register a wasm module from the given in-memory wasm bytes
37///         let wasm_bytes = wat2wasm(
38///             br#"(module
39///             (export "fib" (func $fib))
40///             (func $fib (param $n i32) (result i32)
41///              (if
42///               (i32.lt_s
43///                (local.get $n)
44///                (i32.const 2)
45///               )
46///               (then
47///                (return (i32.const 1))
48///               )
49///              )
50///              (return
51///               (i32.add
52///                (call $fib
53///                 (i32.sub
54///                  (local.get $n)
55///                  (i32.const 2)
56///                 )
57///                )
58///                (call $fib
59///                 (i32.sub
60///                  (local.get $n)
61///                  (i32.const 1)
62///                 )
63///                )
64///               )
65///              )
66///             )
67///            )
68///         "#,
69///         )?;
70///         let mut vm = vm.register_module_from_bytes("extern", wasm_bytes)?;
71///
72///         // run `fib` function in the named module instance
73///         let returns = vm.run_func(Some("extern"), "fib", params!(10))?;
74///         assert_eq!(returns.len(), 1);
75///         assert_eq!(returns[0].to_i32(), 89);
76///     }
77///
78///     Ok(())
79/// }
80/// ```
81#[derive(Debug)]
82pub struct Vm<'inst, T: ?Sized + Send + AsyncInst> {
83    store: Store<'inst, T>,
84    active_instance: Option<sys::Instance>,
85    async_state: AsyncState,
86}
87impl<'inst, T: ?Sized + Send + AsyncInst> Vm<'inst, T> {
88    pub fn new(store: Store<'inst, T>) -> Self {
89        // create a Vm instance
90        Self {
91            store,
92            active_instance: None,
93            async_state: AsyncState::new(),
94        }
95    }
96
97    /// Registers a [wasm module](crate::Module) into this vm as a named or active module [instance](crate::Instance).
98    ///
99    /// # Arguments
100    ///
101    /// * `mod_name` - The exported name for the registered module. If `None`, then the module is registered as an active instance.
102    ///
103    /// * `module` - The module to be registered.
104    ///
105    /// # Error
106    ///
107    /// If fail to register the given [module](crate::Module), then an error is returned.
108    ///
109    pub fn register_module(
110        &mut self,
111        mod_name: Option<&str>,
112        module: Module,
113    ) -> WasmEdgeResult<&mut Self> {
114        match mod_name {
115            Some(name) => {
116                self.store.register_named_module(name, &module)?;
117            }
118            None => {
119                self.active_instance = Some(self.store.register_active_module(&module)?);
120            }
121        };
122
123        Ok(self)
124    }
125
126    /// Runs an exported wasm function in a (named or active) [module instance](crate::Instance).
127    ///
128    /// # Arguments
129    ///
130    /// * `mod_name` - The exported name of the module instance, which holds the target function. If `None`, then the active module is used.
131    ///
132    /// * `func_name` - The exported name of the target wasm function.
133    ///
134    /// * `args` - The arguments to be passed to the target wasm function.
135    ///
136    /// # Error
137    ///
138    /// If fail to run the wasm function, then an error is returned.
139    pub async fn run_func(
140        &mut self,
141        mod_name: Option<&str>,
142        func_name: impl AsRef<str>,
143        args: impl IntoIterator<Item = WasmValue> + Send,
144    ) -> WasmEdgeResult<Vec<WasmValue>> {
145        let (mut func, executor) = match mod_name {
146            Some(mod_name) => {
147                if let Some((inst, executor)) = self.store.get_instance_and_executor(mod_name) {
148                    (inst.get_func_mut(func_name.as_ref())?, executor)
149                } else if let Some((wasm_mod, executor)) =
150                    self.store.get_named_wasm_and_executor(mod_name)
151                {
152                    (wasm_mod.get_func_mut(func_name.as_ref())?, executor)
153                } else {
154                    return Err(Box::new(WasmEdgeError::Vm(VmError::NotFoundModule(
155                        mod_name.into(),
156                    ))));
157                }
158            }
159            None => {
160                let active_inst = self
161                    .active_instance
162                    .as_mut()
163                    .ok_or(Box::new(WasmEdgeError::Vm(VmError::NotFoundActiveModule)))?;
164
165                (
166                    active_inst.get_func_mut(func_name.as_ref())?,
167                    self.store.executor(),
168                )
169            }
170        };
171        executor
172            .call_func_async(&self.async_state, &mut func, args)
173            .await
174    }
175
176    /// Runs an exported wasm function in a (named or active) [module instance](crate::Instance) with a timeout setting
177    ///
178    /// # Arguments
179    ///
180    /// * `mod_name` - The exported name of the module instance, which holds the target function. If `None`, then the active module is used.
181    ///
182    /// * `func_name` - The exported name of the target wasm function.
183    ///
184    /// * `args` - The arguments to be passed to the target wasm function.
185    ///
186    /// * `timeout` - The maximum execution time of the function to be run.
187    ///
188    /// # Error
189    ///
190    /// If fail to run the wasm function, then an error is returned.
191    #[cfg(all(target_os = "linux", not(target_env = "musl")))]
192    pub async fn run_func_with_timeout(
193        &mut self,
194        mod_name: Option<&str>,
195        func_name: impl AsRef<str>,
196        args: impl IntoIterator<Item = WasmValue> + Send,
197        timeout: std::time::Duration,
198    ) -> WasmEdgeResult<Vec<WasmValue>> {
199        let (mut func, executor) = match mod_name {
200            Some(mod_name) => {
201                if let Some((inst, executor)) = self.store.get_instance_and_executor(mod_name) {
202                    (inst.get_func_mut(func_name.as_ref())?, executor)
203                } else if let Some((wasm_mod, executor)) =
204                    self.store.get_named_wasm_and_executor(mod_name)
205                {
206                    (wasm_mod.get_func_mut(func_name.as_ref())?, executor)
207                } else {
208                    return Err(Box::new(WasmEdgeError::Vm(VmError::NotFoundModule(
209                        mod_name.into(),
210                    ))));
211                }
212            }
213            None => {
214                let active_inst = self
215                    .active_instance
216                    .as_mut()
217                    .ok_or(Box::new(WasmEdgeError::Vm(VmError::NotFoundActiveModule)))?;
218
219                (
220                    active_inst.get_func_mut(func_name.as_ref())?,
221                    self.store.executor(),
222                )
223            }
224        };
225        executor
226            .call_func_async_with_timeout(&self.async_state, &mut func, args, timeout)
227            .await
228    }
229
230    /// Returns a reference to the internal [store](crate::Store) from this vm.
231    pub fn store(&self) -> &Store<'inst, T> {
232        &self.store
233    }
234
235    /// Returns a mutable reference to the internal [store](crate::Store) from this vm.
236    pub fn store_mut(&mut self) -> &mut Store<'inst, T> {
237        &mut self.store
238    }
239
240    /// Returns a reference to the active [module instance](crate::Instance) from this vm.
241    ///
242    /// # Error
243    ///
244    /// If fail to get the reference to the active module instance, then an error is returned.
245    pub fn active_module(&self) -> Option<&Instance> {
246        self.active_instance.as_ref()
247    }
248
249    /// Returns a mutable reference to the active [module instance](crate::Instance) from this vm.
250    ///
251    /// # Error
252    ///
253    /// If fail to get the mutable reference to the active module instance, then an error is returned.
254    pub fn active_module_mut(&mut self) -> Option<&mut Instance> {
255        self.active_instance.as_mut()
256    }
257
258    /// Checks if the vm contains a named module instance.
259    ///
260    /// # Argument
261    ///
262    /// * `mod_name` - The exported name of the target module instance.
263    ///
264    pub fn contains_module(&self, mod_name: impl AsRef<str>) -> bool {
265        self.store.contains(mod_name)
266    }
267
268    /// Returns the count of the named [module instances](crate::Instance) this vm holds.
269    pub fn named_instance_count(&self) -> usize {
270        self.store.named_instance_count()
271    }
272
273    /// Returns the names of all named [module instances](crate::Instance) this vm holds.
274    pub fn instance_names(&self) -> Vec<String> {
275        self.store.instance_names()
276    }
277}
278
279#[cfg(test)]
280mod tests {
281    use std::collections::HashMap;
282
283    use wasmedge_types::wat2wasm;
284
285    use super::*;
286    use crate::{io::WasmVal, params};
287
288    #[tokio::test]
289    async fn test_vm_run_func_from_file() {
290        // create a Vm context
291        let mut vm = Vm::new(
292            Store::new(None, HashMap::<String, &mut (dyn AsyncInst + Send)>::new()).unwrap(),
293        );
294
295        // register a wasm module from a specified wasm file
296        let file = std::env::current_dir()
297            .unwrap()
298            .join("examples/wasmedge-sys/data/fibonacci.wat");
299
300        // run `fib` function from the wasm file
301        let fib_module = Module::from_file(None, file).unwrap();
302        vm.register_module(None, fib_module).unwrap();
303        let result = vm.run_func(None, "fib", params!(10)).await;
304        assert!(result.is_ok());
305        let returns = result.unwrap();
306        assert_eq!(returns.len(), 1);
307        assert_eq!(returns[0].to_i32(), 89);
308    }
309
310    #[tokio::test]
311    async fn test_vm_run_func_from_bytes() {
312        // create a Vm context
313        let mut vm = Vm::new(
314            Store::new(None, HashMap::<String, &mut (dyn AsyncInst + Send)>::new()).unwrap(),
315        );
316
317        // register a wasm module from the given in-memory wasm bytes
318        // load wasm module
319        let result = wat2wasm(
320            br#"(module
321            (export "fib" (func $fib))
322            (func $fib (param $n i32) (result i32)
323             (if
324              (i32.lt_s
325               (local.get $n)
326               (i32.const 2)
327              )
328              (then
329                (return (i32.const 1))
330              )
331             )
332             (return
333              (i32.add
334               (call $fib
335                (i32.sub
336                 (local.get $n)
337                 (i32.const 2)
338                )
339               )
340               (call $fib
341                (i32.sub
342                 (local.get $n)
343                 (i32.const 1)
344                )
345               )
346              )
347             )
348            )
349           )
350        "#,
351        );
352        assert!(result.is_ok());
353        let wasm_bytes = result.unwrap();
354
355        // run `fib` function from the wasm bytes
356        let fib_module = Module::from_bytes(None, wasm_bytes).unwrap();
357        vm.register_module(None, fib_module).unwrap();
358        let result = vm.run_func(None, "fib", params!(10)).await;
359        assert!(result.is_ok());
360        let returns = result.unwrap();
361        assert_eq!(returns.len(), 1);
362        assert_eq!(returns[0].to_i32(), 89);
363    }
364
365    #[tokio::test]
366    async fn test_vm_run_func_in_named_module_instance() {
367        // create a Vm context
368        let mut vm = Vm::new(
369            Store::new(None, HashMap::<String, &mut (dyn AsyncInst + Send)>::new()).unwrap(),
370        );
371
372        // register a wasm module from the given in-memory wasm bytes
373        // load wasm module
374        let result = wat2wasm(
375            br#"(module
376            (export "fib" (func $fib))
377            (func $fib (param $n i32) (result i32)
378             (if
379              (i32.lt_s
380               (local.get $n)
381               (i32.const 2)
382              )
383              (then
384                (return (i32.const 1))
385              )
386             )
387             (return
388              (i32.add
389               (call $fib
390                (i32.sub
391                 (local.get $n)
392                 (i32.const 2)
393                )
394               )
395               (call $fib
396                (i32.sub
397                 (local.get $n)
398                 (i32.const 1)
399                )
400               )
401              )
402             )
403            )
404           )
405        "#,
406        );
407        assert!(result.is_ok());
408        let wasm_bytes = result.unwrap();
409        let fib_module = Module::from_bytes(None, wasm_bytes).unwrap();
410        vm.register_module(Some("extern"), fib_module).unwrap();
411        // run `fib` function in the named module instance
412        let result = vm.run_func(Some("extern"), "fib", params!(10)).await;
413        assert!(result.is_ok());
414        let returns = result.unwrap();
415        assert_eq!(returns.len(), 1);
416        assert_eq!(returns[0].to_i32(), 89);
417    }
418}