wai_bindgen_wasmer/
lib.rs1#![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
27unsafe 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}