Skip to main content

wasmedge_sdk/
module.rs

1//! Defines WasmEdge AST Module, ImportType, and ExportType.
2
3use crate::{config::Config, ExternalInstanceType, WasmEdgeResult};
4use std::{borrow::Cow, marker::PhantomData, path::Path, sync::Arc};
5use wasmedge_sys as sys;
6
7/// Defines compiled in-memory representation of an input WASM binary.
8///
9/// A [Module] is a compiled in-memory representation of an input WebAssembly binary. In the instantiation process, a [Module] is instatiated to a module [instance](crate::Instance), from which the exported [function](crate::Func), [table](crate::Table), [memory](crate::Memory), and [global](crate::Global) instances can be fetched.
10#[derive(Debug, Clone)]
11pub struct Module {
12    pub(crate) inner: Arc<sys::Module>,
13}
14impl Module {
15    /// Returns a validated module from a file.
16    ///
17    /// # Arguments
18    ///
19    /// * `config` - The global configuration.
20    ///
21    /// * `file` - A wasm file or an AOT wasm file.
22    ///
23    /// # Error
24    ///
25    /// If fail to load and valiate a module from a file, returns an error.
26    pub fn from_file(config: Option<&Config>, file: impl AsRef<Path>) -> WasmEdgeResult<Self> {
27        let inner_config = config.map(|cfg| cfg.inner.as_ref());
28
29        // load module
30        let inner_module = sys::Loader::create(inner_config)?.from_file(file.as_ref())?;
31
32        // validate module
33        sys::Validator::create(inner_config)?.validate(&inner_module)?;
34
35        Ok(Self {
36            inner: inner_module,
37        })
38    }
39
40    /// Loads a WebAssembly binary module from in-memory bytes.
41    ///
42    /// # Arguments
43    ///
44    /// * `config` - The global configuration.
45    ///
46    /// * `bytes` - The in-memory bytes to be parsed.
47    ///
48    /// # Error
49    ///
50    /// If fail to load and valiate the WebAssembly module from the given in-memory bytes, returns an error.
51    pub fn from_bytes(config: Option<&Config>, bytes: impl AsRef<[u8]>) -> WasmEdgeResult<Self> {
52        let inner_config = config.map(|cfg| cfg.inner.as_ref());
53
54        // load module
55        let inner_module = sys::Loader::create(inner_config)?.from_bytes(bytes.as_ref())?;
56
57        // validate module
58        sys::Validator::create(inner_config)?.validate(&inner_module)?;
59
60        Ok(Self {
61            inner: inner_module,
62        })
63    }
64
65    /// Returns the count of the imported WasmEdge instances in the [module](crate::Module).
66    pub fn count_of_imports(&self) -> u32 {
67        self.inner.count_of_imports()
68    }
69
70    /// Returns the [import types](crate::ImportType) of all imported WasmEdge instances in the [module](crate::Module).
71    pub fn imports(&self) -> Vec<ImportType<'_>> {
72        let mut imports = Vec::new();
73        for inner_import in self.inner.imports() {
74            let import = ImportType {
75                inner: inner_import,
76                _marker: PhantomData,
77            };
78            imports.push(import);
79        }
80        imports
81    }
82
83    /// Returns the count of the exported WasmEdge instances from the [module](crate::Module).
84    pub fn count_of_exports(&self) -> u32 {
85        self.inner.count_of_exports()
86    }
87
88    /// Returns the [export types](crate::ExportType) of all exported WasmEdge instances (including funcs, tables, globals and memories) from the [module](crate::Module).
89    pub fn exports(&self) -> Vec<ExportType<'_>> {
90        let mut exports = Vec::new();
91        for inner_export in self.inner.export() {
92            let export = ExportType {
93                inner: inner_export,
94                _marker: PhantomData,
95            };
96            exports.push(export);
97        }
98        exports
99    }
100
101    /// Gets the [export type](crate::ExportType) by the name of a specific exported WasmEdge instance, such as func, table, global or memory instance.
102    ///
103    /// # Argument
104    ///
105    /// * `name` - The name of the target exported WasmEdge instance, such as func, table, global or memory instance.
106    pub fn get_export(&self, name: impl AsRef<str>) -> Option<ExternalInstanceType> {
107        let exports = self
108            .exports()
109            .into_iter()
110            .filter(|x| x.name() == name.as_ref())
111            .collect::<Vec<_>>();
112        match exports.is_empty() {
113            true => None,
114            false => exports[0].ty().ok(),
115        }
116    }
117}
118
119/// Defines the types of the imported instances.
120#[derive(Debug)]
121pub struct ImportType<'module> {
122    inner: sys::ImportType<'module>,
123    _marker: PhantomData<&'module Module>,
124}
125impl ImportType<'_> {
126    /// Returns the imported name of the WasmEdge instance.
127    pub fn name(&self) -> Cow<'_, str> {
128        self.inner.name()
129    }
130
131    /// Returns the name of the module hosting the imported WasmEdge instance.
132    pub fn module_name(&self) -> Cow<'_, str> {
133        self.inner.module_name()
134    }
135
136    /// Returns the type of the imported WasmEdge instance, which is one of the types defined in [ExternalInstanceType](wasmedge_types::ExternalInstanceType).
137    pub fn ty(&self) -> WasmEdgeResult<ExternalInstanceType> {
138        let ty = self.inner.ty()?;
139        Ok(ty)
140    }
141}
142
143/// Defines the types of the exported instances.
144#[derive(Debug)]
145pub struct ExportType<'module> {
146    inner: sys::ExportType<'module>,
147    _marker: PhantomData<&'module Module>,
148}
149impl ExportType<'_> {
150    /// Returns the exported name of the WasmEdge instance.
151    pub fn name(&self) -> Cow<'_, str> {
152        self.inner.name()
153    }
154
155    /// Returns the type of the exported WasmEdge instance, which is one of the types defined in [ExternalInstanceType](wasmedge_types::ExternalInstanceType).
156    pub fn ty(&self) -> WasmEdgeResult<ExternalInstanceType> {
157        let ty = self.inner.ty()?;
158        Ok(ty)
159    }
160}
161
162#[cfg(test)]
163mod tests {
164    use super::*;
165    use crate::{
166        error::{CoreError, CoreLoadError, WasmEdgeError},
167        wat2wasm,
168    };
169
170    #[test]
171    #[allow(clippy::assertions_on_result_states)]
172    fn test_module_from_wasm() {
173        // load wasm module from a specified wasm file
174        let file = std::env::current_dir()
175            .unwrap()
176            .join("examples/wasmedge-sys/data/fibonacci.wat");
177
178        let result = Module::from_file(None, file);
179        assert!(result.is_ok());
180
181        // attempt to load a non-existent wasm file
182        let result = Module::from_file(None, "not_exist_file.wasm");
183        assert!(result.is_err());
184        assert_eq!(
185            result.unwrap_err(),
186            Box::new(WasmEdgeError::Core(CoreError::Load(
187                CoreLoadError::IllegalPath
188            )))
189        );
190    }
191
192    #[test]
193    #[allow(clippy::assertions_on_result_states)]
194    fn test_module_from_wat() {
195        // load wasm module from a specified wasm file
196        let file = std::env::current_dir()
197            .unwrap()
198            .join("examples/wasmedge-sys/data/fibonacci.wat");
199
200        let result = Module::from_file(None, file);
201        assert!(result.is_ok());
202    }
203
204    #[test]
205    #[allow(clippy::assertions_on_result_states)]
206    fn test_module_from_bytes() {
207        // read the wasm bytes
208        let wasm_bytes = wat2wasm(
209            br#"
210        (module
211            (export "fib" (func $fib))
212            (func $fib (param $n i32) (result i32)
213             (if
214              (i32.lt_s
215               (local.get $n)
216               (i32.const 2)
217              )
218              (then
219                (return (i32.const 1))
220              )
221             )
222             (return
223              (i32.add
224               (call $fib
225                (i32.sub
226                 (local.get $n)
227                 (i32.const 2)
228                )
229               )
230               (call $fib
231                (i32.sub
232                 (local.get $n)
233                 (i32.const 1)
234                )
235               )
236              )
237             )
238            )
239           )
240"#,
241        )
242        .unwrap();
243
244        let result = Module::from_bytes(None, wasm_bytes);
245        assert!(result.is_ok());
246
247        // attempt to load an empty buffer
248        let result = Module::from_bytes(None, []);
249        assert_eq!(
250            result.unwrap_err(),
251            Box::new(WasmEdgeError::Core(CoreError::Load(
252                CoreLoadError::UnexpectedEnd
253            ))),
254        );
255    }
256
257    #[test]
258    #[allow(clippy::assertions_on_result_states)]
259    fn test_module_clone() {
260        // read the wasm bytes
261        let wasm_bytes = wat2wasm(
262            br#"
263        (module
264            (export "fib" (func $fib))
265            (func $fib (param $n i32) (result i32)
266             (if
267              (i32.lt_s
268               (local.get $n)
269               (i32.const 2)
270              )
271              (then
272                (return (i32.const 1))
273              )
274             )
275             (return
276              (i32.add
277               (call $fib
278                (i32.sub
279                 (local.get $n)
280                 (i32.const 2)
281                )
282               )
283               (call $fib
284                (i32.sub
285                 (local.get $n)
286                 (i32.const 1)
287                )
288               )
289              )
290             )
291            )
292           )
293"#,
294        )
295        .unwrap();
296
297        let result = Module::from_bytes(None, wasm_bytes);
298        assert!(result.is_ok());
299        let module = result.unwrap();
300        assert_eq!(module.exports().len(), 1);
301
302        // clone the module
303        let module_clone = module.clone();
304        assert_eq!(module.exports().len(), module_clone.exports().len());
305    }
306}