use crate::{
ffi,
instance::{function::InnerFunc, global::InnerGlobal, memory::InnerMemory, table::InnerTable},
types::WasmEdgeString,
Function, Global, Memory, Table, WasmEdgeResult, HOST_FUNCS, HOST_FUNC_FOOTPRINTS,
};
#[cfg(all(feature = "async", target_os = "linux"))]
use crate::{r#async::AsyncWasiModule, ASYNC_HOST_FUNCS};
use parking_lot::Mutex;
use std::sync::Arc;
use wasmedge_types::error::{InstanceError, WasmEdgeError};
#[derive(Debug)]
pub struct Instance {
pub(crate) inner: Arc<Mutex<InnerInstance>>,
pub(crate) registered: bool,
}
impl Drop for Instance {
fn drop(&mut self) {
if self.registered {
self.inner.lock().0 = std::ptr::null_mut();
} else if Arc::strong_count(&self.inner) == 1 && !self.inner.lock().0.is_null() {
unsafe {
ffi::WasmEdge_ModuleInstanceDelete(self.inner.lock().0);
}
}
}
}
impl Instance {
pub fn name(&self) -> Option<String> {
let name =
unsafe { ffi::WasmEdge_ModuleInstanceGetModuleName(self.inner.lock().0 as *const _) };
let name: String = name.into();
if name.is_empty() {
return None;
}
Some(name)
}
pub fn get_func(&self, name: impl AsRef<str>) -> WasmEdgeResult<Function> {
let func_name: WasmEdgeString = name.as_ref().into();
let func_ctx = unsafe {
ffi::WasmEdge_ModuleInstanceFindFunction(
self.inner.lock().0 as *const _,
func_name.as_raw(),
)
};
match func_ctx.is_null() {
true => Err(Box::new(WasmEdgeError::Instance(
InstanceError::NotFoundFunc(name.as_ref().to_string()),
))),
false => Ok(Function {
inner: Arc::new(Mutex::new(InnerFunc(func_ctx))),
registered: true,
data_owner: false, }),
}
}
pub fn get_table(&self, name: impl AsRef<str>) -> WasmEdgeResult<Table> {
let table_name: WasmEdgeString = name.as_ref().into();
let ctx = unsafe {
ffi::WasmEdge_ModuleInstanceFindTable(
self.inner.lock().0 as *const _,
table_name.as_raw(),
)
};
match ctx.is_null() {
true => Err(Box::new(WasmEdgeError::Instance(
InstanceError::NotFoundTable(name.as_ref().to_string()),
))),
false => Ok(Table {
inner: Arc::new(Mutex::new(InnerTable(ctx))),
registered: true,
}),
}
}
pub fn get_memory(&self, name: impl AsRef<str>) -> WasmEdgeResult<Memory> {
let mem_name: WasmEdgeString = name.as_ref().into();
let ctx = unsafe {
ffi::WasmEdge_ModuleInstanceFindMemory(
self.inner.lock().0 as *const _,
mem_name.as_raw(),
)
};
match ctx.is_null() {
true => Err(Box::new(WasmEdgeError::Instance(
InstanceError::NotFoundMem(name.as_ref().to_string()),
))),
false => Ok(Memory {
inner: Arc::new(Mutex::new(InnerMemory(ctx))),
registered: true,
}),
}
}
pub fn get_global(&self, name: impl AsRef<str>) -> WasmEdgeResult<Global> {
let global_name: WasmEdgeString = name.as_ref().into();
let ctx = unsafe {
ffi::WasmEdge_ModuleInstanceFindGlobal(
self.inner.lock().0 as *const _,
global_name.as_raw(),
)
};
match ctx.is_null() {
true => Err(Box::new(WasmEdgeError::Instance(
InstanceError::NotFoundGlobal(name.as_ref().to_string()),
))),
false => Ok(Global {
inner: Arc::new(Mutex::new(InnerGlobal(ctx))),
registered: true,
}),
}
}
pub fn func_len(&self) -> u32 {
unsafe { ffi::WasmEdge_ModuleInstanceListFunctionLength(self.inner.lock().0) }
}
pub fn func_names(&self) -> Option<Vec<String>> {
let len_func_names = self.func_len();
match len_func_names > 0 {
true => {
let mut func_names = Vec::with_capacity(len_func_names as usize);
unsafe {
ffi::WasmEdge_ModuleInstanceListFunction(
self.inner.lock().0,
func_names.as_mut_ptr(),
len_func_names,
);
func_names.set_len(len_func_names as usize);
}
let names = func_names
.into_iter()
.map(|x| x.into())
.collect::<Vec<String>>();
Some(names)
}
false => None,
}
}
pub fn table_len(&self) -> u32 {
unsafe { ffi::WasmEdge_ModuleInstanceListTableLength(self.inner.lock().0) }
}
pub fn table_names(&self) -> Option<Vec<String>> {
let len_table_names = self.table_len();
match len_table_names > 0 {
true => {
let mut table_names = Vec::with_capacity(len_table_names as usize);
unsafe {
ffi::WasmEdge_ModuleInstanceListTable(
self.inner.lock().0,
table_names.as_mut_ptr(),
len_table_names,
);
table_names.set_len(len_table_names as usize);
}
let names = table_names
.into_iter()
.map(|x| x.into())
.collect::<Vec<String>>();
Some(names)
}
false => None,
}
}
pub fn mem_len(&self) -> u32 {
unsafe { ffi::WasmEdge_ModuleInstanceListMemoryLength(self.inner.lock().0) }
}
pub fn mem_names(&self) -> Option<Vec<String>> {
let len_mem_names = self.mem_len();
match len_mem_names > 0 {
true => {
let mut mem_names = Vec::with_capacity(len_mem_names as usize);
unsafe {
ffi::WasmEdge_ModuleInstanceListMemory(
self.inner.lock().0,
mem_names.as_mut_ptr(),
len_mem_names,
);
mem_names.set_len(len_mem_names as usize);
}
let names = mem_names
.into_iter()
.map(|x| x.into())
.collect::<Vec<String>>();
Some(names)
}
false => None,
}
}
pub fn global_len(&self) -> u32 {
unsafe { ffi::WasmEdge_ModuleInstanceListGlobalLength(self.inner.lock().0) }
}
pub fn global_names(&self) -> Option<Vec<String>> {
let len_global_names = self.global_len();
match len_global_names > 0 {
true => {
let mut global_names = Vec::with_capacity(len_global_names as usize);
unsafe {
ffi::WasmEdge_ModuleInstanceListGlobal(
self.inner.lock().0,
global_names.as_mut_ptr(),
len_global_names,
);
global_names.set_len(len_global_names as usize);
}
let names = global_names
.into_iter()
.map(|x| x.into())
.collect::<Vec<String>>();
Some(names)
}
false => None,
}
}
pub fn host_data<T: Send + Sync + Clone>(&mut self) -> Option<&mut T> {
let ctx = unsafe { ffi::WasmEdge_ModuleInstanceGetHostData(self.inner.lock().0) };
match ctx.is_null() {
true => None,
false => {
let ctx = unsafe { &mut *(ctx as *mut T) };
Some(ctx)
}
}
}
#[cfg(feature = "ffi")]
#[cfg_attr(docsrs, doc(cfg(feature = "ffi")))]
pub fn as_ptr(&self) -> *const ffi::WasmEdge_ModuleInstanceContext {
self.inner.lock().0 as *const _
}
}
impl Clone for Instance {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
registered: self.registered,
}
}
}
#[derive(Debug)]
pub(crate) struct InnerInstance(pub(crate) *mut ffi::WasmEdge_ModuleInstanceContext);
unsafe impl Send for InnerInstance {}
unsafe impl Sync for InnerInstance {}
pub trait AsInstance {
fn get_func(&self, name: impl AsRef<str>) -> WasmEdgeResult<Function>;
fn func_len(&self) -> u32;
fn func_names(&self) -> Option<Vec<String>>;
fn get_table(&self, name: impl AsRef<str>) -> WasmEdgeResult<Table>;
fn table_len(&self) -> u32;
fn table_names(&self) -> Option<Vec<String>>;
fn get_memory(&self, name: impl AsRef<str>) -> WasmEdgeResult<Memory>;
fn mem_len(&self) -> u32;
fn mem_names(&self) -> Option<Vec<String>>;
fn get_global(&self, name: impl AsRef<str>) -> WasmEdgeResult<Global>;
fn global_len(&self) -> u32;
fn global_names(&self) -> Option<Vec<String>>;
}
#[derive(Debug, Clone)]
pub struct ImportModule<T: ?Sized + Send + Sync + Clone> {
pub(crate) inner: Arc<InnerInstance>,
pub(crate) registered: bool,
name: String,
funcs: Vec<Function>,
data: Option<Box<T>>,
}
impl<T: ?Sized + Send + Sync + Clone> Drop for ImportModule<T> {
#[allow(clippy::from_raw_with_void_ptr)]
fn drop(&mut self) {
if !self.registered && Arc::strong_count(&self.inner) == 1 && !self.inner.0.is_null() {
for func in self.funcs.iter() {
assert_eq!(Arc::strong_count(&func.inner), 1, "[wasmedge-sys] The host function is still in use while dropping the import module");
assert!(func.registered, "[wasmedge-sys] Trying to drop a non-registered host function while dropping the import module");
if func.data_owner {
unsafe {
let p = ffi::WasmEdge_FunctionInstanceGetData(func.inner.lock().0);
let _ = Box::from_raw(p as *mut std::ffi::c_void);
}
}
let footprint = func.inner.lock().0 as usize;
if let Some(key) = HOST_FUNC_FOOTPRINTS.lock().remove(&footprint) {
let mut map_host_func = HOST_FUNCS.write();
if map_host_func.contains_key(&key) {
let _ = map_host_func.remove(&key).expect(
"[wasmedge-sys] Failed to remove the host function from HOST_FUNCS_NEW container",
);
}
#[cfg(all(feature = "async", target_os = "linux"))]
{
let mut map_host_func = ASYNC_HOST_FUNCS.write();
if map_host_func.contains_key(&key) {
map_host_func.remove(&key).expect(
"[wasmedge-sys] Failed to remove the host function from ASYNC_HOST_FUNCS container",
);
}
}
}
}
unsafe {
ffi::WasmEdge_ModuleInstanceDelete(self.inner.0);
}
}
}
}
impl<T: ?Sized + Send + Sync + Clone> ImportModule<T> {
pub fn create(name: impl AsRef<str>, data: Option<Box<T>>) -> WasmEdgeResult<Self> {
let mut import = Self {
inner: std::sync::Arc::new(InnerInstance(std::ptr::null_mut())),
registered: false,
name: name.as_ref().to_string(),
funcs: Vec::new(),
data,
};
let raw_name = WasmEdgeString::from(name.as_ref());
let ctx = match import.data.as_mut() {
Some(boxed_data) => {
unsafe {
ffi::WasmEdge_ModuleInstanceCreateWithData(
raw_name.as_raw(),
boxed_data.as_mut() as *mut _ as *mut std::ffi::c_void,
None,
)
}
}
None => unsafe { ffi::WasmEdge_ModuleInstanceCreate(raw_name.as_raw()) },
};
import.inner = Arc::new(InnerInstance(ctx));
Ok(import)
}
#[cfg(feature = "ffi")]
#[cfg_attr(docsrs, doc(cfg(feature = "ffi")))]
pub fn as_ptr(&self) -> *const ffi::WasmEdge_ModuleInstanceContext {
self.inner.0 as *const _
}
}
impl<T: ?Sized + Send + Sync + Clone> AsImport for ImportModule<T> {
fn name(&self) -> &str {
self.name.as_str()
}
fn add_func(&mut self, name: impl AsRef<str>, func: Function) {
self.funcs.push(func);
let f = self.funcs.last_mut().unwrap();
f.registered = true;
let func_name: WasmEdgeString = name.into();
unsafe {
ffi::WasmEdge_ModuleInstanceAddFunction(
self.inner.0,
func_name.as_raw(),
f.inner.lock().0,
);
}
}
fn add_table(&mut self, name: impl AsRef<str>, table: Table) {
let table_name: WasmEdgeString = name.as_ref().into();
unsafe {
ffi::WasmEdge_ModuleInstanceAddTable(
self.inner.0,
table_name.as_raw(),
table.inner.lock().0,
);
}
table.inner.lock().0 = std::ptr::null_mut();
}
fn add_memory(&mut self, name: impl AsRef<str>, memory: Memory) {
let mem_name: WasmEdgeString = name.as_ref().into();
unsafe {
ffi::WasmEdge_ModuleInstanceAddMemory(
self.inner.0,
mem_name.as_raw(),
memory.inner.lock().0,
);
}
memory.inner.lock().0 = std::ptr::null_mut();
}
fn add_global(&mut self, name: impl AsRef<str>, global: Global) {
let global_name: WasmEdgeString = name.as_ref().into();
unsafe {
ffi::WasmEdge_ModuleInstanceAddGlobal(
self.inner.0,
global_name.as_raw(),
global.inner.lock().0,
);
}
global.inner.lock().0 = std::ptr::null_mut();
}
}
#[cfg(not(feature = "async"))]
#[derive(Debug, Clone)]
pub struct WasiModule {
pub(crate) inner: Arc<InnerInstance>,
pub(crate) registered: bool,
funcs: Vec<Function>,
}
#[cfg(not(feature = "async"))]
impl Drop for WasiModule {
fn drop(&mut self) {
if !self.registered && Arc::strong_count(&self.inner) == 1 && !self.inner.0.is_null() {
unsafe {
ffi::WasmEdge_ModuleInstanceDelete(self.inner.0);
}
self.funcs.drain(..);
}
}
}
#[cfg(not(feature = "async"))]
impl WasiModule {
pub fn create(
args: Option<Vec<&str>>,
envs: Option<Vec<&str>>,
preopens: Option<Vec<&str>>,
) -> WasmEdgeResult<Self> {
let cstr_args: Vec<_> = match args {
Some(args) => args
.iter()
.map(|&x| std::ffi::CString::new(x).unwrap())
.collect(),
None => vec![],
};
let mut p_args: Vec<_> = cstr_args.iter().map(|x| x.as_ptr()).collect();
let p_args_len = p_args.len();
p_args.push(std::ptr::null());
let cstr_envs: Vec<_> = match envs {
Some(envs) => envs
.iter()
.map(|&x| std::ffi::CString::new(x).unwrap())
.collect(),
None => vec![],
};
let mut p_envs: Vec<_> = cstr_envs.iter().map(|x| x.as_ptr()).collect();
let p_envs_len = p_envs.len();
p_envs.push(std::ptr::null());
let cstr_preopens: Vec<_> = match preopens {
Some(preopens) => preopens
.iter()
.map(|&x| std::ffi::CString::new(x).unwrap())
.collect(),
None => vec![],
};
let mut p_preopens: Vec<_> = cstr_preopens.iter().map(|x| x.as_ptr()).collect();
let p_preopens_len = p_preopens.len();
p_preopens.push(std::ptr::null());
let ctx = unsafe {
ffi::WasmEdge_ModuleInstanceCreateWASI(
p_args.as_ptr(),
p_args_len as u32,
p_envs.as_ptr(),
p_envs_len as u32,
p_preopens.as_ptr(),
p_preopens_len as u32,
)
};
match ctx.is_null() {
true => Err(Box::new(WasmEdgeError::ImportObjCreate)),
false => Ok(Self {
inner: std::sync::Arc::new(InnerInstance(ctx)),
registered: false,
funcs: Vec::new(),
}),
}
}
pub fn name(&self) -> &str {
"wasi_snapshot_preview1"
}
pub fn init_wasi(
&mut self,
args: Option<Vec<&str>>,
envs: Option<Vec<&str>>,
preopens: Option<Vec<&str>>,
) {
let cstr_args: Vec<_> = match args {
Some(args) => args
.iter()
.map(|&x| std::ffi::CString::new(x).unwrap())
.collect(),
None => vec![],
};
let mut p_args: Vec<_> = cstr_args.iter().map(|x| x.as_ptr()).collect();
let p_args_len = p_args.len();
p_args.push(std::ptr::null());
let cstr_envs: Vec<_> = match envs {
Some(envs) => envs
.iter()
.map(|&x| std::ffi::CString::new(x).unwrap())
.collect(),
None => vec![],
};
let mut p_envs: Vec<_> = cstr_envs.iter().map(|x| x.as_ptr()).collect();
let p_envs_len = p_envs.len();
p_envs.push(std::ptr::null());
let cstr_preopens: Vec<_> = match preopens {
Some(preopens) => preopens
.iter()
.map(|&x| std::ffi::CString::new(x).unwrap())
.collect(),
None => vec![],
};
let mut p_preopens: Vec<_> = cstr_preopens.iter().map(|x| x.as_ptr()).collect();
let p_preopens_len = p_preopens.len();
p_preopens.push(std::ptr::null());
unsafe {
ffi::WasmEdge_ModuleInstanceInitWASI(
self.inner.0,
p_args.as_ptr(),
p_args_len as u32,
p_envs.as_ptr(),
p_envs_len as u32,
p_preopens.as_ptr(),
p_preopens_len as u32,
)
};
}
pub fn exit_code(&self) -> u32 {
unsafe { ffi::WasmEdge_ModuleInstanceWASIGetExitCode(self.inner.0 as *const _) }
}
pub fn get_native_handler(&self, fd: i32) -> WasmEdgeResult<u64> {
let mut handler: u64 = 0;
let code: u32 = unsafe {
ffi::WasmEdge_ModuleInstanceWASIGetNativeHandler(
self.inner.0 as *const _,
fd,
&mut handler as *mut u64,
)
};
match code {
0 => Ok(handler),
_ => Err(Box::new(WasmEdgeError::Instance(
InstanceError::NotFoundMappedFdHandler,
))),
}
}
#[cfg(feature = "ffi")]
#[cfg_attr(docsrs, doc(cfg(feature = "ffi")))]
pub fn as_ptr(&self) -> *const ffi::WasmEdge_ModuleInstanceContext {
self.inner.0 as *const _
}
}
pub trait AsImport {
fn name(&self) -> &str;
fn add_func(&mut self, name: impl AsRef<str>, func: Function);
fn add_table(&mut self, name: impl AsRef<str>, table: Table);
fn add_memory(&mut self, name: impl AsRef<str>, memory: Memory);
fn add_global(&mut self, name: impl AsRef<str>, global: Global);
}
#[derive(Debug, Clone)]
pub enum WasiInstance {
#[cfg(not(feature = "async"))]
#[cfg_attr(docsrs, doc(cfg(not(feature = "async"))))]
Wasi(WasiModule),
#[cfg(all(feature = "async", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "async", target_os = "linux"))))]
AsyncWasi(AsyncWasiModule),
}
impl WasiInstance {
pub fn name(&self) -> &str {
match self {
#[cfg(not(feature = "async"))]
WasiInstance::Wasi(wasi) => wasi.name(),
#[cfg(all(feature = "async", target_os = "linux"))]
WasiInstance::AsyncWasi(async_wasi) => async_wasi.name(),
}
}
#[cfg(feature = "ffi")]
#[cfg_attr(docsrs, doc(cfg(feature = "ffi")))]
pub fn as_raw_ptr(&self) -> *const ffi::WasmEdge_ModuleInstanceContext {
match self {
#[cfg(not(feature = "async"))]
WasiInstance::Wasi(wasi) => wasi.inner.0,
#[cfg(all(feature = "async", target_os = "linux"))]
WasiInstance::AsyncWasi(async_wasi) => async_wasi.inner.0,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
CallingFrame, Config, Executor, FuncType, GlobalType, ImportModule, MemType, Store,
TableType, WasmValue, HOST_FUNCS, HOST_FUNC_FOOTPRINTS,
};
#[cfg(not(feature = "async"))]
use std::sync::{Arc, Mutex};
use std::thread;
use wasmedge_macro::sys_host_function;
use wasmedge_types::{error::HostFuncError, Mutability, NeverType, RefType, ValType};
#[test]
#[allow(clippy::assertions_on_result_states)]
fn test_instance_add_instance() {
assert_eq!(HOST_FUNCS.read().len(), 0);
assert_eq!(HOST_FUNC_FOOTPRINTS.lock().len(), 0);
let host_name = "extern";
let result = ImportModule::<NeverType>::create(host_name, None);
assert!(result.is_ok());
let mut import = result.unwrap();
let result = FuncType::create([ValType::ExternRef, ValType::I32], [ValType::I32]);
assert!(result.is_ok());
let func_ty = result.unwrap();
let result = Function::create_sync_func::<NeverType>(&func_ty, Box::new(real_add), None, 0);
assert!(result.is_ok());
assert_eq!(HOST_FUNCS.read().len(), 1);
assert_eq!(HOST_FUNC_FOOTPRINTS.lock().len(), 1);
let host_func = result.unwrap();
import.add_func("func-add", host_func);
assert_eq!(HOST_FUNCS.read().len(), 1);
assert_eq!(HOST_FUNC_FOOTPRINTS.lock().len(), 1);
let result = TableType::create(RefType::FuncRef, 10, Some(20));
assert!(result.is_ok());
let table_ty = result.unwrap();
let result = Table::create(&table_ty);
assert!(result.is_ok());
let host_table = result.unwrap();
import.add_table("table", host_table);
let result = MemType::create(1, Some(2), false);
assert!(result.is_ok());
let mem_ty = result.unwrap();
let result = Memory::create(&mem_ty);
assert!(result.is_ok());
let host_memory = result.unwrap();
import.add_memory("memory", host_memory);
let result = GlobalType::create(ValType::I32, Mutability::Const);
assert!(result.is_ok());
let global_ty = result.unwrap();
let result = Global::create(&global_ty, WasmValue::from_i32(666));
assert!(result.is_ok());
let host_global = result.unwrap();
import.add_global("global_i32", host_global);
}
#[test]
#[allow(clippy::assertions_on_result_states)]
fn test_instance_import_module_send() {
let host_name = "extern";
let result = ImportModule::<NeverType>::create(host_name, None);
assert!(result.is_ok());
let import = result.unwrap();
let handle = thread::spawn(move || {
assert!(!import.inner.0.is_null());
println!("{:?}", import.inner);
});
handle.join().unwrap();
}
#[test]
#[cfg(not(feature = "async"))]
#[allow(clippy::assertions_on_result_states)]
fn test_instance_import_module_sync() {
let host_name = "extern";
let result = ImportModule::<NeverType>::create(host_name, None);
assert!(result.is_ok());
let mut import = result.unwrap();
let result = FuncType::create(vec![ValType::I32; 2], vec![ValType::I32]);
assert!(result.is_ok());
let func_ty = result.unwrap();
let result = Function::create_sync_func::<NeverType>(&func_ty, Box::new(real_add), None, 0);
assert!(result.is_ok());
let host_func = result.unwrap();
import.add_func("add", host_func);
let result = TableType::create(RefType::FuncRef, 0, Some(u32::MAX));
assert!(result.is_ok());
let ty = result.unwrap();
let result = Table::create(&ty);
assert!(result.is_ok());
let table = result.unwrap();
import.add_table("table", table);
let memory = {
let result = MemType::create(10, Some(20), false);
assert!(result.is_ok());
let mem_ty = result.unwrap();
let result = Memory::create(&mem_ty);
assert!(result.is_ok());
result.unwrap()
};
import.add_memory("memory", memory);
let result = GlobalType::create(ValType::F32, Mutability::Const);
assert!(result.is_ok());
let ty = result.unwrap();
let result = Global::create(&ty, WasmValue::from_f32(3.5));
assert!(result.is_ok());
let global = result.unwrap();
import.add_global("global", global);
let import = Arc::new(Mutex::new(import));
let import_cloned = Arc::clone(&import);
let handle = thread::spawn(move || {
let result = import_cloned.lock();
assert!(result.is_ok());
let import = result.unwrap();
let result = Store::create();
assert!(result.is_ok());
let mut store = result.unwrap();
assert!(!store.inner.0.is_null());
assert!(!store.registered);
let result = Config::create();
assert!(result.is_ok());
let config = result.unwrap();
let result = Executor::create(Some(&config), None);
assert!(result.is_ok());
let mut executor = result.unwrap();
let result = executor.register_import_module(&mut store, &import);
assert!(result.is_ok());
let result = store.module("extern");
assert!(result.is_ok());
let instance = result.unwrap();
let result = instance.get_func("add");
assert!(result.is_ok());
let result = instance.get_global("global");
assert!(result.is_ok());
let global = result.unwrap();
assert!(!global.inner.lock().0.is_null() && global.registered);
let val = global.get_value();
assert_eq!(val.to_f32(), 3.5);
let result = instance.get_memory("memory");
assert!(result.is_ok());
let memory = result.unwrap();
let result = memory.ty();
assert!(result.is_ok());
let ty = result.unwrap();
assert_eq!(ty.min(), 10);
assert_eq!(ty.max(), Some(20));
let result = instance.get_table("table");
assert!(result.is_ok());
});
handle.join().unwrap();
}
#[cfg(all(not(feature = "async"), target_family = "unix"))]
#[test]
#[allow(clippy::assertions_on_result_states)]
fn test_instance_wasi() {
{
let result = WasiModule::create(None, None, None);
assert!(result.is_ok());
let result = WasiModule::create(
Some(vec!["arg1", "arg2"]),
Some(vec!["ENV1=VAL1", "ENV1=VAL2", "ENV3=VAL3"]),
Some(vec![
"apiTestData",
"Makefile",
"CMakeFiles",
"ssvmAPICoreTests",
".:.",
]),
);
assert!(result.is_ok());
let result = WasiModule::create(
None,
Some(vec!["ENV1=VAL1", "ENV1=VAL2", "ENV3=VAL3"]),
Some(vec![
"apiTestData",
"Makefile",
"CMakeFiles",
"ssvmAPICoreTests",
".:.",
]),
);
assert!(result.is_ok());
let wasi_import = result.unwrap();
assert_eq!(wasi_import.exit_code(), 0);
}
}
#[test]
#[cfg(not(feature = "async"))]
#[allow(clippy::assertions_on_result_states)]
fn test_instance_find_xxx() -> Result<(), Box<dyn std::error::Error>> {
let module_name = "extern_module";
let result = ImportModule::<NeverType>::create(module_name, None);
assert!(result.is_ok());
let mut import = result.unwrap();
let result = FuncType::create(vec![ValType::I32; 2], vec![ValType::I32]);
assert!(result.is_ok());
let func_ty = result.unwrap();
let result = Function::create_sync_func::<NeverType>(&func_ty, Box::new(real_add), None, 0);
assert!(result.is_ok());
let host_func = result.unwrap();
import.add_func("add", host_func);
let result = TableType::create(RefType::FuncRef, 0, Some(u32::MAX));
assert!(result.is_ok());
let ty = result.unwrap();
let result = Table::create(&ty);
assert!(result.is_ok());
let table = result.unwrap();
import.add_table("table", table);
let result = MemType::create(0, Some(u32::MAX), false);
assert!(result.is_ok());
let mem_ty = result.unwrap();
let result = Memory::create(&mem_ty);
assert!(result.is_ok());
let memory = result.unwrap();
import.add_memory("mem", memory);
let result = GlobalType::create(ValType::F32, Mutability::Const);
assert!(result.is_ok());
let ty = result.unwrap();
let result = Global::create(&ty, WasmValue::from_f32(3.5));
assert!(result.is_ok());
let global = result.unwrap();
import.add_global("global", global);
let mut executor = Executor::create(None, None)?;
let mut store = Store::create()?;
executor.register_import_module(&mut store, &import)?;
let result = store.module("extern_module");
assert!(result.is_ok());
let instance = result.unwrap();
assert!(instance.name().is_some());
assert_eq!(instance.name().unwrap(), "extern_module");
let result = instance.get_func("add");
assert!(result.is_ok());
let func = result.unwrap();
let result = func.ty();
assert!(result.is_ok());
let ty = result.unwrap();
let param_types = ty.params_type_iter().collect::<Vec<ValType>>();
assert_eq!(param_types, [ValType::I32, ValType::I32]);
let return_types = ty.returns_type_iter().collect::<Vec<ValType>>();
assert_eq!(return_types, [ValType::I32]);
let result = instance.get_table("table");
assert!(result.is_ok());
let table = result.unwrap();
let result = table.ty();
assert!(result.is_ok());
let ty = result.unwrap();
assert_eq!(ty.elem_ty(), RefType::FuncRef);
assert_eq!(ty.min(), 0);
assert_eq!(ty.max(), Some(u32::MAX));
let result = instance.get_memory("mem");
assert!(result.is_ok());
let memory = result.unwrap();
let result = memory.ty();
assert!(result.is_ok());
let ty = result.unwrap();
assert_eq!(ty.min(), 0);
assert_eq!(ty.max(), Some(u32::MAX));
let result = instance.get_global("global");
assert!(result.is_ok());
let global = result.unwrap();
let result = global.ty();
assert!(result.is_ok());
let global = result.unwrap();
assert_eq!(global.value_type(), ValType::F32);
assert_eq!(global.mutability(), Mutability::Const);
Ok(())
}
#[test]
#[cfg(not(feature = "async"))]
#[allow(clippy::assertions_on_result_states)]
fn test_instance_find_names() -> Result<(), Box<dyn std::error::Error>> {
let module_name = "extern_module";
let result = ImportModule::<NeverType>::create(module_name, None);
assert!(result.is_ok());
let mut import = result.unwrap();
let result = FuncType::create(vec![ValType::I32; 2], vec![ValType::I32]);
assert!(result.is_ok());
let func_ty = result.unwrap();
let result = Function::create_sync_func::<NeverType>(&func_ty, Box::new(real_add), None, 0);
assert!(result.is_ok());
let host_func = result.unwrap();
import.add_func("add", host_func);
let result = TableType::create(RefType::FuncRef, 0, Some(u32::MAX));
assert!(result.is_ok());
let ty = result.unwrap();
let result = Table::create(&ty);
assert!(result.is_ok());
let table = result.unwrap();
import.add_table("table", table);
let result = MemType::create(0, Some(u32::MAX), false);
assert!(result.is_ok());
let mem_ty = result.unwrap();
let result = Memory::create(&mem_ty);
assert!(result.is_ok());
let memory = result.unwrap();
import.add_memory("mem", memory);
let result = GlobalType::create(ValType::F32, Mutability::Const);
assert!(result.is_ok());
let ty = result.unwrap();
let result = Global::create(&ty, WasmValue::from_f32(3.5));
assert!(result.is_ok());
let global = result.unwrap();
import.add_global("global", global);
let mut executor = Executor::create(None, None)?;
let mut store = Store::create()?;
executor.register_import_module(&mut store, &import)?;
let result = store.module("extern_module");
assert!(result.is_ok());
let instance = result.unwrap();
assert!(instance.name().is_some());
assert_eq!(instance.name().unwrap(), "extern_module");
assert_eq!(instance.func_len(), 1);
let result = instance.func_names();
assert!(result.is_some());
assert_eq!(result.unwrap(), ["add"]);
assert_eq!(instance.table_len(), 1);
let result = instance.table_names();
assert!(result.is_some());
assert_eq!(result.unwrap(), ["table"]);
assert_eq!(instance.mem_len(), 1);
let result = instance.mem_names();
assert!(result.is_some());
assert_eq!(result.unwrap(), ["mem"]);
assert_eq!(instance.global_len(), 1);
let result = instance.global_names();
assert!(result.is_some());
assert_eq!(result.unwrap(), ["global"]);
Ok(())
}
#[test]
#[cfg(not(feature = "async"))]
#[allow(clippy::assertions_on_result_states)]
fn test_instance_get() {
let module_name = "extern_module";
let result = Store::create();
assert!(result.is_ok());
let mut store = result.unwrap();
assert!(!store.inner.0.is_null());
assert!(!store.registered);
assert_eq!(store.module_len(), 0);
assert!(store.module_names().is_none());
let result = ImportModule::<NeverType>::create(module_name, None);
assert!(result.is_ok());
let mut import = result.unwrap();
let result = FuncType::create(vec![ValType::I32; 2], vec![ValType::I32]);
assert!(result.is_ok());
let func_ty = result.unwrap();
let result = Function::create_sync_func::<NeverType>(&func_ty, Box::new(real_add), None, 0);
assert!(result.is_ok());
let host_func = result.unwrap();
import.add_func("add", host_func);
let result = TableType::create(RefType::FuncRef, 0, Some(u32::MAX));
assert!(result.is_ok());
let ty = result.unwrap();
let result = Table::create(&ty);
assert!(result.is_ok());
let table = result.unwrap();
import.add_table("table", table);
let memory = {
let result = MemType::create(10, Some(20), false);
assert!(result.is_ok());
let mem_ty = result.unwrap();
let result = Memory::create(&mem_ty);
assert!(result.is_ok());
result.unwrap()
};
import.add_memory("mem", memory);
let result = GlobalType::create(ValType::F32, Mutability::Const);
assert!(result.is_ok());
let ty = result.unwrap();
let result = Global::create(&ty, WasmValue::from_f32(3.5));
assert!(result.is_ok());
let global = result.unwrap();
import.add_global("global", global);
let result = Config::create();
assert!(result.is_ok());
let config = result.unwrap();
let result = Executor::create(Some(&config), None);
assert!(result.is_ok());
let mut executor = result.unwrap();
let result = executor.register_import_module(&mut store, &import);
assert!(result.is_ok());
let result = store.module(module_name);
assert!(result.is_ok());
let mut instance = result.unwrap();
let result = instance.get_memory("mem");
assert!(result.is_ok());
let memory = result.unwrap();
let result = memory.ty();
assert!(result.is_ok());
let ty = result.unwrap();
assert_eq!(ty.min(), 10);
assert_eq!(ty.max(), Some(20));
assert!(instance.host_data::<NeverType>().is_none());
}
#[sys_host_function]
fn real_add(
_frame: CallingFrame,
inputs: Vec<WasmValue>,
) -> Result<Vec<WasmValue>, HostFuncError> {
if inputs.len() != 2 {
return Err(HostFuncError::User(1));
}
let a = if inputs[0].ty() == ValType::I32 {
inputs[0].to_i32()
} else {
return Err(HostFuncError::User(2));
};
let b = if inputs[1].ty() == ValType::I32 {
inputs[1].to_i32()
} else {
return Err(HostFuncError::User(3));
};
let c = a + b;
Ok(vec![WasmValue::from_i32(c)])
}
#[cfg(not(feature = "async"))]
#[test]
#[allow(clippy::assertions_on_result_states)]
fn test_instance_clone() {
{
let host_name = "extern";
let result = ImportModule::<NeverType>::create(host_name, None);
assert!(result.is_ok());
let mut import = result.unwrap();
let result = FuncType::create([ValType::ExternRef, ValType::I32], [ValType::I32]);
assert!(result.is_ok());
let func_ty = result.unwrap();
let result =
Function::create_sync_func::<NeverType>(&func_ty, Box::new(real_add), None, 0);
assert!(result.is_ok());
let host_func = result.unwrap();
import.add_func("func-add", host_func);
let result = TableType::create(RefType::FuncRef, 10, Some(20));
assert!(result.is_ok());
let table_ty = result.unwrap();
let result = Table::create(&table_ty);
assert!(result.is_ok());
let host_table = result.unwrap();
import.add_table("table", host_table);
let result = MemType::create(1, Some(2), false);
assert!(result.is_ok());
let mem_ty = result.unwrap();
let result = Memory::create(&mem_ty);
assert!(result.is_ok());
let host_memory = result.unwrap();
import.add_memory("memory", host_memory);
let result = GlobalType::create(ValType::I32, Mutability::Const);
assert!(result.is_ok());
let global_ty = result.unwrap();
let result = Global::create(&global_ty, WasmValue::from_i32(666));
assert!(result.is_ok());
let host_global = result.unwrap();
import.add_global("global_i32", host_global);
assert_eq!(Arc::strong_count(&import.inner), 1);
let import_clone = import.clone();
assert_eq!(Arc::strong_count(&import.inner), 2);
drop(import);
assert_eq!(Arc::strong_count(&import_clone.inner), 1);
drop(import_clone);
}
{
let result = WasiModule::create(None, None, None);
assert!(result.is_ok());
let result = WasiModule::create(
Some(vec!["arg1", "arg2"]),
Some(vec!["ENV1=VAL1", "ENV1=VAL2", "ENV3=VAL3"]),
Some(vec![
"apiTestData",
"Makefile",
"CMakeFiles",
"ssvmAPICoreTests",
".:.",
]),
);
assert!(result.is_ok());
let result = WasiModule::create(
None,
Some(vec!["ENV1=VAL1", "ENV1=VAL2", "ENV3=VAL3"]),
Some(vec![
"apiTestData",
"Makefile",
"CMakeFiles",
"ssvmAPICoreTests",
".:.",
]),
);
assert!(result.is_ok());
let wasi_import = result.unwrap();
assert_eq!(wasi_import.exit_code(), 0);
assert_eq!(std::sync::Arc::strong_count(&wasi_import.inner), 1);
let wasi_import_clone = wasi_import.clone();
assert_eq!(std::sync::Arc::strong_count(&wasi_import.inner), 2);
drop(wasi_import);
assert_eq!(std::sync::Arc::strong_count(&wasi_import_clone.inner), 1);
drop(wasi_import_clone);
}
}
#[test]
fn test_instance_create_import_with_data() {
let module_name = "extern_module";
#[derive(Clone, Debug)]
struct Circle {
radius: i32,
}
let circle = Circle { radius: 10 };
let result = ImportModule::create(module_name, Some(Box::new(circle)));
assert!(result.is_ok());
let import = result.unwrap();
let result = Config::create();
assert!(result.is_ok());
let config = result.unwrap();
let result = Executor::create(Some(&config), None);
assert!(result.is_ok());
let mut executor = result.unwrap();
let result = Store::create();
assert!(result.is_ok());
let mut store = result.unwrap();
let result = executor.register_import_module(&mut store, &import);
assert!(result.is_ok());
let result = store.module(module_name);
assert!(result.is_ok());
let mut instance = result.unwrap();
let result = instance.host_data::<Circle>();
assert!(result.is_some());
let host_data = result.unwrap();
assert_eq!(host_data.radius, 10);
}
#[test]
fn test_instance_valgrind_memory_check() {
#[derive(Clone, Debug)]
struct Circle {
radius: i32,
}
impl Drop for Circle {
fn drop(&mut self) {
println!("dropping circle: {}", self.radius);
}
}
#[sys_host_function]
fn real_add_new(
_frame: CallingFrame,
inputs: Vec<WasmValue>,
data: &mut Circle,
) -> Result<Vec<WasmValue>, HostFuncError> {
println!("radius of circle: {}", data.radius);
if inputs.len() != 2 {
return Err(HostFuncError::User(1));
}
let a = if inputs[0].ty() == ValType::I32 {
inputs[0].to_i32()
} else {
return Err(HostFuncError::User(2));
};
let b = if inputs[1].ty() == ValType::I32 {
inputs[1].to_i32()
} else {
return Err(HostFuncError::User(3));
};
let c = a + b;
Ok(vec![WasmValue::from_i32(c)])
}
let host_name = "extern";
let result = ImportModule::<NeverType>::create(host_name, None);
assert!(result.is_ok());
let mut import = result.unwrap();
let result = FuncType::create([ValType::ExternRef, ValType::I32], [ValType::I32]);
assert!(result.is_ok());
let func_ty = result.unwrap();
let result = Function::create_sync_func::<Circle>(
&func_ty,
Box::new(real_add_new),
Some(Box::new(Circle { radius: 10 })),
0,
);
let host_func = result.unwrap();
import.add_func("func-add", host_func);
drop(import);
}
}