1use crate::config;
2use crate::error;
3use crate::imports;
4use crate::utils;
5use crate::wasi;
6use std::sync::{Arc, Weak};
7
8use zwasm_sys as sys;
17
18struct ModuleInner {
19 ptr: *mut sys::zwasm_module_t,
20}
21
22impl Drop for ModuleInner {
23 fn drop(&mut self) {
24 unsafe {
25 sys::zwasm_module_delete(self.ptr);
26 }
27 }
28}
29
30unsafe impl Send for ModuleInner {}
34unsafe impl Sync for ModuleInner {}
35
36#[derive(Clone)]
41pub struct CancelHandle {
42 inner: Weak<ModuleInner>,
43}
44
45impl CancelHandle {
46 pub fn cancel(&self) {
48 if let Some(inner) = self.inner.upgrade() {
49 unsafe { sys::zwasm_module_cancel(inner.ptr) };
50 }
51 }
52}
53
54pub struct Module {
55 inner: Arc<ModuleInner>,
56 _not_send_sync: std::marker::PhantomData<std::rc::Rc<()>>,
57}
58
59impl Module {
60 fn from_raw(ptr: *mut sys::zwasm_module_t) -> Result<Self, error::ZwasmError> {
61 if ptr.is_null() {
62 Err(error::last_error()
63 .unwrap_or_else(|| error::ZwasmError("Unknown error".to_string())))
64 } else {
65 Ok(Module {
66 inner: Arc::new(ModuleInner { ptr }),
67 _not_send_sync: std::marker::PhantomData,
68 })
69 }
70 }
71
72 fn raw_ptr(&self) -> *mut sys::zwasm_module_t {
73 self.inner.ptr
74 }
75
76 pub fn new(wasm_bytes: &[u8]) -> Result<Self, error::ZwasmError> {
84 let ptr = unsafe { sys::zwasm_module_new(wasm_bytes.as_ptr(), wasm_bytes.len()) };
85
86 Self::from_raw(ptr)
87 }
88
89 pub fn new_wasi(wasm_bytes: &[u8]) -> Result<Self, error::ZwasmError> {
93 let ptr = unsafe { sys::zwasm_module_new_wasi(wasm_bytes.as_ptr(), wasm_bytes.len()) };
94
95 Self::from_raw(ptr)
96 }
97
98 pub fn new_configured(
102 wasm_bytes: &[u8],
103 config: &config::Config,
104 ) -> Result<Self, error::ZwasmError> {
105 let ptr = unsafe {
106 sys::zwasm_module_new_configured(wasm_bytes.as_ptr(), wasm_bytes.len(), config.ptr)
107 };
108
109 Self::from_raw(ptr)
110 }
111
112 pub fn new_wasi_configured(
116 wasm_bytes: &[u8],
117 wasi_config: &wasi::WasiConfig,
118 ) -> Result<Self, error::ZwasmError> {
119 let ptr = unsafe {
120 sys::zwasm_module_new_wasi_configured(
121 wasm_bytes.as_ptr(),
122 wasm_bytes.len(),
123 wasi_config.ptr,
124 )
125 };
126
127 Self::from_raw(ptr)
128 }
129
130 pub fn new_wasi_configured2(
134 wasm_bytes: &[u8],
135 wasi_config: &wasi::WasiConfig,
136 config: &config::Config,
137 ) -> Result<Self, error::ZwasmError> {
138 let ptr = unsafe {
139 sys::zwasm_module_new_wasi_configured2(
140 wasm_bytes.as_ptr(),
141 wasm_bytes.len(),
142 wasi_config.ptr,
143 config.ptr,
144 )
145 };
146
147 Self::from_raw(ptr)
148 }
149
150 pub fn new_with_imports(
154 wasm_bytes: &[u8],
155 imports: &imports::Imports,
156 ) -> Result<Self, error::ZwasmError> {
157 let ptr = unsafe {
158 sys::zwasm_module_new_with_imports(wasm_bytes.as_ptr(), wasm_bytes.len(), imports.ptr)
159 };
160
161 Self::from_raw(ptr)
162 }
163
164 pub fn validate(wasm_bytes: &[u8]) -> Result<(), error::ZwasmError> {
168 let ok = unsafe { sys::zwasm_module_validate(wasm_bytes.as_ptr(), wasm_bytes.len()) };
169
170 if !ok {
171 Err(error::last_error()
172 .unwrap_or_else(|| error::ZwasmError("Unknown error".to_string())))
173 } else {
174 Ok(())
175 }
176 }
177
178 pub fn invoke(&self, name: &str, args: &[u64]) -> Result<Vec<u64>, error::ZwasmError> {
192 let c_name = std::ffi::CString::new(name)
193 .map_err(|_| error::ZwasmError("function name contains NUL byte".into()))?;
194 let nresults = self.export_result_count_by_name(name)? as usize;
195 let mut results = vec![0u64; nresults];
196
197 let ok = unsafe {
198 sys::zwasm_module_invoke(
199 self.raw_ptr(),
200 c_name.as_ptr(),
201 if args.is_empty() {
202 std::ptr::null_mut()
203 } else {
204 args.as_ptr() as *mut u64
205 },
206 args.len() as u32,
207 if results.is_empty() {
208 std::ptr::null_mut()
209 } else {
210 results.as_mut_ptr()
211 },
212 results.len() as u32,
213 )
214 };
215
216 if !ok {
217 Err(error::last_error()
218 .unwrap_or_else(|| error::ZwasmError("Unknown error".to_string())))
219 } else {
220 Ok(results)
221 }
222 }
223
224 pub fn invoke_start(&self) -> Result<(), error::ZwasmError> {
232 let ok = unsafe { sys::zwasm_module_invoke_start(self.raw_ptr()) };
233
234 if !ok {
235 Err(error::last_error()
236 .unwrap_or_else(|| error::ZwasmError("Unknown error".to_string())))
237 } else {
238 Ok(())
239 }
240 }
241
242 pub fn export_count(&self) -> u32 {
248 unsafe { sys::zwasm_module_export_count(self.raw_ptr()) }
249 }
250
251 pub fn export_name(&self, index: u32) -> Option<String> {
255 let ptr = unsafe { sys::zwasm_module_export_name(self.raw_ptr(), index) };
256
257 if ptr.is_null() {
258 None
259 } else {
260 Some(
261 unsafe { std::ffi::CStr::from_ptr(ptr) }
262 .to_string_lossy()
263 .into_owned(),
264 )
265 }
266 }
267
268 pub fn export_param_count(&self, index: u32) -> u32 {
272 unsafe { sys::zwasm_module_export_param_count(self.raw_ptr(), index) }
273 }
274
275 pub fn export_result_count(&self, index: u32) -> u32 {
279 unsafe { sys::zwasm_module_export_result_count(self.raw_ptr(), index) }
280 }
281
282 pub fn cancel_handle(&self) -> CancelHandle {
284 CancelHandle {
285 inner: Arc::downgrade(&self.inner),
286 }
287 }
288
289 pub fn cancel(&self) {
295 self.cancel_handle().cancel();
296 }
297
298 pub unsafe fn memory_data(&self) -> Option<&[u8]> {
311 let ptr = unsafe { sys::zwasm_module_memory_data(self.raw_ptr()) };
312
313 if ptr.is_null() {
314 None
315 } else {
316 Some(unsafe { std::slice::from_raw_parts(ptr, self.memory_size()) })
317 }
318 }
319
320 pub fn memory_data_copy(&self) -> Option<Vec<u8>> {
328 unsafe { self.memory_data().map(|s| s.to_vec()) }
329 }
330
331 pub fn memory_size(&self) -> usize {
333 unsafe { sys::zwasm_module_memory_size(self.raw_ptr()) }
334 }
335
336 pub fn memory_read(&self, offset: u32, buf: &mut [u8]) -> Result<(), error::ZwasmError> {
342 let len = utils::to_u32_len(buf.len())?;
343 let ok =
344 unsafe { sys::zwasm_module_memory_read(self.raw_ptr(), offset, len, buf.as_mut_ptr()) };
345
346 if !ok {
347 Err(error::last_error()
348 .unwrap_or_else(|| error::ZwasmError("Unknown error".to_string())))
349 } else {
350 Ok(())
351 }
352 }
353
354 pub fn memory_write(&self, offset: u32, data: &[u8]) -> Result<(), error::ZwasmError> {
360 let len = utils::to_u32_len(data.len())?;
361 let ok =
362 unsafe { sys::zwasm_module_memory_write(self.raw_ptr(), offset, data.as_ptr(), len) };
363
364 if !ok {
365 Err(error::last_error()
366 .unwrap_or_else(|| error::ZwasmError("Unknown error".to_string())))
367 } else {
368 Ok(())
369 }
370 }
371
372 fn export_result_count_by_name(&self, name: &str) -> Result<u32, error::ZwasmError> {
373 let index = self.export_index_by_name(name)?;
374 Ok(self.export_result_count(index))
375 }
376
377 fn export_index_by_name(&self, name: &str) -> Result<u32, error::ZwasmError> {
378 let count = self.export_count();
379 for index in 0..count {
380 if let Some(export_name) = self.export_name(index) {
381 if export_name == name {
382 return Ok(index);
383 }
384 }
385 }
386 Err(error::ZwasmError(format!("export not found: {name}")))
387 }
388}
389
390#[cfg(test)]
391mod tests {
392 use super::*;
393 use crate::test_fixtures;
394
395 #[test]
396 fn test_module_lifecycle() {
397 let module = Module::new(test_fixtures::MINIMAL_WASM);
398 assert!(module.is_ok(), "module_new minimal");
399
400 let module = Module::new(&[0x00, 0x00, 0x00, 0x00]);
401 assert!(module.is_err(), "module_new invalid returns Err");
402
403 let err = module.err();
404 assert!(
405 !err.unwrap().0.is_empty(),
406 "last_error non-empty after failure"
407 );
408 }
409
410 #[test]
411 fn test_validate() {
412 let result = Module::validate(test_fixtures::RETURN42_WASM);
413 assert!(result.is_ok(), "validate valid module");
414
415 let result = Module::validate(&[0x00, 0x00, 0x00, 0x00]);
416 assert!(result.is_err(), "validate invalid module returns Err");
417
418 let err = result.err();
419 assert!(
420 !err.unwrap().0.is_empty(),
421 "last_error non-empty after failure"
422 );
423 }
424
425 #[test]
426 fn test_invoke_no_args() {
427 let module = Module::new(test_fixtures::RETURN42_WASM).expect("Failed to create module");
428 let results = module.invoke("f", &[]).expect("Failed to invoke function");
429 println!("f() = {}", results[0]);
430 assert_eq!(results[0], 42);
431
432 let results = module
434 .invoke("f", &[])
435 .expect("Failed to invoke function second time");
436 println!("f() again = {}", results[0]);
437 assert_eq!(results[0], 42);
438 }
439
440 #[test]
441 fn test_invoke_with_args() {
442 let module = Module::new(test_fixtures::ADD_WASM).expect("Failed to create module");
443 let args = [10, 32];
444 let results = module
445 .invoke("add", &args)
446 .expect("Failed to invoke function");
447 println!("add(10, 32) = {}", results[0]);
448 assert_eq!(results[0], 42);
449
450 let args = [0, 0];
452 let results = module
453 .invoke("add", &args)
454 .expect("Failed to invoke function");
455 println!("add(0, 0) = {}", results[0]);
456 assert_eq!(results[0], 0);
457
458 let args = [0xFFFFFFFF, 1];
460 let results = module
461 .invoke("add", &args)
462 .expect("Failed to invoke function");
463 println!("add(MAX, 1) = {}", results[0]);
464 assert_eq!(results[0] & 0xFFFFFFFF, 0);
465 }
466
467 #[test]
468 fn test_invoke_nonexistent() {
469 let module = Module::new(test_fixtures::RETURN42_WASM).expect("Failed to create module");
470 let result = module.invoke("nonexistent", &[]);
471 assert!(result.is_err(), "invoke nonexistent returns Err");
472
473 let err = result.err();
474 assert!(
475 !err.unwrap().0.is_empty(),
476 "last_error non-empty after failure"
477 );
478 }
479
480 #[test]
481 fn test_export_introspection() {
482 let module = Module::new(test_fixtures::RETURN42_WASM).expect("Failed to create module");
483 assert_eq!(module.export_count(), 1, "1 export");
484
485 let name = module.export_name(0).expect("export name not null");
486 assert_eq!(name, "f", "export name == 'f'");
487 assert_eq!(module.export_param_count(0), 0, "0 params");
488 assert_eq!(module.export_result_count(0), 1, "1 result");
489 assert!(module.export_name(99).is_none(), "export_name(99) == None");
490 }
491
492 #[test]
493 fn test_memory_access() {
494 let module = Module::new(test_fixtures::MEMORY_WASM).expect("Failed to create module");
495 let data = unsafe { module.memory_data() }.expect("memory_data not null");
496 let size = module.memory_size();
497 assert!(size >= 65536, "memory >= 1 page");
498 let write_buf = [0xDE, 0xAD, 0xBE, 0xEF];
499 module.memory_write(0, &write_buf).expect("memory_write");
500 let mut read_buf = [0u8; 4];
501 module.memory_read(0, &mut read_buf).expect("memory_read");
502 assert_eq!(write_buf, read_buf, "read == write");
503 assert!(data[0] == 0xDE && data[1] == 0xAD, "data ptr matches write");
504 let oob_write_result = module.memory_write(size as u32, &write_buf);
505 assert!(oob_write_result.is_err(), "OOB write returns Err");
506 let oob_read_result = module.memory_read(size as u32, &mut read_buf);
507 assert!(oob_read_result.is_err(), "OOB read returns Err");
508 module.invoke("f", &[]).expect("invoke store fn");
509 let data = unsafe { module.memory_data() }.expect("memory_data not null after invoke");
510 let val = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
511 assert_eq!(val, 42, "store fn wrote 42 at offset 0");
512 }
513
514 #[test]
515 fn test_no_memory_module() {
516 let module = Module::new(test_fixtures::RETURN42_WASM).expect("Failed to create module");
517 assert!(
518 unsafe { module.memory_data() }.is_none(),
519 "memory_data == None for no-memory"
520 );
521 assert_eq!(module.memory_size(), 0, "memory_size == 0 for no-memory");
522 }
523
524 #[test]
525 fn test_cancel_handle_after_module_drop_is_noop() {
526 let handle = {
527 let module =
528 Module::new(test_fixtures::RETURN42_WASM).expect("Failed to create module");
529 module.cancel_handle()
530 };
531
532 handle.cancel();
533 }
534}