Skip to main content

wasmedge_sys/
loader.rs

1//! Defines WasmEdge Loader struct.
2
3use crate::{
4    ast_module::{InnerModule, Module},
5    ffi, utils,
6    utils::check,
7    Config, WasmEdgeResult,
8};
9use std::{path::Path, sync::Arc};
10use wasmedge_types::error::WasmEdgeError;
11
12/// [Loader](crate::Loader) is used to load WASM modules from the given WASM files or buffers.
13#[derive(Debug)]
14pub struct Loader {
15    pub(crate) inner: InnerLoader,
16}
17impl Loader {
18    /// Create a new [Loader](crate::Loader) to be associated with the given global configuration.
19    ///
20    /// # Arguments
21    ///
22    /// * `config` - A global configuration.
23    ///
24    /// # Error
25    ///
26    /// If fail to create a [Loader](crate), then an error is returned.
27    pub fn create(config: Option<&Config>) -> WasmEdgeResult<Self> {
28        let ctx = match config {
29            Some(config) => unsafe { ffi::WasmEdge_LoaderCreate(config.inner.0) },
30            None => unsafe { ffi::WasmEdge_LoaderCreate(std::ptr::null_mut()) },
31        };
32
33        match ctx.is_null() {
34            true => Err(Box::new(WasmEdgeError::LoaderCreate)),
35            false => Ok(Self {
36                inner: InnerLoader(ctx),
37            }),
38        }
39    }
40
41    /// Loads a WASM module from a WASM file.
42    ///
43    /// # Arguments
44    ///
45    /// * `file` - A wasm file or an AOT wasm file.
46    ///
47    /// # Error
48    ///
49    /// If fail to load, then an error is returned.
50    ///
51    /// # Example
52    ///
53    /// ```ignore
54    /// let file = "path/to/foo.wasm"
55    /// let module = loader.from_file(file)?;
56    /// ```
57    pub fn from_file(&self, file: impl AsRef<Path>) -> WasmEdgeResult<Arc<Module>> {
58        match file.as_ref().extension() {
59            Some(extension) => match extension.to_str() {
60                Some("wasm") => self.load_from_wasm_or_aot_file(&file),
61                #[cfg(target_os = "macos")]
62                Some("dylib") => self.load_from_wasm_or_aot_file(&file),
63                #[cfg(target_os = "linux")]
64                Some("so") => self.load_from_wasm_or_aot_file(&file),
65                #[cfg(target_os = "windows")]
66                Some("dll") => self.load_from_wasm_or_aot_file(&file),
67                Some("wat") => {
68                    let bytes = wat::parse_file(file.as_ref())
69                        .map_err(|_| WasmEdgeError::Operation("Failed to parse wat file".into()))?;
70                    self.from_bytes(bytes)
71                }
72                _ => Err(Box::new(WasmEdgeError::Operation(
73                    "The source file's extension should be one of `wasm`, `wat`, `dylib` on macOS, `so` on Linux or `dll` on Windows.".into(),
74                ))),
75            },
76            None => self.load_from_wasm_or_aot_file(&file),
77        }
78    }
79
80    fn load_from_wasm_or_aot_file(&self, file: impl AsRef<Path>) -> WasmEdgeResult<Arc<Module>> {
81        let c_path = utils::path_to_cstring(file.as_ref())?;
82        let mut mod_ctx = std::ptr::null_mut();
83        unsafe {
84            check(ffi::WasmEdge_LoaderParseFromFile(
85                self.inner.0,
86                &mut mod_ctx,
87                c_path.as_ptr(),
88            ))?;
89        }
90
91        match mod_ctx.is_null() {
92            true => Err(Box::new(WasmEdgeError::ModuleCreate)),
93            false => Ok(Module {
94                inner: InnerModule(mod_ctx),
95            }
96            .into()),
97        }
98    }
99
100    /// Loads a WASM module from a in-memory bytes.
101    ///
102    /// # Arguments
103    ///
104    /// * `bytes` - A in-memory WASM bytes.
105    ///
106    /// # Error
107    ///
108    /// If fail to load, then an error is returned.
109    ///
110    /// # Example
111    ///
112    /// ```ignore
113    /// let bytes = b"\0asm\x01\0\0\0";
114    /// let module = loader.from_bytes(&bytes)?;
115    /// ```
116    ///
117    /// Note that the text format is not accepted:
118    ///
119    /// ```ignore
120    /// assert!(loader.from_bytes(b"(module)").is_err());
121    /// ```
122    pub fn from_bytes(&self, bytes: impl AsRef<[u8]>) -> WasmEdgeResult<Arc<Module>> {
123        let bytes = bytes.as_ref();
124        let mut mod_ctx: *mut ffi::WasmEdge_ASTModuleContext = std::ptr::null_mut();
125
126        unsafe {
127            check(ffi::WasmEdge_LoaderParseFromBuffer(
128                self.inner.0,
129                &mut mod_ctx,
130                bytes.as_ptr(),
131                bytes.len() as u32,
132            ))?;
133        }
134
135        match mod_ctx.is_null() {
136            true => Err(Box::new(WasmEdgeError::ModuleCreate)),
137            false => Ok(Module {
138                inner: InnerModule(mod_ctx),
139            }
140            .into()),
141        }
142    }
143}
144impl Drop for Loader {
145    fn drop(&mut self) {
146        unsafe { ffi::WasmEdge_LoaderDelete(self.inner.0) }
147    }
148}
149
150#[derive(Debug)]
151pub(crate) struct InnerLoader(pub(crate) *mut ffi::WasmEdge_LoaderContext);
152unsafe impl Send for InnerLoader {}
153unsafe impl Sync for InnerLoader {}
154
155#[cfg(test)]
156mod tests {
157    use super::Loader;
158    use crate::Config;
159    use std::{
160        sync::{Arc, Mutex},
161        thread,
162    };
163    use wasmedge_types::error::{CoreError, CoreLoadError, WasmEdgeError};
164
165    #[test]
166    #[allow(clippy::assertions_on_result_states)]
167    fn test_loader() {
168        // create a Loader instance without configuration
169        let result = Loader::create(None);
170        assert!(result.is_ok());
171
172        // create a Loader instance with configuration
173        let result = Config::create();
174        assert!(result.is_ok());
175        let mut config = result.unwrap();
176        config.reference_types(true);
177        let result = Loader::create(Some(&config));
178        assert!(result.is_ok());
179        let loader = result.unwrap();
180
181        // load from file
182        {
183            // load .wasm file
184            let path = std::env::current_dir()
185                .unwrap()
186                .ancestors()
187                .nth(2)
188                .unwrap()
189                .join("examples/wasmedge-sys/data/fibonacci.wat");
190            let result = loader.from_file(path);
191            assert!(result.is_ok());
192            let module = result.unwrap();
193            assert!(!module.inner.0.is_null());
194
195            let path = std::env::current_dir()
196                .unwrap()
197                .ancestors()
198                .nth(2)
199                .unwrap()
200                .join("examples/wasmedge-sys/data/fibonacci.wat");
201            let result = loader.from_file(path);
202            assert!(result.is_ok());
203
204            let result = loader.from_file("not_exist_file.wasm");
205            assert!(result.is_err());
206            assert_eq!(
207                result.unwrap_err(),
208                Box::new(WasmEdgeError::Core(CoreError::Load(
209                    CoreLoadError::IllegalPath
210                )))
211            );
212        }
213
214        // load from buffer
215        {
216            let buffer = b"\0asm\x01\0\0\0";
217            let result = loader.from_bytes(buffer);
218            assert!(result.is_ok());
219            let module = result.unwrap();
220            assert!(!module.inner.0.is_null());
221
222            // the text format is not accepted
223            let result = loader.from_bytes(b"(module)");
224            assert!(result.is_err());
225            assert_eq!(
226                result.unwrap_err(),
227                Box::new(WasmEdgeError::Core(CoreError::Load(
228                    CoreLoadError::MalformedMagic
229                )))
230            );
231
232            // empty is not accepted
233            let result = loader.from_bytes([]);
234            assert!(result.is_err());
235            assert_eq!(
236                result.unwrap_err(),
237                Box::new(WasmEdgeError::Core(CoreError::Load(
238                    CoreLoadError::UnexpectedEnd
239                )))
240            );
241        }
242    }
243
244    #[test]
245    #[allow(clippy::assertions_on_result_states)]
246    fn test_loader_send() {
247        // create a Loader instance without configuration
248        let result = Loader::create(None);
249        assert!(result.is_ok());
250
251        // create a Loader instance with configuration
252        let result = Config::create();
253        assert!(result.is_ok());
254        let mut config = result.unwrap();
255        config.reference_types(true);
256        let result = Loader::create(Some(&config));
257        assert!(result.is_ok());
258        let loader = result.unwrap();
259
260        let handle = thread::spawn(move || {
261            assert!(!loader.inner.0.is_null());
262            println!("{:?}", loader.inner);
263        });
264
265        handle.join().unwrap();
266    }
267
268    #[test]
269    #[allow(clippy::assertions_on_result_states)]
270    fn test_loader_sync() {
271        // create a Loader instance without configuration
272        let result = Loader::create(None);
273        assert!(result.is_ok());
274
275        // create a Loader instance with configuration
276        let result = Config::create();
277        assert!(result.is_ok());
278        let mut config = result.unwrap();
279        config.reference_types(true);
280        let result = Loader::create(Some(&config));
281        assert!(result.is_ok());
282        let loader = Arc::new(Mutex::new(result.unwrap()));
283
284        let loader_cloned = Arc::clone(&loader);
285        let handle = thread::spawn(move || {
286            let result = loader_cloned.lock();
287            assert!(result.is_ok());
288            let loader = result.unwrap();
289
290            assert!(!loader.inner.0.is_null());
291        });
292
293        handle.join().unwrap();
294    }
295}