use crate::{config::Config, ExternalInstanceType, WasmEdgeResult};
use std::{borrow::Cow, marker::PhantomData, path::Path, sync::Arc};
use wasmedge_sys as sys;
#[derive(Debug, Clone)]
pub struct Module {
pub(crate) inner: Arc<sys::Module>,
}
impl Module {
pub fn from_file(config: Option<&Config>, file: impl AsRef<Path>) -> WasmEdgeResult<Self> {
let inner_config = config.map(|cfg| cfg.inner.as_ref());
let inner_module = sys::Loader::create(inner_config)?.from_file(file.as_ref())?;
sys::Validator::create(inner_config)?.validate(&inner_module)?;
Ok(Self {
inner: inner_module,
})
}
pub fn from_bytes(config: Option<&Config>, bytes: impl AsRef<[u8]>) -> WasmEdgeResult<Self> {
let inner_config = config.map(|cfg| cfg.inner.as_ref());
let inner_module = sys::Loader::create(inner_config)?.from_bytes(bytes.as_ref())?;
sys::Validator::create(inner_config)?.validate(&inner_module)?;
Ok(Self {
inner: inner_module,
})
}
pub fn count_of_imports(&self) -> u32 {
self.inner.count_of_imports()
}
pub fn imports(&self) -> Vec<ImportType> {
let mut imports = Vec::new();
for inner_import in self.inner.imports() {
let import = ImportType {
inner: inner_import,
_marker: PhantomData,
};
imports.push(import);
}
imports
}
pub fn count_of_exports(&self) -> u32 {
self.inner.count_of_exports()
}
pub fn exports(&self) -> Vec<ExportType> {
let mut exports = Vec::new();
for inner_export in self.inner.export() {
let export = ExportType {
inner: inner_export,
_marker: PhantomData,
};
exports.push(export);
}
exports
}
pub fn get_export(&self, name: impl AsRef<str>) -> Option<ExternalInstanceType> {
let exports = self
.exports()
.into_iter()
.filter(|x| x.name() == name.as_ref())
.collect::<Vec<_>>();
match exports.is_empty() {
true => None,
false => exports[0].ty().ok(),
}
}
}
#[derive(Debug)]
pub struct ImportType<'module> {
inner: sys::ImportType<'module>,
_marker: PhantomData<&'module Module>,
}
impl<'module> ImportType<'module> {
pub fn name(&self) -> Cow<'_, str> {
self.inner.name()
}
pub fn module_name(&self) -> Cow<'_, str> {
self.inner.module_name()
}
pub fn ty(&self) -> WasmEdgeResult<ExternalInstanceType> {
let ty = self.inner.ty()?;
Ok(ty)
}
}
#[derive(Debug)]
pub struct ExportType<'module> {
inner: sys::ExportType<'module>,
_marker: PhantomData<&'module Module>,
}
impl<'module> ExportType<'module> {
pub fn name(&self) -> Cow<'_, str> {
self.inner.name()
}
pub fn ty(&self) -> WasmEdgeResult<ExternalInstanceType> {
let ty = self.inner.ty()?;
Ok(ty)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
error::{CoreError, CoreLoadError, WasmEdgeError},
wat2wasm,
};
#[test]
#[allow(clippy::assertions_on_result_states)]
fn test_module_from_wasm() {
let file = std::env::current_dir()
.unwrap()
.join("examples/wasmedge-sys/data/fibonacci.wat");
let result = Module::from_file(None, file);
assert!(result.is_ok());
let result = Module::from_file(None, "not_exist_file.wasm");
assert!(result.is_err());
assert_eq!(
result.unwrap_err(),
Box::new(WasmEdgeError::Core(CoreError::Load(
CoreLoadError::IllegalPath
)))
);
}
#[test]
#[allow(clippy::assertions_on_result_states)]
fn test_module_from_wat() {
let file = std::env::current_dir()
.unwrap()
.join("examples/wasmedge-sys/data/fibonacci.wat");
let result = Module::from_file(None, file);
assert!(result.is_ok());
}
#[test]
#[allow(clippy::assertions_on_result_states)]
fn test_module_from_bytes() {
let wasm_bytes = 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)
)
)
)
)
)
)
"#,
)
.unwrap();
let result = Module::from_bytes(None, wasm_bytes);
assert!(result.is_ok());
let result = Module::from_bytes(None, []);
assert_eq!(
result.unwrap_err(),
Box::new(WasmEdgeError::Core(CoreError::Load(
CoreLoadError::UnexpectedEnd
))),
);
}
#[test]
#[allow(clippy::assertions_on_result_states)]
fn test_module_clone() {
let wasm_bytes = 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)
)
)
)
)
)
)
"#,
)
.unwrap();
let result = Module::from_bytes(None, wasm_bytes);
assert!(result.is_ok());
let module = result.unwrap();
assert_eq!(module.exports().len(), 1);
let module_clone = module.clone();
assert_eq!(module.exports().len(), module_clone.exports().len());
}
}