use crate::{
error::{VmError, WasmEdgeError},
ImportObject, Instance, Module, Store, WasmEdgeResult, WasmValue,
};
use sys::AsInstance;
use wasmedge_sys as sys;
pub trait SyncInst: AsInstance {}
impl<T> SyncInst for ImportObject<T> {}
impl SyncInst for Instance {}
#[derive(Debug)]
pub struct Vm<'inst, T: ?Sized + SyncInst> {
store: Store<'inst, T>,
active_instance: Option<sys::Instance>,
}
impl<'inst, T: ?Sized + SyncInst> Vm<'inst, T> {
pub fn new(store: Store<'inst, T>) -> Self {
Vm {
store,
active_instance: None,
}
}
pub fn register_module(
&mut self,
mod_name: Option<&str>,
module: Module,
) -> WasmEdgeResult<&mut Self> {
match mod_name {
Some(name) => {
self.store.register_named_module(name, &module)?;
}
None => {
self.active_instance = Some(self.store.register_active_module(&module)?);
}
};
Ok(self)
}
pub fn run_func(
&mut self,
mod_name: Option<&str>,
func_name: impl AsRef<str>,
args: impl IntoIterator<Item = WasmValue>,
) -> WasmEdgeResult<Vec<WasmValue>> {
let (mut func, executor) = match mod_name {
Some(mod_name) => {
if let Some((inst, executor)) = self.store.get_instance_and_executor(mod_name) {
(inst.get_func_mut(func_name.as_ref())?, executor)
} else if let Some((wasm_mod, executor)) =
self.store.get_named_wasm_and_executor(mod_name)
{
(wasm_mod.get_func_mut(func_name.as_ref())?, executor)
} else {
return Err(Box::new(WasmEdgeError::Vm(VmError::NotFoundModule(
mod_name.into(),
))));
}
}
None => {
let active_inst = self
.active_instance
.as_mut()
.ok_or(Box::new(WasmEdgeError::Vm(VmError::NotFoundActiveModule)))?;
(
active_inst.get_func_mut(func_name.as_ref())?,
self.store.executor(),
)
}
};
executor.call_func(&mut func, args)
}
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
pub fn run_func_with_timeout(
&mut self,
mod_name: Option<&str>,
func_name: impl AsRef<str>,
args: impl IntoIterator<Item = WasmValue>,
timeout: std::time::Duration,
) -> WasmEdgeResult<Vec<WasmValue>> {
let (mut func, executor) = match mod_name {
Some(mod_name) => {
if let Some((inst, executor)) = self.store.get_instance_and_executor(mod_name) {
(inst.get_func_mut(func_name.as_ref())?, executor)
} else if let Some((wasm_mod, executor)) =
self.store.get_named_wasm_and_executor(mod_name)
{
(wasm_mod.get_func_mut(func_name.as_ref())?, executor)
} else {
return Err(Box::new(WasmEdgeError::Vm(VmError::NotFoundModule(
mod_name.into(),
))));
}
}
None => {
let active_inst = self
.active_instance
.as_mut()
.ok_or(Box::new(WasmEdgeError::Vm(VmError::NotFoundActiveModule)))?;
(
active_inst.get_func_mut(func_name.as_ref())?,
self.store.executor(),
)
}
};
executor.call_func_with_timeout(&mut func, args, timeout)
}
pub fn store(&self) -> &Store<'inst, T> {
&self.store
}
pub fn store_mut(&mut self) -> &mut Store<'inst, T> {
&mut self.store
}
pub fn active_module(&self) -> Option<&Instance> {
self.active_instance.as_ref()
}
pub fn active_module_mut(&mut self) -> Option<&mut Instance> {
self.active_instance.as_mut()
}
pub fn contains_module(&self, mod_name: impl AsRef<str>) -> bool {
self.store.contains(mod_name)
}
pub fn named_instance_count(&self) -> usize {
self.store.named_instance_count()
}
pub fn instance_names(&self) -> Vec<String> {
self.store.instance_names()
}
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use wasmedge_types::wat2wasm;
use super::*;
use crate::{params, WasmVal};
#[test]
#[cfg(target_os = "linux")]
fn test_vmbuilder() -> Result<(), Box<dyn std::error::Error>> {
use crate::{params, plugin::PluginManager};
PluginManager::load(None)?;
PluginManager::names().iter().for_each(|name| {
println!("plugin name: {}", name);
});
let wasm_app_file = "examples/wasmedge-sys/data/test_crypto.wasm";
let mut wasi = crate::wasi::WasiModule::create(None, None, None).unwrap();
let mut wasi_crypto_asymmetric_common =
PluginManager::load_wasi_crypto_asymmetric_common().unwrap();
let mut wasi_crypto_signatures = PluginManager::load_wasi_crypto_signatures().unwrap();
let mut wasi_crypto_symmetric = PluginManager::load_wasi_crypto_symmetric().unwrap();
let mut instances = HashMap::new();
instances.insert(wasi.name().to_string(), wasi.as_mut());
instances.insert(
wasi_crypto_asymmetric_common.name().unwrap(),
&mut wasi_crypto_asymmetric_common,
);
instances.insert(
wasi_crypto_signatures.name().unwrap(),
&mut wasi_crypto_signatures,
);
instances.insert(
wasi_crypto_symmetric.name().unwrap(),
&mut wasi_crypto_symmetric,
);
let mut vm = Vm::new(Store::new(None, instances).unwrap());
let module = Module::from_file(None, &wasm_app_file).unwrap();
vm.register_module(Some("wasm-app"), module).unwrap();
vm.run_func(Some("wasm-app"), "_start", params!())?;
Ok(())
}
#[test]
fn test_vm_run_func_from_file() {
let mut vm =
Vm::new(Store::new(None, HashMap::<String, &mut dyn SyncInst>::new()).unwrap());
let file = std::env::current_dir()
.unwrap()
.join("examples/wasmedge-sys/data/fibonacci.wat");
let fib_module = Module::from_file(None, file).unwrap();
vm.register_module(None, fib_module).unwrap();
let result = vm.run_func(None, "fib", params!(10i32));
assert!(result.is_ok());
let returns = result.unwrap();
assert_eq!(returns.len(), 1);
assert_eq!(returns[0].to_i32(), 89);
}
#[test]
fn test_vm_run_func_from_bytes() {
let mut vm =
Vm::new(Store::new(None, HashMap::<String, &mut dyn SyncInst>::new()).unwrap());
let result = wat2wasm(
br#"(module
(export "fib" (func $fib))
(func $fib (param $n i32) (result i32)
(if
(i32.lt_s
(local.get $n)
(i32.const 2)
)
(then
(return (i32.const 1))
)
)
(return
(i32.add
(call $fib
(i32.sub
(local.get $n)
(i32.const 2)
)
)
(call $fib
(i32.sub
(local.get $n)
(i32.const 1)
)
)
)
)
)
)
"#,
);
assert!(result.is_ok());
let wasm_bytes = result.unwrap();
let fib_module = Module::from_bytes(None, wasm_bytes).unwrap();
vm.register_module(None, fib_module).unwrap();
let result = vm.run_func(None, "fib", params!(10i32));
assert!(result.is_ok());
let returns = result.unwrap();
assert_eq!(returns.len(), 1);
assert_eq!(returns[0].to_i32(), 89);
}
#[test]
fn test_vm_run_func_in_named_module_instance() {
let mut vm =
Vm::new(Store::new(None, HashMap::<String, &mut dyn SyncInst>::new()).unwrap());
let result = wat2wasm(
br#"(module
(export "fib" (func $fib))
(func $fib (param $n i32) (result i32)
(if
(i32.lt_s
(local.get $n)
(i32.const 2)
)
(then
(return (i32.const 1))
)
)
(return
(i32.add
(call $fib
(i32.sub
(local.get $n)
(i32.const 2)
)
)
(call $fib
(i32.sub
(local.get $n)
(i32.const 1)
)
)
)
)
)
)
"#,
);
assert!(result.is_ok());
let wasm_bytes = result.unwrap();
let fib_module = Module::from_bytes(None, wasm_bytes).unwrap();
vm.register_module(Some("extern"), fib_module).unwrap();
let result = vm.run_func(Some("extern"), "fib", params!(10));
assert!(result.is_ok());
let returns = result.unwrap();
assert_eq!(returns.len(), 1);
assert_eq!(returns[0].to_i32(), 89);
}
}