wai_bindgen_wasmtime/
lib.rs1pub use wai_bindgen_wasmtime_impl::{export, import};
2
3#[cfg(feature = "async")]
4pub use async_trait::async_trait;
5#[cfg(feature = "tracing-lib")]
6pub use tracing_lib as tracing;
7#[doc(hidden)]
8pub use {anyhow, bitflags, wasmtime};
9
10mod error;
11mod le;
12mod region;
13mod slab;
14mod table;
15
16pub use error::GuestError;
17pub use le::{Endian, Le};
18pub use region::{AllBytesValid, BorrowChecker, Region};
19pub use table::*;
20
21#[doc(hidden)]
22pub mod rt {
23 use crate::slab::Slab;
24 use crate::{Endian, Le};
25 use std::mem;
26 use wasmtime::*;
27
28 pub trait RawMem {
29 fn store<T: Endian>(&mut self, offset: i32, val: T) -> Result<(), Trap>;
30 fn store_many<T: Endian>(&mut self, offset: i32, vals: &[T]) -> Result<(), Trap>;
31 fn load<T: Endian>(&self, offset: i32) -> Result<T, Trap>;
32 }
33
34 impl RawMem for [u8] {
35 fn store<T: Endian>(&mut self, offset: i32, val: T) -> Result<(), Trap> {
36 let mem = self
37 .get_mut(offset as usize..)
38 .and_then(|m| m.get_mut(..mem::size_of::<T>()))
39 .ok_or_else(|| Trap::new("out of bounds write"))?;
40 Le::from_slice_mut(mem)[0].set(val);
41 Ok(())
42 }
43
44 fn store_many<T: Endian>(&mut self, offset: i32, val: &[T]) -> Result<(), Trap> {
45 let mem = self
46 .get_mut(offset as usize..)
47 .and_then(|m| {
48 let len = mem::size_of::<T>().checked_mul(val.len())?;
49 m.get_mut(..len)
50 })
51 .ok_or_else(|| Trap::new("out of bounds write"))?;
52 for (slot, val) in Le::from_slice_mut(mem).iter_mut().zip(val) {
53 slot.set(*val);
54 }
55 Ok(())
56 }
57
58 fn load<T: Endian>(&self, offset: i32) -> Result<T, Trap> {
59 let mem = self
60 .get(offset as usize..)
61 .and_then(|m| m.get(..mem::size_of::<Le<T>>()))
62 .ok_or_else(|| Trap::new("out of bounds read"))?;
63 Ok(Le::from_slice(mem)[0].get())
64 }
65 }
66
67 pub fn char_from_i32(val: i32) -> Result<char, Trap> {
68 core::char::from_u32(val as u32).ok_or_else(|| Trap::new("char value out of valid range"))
69 }
70
71 pub fn invalid_variant(name: &str) -> Trap {
72 let msg = format!("invalid discriminant for `{}`", name);
73 Trap::new(msg)
74 }
75
76 pub fn validate_flags<T, U>(
77 bits: T,
78 all: T,
79 name: &str,
80 mk: impl FnOnce(T) -> U,
81 ) -> Result<U, Trap>
82 where
83 T: std::ops::Not<Output = T> + std::ops::BitAnd<Output = T> + From<u8> + PartialEq + Copy,
84 {
85 if bits & !all != 0u8.into() {
86 let msg = format!("invalid flags specified for `{}`", name);
87 Err(Trap::new(msg))
88 } else {
89 Ok(mk(bits))
90 }
91 }
92
93 pub fn get_func<T>(caller: &mut Caller<'_, T>, func: &str) -> Result<Func, wasmtime::Trap> {
94 let func = caller
95 .get_export(func)
96 .ok_or_else(|| {
97 let msg = format!("`{}` export not available", func);
98 Trap::new(msg)
99 })?
100 .into_func()
101 .ok_or_else(|| {
102 let msg = format!("`{}` export not a function", func);
103 Trap::new(msg)
104 })?;
105 Ok(func)
106 }
107
108 pub fn get_memory<T>(caller: &mut Caller<'_, T>, mem: &str) -> Result<Memory, wasmtime::Trap> {
109 let mem = caller
110 .get_export(mem)
111 .ok_or_else(|| {
112 let msg = format!("`{}` export not available", mem);
113 Trap::new(msg)
114 })?
115 .into_memory()
116 .ok_or_else(|| {
117 let msg = format!("`{}` export not a memory", mem);
118 Trap::new(msg)
119 })?;
120 Ok(mem)
121 }
122
123 pub fn bad_int(_: std::num::TryFromIntError) -> Trap {
124 let msg = "out-of-bounds integer conversion";
125 Trap::new(msg)
126 }
127
128 pub fn copy_slice<T: Endian>(
129 store: impl AsContextMut,
130 memory: &Memory,
131 base: i32,
132 len: i32,
133 _align: i32,
134 ) -> Result<Vec<T>, Trap> {
135 let size = (len as u32)
136 .checked_mul(mem::size_of::<T>() as u32)
137 .ok_or_else(|| Trap::new("array too large to fit in wasm memory"))?;
138 let slice = memory
139 .data(&store)
140 .get(base as usize..)
141 .and_then(|s| s.get(..size as usize))
142 .ok_or_else(|| Trap::new("out of bounds read"))?;
143 Ok(Le::from_slice(slice).iter().map(|s| s.get()).collect())
144 }
145
146 macro_rules! as_traits {
147 ($(($name:ident $tr:ident $ty:ident ($($tys:ident)*)))*) => ($(
148 pub fn $name<T: $tr>(t: T) -> $ty {
149 t.$name()
150 }
151
152 pub trait $tr {
153 fn $name(self) -> $ty;
154 }
155
156 impl<'a, T: Copy + $tr> $tr for &'a T {
157 fn $name(self) -> $ty {
158 (*self).$name()
159 }
160 }
161
162 $(
163 impl $tr for $tys {
164 #[inline]
165 fn $name(self) -> $ty {
166 self as $ty
167 }
168 }
169 )*
170 )*)
171 }
172
173 as_traits! {
174 (as_i32 AsI32 i32 (char i8 u8 i16 u16 i32 u32))
175 (as_i64 AsI64 i64 (i64 u64))
176 (as_f32 AsF32 f32 (f32))
177 (as_f64 AsF64 f64 (f64))
178 }
179
180 #[derive(Default, Debug)]
181 pub struct IndexSlab {
182 slab: Slab<ResourceIndex>,
183 }
184
185 impl IndexSlab {
186 pub fn insert(&mut self, resource: ResourceIndex) -> u32 {
187 self.slab.insert(resource)
188 }
189
190 pub fn get(&self, slab_idx: u32) -> Result<ResourceIndex, Trap> {
191 match self.slab.get(slab_idx) {
192 Some(idx) => Ok(*idx),
193 None => Err(Trap::new("invalid index specified for handle")),
194 }
195 }
196
197 pub fn remove(&mut self, slab_idx: u32) -> Result<ResourceIndex, Trap> {
198 match self.slab.remove(slab_idx) {
199 Some(idx) => Ok(idx),
200 None => Err(Trap::new("invalid index specified for handle")),
201 }
202 }
203 }
204
205 #[derive(Default, Debug)]
206 pub struct ResourceSlab {
207 slab: Slab<Resource>,
208 }
209
210 #[derive(Debug)]
211 struct Resource {
212 wasm: i32,
213 refcnt: u32,
214 }
215
216 #[derive(Debug, Copy, Clone)]
217 pub struct ResourceIndex(u32);
218
219 impl ResourceSlab {
220 pub fn insert(&mut self, wasm: i32) -> ResourceIndex {
221 ResourceIndex(self.slab.insert(Resource { wasm, refcnt: 1 }))
222 }
223
224 pub fn get(&self, idx: ResourceIndex) -> i32 {
225 self.slab.get(idx.0).unwrap().wasm
226 }
227
228 pub fn clone(&mut self, idx: ResourceIndex) -> Result<(), Trap> {
229 let resource = self.slab.get_mut(idx.0).unwrap();
230 resource.refcnt = match resource.refcnt.checked_add(1) {
231 Some(cnt) => cnt,
232 None => return Err(Trap::new("resource index count overflow")),
233 };
234 Ok(())
235 }
236
237 pub fn drop(&mut self, idx: ResourceIndex) -> Option<i32> {
238 let resource = self.slab.get_mut(idx.0).unwrap();
239 assert!(resource.refcnt > 0);
240 resource.refcnt -= 1;
241 if resource.refcnt != 0 {
242 return None;
243 }
244 let resource = self.slab.remove(idx.0).unwrap();
245 Some(resource.wasm)
246 }
247 }
248}