wamr_rust_sdk/
module.rs

1/*
2 * Copyright (C) 2019 Intel Corporation. All rights reserved.
3 * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4 */
5
6//! .wasm compiled, in-memory representation
7//! get one via `Module::from_file()` or `Module::from_buf()`
8
9use crate::{
10    helper::error_buf_to_string, helper::DEFAULT_ERROR_BUF_SIZE, runtime::Runtime,
11    wasi_context::WasiCtx, RuntimeError,
12};
13use core::marker::PhantomData;
14use std::{
15    ffi::{c_char, CString},
16    fs::File,
17    io::Read,
18    path::Path,
19    ptr,
20    string::String,
21    vec::Vec,
22};
23use wamr_sys::{
24    wasm_module_t, wasm_runtime_load, wasm_runtime_set_module_name,
25    wasm_runtime_set_wasi_addr_pool, wasm_runtime_set_wasi_args,
26    wasm_runtime_set_wasi_ns_lookup_pool, wasm_runtime_unload,
27};
28
29#[allow(dead_code)]
30#[derive(Debug)]
31pub struct Module<'runtime> {
32    name: String,
33    module: wasm_module_t,
34    // to keep the module content in memory
35    content: Vec<u8>,
36    wasi_ctx: WasiCtx,
37    _phantom: PhantomData<&'runtime Runtime>,
38}
39
40impl<'runtime> Module<'runtime> {
41    /// compile a module with the given wasm file path, use the file name as the module name
42    ///
43    /// # Error
44    ///
45    /// If the file does not exist or the file cannot be read, an `RuntimeError::WasmFileFSError` will be returned.
46    /// If the wasm file is not a valid wasm file, an `RuntimeError::CompilationError` will be returned.
47    pub fn from_file(runtime: &'runtime Runtime, wasm_file: &Path) -> Result<Self, RuntimeError> {
48        let name = wasm_file.file_name().unwrap().to_str().unwrap();
49        let mut wasm_file = File::open(wasm_file)?;
50
51        let mut binary: Vec<u8> = Vec::new();
52        wasm_file.read_to_end(&mut binary)?;
53
54        Self::from_vec(runtime, binary, name)
55    }
56
57    /// compile a module int the given buffer,
58    ///
59    /// # Error
60    ///
61    /// If the file does not exist or the file cannot be read, an `RuntimeError::WasmFileFSError` will be returned.
62    /// If the wasm file is not a valid wasm file, an `RuntimeError::CompilationError` will be returned.
63    pub fn from_vec(
64        _runtime: &'runtime Runtime,
65        mut content: Vec<u8>,
66        name: &str,
67    ) -> Result<Self, RuntimeError> {
68        let mut error_buf: [c_char; DEFAULT_ERROR_BUF_SIZE] = [0; DEFAULT_ERROR_BUF_SIZE];
69        let module = unsafe {
70            wasm_runtime_load(
71                content.as_mut_ptr(),
72                content.len() as u32,
73                error_buf.as_mut_ptr(),
74                error_buf.len() as u32,
75            )
76        };
77
78        if module.is_null() {
79            match error_buf.len() {
80                0 => {
81                    return Err(RuntimeError::CompilationError(String::from(
82                        "load module failed",
83                    )))
84                }
85                _ => {
86                    return Err(RuntimeError::CompilationError(error_buf_to_string(
87                        &error_buf,
88                    )))
89                }
90            }
91        }
92
93        unsafe {
94            let name_c = CString::new(name.as_bytes()).unwrap();
95            if !wasm_runtime_set_module_name(
96                module,
97                name_c.as_ptr() as *mut c_char,
98                error_buf.as_mut_ptr(),
99                error_buf.len() as u32,
100            ) {
101                return Err(RuntimeError::CompilationError(error_buf_to_string(
102                    &error_buf,
103                )));
104            }
105        }
106
107        Ok(Module {
108            name: String::from(name),
109            module,
110            content,
111            wasi_ctx: WasiCtx::default(),
112            _phantom: PhantomData,
113        })
114    }
115
116    /// set Wasi context for a module
117    ///
118    /// This function should be called before `Instance::new`
119    pub fn set_wasi_context(&mut self, wasi_ctx: WasiCtx) {
120        self.wasi_ctx = wasi_ctx;
121
122        let real_paths = if self.wasi_ctx.get_preopen_real_paths().is_empty() {
123            ptr::null_mut()
124        } else {
125            self.wasi_ctx.get_preopen_real_paths().as_ptr() as *mut *const c_char
126        };
127
128        let mapped_paths = if self.wasi_ctx.get_preopen_mapped_paths().is_empty() {
129            ptr::null_mut()
130        } else {
131            self.wasi_ctx.get_preopen_mapped_paths().as_ptr() as *mut *const c_char
132        };
133
134        let env = if self.wasi_ctx.get_env_vars().is_empty() {
135            ptr::null_mut()
136        } else {
137            self.wasi_ctx.get_env_vars_ptrs().as_ptr() as *mut *const c_char
138        };
139
140        let args = if self.wasi_ctx.get_arguments().is_empty() {
141            ptr::null_mut()
142        } else {
143            self.wasi_ctx.get_arguments_ptrs().as_ptr() as *mut *mut c_char
144        };
145
146        unsafe {
147            wasm_runtime_set_wasi_args(
148                self.get_inner_module(),
149                real_paths,
150                self.wasi_ctx.get_preopen_real_paths().len() as u32,
151                mapped_paths,
152                self.wasi_ctx.get_preopen_mapped_paths().len() as u32,
153                env,
154                self.wasi_ctx.get_env_vars().len() as u32,
155                args,
156                self.wasi_ctx.get_arguments().len() as i32,
157            );
158
159            let ns_lookup_pool = if self.wasi_ctx.get_allowed_dns().is_empty() {
160                ptr::null_mut()
161            } else {
162                self.wasi_ctx.get_allowed_dns().as_ptr() as *mut *const c_char
163            };
164
165            wasm_runtime_set_wasi_ns_lookup_pool(
166                self.get_inner_module(),
167                ns_lookup_pool,
168                self.wasi_ctx.get_allowed_dns().len() as u32,
169            );
170
171            let addr_pool = if self.wasi_ctx.get_allowed_address().is_empty() {
172                ptr::null_mut()
173            } else {
174                self.wasi_ctx.get_allowed_address().as_ptr() as *mut *const c_char
175            };
176            wasm_runtime_set_wasi_addr_pool(
177                self.get_inner_module(),
178                addr_pool,
179                self.wasi_ctx.get_allowed_address().len() as u32,
180            );
181        }
182    }
183
184    pub fn get_inner_module(&self) -> wasm_module_t {
185        self.module
186    }
187
188    pub fn get_name(&self) -> &str {
189        &self.name
190    }
191}
192
193impl Drop for Module<'_> {
194    fn drop(&mut self) {
195        unsafe {
196            wasm_runtime_unload(self.module);
197        }
198    }
199}
200
201#[cfg(test)]
202mod tests {
203    use super::*;
204    use crate::{helper::cstr_to_string, runtime::Runtime, wasi_context::WasiCtxBuilder};
205    use std::path::PathBuf;
206    use wamr_sys::wasm_runtime_get_module_name;
207
208    #[test]
209    fn test_module_not_exist() {
210        let runtime = Runtime::new();
211        assert!(runtime.is_ok());
212
213        let runtime = runtime.unwrap();
214
215        let module = Module::from_file(&runtime, Path::new("not_exist"));
216        assert!(module.is_err());
217    }
218
219    #[test]
220    fn test_module_from_buf() {
221        let runtime = Runtime::new().unwrap();
222
223        // (module
224        //   (func (export "add") (param i32 i32) (result i32)
225        //     (local.get 0)
226        //     (local.get 1)
227        //     (i32.add)
228        //   )
229        // )
230        let binary = vec![
231            0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x01, 0x60, 0x02, 0x7f,
232            0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 0x07, 0x01, 0x03, 0x61, 0x64, 0x64,
233            0x00, 0x00, 0x0a, 0x09, 0x01, 0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x6a, 0x0b,
234        ];
235        let binary = binary.into_iter().map(|c| c as u8).collect::<Vec<u8>>();
236
237        let module = Module::from_vec(&runtime, binary, "");
238        assert!(module.is_ok());
239    }
240
241    #[test]
242    fn test_module_from_file() {
243        let runtime = Runtime::new().unwrap();
244
245        let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
246        d.push("resources/test");
247        d.push("gcd_wasm32_wasi.wasm");
248        let module = Module::from_file(&runtime, d.as_path());
249        assert!(module.is_ok());
250    }
251
252    #[test]
253    fn test_module_with_wasi_args() {
254        let runtime = Runtime::new().unwrap();
255
256        // (module
257        //   (func (export "add") (param i32 i32) (result i32)
258        //     (local.get 0)
259        //     (local.get 1)
260        //     (i32.add)
261        //   )
262        // )
263        let binary = vec![
264            0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x01, 0x60, 0x02, 0x7f,
265            0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 0x07, 0x01, 0x03, 0x61, 0x64, 0x64,
266            0x00, 0x00, 0x0a, 0x09, 0x01, 0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x6a, 0x0b,
267        ];
268        let binary = binary.into_iter().map(|c| c as u8).collect::<Vec<u8>>();
269
270        let module = Module::from_vec(&runtime, binary, "add");
271        assert!(module.is_ok());
272        let mut module = module.unwrap();
273
274        let wasi_ctx = WasiCtxBuilder::new()
275            .set_pre_open_path(vec!["."], vec![])
276            .set_env_vars(vec![])
277            .set_allowed_address(vec![])
278            .set_allowed_dns(vec![])
279            .build();
280
281        module.set_wasi_context(wasi_ctx);
282    }
283
284    #[test]
285    fn test_module_name() -> Result<(), RuntimeError> {
286        let runtime = Runtime::new()?;
287
288        // (module
289        //   (func (export "add") (param i32 i32) (result i32)
290        //     (local.get 0)
291        //     (local.get 1)
292        //     (i32.add)
293        //   )
294        // )
295        let binary = vec![
296            0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x01, 0x60, 0x02, 0x7f,
297            0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 0x07, 0x01, 0x03, 0x61, 0x64, 0x64,
298            0x00, 0x00, 0x0a, 0x09, 0x01, 0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x6a, 0x0b,
299        ];
300        let binary = binary.into_iter().map(|c| c as u8).collect::<Vec<u8>>();
301
302        let module = Module::from_vec(&runtime, binary, "add")?;
303
304        assert_eq!(module.get_name(), "add");
305
306        let name =
307            cstr_to_string(unsafe { wasm_runtime_get_module_name(module.get_inner_module()) });
308        assert_eq!(&name, module.get_name());
309
310        Ok(())
311    }
312}