mielin_wasm/
lib.rs

1//! MielinWasm - Wasmtime Runtime Integration
2//!
3//! Provides WebAssembly sandboxing and execution for agent cells.
4
5pub mod aot;
6pub mod async_exec;
7pub mod cache;
8pub mod component;
9pub mod debug;
10pub mod deterministic;
11pub mod executor;
12pub mod extensions;
13pub mod filesystem;
14pub mod host;
15pub mod jit;
16pub mod memory;
17pub mod module;
18pub mod network;
19pub mod resource;
20pub mod runtime;
21pub mod sandbox;
22pub mod security;
23pub mod system;
24pub mod validation;
25pub mod verification;
26pub mod wasi;
27pub mod wasi_debug;
28
29use thiserror::Error;
30
31#[derive(Debug, Error)]
32pub enum WasmError {
33    #[error("Compilation failed: {0}")]
34    CompilationFailed(String),
35    #[error("Execution failed: {0}")]
36    ExecutionFailed(String),
37    #[error("Invalid module: {0}")]
38    InvalidModule(String),
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44    use crate::executor::WasmExecutor;
45    use mielin_hal::capabilities::HardwareCapabilities;
46
47    #[test]
48    fn test_wasm_error_types() {
49        let err = WasmError::InvalidModule("test".to_string());
50        assert!(err.to_string().contains("Invalid module"));
51    }
52
53    #[test]
54    fn test_tensor_host_functions() {
55        // Create executor with tensor support
56        let executor = WasmExecutor::with_capabilities(HardwareCapabilities::NEON).unwrap();
57
58        // Create a simple WASM module that uses tensor operations
59        let wasm = wat::parse_str(
60            r#"
61            (module
62                (import "mielin" "tensor_supports_neon" (func $tensor_supports_neon (result i32)))
63                (import "mielin" "tensor_zeros" (func $tensor_zeros (param i32 i32) (result i32)))
64                (import "mielin" "tensor_free" (func $tensor_free (param i32) (result i32)))
65                (memory (export "memory") 1)
66                (func (export "_start")
67                    ;; Check NEON support
68                    call $tensor_supports_neon
69                    drop
70
71                    ;; Create a 2x2 tensor - shape is at offset 0
72                    i32.const 0
73                    i32.const 2
74                    i32.store
75                    i32.const 4
76                    i32.const 2
77                    i32.store
78
79                    ;; Call tensor_zeros with shape pointer and length
80                    i32.const 0
81                    i32.const 2
82                    call $tensor_zeros
83
84                    ;; Free the tensor
85                    call $tensor_free
86                    drop
87                )
88            )
89            "#,
90        )
91        .unwrap();
92
93        let module = executor.compile_module(&wasm).unwrap();
94        let (instance, mut store) = executor
95            .instantiate(&module, HardwareCapabilities::NEON)
96            .unwrap();
97
98        // Execute the start function
99        let start = instance.get_func(&mut store, "_start").unwrap();
100        start.call(&mut store, &[], &mut []).unwrap();
101
102        // Verify host state is accessible
103        assert!(store.data().tensor_runtime().supports_neon());
104    }
105
106    #[test]
107    fn test_tensor_operations_integration() {
108        // Create a WASM module that performs tensor operations
109        let wasm = wat::parse_str(
110            r#"
111            (module
112                (import "mielin" "tensor_zeros" (func $tensor_zeros (param i32 i32) (result i32)))
113                (import "mielin" "tensor_ones" (func $tensor_ones (param i32 i32) (result i32)))
114                (import "mielin" "tensor_add" (func $tensor_add (param i32 i32) (result i32)))
115                (import "mielin" "tensor_free" (func $tensor_free (param i32) (result i32)))
116                (memory (export "memory") 1)
117                (func (export "_start") (local i32) (local i32) (local i32)
118                    ;; Setup shape [3] at offset 0
119                    i32.const 0
120                    i32.const 3
121                    i32.store
122
123                    ;; Create zeros tensor
124                    i32.const 0
125                    i32.const 1
126                    call $tensor_zeros
127                    local.set 0
128
129                    ;; Create ones tensor
130                    i32.const 0
131                    i32.const 1
132                    call $tensor_ones
133                    local.set 1
134
135                    ;; Add them
136                    local.get 0
137                    local.get 1
138                    call $tensor_add
139                    local.set 2
140
141                    ;; Free all tensors
142                    local.get 0
143                    call $tensor_free
144                    drop
145                    local.get 1
146                    call $tensor_free
147                    drop
148                    local.get 2
149                    call $tensor_free
150                    drop
151                )
152            )
153            "#,
154        )
155        .unwrap();
156
157        let executor = WasmExecutor::new().unwrap();
158        let module = executor.compile_module(&wasm).unwrap();
159        let (instance, mut store) = executor
160            .instantiate(&module, HardwareCapabilities::NONE)
161            .unwrap();
162
163        let start = instance.get_func(&mut store, "_start").unwrap();
164        start.call(&mut store, &[], &mut []).unwrap();
165
166        // All tensors should be freed
167        let tensors = store.data().tensors().lock().unwrap();
168        assert!(tensors.is_empty());
169    }
170
171    #[test]
172    fn test_time_host_functions() {
173        // Create a WASM module that uses time functions
174        let wasm = wat::parse_str(
175            r#"
176            (module
177                (import "mielin" "time_now_millis" (func $time_now_millis (result i64)))
178                (import "mielin" "time_monotonic_nanos" (func $time_monotonic_nanos (result i64)))
179                (memory (export "memory") 1)
180                (global $time1 (mut i64) (i64.const 0))
181                (global $time2 (mut i64) (i64.const 0))
182                (func (export "_start")
183                    ;; Get current time in millis (should be non-zero)
184                    call $time_now_millis
185                    global.set $time1
186
187                    ;; Get monotonic time (should be small positive)
188                    call $time_monotonic_nanos
189                    global.set $time2
190                )
191                (func (export "get_time1") (result i64)
192                    global.get $time1
193                )
194                (func (export "get_time2") (result i64)
195                    global.get $time2
196                )
197            )
198            "#,
199        )
200        .unwrap();
201
202        let executor = WasmExecutor::new().unwrap();
203        let module = executor.compile_module(&wasm).unwrap();
204        let (instance, mut store) = executor
205            .instantiate(&module, HardwareCapabilities::NONE)
206            .unwrap();
207
208        let start = instance.get_func(&mut store, "_start").unwrap();
209        start.call(&mut store, &[], &mut []).unwrap();
210
211        // Verify time values
212        let get_time1 = instance
213            .get_typed_func::<(), i64>(&mut store, "get_time1")
214            .unwrap();
215        let time1 = get_time1.call(&mut store, ()).unwrap();
216        assert!(time1 > 0, "time_now_millis should return positive value");
217
218        let get_time2 = instance
219            .get_typed_func::<(), i64>(&mut store, "get_time2")
220            .unwrap();
221        let time2 = get_time2.call(&mut store, ()).unwrap();
222        assert!(
223            time2 >= 0,
224            "time_monotonic_nanos should return non-negative"
225        );
226    }
227
228    #[test]
229    fn test_random_host_functions() {
230        // Create a WASM module that uses random functions
231        let wasm = wat::parse_str(
232            r#"
233            (module
234                (import "mielin" "random_u32" (func $random_u32 (result i32)))
235                (import "mielin" "random_f32" (func $random_f32 (result f32)))
236                (import "mielin" "random_bytes" (func $random_bytes (param i32 i32) (result i32)))
237                (memory (export "memory") 1)
238                (global $rand1 (mut i32) (i32.const 0))
239                (global $rand2 (mut i32) (i32.const 0))
240                (global $randf (mut f32) (f32.const 0))
241                (func (export "_start")
242                    ;; Get two random u32 values
243                    call $random_u32
244                    global.set $rand1
245                    call $random_u32
246                    global.set $rand2
247
248                    ;; Get random f32
249                    call $random_f32
250                    global.set $randf
251
252                    ;; Fill 16 bytes with random data at offset 100
253                    i32.const 100
254                    i32.const 16
255                    call $random_bytes
256                    drop
257                )
258                (func (export "get_rand1") (result i32)
259                    global.get $rand1
260                )
261                (func (export "get_rand2") (result i32)
262                    global.get $rand2
263                )
264                (func (export "get_randf") (result f32)
265                    global.get $randf
266                )
267            )
268            "#,
269        )
270        .unwrap();
271
272        let executor = WasmExecutor::new().unwrap();
273        let module = executor.compile_module(&wasm).unwrap();
274        let (instance, mut store) = executor
275            .instantiate(&module, HardwareCapabilities::NONE)
276            .unwrap();
277
278        let start = instance.get_func(&mut store, "_start").unwrap();
279        start.call(&mut store, &[], &mut []).unwrap();
280
281        // Random values should be generated (may be zero by chance, but unlikely both)
282        let get_rand1 = instance
283            .get_typed_func::<(), i32>(&mut store, "get_rand1")
284            .unwrap();
285        let get_rand2 = instance
286            .get_typed_func::<(), i32>(&mut store, "get_rand2")
287            .unwrap();
288        let r1 = get_rand1.call(&mut store, ()).unwrap();
289        let r2 = get_rand2.call(&mut store, ()).unwrap();
290
291        // Two consecutive random calls should produce different values (very high probability)
292        // Note: This could theoretically fail, but probability is 1/2^32
293        assert!(
294            r1 != r2 || r1 == 0,
295            "Two random values should likely differ"
296        );
297
298        // Check f32 is in [0, 1)
299        let get_randf = instance
300            .get_typed_func::<(), f32>(&mut store, "get_randf")
301            .unwrap();
302        let f = get_randf.call(&mut store, ()).unwrap();
303        assert!((0.0..1.0).contains(&f), "random_f32 should be in [0, 1)");
304    }
305
306    #[test]
307    fn test_process_host_functions() {
308        // Create a WASM module that queries process info
309        let wasm = wat::parse_str(
310            r#"
311            (module
312                (import "mielin" "process_platform" (func $process_platform (result i32)))
313                (import "mielin" "process_arch" (func $process_arch (result i32)))
314                (import "mielin" "process_cpu_count" (func $process_cpu_count (result i32)))
315                (import "mielin" "process_pointer_bits" (func $process_pointer_bits (result i32)))
316                (memory (export "memory") 1)
317                (global $platform (mut i32) (i32.const 0))
318                (global $arch (mut i32) (i32.const 0))
319                (global $cpus (mut i32) (i32.const 0))
320                (global $bits (mut i32) (i32.const 0))
321                (func (export "_start")
322                    call $process_platform
323                    global.set $platform
324                    call $process_arch
325                    global.set $arch
326                    call $process_cpu_count
327                    global.set $cpus
328                    call $process_pointer_bits
329                    global.set $bits
330                )
331                (func (export "get_platform") (result i32)
332                    global.get $platform
333                )
334                (func (export "get_arch") (result i32)
335                    global.get $arch
336                )
337                (func (export "get_cpus") (result i32)
338                    global.get $cpus
339                )
340                (func (export "get_bits") (result i32)
341                    global.get $bits
342                )
343            )
344            "#,
345        )
346        .unwrap();
347
348        let executor = WasmExecutor::new().unwrap();
349        let module = executor.compile_module(&wasm).unwrap();
350        let (instance, mut store) = executor
351            .instantiate(&module, HardwareCapabilities::NONE)
352            .unwrap();
353
354        let start = instance.get_func(&mut store, "_start").unwrap();
355        start.call(&mut store, &[], &mut []).unwrap();
356
357        // Verify process info
358        let get_cpus = instance
359            .get_typed_func::<(), i32>(&mut store, "get_cpus")
360            .unwrap();
361        let cpus = get_cpus.call(&mut store, ()).unwrap();
362        assert!(cpus >= 1, "CPU count should be at least 1");
363
364        let get_bits = instance
365            .get_typed_func::<(), i32>(&mut store, "get_bits")
366            .unwrap();
367        let bits = get_bits.call(&mut store, ()).unwrap();
368        assert!(bits == 32 || bits == 64, "Pointer bits should be 32 or 64");
369    }
370}