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