wai_bindgen_wasmer/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]

pub use wai_bindgen_wasmer_impl::{export, import};

#[cfg(feature = "async")]
pub use async_trait::async_trait;
#[cfg(feature = "tracing-lib")]
pub use tracing_lib as tracing;
#[doc(hidden)]
pub use {anyhow, bitflags, once_cell, wasmer};

mod error;
mod le;
mod region;
mod slab;
mod table;

pub use error::GuestError;
pub use le::{Endian, Le};
pub use region::{AllBytesValid, BorrowChecker, Region};
pub use table::*;

pub struct RawMemory {
    pub slice: *mut [u8],
}

// This type is threadsafe despite its internal pointer because it allows no
// safe access to the internal pointer. Consumers must uphold Send/Sync
// guarantees themselves.
unsafe impl Send for RawMemory {}
unsafe impl Sync for RawMemory {}

#[doc(hidden)]
pub mod rt {
    use crate::slab::Slab;
    use crate::{Endian, Le};
    use std::mem;
    use wasmer::*;

    pub trait RawMem {
        fn store<T: Endian>(&mut self, offset: i32, val: T) -> Result<(), RuntimeError>;
        fn store_many<T: Endian>(&mut self, offset: i32, vals: &[T]) -> Result<(), RuntimeError>;
        fn load<T: Endian>(&self, offset: i32) -> Result<T, RuntimeError>;
    }

    impl RawMem for [u8] {
        fn store<T: Endian>(&mut self, offset: i32, val: T) -> Result<(), RuntimeError> {
            let mem = self
                .get_mut(offset as usize..)
                .and_then(|m| m.get_mut(..mem::size_of::<T>()))
                .ok_or_else(|| RuntimeError::new("out of bounds write"))?;
            Le::from_slice_mut(mem)[0].set(val);
            Ok(())
        }

        fn store_many<T: Endian>(&mut self, offset: i32, val: &[T]) -> Result<(), RuntimeError> {
            let mem = self
                .get_mut(offset as usize..)
                .and_then(|m| {
                    let len = mem::size_of::<T>().checked_mul(val.len())?;
                    m.get_mut(..len)
                })
                .ok_or_else(|| RuntimeError::new("out of bounds write"))?;
            for (slot, val) in Le::from_slice_mut(mem).iter_mut().zip(val) {
                slot.set(*val);
            }
            Ok(())
        }

        fn load<T: Endian>(&self, offset: i32) -> Result<T, RuntimeError> {
            let mem = self
                .get(offset as usize..)
                .and_then(|m| m.get(..mem::size_of::<Le<T>>()))
                .ok_or_else(|| RuntimeError::new("out of bounds read"))?;
            Ok(Le::from_slice(mem)[0].get())
        }
    }

    pub fn char_from_i32(val: i32) -> Result<char, RuntimeError> {
        core::char::from_u32(val as u32)
            .ok_or_else(|| RuntimeError::new("char value out of valid range"))
    }

    pub fn invalid_variant(name: &str) -> RuntimeError {
        let msg = format!("invalid discriminant for `{}`", name);
        RuntimeError::new(msg)
    }

    pub fn validate_flags<T, U>(
        bits: T,
        all: T,
        name: &str,
        mk: impl FnOnce(T) -> U,
    ) -> Result<U, RuntimeError>
    where
        T: std::ops::Not<Output = T> + std::ops::BitAnd<Output = T> + From<u8> + PartialEq + Copy,
    {
        if bits & !all != 0u8.into() {
            let msg = format!("invalid flags specified for `{}`", name);
            Err(RuntimeError::new(msg))
        } else {
            Ok(mk(bits))
        }
    }

    pub fn bad_int(_: std::num::TryFromIntError) -> RuntimeError {
        let msg = "out-of-bounds integer conversion";
        RuntimeError::new(msg)
    }

    pub fn copy_slice<T: Endian>(
        store: &mut wasmer::Store,
        memory: &Memory,
        free: &TypedFunction<(i32, i32, i32), ()>,
        base: i32,
        len: i32,
        align: i32,
    ) -> Result<Vec<T>, RuntimeError> {
        let size = (len as u32)
            .checked_mul(mem::size_of::<T>() as u32)
            .ok_or_else(|| RuntimeError::new("array too large to fit in wasm memory"))?;
        let memory_view = memory.view(store);
        let slice = unsafe {
            memory_view
                .data_unchecked()
                .get(base as usize..)
                .and_then(|s| s.get(..size as usize))
                .ok_or_else(|| RuntimeError::new("out of bounds read"))?
        };
        let result = Le::from_slice(slice).iter().map(|s| s.get()).collect();
        free.call(store, base, size as i32, align)?;
        Ok(result)
    }

    macro_rules! as_traits {
        ($(($name:ident $tr:ident $ty:ident ($($tys:ident)*)))*) => ($(
            pub fn $name<T: $tr>(t: T) -> $ty {
                t.$name()
            }

            pub trait $tr {
                #[allow(clippy::wrong_self_convention)]
                fn $name(self) -> $ty;
            }

            impl<'a, T: Copy + $tr> $tr for &'a T {
                fn $name(self) -> $ty {
                    (*self).$name()
                }
            }

            $(
                impl $tr for $tys {
                    #[inline]
                    fn $name(self) -> $ty {
                        self as $ty
                    }
                }
            )*
        )*)
    }

    as_traits! {
        (as_i32 AsI32 i32 (char i8 u8 i16 u16 i32 u32))
        (as_i64 AsI64 i64 (i64 u64))
        (as_f32 AsF32 f32 (f32))
        (as_f64 AsF64 f64 (f64))
    }

    #[derive(Default, Debug)]
    pub struct IndexSlab {
        slab: Slab<ResourceIndex>,
    }

    impl IndexSlab {
        pub fn insert(&mut self, resource: ResourceIndex) -> u32 {
            self.slab.insert(resource)
        }

        pub fn get(&self, slab_idx: u32) -> Result<ResourceIndex, RuntimeError> {
            match self.slab.get(slab_idx) {
                Some(idx) => Ok(*idx),
                None => Err(RuntimeError::new("invalid index specified for handle")),
            }
        }

        pub fn remove(&mut self, slab_idx: u32) -> Result<ResourceIndex, RuntimeError> {
            match self.slab.remove(slab_idx) {
                Some(idx) => Ok(idx),
                None => Err(RuntimeError::new("invalid index specified for handle")),
            }
        }
    }

    #[derive(Default, Debug)]
    pub struct ResourceSlab {
        slab: Slab<Resource>,
    }

    #[derive(Debug)]
    struct Resource {
        wasm: i32,
        refcnt: u32,
    }

    #[derive(Debug, Copy, Clone)]
    pub struct ResourceIndex(u32);

    impl ResourceSlab {
        pub fn insert(&mut self, wasm: i32) -> ResourceIndex {
            ResourceIndex(self.slab.insert(Resource { wasm, refcnt: 1 }))
        }

        pub fn get(&self, idx: ResourceIndex) -> i32 {
            self.slab.get(idx.0).unwrap().wasm
        }

        pub fn clone(&mut self, idx: ResourceIndex) -> Result<(), RuntimeError> {
            let resource = self.slab.get_mut(idx.0).unwrap();
            resource.refcnt = match resource.refcnt.checked_add(1) {
                Some(cnt) => cnt,
                None => return Err(RuntimeError::new("resource index count overflow")),
            };
            Ok(())
        }

        pub fn drop(&mut self, idx: ResourceIndex) -> Option<i32> {
            let resource = self.slab.get_mut(idx.0).unwrap();
            assert!(resource.refcnt > 0);
            resource.refcnt -= 1;
            if resource.refcnt != 0 {
                return None;
            }
            let resource = self.slab.remove(idx.0).unwrap();
            Some(resource.wasm)
        }
    }
}