wai_bindgen_wasmer/
lib.rs

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