Skip to main content

linera_wit_bindgen_host_wasmer_rust/
lib.rs

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