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}