Skip to main content

wasmedge_sdk/
vm.rs

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