wai_bindgen_wasmtime/
lib.rs

1pub 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}