wasmer_vm/
store.rs

1use crate::{
2    VMExceptionObj, VMExternObj, VMFunction, VMFunctionEnvironment, VMGlobal, VMInstance, VMMemory,
3    VMTable, VMTag,
4};
5use core::slice::Iter;
6use std::{cell::UnsafeCell, fmt, marker::PhantomData, num::NonZeroUsize, ptr::NonNull};
7use wasmer_types::StoreId;
8
9/// Trait to represent an object managed by a context. This is implemented on
10/// the VM types managed by the context.
11pub trait StoreObject: Sized {
12    /// List the objects in the store.
13    fn list(ctx: &StoreObjects) -> &Vec<Self>;
14
15    /// List the objects in the store, mutably.
16    fn list_mut(ctx: &mut StoreObjects) -> &mut Vec<Self>;
17}
18
19macro_rules! impl_context_object {
20    ($($field:ident => $ty:ty,)*) => {
21        $(
22            impl StoreObject for $ty {
23                fn list(ctx: &StoreObjects) -> &Vec<Self> {
24                    &ctx.$field
25                }
26                fn list_mut(ctx: &mut StoreObjects) -> &mut Vec<Self> {
27                    &mut ctx.$field
28                }
29            }
30        )*
31    };
32}
33
34impl_context_object! {
35    functions => VMFunction,
36    tables => VMTable,
37    globals => VMGlobal,
38    instances => VMInstance,
39    memories => VMMemory,
40    extern_objs => VMExternObj,
41    exceptions => VMExceptionObj,
42    tags => VMTag,
43    function_environments => VMFunctionEnvironment,
44}
45
46/// Set of objects managed by a context.
47#[derive(Debug, Default)]
48pub struct StoreObjects {
49    id: StoreId,
50    memories: Vec<VMMemory>,
51    tables: Vec<VMTable>,
52    globals: Vec<VMGlobal>,
53    functions: Vec<VMFunction>,
54    instances: Vec<VMInstance>,
55    extern_objs: Vec<VMExternObj>,
56    exceptions: Vec<VMExceptionObj>,
57    tags: Vec<VMTag>,
58    function_environments: Vec<VMFunctionEnvironment>,
59}
60
61impl StoreObjects {
62    /// Create a new instance of [`Self`]
63    #[allow(clippy::too_many_arguments)]
64    pub fn new(
65        id: StoreId,
66        memories: Vec<VMMemory>,
67        tables: Vec<VMTable>,
68        globals: Vec<VMGlobal>,
69        functions: Vec<VMFunction>,
70        instances: Vec<VMInstance>,
71        extern_objs: Vec<VMExternObj>,
72        exceptions: Vec<VMExceptionObj>,
73        tags: Vec<VMTag>,
74        function_environments: Vec<VMFunctionEnvironment>,
75    ) -> Self {
76        Self {
77            id,
78            memories,
79            tables,
80            globals,
81            functions,
82            instances,
83            extern_objs,
84            function_environments,
85            exceptions,
86            tags,
87        }
88    }
89
90    /// Returns the ID of this context.
91    pub fn id(&self) -> StoreId {
92        self.id
93    }
94
95    /// Sets the ID of this store
96    pub fn set_id(&mut self, id: StoreId) {
97        self.id = id;
98    }
99
100    /// Returns a pair of mutable references from two handles.
101    ///
102    /// Panics if both handles point to the same object.
103    pub fn get_2_mut<T: StoreObject>(
104        &mut self,
105        a: InternalStoreHandle<T>,
106        b: InternalStoreHandle<T>,
107    ) -> (&mut T, &mut T) {
108        assert_ne!(a.index(), b.index());
109        let list = T::list_mut(self);
110        if a.index() < b.index() {
111            let (low, high) = list.split_at_mut(b.index());
112            (&mut low[a.index()], &mut high[0])
113        } else {
114            let (low, high) = list.split_at_mut(a.index());
115            (&mut high[0], &mut low[a.index()])
116        }
117    }
118
119    /// Return an immutable iterator over all globals
120    pub fn iter_globals(&self) -> Iter<'_, VMGlobal> {
121        self.globals.iter()
122    }
123
124    /// Return an vector of all globals and converted to u128
125    pub fn as_u128_globals(&self) -> Vec<u128> {
126        self.iter_globals()
127            .map(|v| unsafe { v.vmglobal().as_ref().val.u128 })
128            .collect()
129    }
130
131    /// Set a global, at index idx. Will panic if idx is out of range
132    /// Safety: the caller should check taht the raw value is compatible
133    /// with destination VMGlobal type
134    pub fn set_global_unchecked(&self, idx: usize, val: u128) {
135        assert!(idx < self.globals.len());
136        unsafe {
137            self.globals[idx].vmglobal().as_mut().val.u128 = val;
138        }
139    }
140}
141
142/// Handle to an object managed by a context.
143///
144/// Internally this is just an integer index into a context. A reference to the
145/// context must be passed in separately to access the actual object.
146#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
147pub struct StoreHandle<T> {
148    id: StoreId,
149    internal: InternalStoreHandle<T>,
150}
151
152impl<T> Clone for StoreHandle<T> {
153    fn clone(&self) -> Self {
154        Self {
155            id: self.id,
156            internal: self.internal,
157        }
158    }
159}
160
161impl<T> std::hash::Hash for StoreHandle<T> {
162    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
163        self.id.hash(state);
164        self.internal.idx.hash(state);
165    }
166}
167
168impl<T: StoreObject> fmt::Debug for StoreHandle<T> {
169    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170        f.debug_struct("StoreHandle")
171            .field("id", &self.id)
172            .field("internal", &self.internal.index())
173            .finish()
174    }
175}
176
177impl<T: StoreObject> PartialEq for StoreHandle<T> {
178    fn eq(&self, other: &Self) -> bool {
179        self.id == other.id && self.internal == other.internal
180    }
181}
182
183impl<T: StoreObject> Eq for StoreHandle<T> {}
184
185impl<T: StoreObject> StoreHandle<T> {
186    /// Moves the given object into a context and returns a handle to it.
187    pub fn new(ctx: &mut StoreObjects, val: T) -> Self {
188        Self {
189            id: ctx.id,
190            internal: InternalStoreHandle::new(ctx, val),
191        }
192    }
193
194    /// Returns a reference to the object that this handle points to.
195    pub fn get<'a>(&self, ctx: &'a StoreObjects) -> &'a T {
196        assert_eq!(self.id, ctx.id, "object used with the wrong context");
197        self.internal.get(ctx)
198    }
199
200    /// Returns a mutable reference to the object that this handle points to.
201    pub fn get_mut<'a>(&self, ctx: &'a mut StoreObjects) -> &'a mut T {
202        assert_eq!(self.id, ctx.id, "object used with the wrong context");
203        self.internal.get_mut(ctx)
204    }
205
206    /// Returns the internal handle contains within this handle.
207    pub fn internal_handle(&self) -> InternalStoreHandle<T> {
208        self.internal
209    }
210
211    /// Returns the ID of the context associated with the handle.
212    pub fn store_id(&self) -> StoreId {
213        self.id
214    }
215
216    /// Overrides the store id with a new ID
217    pub fn set_store_id(&mut self, id: StoreId) {
218        self.id = id;
219    }
220
221    /// Constructs a `StoreHandle` from a `StoreId` and an `InternalStoreHandle`.
222    ///
223    /// # Safety
224    /// Handling `InternalStoreHandle` values is unsafe because they do not track context ID.
225    pub unsafe fn from_internal(id: StoreId, internal: InternalStoreHandle<T>) -> Self {
226        Self { id, internal }
227    }
228}
229
230/// Internal handle to an object owned by the current context.
231///
232/// Unlike `StoreHandle` this does not track the context ID: it is only
233/// intended to be used within objects already owned by a context.
234#[repr(transparent)]
235pub struct InternalStoreHandle<T> {
236    // Use a NonZero here to reduce the size of Option<InternalStoreHandle>.
237    idx: NonZeroUsize,
238    marker: PhantomData<fn() -> T>,
239}
240
241#[cfg(feature = "artifact-size")]
242impl<T> loupe::MemoryUsage for InternalStoreHandle<T> {
243    fn size_of_val(&self, _tracker: &mut dyn loupe::MemoryUsageTracker) -> usize {
244        std::mem::size_of_val(&self)
245    }
246}
247
248impl<T> Clone for InternalStoreHandle<T> {
249    fn clone(&self) -> Self {
250        *self
251    }
252}
253impl<T> Copy for InternalStoreHandle<T> {}
254
255impl<T> fmt::Debug for InternalStoreHandle<T> {
256    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
257        f.debug_struct("InternalStoreHandle")
258            .field("idx", &self.idx)
259            .finish()
260    }
261}
262impl<T> PartialEq for InternalStoreHandle<T> {
263    fn eq(&self, other: &Self) -> bool {
264        self.idx == other.idx
265    }
266}
267impl<T> Eq for InternalStoreHandle<T> {}
268
269impl<T: StoreObject> InternalStoreHandle<T> {
270    /// Moves the given object into a context and returns a handle to it.
271    pub fn new(ctx: &mut StoreObjects, val: T) -> Self {
272        let list = T::list_mut(ctx);
273        let idx = NonZeroUsize::new(list.len() + 1).unwrap();
274        list.push(val);
275        Self {
276            idx,
277            marker: PhantomData,
278        }
279    }
280
281    /// Returns a reference to the object that this handle points to.
282    pub fn get<'a>(&self, ctx: &'a StoreObjects) -> &'a T {
283        &T::list(ctx)[self.idx.get() - 1]
284    }
285
286    /// Returns a mutable reference to the object that this handle points to.
287    pub fn get_mut<'a>(&self, ctx: &'a mut StoreObjects) -> &'a mut T {
288        &mut T::list_mut(ctx)[self.idx.get() - 1]
289    }
290
291    pub(crate) fn index(&self) -> usize {
292        self.idx.get()
293    }
294
295    pub(crate) fn from_index(idx: usize) -> Option<Self> {
296        NonZeroUsize::new(idx).map(|idx| Self {
297            idx,
298            marker: PhantomData,
299        })
300    }
301}
302
303/// Data used by the generated code is generally located inline within the
304/// `VMContext` for items defined in an instance. Host-defined objects are
305/// allocated separately and owned directly by the context.
306pub enum MaybeInstanceOwned<T> {
307    /// The data is owned here.
308    Host(Box<UnsafeCell<T>>),
309
310    /// The data is stored inline in the `VMContext` of an instance.
311    Instance(NonNull<T>),
312}
313
314impl<T> MaybeInstanceOwned<T> {
315    /// Returns underlying pointer to the VM data.
316    pub fn as_ptr(&self) -> NonNull<T> {
317        match self {
318            Self::Host(p) => unsafe { NonNull::new_unchecked(p.get()) },
319            Self::Instance(p) => *p,
320        }
321    }
322}
323
324impl<T> std::fmt::Debug for MaybeInstanceOwned<T>
325where
326    T: std::fmt::Debug,
327{
328    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
329        match self {
330            Self::Host(p) => {
331                write!(f, "host(")?;
332                p.as_ref().fmt(f)?;
333                write!(f, ")")
334            }
335            Self::Instance(p) => {
336                write!(f, "instance(")?;
337                unsafe { p.as_ref().fmt(f)? };
338                write!(f, ")")
339            }
340        }
341    }
342}