1 use wasmtime::{Engine, Linker, Module, Store, Val};
2 use wiggle::GuestMemory;
3 
4 wiggle::from_witx!({
5     witx: ["tests/atoms.witx"],
6     block_on: {
7         atoms::double_int_return_float
8     }
9 });
10 
11 pub struct Ctx;
12 impl wiggle::GuestErrorType for types::Errno {
success() -> Self13     fn success() -> Self {
14         types::Errno::Ok
15     }
16 }
17 
18 const TRIGGER_PENDING: u32 = 0;
19 
20 impl atoms::Atoms for Ctx {
int_float_args( &mut self, _: &mut GuestMemory<'_>, an_int: u32, an_float: f32, ) -> Result<(), types::Errno>21     fn int_float_args(
22         &mut self,
23         _: &mut GuestMemory<'_>,
24         an_int: u32,
25         an_float: f32,
26     ) -> Result<(), types::Errno> {
27         println!("INT FLOAT ARGS: {an_int} {an_float}");
28         Ok(())
29     }
double_int_return_float( &mut self, _: &mut GuestMemory<'_>, an_int: u32, ) -> Result<types::AliasToFloat, types::Errno>30     async fn double_int_return_float(
31         &mut self,
32         _: &mut GuestMemory<'_>,
33         an_int: u32,
34     ) -> Result<types::AliasToFloat, types::Errno> {
35         if an_int == TRIGGER_PENDING {
36             // Define a Future that is pending forever. This is `futures::future::pending()`
37             // without incurring the dep.
38             use std::future::Future;
39             use std::pin::Pin;
40             use std::task::{Context, Poll};
41             struct Pending;
42             impl Future for Pending {
43                 type Output = ();
44                 fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
45                     Poll::Pending
46                 }
47             }
48             // This await will pend, which should cause the dummy executor to Trap.
49             Pending.await;
50         }
51         Ok((an_int as f32) * 2.0)
52     }
53 }
54 
55 #[test]
test_sync_host_func()56 fn test_sync_host_func() {
57     let engine = Engine::default();
58     let mut linker = Linker::new(&engine);
59     atoms::add_to_linker(&mut linker, |cx| cx).unwrap();
60     let mut store = store(&engine);
61     let shim_mod = shim_module(&engine);
62     let shim_inst = linker.instantiate(&mut store, &shim_mod).unwrap();
63 
64     let mut results = [Val::I32(0)];
65     shim_inst
66         .get_func(&mut store, "int_float_args_shim")
67         .unwrap()
68         .call(&mut store, &[0i32.into(), 123.45f32.into()], &mut results)
69         .unwrap();
70 
71     assert_eq!(
72         results[0].unwrap_i32(),
73         types::Errno::Ok as i32,
74         "int_float_args errno"
75     );
76 }
77 
78 #[test]
test_async_host_func()79 fn test_async_host_func() {
80     let engine = Engine::default();
81     let mut linker = Linker::new(&engine);
82     atoms::add_to_linker(&mut linker, |cx| cx).unwrap();
83     let mut store = store(&engine);
84 
85     let shim_mod = shim_module(&engine);
86     let shim_inst = linker.instantiate(&mut store, &shim_mod).unwrap();
87 
88     let input: i32 = 123;
89     let result_location: i32 = 0;
90 
91     let mut results = [Val::I32(0)];
92     shim_inst
93         .get_func(&mut store, "double_int_return_float_shim")
94         .unwrap()
95         .call(
96             &mut store,
97             &[input.into(), result_location.into()],
98             &mut results,
99         )
100         .unwrap();
101 
102     assert_eq!(
103         results[0].unwrap_i32(),
104         types::Errno::Ok as i32,
105         "double_int_return_float errno"
106     );
107 
108     // The actual result is in memory:
109     let mem = shim_inst.get_memory(&mut store, "memory").unwrap();
110     let mut result_bytes: [u8; 4] = [0, 0, 0, 0];
111     mem.read(&store, result_location as usize, &mut result_bytes)
112         .unwrap();
113     let result = f32::from_le_bytes(result_bytes);
114     assert_eq!((input * 2) as f32, result);
115 }
116 
117 #[test]
test_async_host_func_pending()118 fn test_async_host_func_pending() {
119     let engine = Engine::default();
120     let mut linker = Linker::new(&engine);
121     atoms::add_to_linker(&mut linker, |cx| cx).unwrap();
122     let mut store = store(&engine);
123 
124     let shim_mod = shim_module(&engine);
125     let shim_inst = linker.instantiate(&mut store, &shim_mod).unwrap();
126 
127     let result_location: i32 = 0;
128 
129     // This input triggers the host func pending forever
130     let input: i32 = TRIGGER_PENDING as i32;
131     let trap = shim_inst
132         .get_func(&mut store, "double_int_return_float_shim")
133         .unwrap()
134         .call(
135             &mut store,
136             &[input.into(), result_location.into()],
137             &mut [Val::I32(0)],
138         )
139         .unwrap_err();
140     assert!(
141         format!("{trap:?}").contains("Cannot wait on pending future"),
142         "expected get a pending future Trap from dummy executor, got: {trap}"
143     );
144 }
145 
store(engine: &Engine) -> Store<Ctx>146 fn store(engine: &Engine) -> Store<Ctx> {
147     Store::new(engine, Ctx)
148 }
149 
150 // Wiggle expects the caller to have an exported memory. Wasmtime can only
151 // provide this if the caller is a WebAssembly module, so we need to write
152 // a shim module:
shim_module(engine: &Engine) -> Module153 fn shim_module(engine: &Engine) -> Module {
154     Module::new(
155         engine,
156         r#"
157         (module
158             (import "atoms" "int_float_args" (func $int_float_args (param i32 f32) (result i32)))
159             (import "atoms" "double_int_return_float" (func $double_int_return_float (param i32 i32) (result i32)))
160 
161             (memory 1)
162             (export "memory" (memory 0))
163 
164             (func $int_float_args_shim (param i32 f32) (result i32)
165                 local.get 0
166                 local.get 1
167                 call $int_float_args
168             )
169             (func $double_int_return_float_shim (param i32 i32) (result i32)
170                 local.get 0
171                 local.get 1
172                 call $double_int_return_float
173             )
174             (export "int_float_args_shim" (func $int_float_args_shim))
175             (export "double_int_return_float_shim" (func $double_int_return_float_shim))
176         )
177     "#,
178     )
179     .unwrap()
180 }
181