Skip to main content

wasmer_vm/
memory.rs

1// This file contains code from external sources.
2// Attributions: https://github.com/wasmerio/wasmer/blob/main/docs/ATTRIBUTIONS.md
3
4//! Memory management for linear memories.
5//!
6//! `Memory` is to WebAssembly linear memories what `Table` is to WebAssembly tables.
7
8use crate::threadconditions::ThreadConditions;
9pub use crate::threadconditions::{NotifyLocation, WaiterError};
10use crate::trap::Trap;
11use crate::{
12    mmap::{Mmap, MmapType},
13    store::MaybeInstanceOwned,
14    threadconditions::ExpectedValue,
15    vmcontext::VMMemoryDefinition,
16};
17use more_asserts::assert_ge;
18use std::cell::UnsafeCell;
19use std::convert::TryInto;
20use std::ptr::NonNull;
21use std::slice;
22use std::sync::{Arc, RwLock};
23use std::time::Duration;
24use wasmer_types::{Bytes, MemoryError, MemoryStyle, MemoryType, Pages, WASM_PAGE_SIZE};
25
26// The memory mapped area
27#[derive(Debug)]
28struct WasmMmap {
29    // Our OS allocation of mmap'd memory.
30    alloc: Mmap,
31    // The current logical size in wasm pages of this linear memory.
32    size: Pages,
33    /// The owned memory definition used by the generated code
34    vm_memory_definition: MaybeInstanceOwned<VMMemoryDefinition>,
35}
36
37/// # SAFETY: Not safe by rust standards, since guest code may do weird things
38/// with its memory. However, this is still safe to send across threads as
39/// far as the WASM spec is concerned.
40unsafe impl Send for WasmMmap {}
41/// # SAFETY: see above.
42unsafe impl Sync for WasmMmap {}
43
44impl WasmMmap {
45    fn get_vm_memory_definition(&self) -> NonNull<VMMemoryDefinition> {
46        self.vm_memory_definition.as_ptr()
47    }
48
49    fn size(&self) -> Pages {
50        unsafe {
51            let md_ptr = self.get_vm_memory_definition();
52            let md = md_ptr.as_ref();
53            Bytes::from(md.current_length).try_into().unwrap()
54        }
55    }
56
57    fn grow(&mut self, delta: Pages, conf: VMMemoryConfig) -> Result<Pages, MemoryError> {
58        // Optimization of memory.grow 0 calls.
59        if delta.0 == 0 {
60            return Ok(self.size);
61        }
62
63        let new_pages = self
64            .size
65            .checked_add(delta)
66            .ok_or(MemoryError::CouldNotGrow {
67                current: self.size,
68                attempted_delta: delta,
69            })?;
70        let prev_pages = self.size;
71
72        if let Some(maximum) = conf.maximum
73            && new_pages > maximum
74        {
75            return Err(MemoryError::CouldNotGrow {
76                current: self.size,
77                attempted_delta: delta,
78            });
79        }
80
81        // Wasm linear memories are never allowed to grow beyond what is
82        // indexable. If the memory has no maximum, enforce the greatest
83        // limit here.
84        if new_pages > Pages::max_value() {
85            // Linear memory size would exceed the index range.
86            return Err(MemoryError::CouldNotGrow {
87                current: self.size,
88                attempted_delta: delta,
89            });
90        }
91
92        let delta_bytes = delta.bytes().0;
93        let prev_bytes = prev_pages.bytes().0;
94        let new_bytes = new_pages.bytes().0;
95
96        if new_bytes > self.alloc.len() - conf.offset_guard_size {
97            // If the new size is within the declared maximum, but needs more memory than we
98            // have on hand, it's a dynamic heap and it can move.
99            let guard_bytes = conf.offset_guard_size;
100            let request_bytes =
101                new_bytes
102                    .checked_add(guard_bytes)
103                    .ok_or_else(|| MemoryError::CouldNotGrow {
104                        current: new_pages,
105                        attempted_delta: Bytes(guard_bytes).try_into().unwrap(),
106                    })?;
107
108            let mut new_mmap =
109                Mmap::accessible_reserved(new_bytes, request_bytes, None, MmapType::Private, true)
110                    .map_err(MemoryError::Region)?;
111
112            let copy_len = self.alloc.len() - conf.offset_guard_size;
113            new_mmap.as_mut_slice()[..copy_len].copy_from_slice(&self.alloc.as_slice()[..copy_len]);
114
115            self.alloc = new_mmap;
116        } else if delta_bytes > 0 {
117            // Make the newly allocated pages accessible.
118            self.alloc
119                .make_accessible(prev_bytes, delta_bytes)
120                .map_err(MemoryError::Region)?;
121        }
122
123        self.size = new_pages;
124
125        // update memory definition
126        unsafe {
127            let mut md_ptr = self.vm_memory_definition.as_ptr();
128            let md = md_ptr.as_mut();
129            md.current_length = new_pages.bytes().0;
130            md.base = self.alloc.as_mut_ptr() as _;
131        }
132
133        Ok(prev_pages)
134    }
135
136    /// Grows the memory to at least a minimum size. If the memory is already big enough
137    /// for the min size then this function does nothing
138    fn grow_at_least(&mut self, min_size: u64, conf: VMMemoryConfig) -> Result<(), MemoryError> {
139        let cur_size = self.size.bytes().0 as u64;
140        if cur_size < min_size {
141            let growth = min_size - cur_size;
142            let growth_pages = ((growth - 1) / WASM_PAGE_SIZE as u64) + 1;
143            self.grow(Pages(growth_pages as u32), conf)?;
144        }
145
146        Ok(())
147    }
148
149    /// Resets the memory down to a zero size
150    fn reset(&mut self) -> Result<(), MemoryError> {
151        self.size.0 = 0;
152        // update memory definition
153        unsafe {
154            let mut md_ptr = self.vm_memory_definition.as_ptr();
155            let md = md_ptr.as_mut();
156            md.current_length = 0;
157        }
158        Ok(())
159    }
160
161    /// Copies the memory
162    /// (in this case it performs a copy-on-write to save memory)
163    pub fn copy(&self) -> Result<Self, MemoryError> {
164        let mem_length = self.size.bytes().0;
165        let mut alloc = self
166            .alloc
167            .copy(Some(mem_length))
168            .map_err(MemoryError::Generic)?;
169        let base_ptr = alloc.as_mut_ptr();
170        Ok(Self {
171            vm_memory_definition: MaybeInstanceOwned::Host(Box::new(UnsafeCell::new(
172                VMMemoryDefinition {
173                    base: base_ptr,
174                    current_length: mem_length,
175                },
176            ))),
177            alloc,
178            size: self.size,
179        })
180    }
181}
182
183/// A linear memory instance.
184#[derive(Debug, Clone)]
185struct VMMemoryConfig {
186    // The optional maximum size in wasm pages of this linear memory.
187    maximum: Option<Pages>,
188    /// The WebAssembly linear memory description.
189    memory: MemoryType,
190    /// Our chosen implementation style.
191    style: MemoryStyle,
192    // Size in bytes of extra guard pages after the end to optimize loads and stores with
193    // constant offsets.
194    offset_guard_size: usize,
195}
196
197impl VMMemoryConfig {
198    fn ty(&self, minimum: Pages) -> MemoryType {
199        let mut out = self.memory;
200        out.minimum = minimum;
201
202        out
203    }
204
205    fn style(&self) -> MemoryStyle {
206        self.style
207    }
208}
209
210/// A linear memory instance.
211#[derive(Debug)]
212pub struct VMOwnedMemory {
213    // The underlying allocation.
214    mmap: WasmMmap,
215    // Configuration of this memory
216    config: VMMemoryConfig,
217}
218
219unsafe impl Send for VMOwnedMemory {}
220unsafe impl Sync for VMOwnedMemory {}
221
222impl VMOwnedMemory {
223    /// Create a new linear memory instance with specified minimum and maximum number of wasm pages.
224    ///
225    /// This creates a `Memory` with owned metadata: this can be used to create a memory
226    /// that will be imported into Wasm modules.
227    pub fn new(memory: &MemoryType, style: &MemoryStyle) -> Result<Self, MemoryError> {
228        unsafe { Self::new_internal(memory, style, None, None, MmapType::Private) }
229    }
230
231    /// Create a new linear memory instance with specified minimum and maximum number of wasm pages
232    /// that is backed by a memory file. When set to private the file will be remaining in memory and
233    /// never flush to disk, when set to shared the memory will be flushed to disk.
234    ///
235    /// This creates a `Memory` with owned metadata: this can be used to create a memory
236    /// that will be imported into Wasm modules.
237    pub fn new_with_file(
238        memory: &MemoryType,
239        style: &MemoryStyle,
240        backing_file: std::path::PathBuf,
241        memory_type: MmapType,
242    ) -> Result<Self, MemoryError> {
243        unsafe { Self::new_internal(memory, style, None, Some(backing_file), memory_type) }
244    }
245
246    /// Create a new linear memory instance with specified minimum and maximum number of wasm pages.
247    ///
248    /// This creates a `Memory` with metadata owned by a VM, pointed to by
249    /// `vm_memory_location`: this can be used to create a local memory.
250    ///
251    /// # Safety
252    /// - `vm_memory_location` must point to a valid location in VM memory.
253    pub unsafe fn from_definition(
254        memory: &MemoryType,
255        style: &MemoryStyle,
256        vm_memory_location: NonNull<VMMemoryDefinition>,
257    ) -> Result<Self, MemoryError> {
258        unsafe {
259            Self::new_internal(
260                memory,
261                style,
262                Some(vm_memory_location),
263                None,
264                MmapType::Private,
265            )
266        }
267    }
268
269    /// Create a new linear memory instance with specified minimum and maximum number of wasm pages
270    /// that is backed by a file. When set to private the file will be remaining in memory and
271    /// never flush to disk, when set to shared the memory will be flushed to disk.
272    ///
273    /// This creates a `Memory` with metadata owned by a VM, pointed to by
274    /// `vm_memory_location`: this can be used to create a local memory.
275    ///
276    /// # Safety
277    /// - `vm_memory_location` must point to a valid location in VM memory.
278    pub unsafe fn from_definition_with_file(
279        memory: &MemoryType,
280        style: &MemoryStyle,
281        vm_memory_location: NonNull<VMMemoryDefinition>,
282        backing_file: Option<std::path::PathBuf>,
283        memory_type: MmapType,
284    ) -> Result<Self, MemoryError> {
285        unsafe {
286            Self::new_internal(
287                memory,
288                style,
289                Some(vm_memory_location),
290                backing_file,
291                memory_type,
292            )
293        }
294    }
295
296    /// Build a `Memory` with either self-owned or VM owned metadata.
297    unsafe fn new_internal(
298        memory: &MemoryType,
299        style: &MemoryStyle,
300        vm_memory_location: Option<NonNull<VMMemoryDefinition>>,
301        backing_file: Option<std::path::PathBuf>,
302        memory_type: MmapType,
303    ) -> Result<Self, MemoryError> {
304        unsafe {
305            if memory.minimum > Pages::max_value() {
306                return Err(MemoryError::MinimumMemoryTooLarge {
307                    min_requested: memory.minimum,
308                    max_allowed: Pages::max_value(),
309                });
310            }
311            // `maximum` cannot be set to more than `65536` pages.
312            if let Some(max) = memory.maximum {
313                if max > Pages::max_value() {
314                    return Err(MemoryError::MaximumMemoryTooLarge {
315                        max_requested: max,
316                        max_allowed: Pages::max_value(),
317                    });
318                }
319                if max < memory.minimum {
320                    return Err(MemoryError::InvalidMemory {
321                        reason: format!(
322                            "the maximum ({} pages) is less than the minimum ({} pages)",
323                            max.0, memory.minimum.0
324                        ),
325                    });
326                }
327            }
328
329            let offset_guard_bytes = usize::try_from(style.offset_guard_size()).map_err(|e| {
330                MemoryError::Generic(format!("cannot install memory guard page: {e}"))
331            })?;
332
333            let minimum_pages = match style {
334                MemoryStyle::Dynamic { .. } => memory.minimum,
335                MemoryStyle::Static => {
336                    let bound = MemoryStyle::static_bound();
337                    assert_ge!(bound, memory.minimum);
338                    bound
339                }
340            };
341            let minimum_bytes = minimum_pages.bytes().0;
342            let request_bytes = minimum_bytes.checked_add(offset_guard_bytes).unwrap();
343            let mapped_pages = memory.minimum;
344            let mapped_bytes = mapped_pages.bytes();
345
346            let mut alloc = Mmap::accessible_reserved(
347                mapped_bytes.0,
348                request_bytes,
349                backing_file,
350                memory_type,
351                true,
352            )
353            .map_err(MemoryError::Region)?;
354
355            let base_ptr = alloc.as_mut_ptr();
356            let mem_length = memory
357                .minimum
358                .bytes()
359                .0
360                .max(alloc.as_slice_accessible().len());
361            let mmap = WasmMmap {
362                vm_memory_definition: if let Some(mem_loc) = vm_memory_location {
363                    {
364                        let mut ptr = mem_loc;
365                        let md = ptr.as_mut();
366                        md.base = base_ptr;
367                        md.current_length = mem_length;
368                    }
369                    MaybeInstanceOwned::Instance(mem_loc)
370                } else {
371                    MaybeInstanceOwned::Host(Box::new(UnsafeCell::new(VMMemoryDefinition {
372                        base: base_ptr,
373                        current_length: mem_length,
374                    })))
375                },
376                alloc,
377                size: Bytes::from(mem_length).try_into().unwrap(),
378            };
379
380            Ok(Self {
381                mmap,
382                config: VMMemoryConfig {
383                    maximum: memory.maximum,
384                    offset_guard_size: offset_guard_bytes,
385                    memory: *memory,
386                    style: *style,
387                },
388            })
389        }
390    }
391
392    /// Converts this owned memory into shared memory
393    pub fn to_shared(self) -> VMSharedMemory {
394        VMSharedMemory {
395            mmap: Arc::new(RwLock::new(self.mmap)),
396            config: self.config,
397            conditions: ThreadConditions::new(),
398        }
399    }
400
401    /// Copies this memory to a new memory
402    pub fn copy(&self) -> Result<Self, MemoryError> {
403        Ok(Self {
404            mmap: self.mmap.copy()?,
405            config: self.config.clone(),
406        })
407    }
408}
409
410// TODO: why doesn't this support wait/notify? wait should block indefinitely if you ask me
411impl LinearMemory for VMOwnedMemory {
412    /// Returns the type for this memory.
413    fn ty(&self) -> MemoryType {
414        let minimum = self.mmap.size();
415        self.config.ty(minimum)
416    }
417
418    /// Returns the size of the memory in pages
419    fn size(&self) -> Pages {
420        self.mmap.size()
421    }
422
423    /// Returns the memory style for this memory.
424    fn style(&self) -> MemoryStyle {
425        self.config.style()
426    }
427
428    /// Grow memory by the specified amount of wasm pages.
429    ///
430    /// Returns `None` if memory can't be grown by the specified amount
431    /// of wasm pages.
432    fn grow(&mut self, delta: Pages) -> Result<Pages, MemoryError> {
433        self.mmap.grow(delta, self.config.clone())
434    }
435
436    /// Grows the memory to at least a minimum size. If the memory is already big enough
437    /// for the min size then this function does nothing
438    fn grow_at_least(&mut self, min_size: u64) -> Result<(), MemoryError> {
439        self.mmap.grow_at_least(min_size, self.config.clone())
440    }
441
442    /// Resets the memory down to a zero size
443    fn reset(&mut self) -> Result<(), MemoryError> {
444        self.mmap.reset()?;
445        Ok(())
446    }
447
448    /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code.
449    fn vmmemory(&self) -> NonNull<VMMemoryDefinition> {
450        self.mmap.vm_memory_definition.as_ptr()
451    }
452
453    /// Owned memory can not be cloned (this will always return None)
454    fn try_clone(&self) -> Result<Box<dyn LinearMemory + Send + Sync + 'static>, MemoryError> {
455        Err(MemoryError::MemoryNotShared)
456    }
457
458    /// Copies this memory to a new memory
459    fn copy(&self) -> Result<Box<dyn LinearMemory + Send + Sync + 'static>, MemoryError> {
460        let forked = Self::copy(self)?;
461        Ok(Box::new(forked))
462    }
463
464    /// Return a concrete shared memory handle for detached API sharing.
465    fn as_shared(&self) -> Result<VMSharedMemory, MemoryError> {
466        Err(MemoryError::MemoryNotShared)
467    }
468}
469
470/// A shared linear memory instance.
471#[derive(Debug, Clone)]
472pub struct VMSharedMemory {
473    // The underlying allocation.
474    mmap: Arc<RwLock<WasmMmap>>,
475    // Configuration of this memory
476    config: VMMemoryConfig,
477    // waiters list for this memory
478    conditions: ThreadConditions,
479}
480
481impl VMSharedMemory {
482    /// Create a new linear memory instance with specified minimum and maximum number of wasm pages.
483    ///
484    /// This creates a `Memory` with owned metadata: this can be used to create a memory
485    /// that will be imported into Wasm modules.
486    pub fn new(memory: &MemoryType, style: &MemoryStyle) -> Result<Self, MemoryError> {
487        Ok(VMOwnedMemory::new(memory, style)?.to_shared())
488    }
489
490    /// Create a new linear memory instance with specified minimum and maximum number of wasm pages
491    /// that is backed by a file. When set to private the file will be remaining in memory and
492    /// never flush to disk, when set to shared the memory will be flushed to disk.
493    ///
494    /// This creates a `Memory` with owned metadata: this can be used to create a memory
495    /// that will be imported into Wasm modules.
496    pub fn new_with_file(
497        memory: &MemoryType,
498        style: &MemoryStyle,
499        backing_file: std::path::PathBuf,
500        memory_type: MmapType,
501    ) -> Result<Self, MemoryError> {
502        Ok(VMOwnedMemory::new_with_file(memory, style, backing_file, memory_type)?.to_shared())
503    }
504
505    /// Create a new linear memory instance with specified minimum and maximum number of wasm pages.
506    ///
507    /// This creates a `Memory` with metadata owned by a VM, pointed to by
508    /// `vm_memory_location`: this can be used to create a local memory.
509    ///
510    /// # Safety
511    /// - `vm_memory_location` must point to a valid location in VM memory.
512    pub unsafe fn from_definition(
513        memory: &MemoryType,
514        style: &MemoryStyle,
515        vm_memory_location: NonNull<VMMemoryDefinition>,
516    ) -> Result<Self, MemoryError> {
517        unsafe {
518            Ok(VMOwnedMemory::from_definition(memory, style, vm_memory_location)?.to_shared())
519        }
520    }
521
522    /// Create a new linear memory instance with specified minimum and maximum number of wasm pages
523    /// that is backed by a file. When set to private the file will be remaining in memory and
524    /// never flush to disk, when set to shared the memory will be flushed to disk.
525    ///
526    /// This creates a `Memory` with metadata owned by a VM, pointed to by
527    /// `vm_memory_location`: this can be used to create a local memory.
528    ///
529    /// # Safety
530    /// - `vm_memory_location` must point to a valid location in VM memory.
531    pub unsafe fn from_definition_with_file(
532        memory: &MemoryType,
533        style: &MemoryStyle,
534        vm_memory_location: NonNull<VMMemoryDefinition>,
535        backing_file: Option<std::path::PathBuf>,
536        memory_type: MmapType,
537    ) -> Result<Self, MemoryError> {
538        unsafe {
539            Ok(VMOwnedMemory::from_definition_with_file(
540                memory,
541                style,
542                vm_memory_location,
543                backing_file,
544                memory_type,
545            )?
546            .to_shared())
547        }
548    }
549
550    /// Copies this memory to a new memory
551    pub fn copy(&self) -> Result<Self, MemoryError> {
552        let guard = self.mmap.read().unwrap();
553        Ok(Self {
554            mmap: Arc::new(RwLock::new(guard.copy()?)),
555            config: self.config.clone(),
556            conditions: ThreadConditions::new(),
557        })
558    }
559}
560
561impl LinearMemory for VMSharedMemory {
562    /// Returns the type for this memory.
563    fn ty(&self) -> MemoryType {
564        let minimum = {
565            let guard = self.mmap.read().unwrap();
566            guard.size()
567        };
568        self.config.ty(minimum)
569    }
570
571    /// Returns the size of the memory in pages
572    fn size(&self) -> Pages {
573        let guard = self.mmap.read().unwrap();
574        guard.size()
575    }
576
577    /// Returns the memory style for this memory.
578    fn style(&self) -> MemoryStyle {
579        self.config.style()
580    }
581
582    /// Grow memory by the specified amount of wasm pages.
583    ///
584    /// Returns `None` if memory can't be grown by the specified amount
585    /// of wasm pages.
586    fn grow(&mut self, delta: Pages) -> Result<Pages, MemoryError> {
587        let mut guard = self.mmap.write().unwrap();
588        guard.grow(delta, self.config.clone())
589    }
590
591    /// Grows the memory to at least a minimum size. If the memory is already big enough
592    /// for the min size then this function does nothing
593    fn grow_at_least(&mut self, min_size: u64) -> Result<(), MemoryError> {
594        let mut guard = self.mmap.write().unwrap();
595        guard.grow_at_least(min_size, self.config.clone())
596    }
597
598    /// Resets the memory down to a zero size
599    fn reset(&mut self) -> Result<(), MemoryError> {
600        let mut guard = self.mmap.write().unwrap();
601        guard.reset()?;
602        Ok(())
603    }
604
605    /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code.
606    fn vmmemory(&self) -> NonNull<VMMemoryDefinition> {
607        let guard = self.mmap.read().unwrap();
608        guard.vm_memory_definition.as_ptr()
609    }
610
611    /// Shared memory can always be cloned
612    fn try_clone(&self) -> Result<Box<dyn LinearMemory + Send + Sync + 'static>, MemoryError> {
613        Ok(Box::new(self.clone()))
614    }
615
616    /// Copies this memory to a new memory
617    fn copy(&self) -> Result<Box<dyn LinearMemory + Send + Sync + 'static>, MemoryError> {
618        let forked = Self::copy(self)?;
619        Ok(Box::new(forked))
620    }
621
622    /// Return a concrete shared memory handle for detached API sharing.
623    fn as_shared(&self) -> Result<VMSharedMemory, MemoryError> {
624        Ok(self.clone())
625    }
626
627    // Add current thread to waiter list
628    unsafe fn do_wait(
629        &mut self,
630        dst: u32,
631        expected: ExpectedValue,
632        timeout: Option<Duration>,
633    ) -> Result<u32, WaiterError> {
634        let dst = NotifyLocation {
635            address: dst,
636            memory_base: self.mmap.read().unwrap().alloc.as_ptr() as *mut _,
637        };
638        unsafe { self.conditions.do_wait(dst, expected, timeout) }
639    }
640
641    /// Notify waiters from the wait list. Return the number of waiters notified
642    fn do_notify(&mut self, dst: u32, count: u32) -> u32 {
643        self.conditions.do_notify(dst, count)
644    }
645
646    fn thread_conditions(&self) -> Option<&ThreadConditions> {
647        Some(&self.conditions)
648    }
649}
650
651impl From<VMOwnedMemory> for VMMemory {
652    fn from(mem: VMOwnedMemory) -> Self {
653        Self(Box::new(mem))
654    }
655}
656
657impl From<VMSharedMemory> for VMMemory {
658    fn from(mem: VMSharedMemory) -> Self {
659        Self(Box::new(mem))
660    }
661}
662
663/// Represents linear memory that can be either owned or shared
664#[derive(Debug)]
665pub struct VMMemory(pub Box<dyn LinearMemory + Send + Sync + 'static>);
666
667impl From<Box<dyn LinearMemory + Send + Sync + 'static>> for VMMemory {
668    fn from(mem: Box<dyn LinearMemory + Send + Sync + 'static>) -> Self {
669        Self(mem)
670    }
671}
672
673impl LinearMemory for VMMemory {
674    /// Returns the type for this memory.
675    fn ty(&self) -> MemoryType {
676        self.0.ty()
677    }
678
679    /// Returns the size of the memory in pages
680    fn size(&self) -> Pages {
681        self.0.size()
682    }
683
684    /// Grow memory by the specified amount of wasm pages.
685    ///
686    /// Returns `None` if memory can't be grown by the specified amount
687    /// of wasm pages.
688    fn grow(&mut self, delta: Pages) -> Result<Pages, MemoryError> {
689        self.0.grow(delta)
690    }
691
692    /// Grows the memory to at least a minimum size. If the memory is already big enough
693    /// for the min size then this function does nothing
694    fn grow_at_least(&mut self, min_size: u64) -> Result<(), MemoryError> {
695        self.0.grow_at_least(min_size)
696    }
697
698    /// Resets the memory down to a zero size
699    fn reset(&mut self) -> Result<(), MemoryError> {
700        self.0.reset()?;
701        Ok(())
702    }
703
704    /// Returns the memory style for this memory.
705    fn style(&self) -> MemoryStyle {
706        self.0.style()
707    }
708
709    /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code.
710    fn vmmemory(&self) -> NonNull<VMMemoryDefinition> {
711        self.0.vmmemory()
712    }
713
714    /// Attempts to clone this memory (if its cloneable)
715    fn try_clone(&self) -> Result<Box<dyn LinearMemory + Send + Sync + 'static>, MemoryError> {
716        self.0.try_clone()
717    }
718
719    /// Initialize memory with data
720    unsafe fn initialize_with_data(&self, start: usize, data: &[u8]) -> Result<(), Trap> {
721        unsafe { self.0.initialize_with_data(start, data) }
722    }
723
724    /// Copies this memory to a new memory
725    fn copy(&self) -> Result<Box<dyn LinearMemory + Send + Sync + 'static>, MemoryError> {
726        self.0.copy()
727    }
728
729    fn as_shared(&self) -> Result<VMSharedMemory, MemoryError> {
730        self.0.as_shared()
731    }
732
733    // Add current thread to waiter list
734    unsafe fn do_wait(
735        &mut self,
736        dst: u32,
737        expected: ExpectedValue,
738        timeout: Option<Duration>,
739    ) -> Result<u32, WaiterError> {
740        unsafe { self.0.do_wait(dst, expected, timeout) }
741    }
742
743    /// Notify waiters from the wait list. Return the number of waiters notified
744    fn do_notify(&mut self, dst: u32, count: u32) -> u32 {
745        self.0.do_notify(dst, count)
746    }
747
748    fn thread_conditions(&self) -> Option<&ThreadConditions> {
749        self.0.thread_conditions()
750    }
751}
752
753impl VMMemory {
754    /// Creates a new linear memory instance of the correct type with specified
755    /// minimum and maximum number of wasm pages.
756    ///
757    /// This creates a `Memory` with owned metadata: this can be used to create a memory
758    /// that will be imported into Wasm modules.
759    pub fn new(memory: &MemoryType, style: &MemoryStyle) -> Result<Self, MemoryError> {
760        Ok(if memory.shared {
761            Self(Box::new(VMSharedMemory::new(memory, style)?))
762        } else {
763            Self(Box::new(VMOwnedMemory::new(memory, style)?))
764        })
765    }
766
767    /// Returns the number of pages in the allocated memory block
768    pub fn get_runtime_size(&self) -> u32 {
769        self.0.size().0
770    }
771
772    /// Create a new linear memory instance with specified minimum and maximum number of wasm pages.
773    ///
774    /// This creates a `Memory` with metadata owned by a VM, pointed to by
775    /// `vm_memory_location`: this can be used to create a local memory.
776    ///
777    /// # Safety
778    /// - `vm_memory_location` must point to a valid location in VM memory.
779    pub unsafe fn from_definition(
780        memory: &MemoryType,
781        style: &MemoryStyle,
782        vm_memory_location: NonNull<VMMemoryDefinition>,
783    ) -> Result<Self, MemoryError> {
784        unsafe {
785            Ok(if memory.shared {
786                Self(Box::new(VMSharedMemory::from_definition(
787                    memory,
788                    style,
789                    vm_memory_location,
790                )?))
791            } else {
792                Self(Box::new(VMOwnedMemory::from_definition(
793                    memory,
794                    style,
795                    vm_memory_location,
796                )?))
797            })
798        }
799    }
800
801    /// Creates VMMemory from a custom implementation - the following into implementations
802    /// are natively supported
803    /// - VMOwnedMemory -> VMMemory
804    /// - Box<dyn LinearMemory + 'static> -> VMMemory
805    pub fn from_custom<IntoVMMemory>(memory: IntoVMMemory) -> Self
806    where
807        IntoVMMemory: Into<Self>,
808    {
809        memory.into()
810    }
811
812    /// Copies this memory to a new memory
813    pub fn copy(&self) -> Result<Box<dyn LinearMemory + Send + Sync + 'static>, MemoryError> {
814        LinearMemory::copy(self)
815    }
816
817    /// Attempts to clone this memory handle.
818    pub fn try_clone(&self) -> Result<Self, MemoryError> {
819        LinearMemory::try_clone(self).map(Self)
820    }
821}
822
823#[doc(hidden)]
824/// Default implementation to initialize memory with data
825pub unsafe fn initialize_memory_with_data(
826    memory: &VMMemoryDefinition,
827    start: usize,
828    data: &[u8],
829) -> Result<(), Trap> {
830    unsafe {
831        let mem_slice = slice::from_raw_parts_mut(memory.base, memory.current_length);
832        let end = start + data.len();
833        let to_init = &mut mem_slice[start..end];
834        to_init.copy_from_slice(data);
835
836        Ok(())
837    }
838}
839
840/// Represents memory that is used by the WebAssembly module
841pub trait LinearMemory
842where
843    Self: std::fmt::Debug + Send,
844{
845    /// Returns the type for this memory.
846    fn ty(&self) -> MemoryType;
847
848    /// Returns the size of the memory in pages
849    fn size(&self) -> Pages;
850
851    /// Returns the memory style for this memory.
852    fn style(&self) -> MemoryStyle;
853
854    /// Grow memory by the specified amount of wasm pages.
855    ///
856    /// Returns `None` if memory can't be grown by the specified amount
857    /// of wasm pages.
858    fn grow(&mut self, delta: Pages) -> Result<Pages, MemoryError>;
859
860    /// Grows the memory to at least a minimum size. If the memory is already big enough
861    /// for the min size then this function does nothing
862    fn grow_at_least(&mut self, _min_size: u64) -> Result<(), MemoryError> {
863        Err(MemoryError::UnsupportedOperation {
864            message: "grow_at_least() is not supported".to_string(),
865        })
866    }
867
868    /// Resets the memory back to zero length
869    fn reset(&mut self) -> Result<(), MemoryError> {
870        Err(MemoryError::UnsupportedOperation {
871            message: "reset() is not supported".to_string(),
872        })
873    }
874
875    /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code.
876    fn vmmemory(&self) -> NonNull<VMMemoryDefinition>;
877
878    /// Attempts to clone this memory (if its cloneable)
879    fn try_clone(&self) -> Result<Box<dyn LinearMemory + Send + Sync + 'static>, MemoryError>;
880
881    #[doc(hidden)]
882    /// # Safety
883    /// This function is unsafe because WebAssembly specification requires that data is always set at initialization time.
884    /// It should be the implementors responsibility to make sure this respects the spec
885    unsafe fn initialize_with_data(&self, start: usize, data: &[u8]) -> Result<(), Trap> {
886        unsafe {
887            let memory = self.vmmemory().as_ref();
888
889            initialize_memory_with_data(memory, start, data)
890        }
891    }
892
893    /// Copies this memory to a new memory
894    fn copy(&self) -> Result<Box<dyn LinearMemory + Send + Sync + 'static>, MemoryError>;
895
896    /// Returns a concrete shared memory handle if this memory is shared.
897    fn as_shared(&self) -> Result<VMSharedMemory, MemoryError> {
898        Err(MemoryError::MemoryNotShared)
899    }
900
901    /// Add current thread to the waiter hash, and wait until notified or timeout.
902    /// Return 0 if the waiter has been notified, 1 if there was a value mismatch,
903    /// or 2 if the timeout occurred.
904    ///
905    /// # Safety
906    /// the destination address must be a valid offset within this memory. It must also
907    /// be properly aligned for the expected value type; either 4-byte aligned for
908    /// `ExpectedValue::U32` or 8-byte aligned for `ExpectedValue::u64`.
909    unsafe fn do_wait(
910        &mut self,
911        _dst: u32,
912        _expected: ExpectedValue,
913        _timeout: Option<Duration>,
914    ) -> Result<u32, WaiterError> {
915        Err(WaiterError::Unimplemented)
916    }
917
918    /// Notify waiters from the wait list. Return the number of waiters notified
919    fn do_notify(&mut self, _dst: u32, _count: u32) -> u32 {
920        0
921    }
922
923    /// Access the internal atomics handler.
924    ///
925    /// Will be [`None`] if the memory does not support atomics.
926    fn thread_conditions(&self) -> Option<&ThreadConditions> {
927        None
928    }
929}