linera_wasmer/externals/
memory.rs

1#[cfg(feature = "js")]
2use crate::js::externals::memory as memory_impl;
3#[cfg(feature = "jsc")]
4use crate::jsc::externals::memory as memory_impl;
5#[cfg(feature = "sys")]
6use crate::sys::externals::memory as memory_impl;
7
8use super::memory_view::MemoryView;
9use crate::exports::{ExportError, Exportable};
10use crate::store::{AsStoreMut, AsStoreRef};
11use crate::vm::{VMExtern, VMExternMemory, VMMemory};
12use crate::MemoryAccessError;
13use crate::MemoryType;
14use crate::{AtomicsError, Extern};
15use std::mem::MaybeUninit;
16use wasmer_types::{MemoryError, Pages};
17
18/// A WebAssembly `memory` instance.
19///
20/// A memory instance is the runtime representation of a linear memory.
21/// It consists of a vector of bytes and an optional maximum size.
22///
23/// The length of the vector always is a multiple of the WebAssembly
24/// page size, which is defined to be the constant 65536 – abbreviated 64Ki.
25/// Like in a memory type, the maximum size in a memory instance is
26/// given in units of this page size.
27///
28/// A memory created by the host or in WebAssembly code will be accessible and
29/// mutable from both host and WebAssembly.
30///
31/// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances>
32#[derive(Debug, Clone, PartialEq)]
33#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
34pub struct Memory(pub(crate) memory_impl::Memory);
35
36impl Memory {
37    /// Creates a new host `Memory` from the provided [`MemoryType`].
38    ///
39    /// This function will construct the `Memory` using the store
40    /// `BaseTunables`.
41    ///
42    /// # Example
43    ///
44    /// ```
45    /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value};
46    /// # let mut store = Store::default();
47    /// #
48    /// let m = Memory::new(&mut store, MemoryType::new(1, None, false)).unwrap();
49    /// ```
50    pub fn new(store: &mut impl AsStoreMut, ty: MemoryType) -> Result<Self, MemoryError> {
51        Ok(Self(memory_impl::Memory::new(store, ty)?))
52    }
53
54    /// Create a memory object from an existing memory and attaches it to the store
55    pub fn new_from_existing(new_store: &mut impl AsStoreMut, memory: VMMemory) -> Self {
56        Self(memory_impl::Memory::new_from_existing(new_store, memory))
57    }
58
59    /// Returns the [`MemoryType`] of the `Memory`.
60    ///
61    /// # Example
62    ///
63    /// ```
64    /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value};
65    /// # let mut store = Store::default();
66    /// #
67    /// let mt = MemoryType::new(1, None, false);
68    /// let m = Memory::new(&mut store, mt).unwrap();
69    ///
70    /// assert_eq!(m.ty(&mut store), mt);
71    /// ```
72    pub fn ty(&self, store: &impl AsStoreRef) -> MemoryType {
73        self.0.ty(store)
74    }
75
76    /// Creates a view into the memory that then allows for
77    /// read and write
78    pub fn view<'a>(&self, store: &'a (impl AsStoreRef + ?Sized)) -> MemoryView<'a> {
79        MemoryView::new(self, store)
80    }
81
82    /// Grow memory by the specified amount of WebAssembly [`Pages`] and return
83    /// the previous memory size.
84    ///
85    /// # Example
86    ///
87    /// ```
88    /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value, WASM_MAX_PAGES};
89    /// # let mut store = Store::default();
90    /// #
91    /// let m = Memory::new(&mut store, MemoryType::new(1, Some(3), false)).unwrap();
92    /// let p = m.grow(&mut store, 2).unwrap();
93    ///
94    /// assert_eq!(p, Pages(1));
95    /// assert_eq!(m.view(&mut store).size(), Pages(3));
96    /// ```
97    ///
98    /// # Errors
99    ///
100    /// Returns an error if memory can't be grown by the specified amount
101    /// of pages.
102    ///
103    /// ```should_panic
104    /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value, WASM_MAX_PAGES};
105    /// # use wasmer::FunctionEnv;
106    /// # let mut store = Store::default();
107    /// # let env = FunctionEnv::new(&mut store, ());
108    /// #
109    /// let m = Memory::new(&mut store, MemoryType::new(1, Some(1), false)).unwrap();
110    ///
111    /// // This results in an error: `MemoryError::CouldNotGrow`.
112    /// let s = m.grow(&mut store, 1).unwrap();
113    /// ```
114    pub fn grow<IntoPages>(
115        &self,
116        store: &mut impl AsStoreMut,
117        delta: IntoPages,
118    ) -> Result<Pages, MemoryError>
119    where
120        IntoPages: Into<Pages>,
121    {
122        self.0.grow(store, delta)
123    }
124
125    /// Grows the memory to at least a minimum size. If the memory is already big enough
126    /// for the min size then this function does nothing
127    pub fn grow_at_least(
128        &self,
129        store: &mut impl AsStoreMut,
130        min_size: u64,
131    ) -> Result<(), MemoryError> {
132        self.0.grow_at_least(store, min_size)
133    }
134
135    /// Resets the memory back to zero length
136    pub fn reset(&self, store: &mut impl AsStoreMut) -> Result<(), MemoryError> {
137        self.0.reset(store)?;
138        Ok(())
139    }
140
141    /// Attempts to duplicate this memory (if its clonable) in a new store
142    /// (copied memory)
143    pub fn copy_to_store(
144        &self,
145        store: &impl AsStoreRef,
146        new_store: &mut impl AsStoreMut,
147    ) -> Result<Self, MemoryError> {
148        if !self.ty(store).shared {
149            // We should only be able to duplicate in a new store if the memory is shared
150            return Err(MemoryError::InvalidMemory {
151                reason: "memory is not a shared memory type".to_string(),
152            });
153        }
154        self.0
155            .try_copy(&store)
156            .map(|new_memory| Self::new_from_existing(new_store, new_memory.into()))
157    }
158
159    pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExternMemory) -> Self {
160        Self(memory_impl::Memory::from_vm_extern(store, vm_extern))
161    }
162
163    /// Checks whether this `Memory` can be used with the given context.
164    pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool {
165        self.0.is_from_store(store)
166    }
167
168    /// Attempts to clone this memory (if its clonable)
169    pub fn try_clone(&self, store: &impl AsStoreRef) -> Result<VMMemory, MemoryError> {
170        self.0.try_clone(store)
171    }
172
173    /// Attempts to clone this memory (if its clonable) in a new store
174    /// (cloned memory will be shared between those that clone it)
175    pub fn share_in_store(
176        &self,
177        store: &impl AsStoreRef,
178        new_store: &mut impl AsStoreMut,
179    ) -> Result<Self, MemoryError> {
180        if !self.ty(store).shared {
181            // We should only be able to duplicate in a new store if the memory is shared
182            return Err(MemoryError::InvalidMemory {
183                reason: "memory is not a shared memory type".to_string(),
184            });
185        }
186        self.0
187            .try_clone(&store)
188            .map(|new_memory| Self::new_from_existing(new_store, new_memory))
189    }
190
191    /// Get a [`SharedMemory`].
192    ///
193    /// Only returns `Some(_)` if the memory is shared, and if the target
194    /// backend supports shared memory operations.
195    ///
196    /// See [`SharedMemory`] and its methods for more information.
197    pub fn as_shared(&self, store: &impl AsStoreRef) -> Option<SharedMemory> {
198        if !self.ty(store).shared {
199            return None;
200        }
201        self.0.as_shared(store)
202    }
203
204    /// To `VMExtern`.
205    pub(crate) fn to_vm_extern(&self) -> VMExtern {
206        self.0.to_vm_extern()
207    }
208}
209
210impl std::cmp::Eq for Memory {}
211
212impl<'a> Exportable<'a> for Memory {
213    fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> {
214        match _extern {
215            Extern::Memory(memory) => Ok(memory),
216            _ => Err(ExportError::IncompatibleType),
217        }
218    }
219}
220
221/// Location in a WebAssembly memory.
222#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
223pub struct MemoryLocation {
224    // NOTE: must be expanded to an enum that also supports 64bit memory in
225    // the future
226    // That's why this is private.
227    pub(crate) address: u32,
228}
229
230impl MemoryLocation {
231    /// Create a new memory location for a 32bit memory.
232    pub fn new_32(address: u32) -> Self {
233        Self { address }
234    }
235}
236
237impl From<u32> for MemoryLocation {
238    fn from(value: u32) -> Self {
239        Self::new_32(value)
240    }
241}
242
243/// See [`SharedMemory`].
244pub(crate) trait SharedMemoryOps {
245    /// See [`SharedMemory::disable_atomics`].
246    fn disable_atomics(&self) -> Result<(), MemoryError> {
247        Err(MemoryError::AtomicsNotSupported)
248    }
249
250    /// See [`SharedMemory::wake_all_atomic_waiters`].
251    fn wake_all_atomic_waiters(&self) -> Result<(), MemoryError> {
252        Err(MemoryError::AtomicsNotSupported)
253    }
254
255    /// See [`SharedMemory::notify`].
256    fn notify(&self, _dst: MemoryLocation, _count: u32) -> Result<u32, AtomicsError> {
257        Err(AtomicsError::Unimplemented)
258    }
259
260    /// See [`SharedMemory::wait`].
261    fn wait(
262        &self,
263        _dst: MemoryLocation,
264        _timeout: Option<std::time::Duration>,
265    ) -> Result<u32, AtomicsError> {
266        Err(AtomicsError::Unimplemented)
267    }
268}
269
270/// A handle that exposes operations only relevant for shared memories.
271///
272/// Enables interaction independent from the [`crate::Store`], and thus allows calling
273/// some methods an instane is running.
274///
275/// **NOTE**: Not all methods are supported by all backends.
276#[derive(Clone)]
277pub struct SharedMemory {
278    memory: Memory,
279    ops: std::sync::Arc<dyn SharedMemoryOps + Send + Sync>,
280}
281
282impl std::fmt::Debug for SharedMemory {
283    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
284        f.debug_struct("SharedMemory").finish()
285    }
286}
287
288impl SharedMemory {
289    /// Get the underlying memory.
290    pub fn memory(&self) -> &Memory {
291        &self.memory
292    }
293
294    /// Create a new handle from ops.
295    #[allow(unused)]
296    pub(crate) fn new(memory: Memory, ops: impl SharedMemoryOps + Send + Sync + 'static) -> Self {
297        Self {
298            memory,
299            ops: std::sync::Arc::new(ops),
300        }
301    }
302
303    /// Notify up to `count` waiters waiting for the memory location.
304    pub fn notify(&self, location: MemoryLocation, count: u32) -> Result<u32, AtomicsError> {
305        self.ops.notify(location, count)
306    }
307
308    /// Wait for the memory location to be notified.
309    pub fn wait(
310        &self,
311        location: MemoryLocation,
312        timeout: Option<std::time::Duration>,
313    ) -> Result<u32, AtomicsError> {
314        self.ops.wait(location, timeout)
315    }
316
317    /// Disable atomics for this memory.
318    ///
319    /// All subsequent atomic wait calls will produce a trap.
320    ///
321    /// This can be used or forced shutdown of instances that continuously try
322    /// to wait on atomics.
323    ///
324    /// NOTE: this operation might not be supported by all memory implementations.
325    /// In that case, this function will return an error.
326    pub fn disable_atomics(&self) -> Result<(), MemoryError> {
327        self.ops.disable_atomics()
328    }
329
330    /// Wake up all atomic waiters.
331    ///
332    /// This can be used to force-resume waiting execution.
333    ///
334    /// NOTE: this operation might not be supported by all memory implementations.
335    /// In that case, this function will return an error.
336    pub fn wake_all_atomic_waiters(&self) -> Result<(), MemoryError> {
337        self.ops.wake_all_atomic_waiters()
338    }
339}
340
341/// Underlying buffer for a memory.
342#[derive(Debug, Copy, Clone)]
343pub(crate) struct MemoryBuffer<'a>(pub(crate) memory_impl::MemoryBuffer<'a>);
344
345impl<'a> MemoryBuffer<'a> {
346    #[allow(unused)]
347    pub(crate) fn read(&self, offset: u64, buf: &mut [u8]) -> Result<(), MemoryAccessError> {
348        self.0.read(offset, buf)
349    }
350
351    #[allow(unused)]
352    pub(crate) fn read_uninit<'b>(
353        &self,
354        offset: u64,
355        buf: &'b mut [MaybeUninit<u8>],
356    ) -> Result<&'b mut [u8], MemoryAccessError> {
357        self.0.read_uninit(offset, buf)
358    }
359
360    #[allow(unused)]
361    pub(crate) fn write(&self, offset: u64, data: &[u8]) -> Result<(), MemoryAccessError> {
362        self.0.write(offset, data)
363    }
364}