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}