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)
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 =
347                Mmap::accessible_reserved(mapped_bytes.0, request_bytes, backing_file, memory_type)
348                    .map_err(MemoryError::Region)?;
349
350            let base_ptr = alloc.as_mut_ptr();
351            let mem_length = memory
352                .minimum
353                .bytes()
354                .0
355                .max(alloc.as_slice_accessible().len());
356            let mmap = WasmMmap {
357                vm_memory_definition: if let Some(mem_loc) = vm_memory_location {
358                    {
359                        let mut ptr = mem_loc;
360                        let md = ptr.as_mut();
361                        md.base = base_ptr;
362                        md.current_length = mem_length;
363                    }
364                    MaybeInstanceOwned::Instance(mem_loc)
365                } else {
366                    MaybeInstanceOwned::Host(Box::new(UnsafeCell::new(VMMemoryDefinition {
367                        base: base_ptr,
368                        current_length: mem_length,
369                    })))
370                },
371                alloc,
372                size: Bytes::from(mem_length).try_into().unwrap(),
373            };
374
375            Ok(Self {
376                mmap,
377                config: VMMemoryConfig {
378                    maximum: memory.maximum,
379                    offset_guard_size: offset_guard_bytes,
380                    memory: *memory,
381                    style: *style,
382                },
383            })
384        }
385    }
386
387    /// Converts this owned memory into shared memory
388    pub fn to_shared(self) -> VMSharedMemory {
389        VMSharedMemory {
390            mmap: Arc::new(RwLock::new(self.mmap)),
391            config: self.config,
392            conditions: ThreadConditions::new(),
393        }
394    }
395
396    /// Copies this memory to a new memory
397    pub fn copy(&self) -> Result<Self, MemoryError> {
398        Ok(Self {
399            mmap: self.mmap.copy()?,
400            config: self.config.clone(),
401        })
402    }
403}
404
405// TODO: why doesn't this support wait/notify? wait should block indefinitely if you ask me
406impl LinearMemory for VMOwnedMemory {
407    /// Returns the type for this memory.
408    fn ty(&self) -> MemoryType {
409        let minimum = self.mmap.size();
410        self.config.ty(minimum)
411    }
412
413    /// Returns the size of the memory in pages
414    fn size(&self) -> Pages {
415        self.mmap.size()
416    }
417
418    /// Returns the memory style for this memory.
419    fn style(&self) -> MemoryStyle {
420        self.config.style()
421    }
422
423    /// Grow memory by the specified amount of wasm pages.
424    ///
425    /// Returns `None` if memory can't be grown by the specified amount
426    /// of wasm pages.
427    fn grow(&mut self, delta: Pages) -> Result<Pages, MemoryError> {
428        self.mmap.grow(delta, self.config.clone())
429    }
430
431    /// Grows the memory to at least a minimum size. If the memory is already big enough
432    /// for the min size then this function does nothing
433    fn grow_at_least(&mut self, min_size: u64) -> Result<(), MemoryError> {
434        self.mmap.grow_at_least(min_size, self.config.clone())
435    }
436
437    /// Resets the memory down to a zero size
438    fn reset(&mut self) -> Result<(), MemoryError> {
439        self.mmap.reset()?;
440        Ok(())
441    }
442
443    /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code.
444    fn vmmemory(&self) -> NonNull<VMMemoryDefinition> {
445        self.mmap.vm_memory_definition.as_ptr()
446    }
447
448    /// Owned memory can not be cloned (this will always return None)
449    fn try_clone(&self) -> Result<Box<dyn LinearMemory + Send + Sync + 'static>, MemoryError> {
450        Err(MemoryError::MemoryNotShared)
451    }
452
453    /// Copies this memory to a new memory
454    fn copy(&self) -> Result<Box<dyn LinearMemory + Send + Sync + 'static>, MemoryError> {
455        let forked = Self::copy(self)?;
456        Ok(Box::new(forked))
457    }
458
459    /// Return a concrete shared memory handle for detached API sharing.
460    fn as_shared(&self) -> Result<VMSharedMemory, MemoryError> {
461        Err(MemoryError::MemoryNotShared)
462    }
463}
464
465/// A shared linear memory instance.
466#[derive(Debug, Clone)]
467pub struct VMSharedMemory {
468    // The underlying allocation.
469    mmap: Arc<RwLock<WasmMmap>>,
470    // Configuration of this memory
471    config: VMMemoryConfig,
472    // waiters list for this memory
473    conditions: ThreadConditions,
474}
475
476impl VMSharedMemory {
477    /// Create a new linear memory instance with specified minimum and maximum number of wasm pages.
478    ///
479    /// This creates a `Memory` with owned metadata: this can be used to create a memory
480    /// that will be imported into Wasm modules.
481    pub fn new(memory: &MemoryType, style: &MemoryStyle) -> Result<Self, MemoryError> {
482        Ok(VMOwnedMemory::new(memory, style)?.to_shared())
483    }
484
485    /// Create a new linear memory instance with specified minimum and maximum number of wasm pages
486    /// that is backed by a file. When set to private the file will be remaining in memory and
487    /// never flush to disk, when set to shared the memory will be flushed to disk.
488    ///
489    /// This creates a `Memory` with owned metadata: this can be used to create a memory
490    /// that will be imported into Wasm modules.
491    pub fn new_with_file(
492        memory: &MemoryType,
493        style: &MemoryStyle,
494        backing_file: std::path::PathBuf,
495        memory_type: MmapType,
496    ) -> Result<Self, MemoryError> {
497        Ok(VMOwnedMemory::new_with_file(memory, style, backing_file, memory_type)?.to_shared())
498    }
499
500    /// Create a new linear memory instance with specified minimum and maximum number of wasm pages.
501    ///
502    /// This creates a `Memory` with metadata owned by a VM, pointed to by
503    /// `vm_memory_location`: this can be used to create a local memory.
504    ///
505    /// # Safety
506    /// - `vm_memory_location` must point to a valid location in VM memory.
507    pub unsafe fn from_definition(
508        memory: &MemoryType,
509        style: &MemoryStyle,
510        vm_memory_location: NonNull<VMMemoryDefinition>,
511    ) -> Result<Self, MemoryError> {
512        unsafe {
513            Ok(VMOwnedMemory::from_definition(memory, style, vm_memory_location)?.to_shared())
514        }
515    }
516
517    /// Create a new linear memory instance with specified minimum and maximum number of wasm pages
518    /// that is backed by a file. When set to private the file will be remaining in memory and
519    /// never flush to disk, when set to shared the memory will be flushed to disk.
520    ///
521    /// This creates a `Memory` with metadata owned by a VM, pointed to by
522    /// `vm_memory_location`: this can be used to create a local memory.
523    ///
524    /// # Safety
525    /// - `vm_memory_location` must point to a valid location in VM memory.
526    pub unsafe fn from_definition_with_file(
527        memory: &MemoryType,
528        style: &MemoryStyle,
529        vm_memory_location: NonNull<VMMemoryDefinition>,
530        backing_file: Option<std::path::PathBuf>,
531        memory_type: MmapType,
532    ) -> Result<Self, MemoryError> {
533        unsafe {
534            Ok(VMOwnedMemory::from_definition_with_file(
535                memory,
536                style,
537                vm_memory_location,
538                backing_file,
539                memory_type,
540            )?
541            .to_shared())
542        }
543    }
544
545    /// Copies this memory to a new memory
546    pub fn copy(&self) -> Result<Self, MemoryError> {
547        let guard = self.mmap.read().unwrap();
548        Ok(Self {
549            mmap: Arc::new(RwLock::new(guard.copy()?)),
550            config: self.config.clone(),
551            conditions: ThreadConditions::new(),
552        })
553    }
554}
555
556impl LinearMemory for VMSharedMemory {
557    /// Returns the type for this memory.
558    fn ty(&self) -> MemoryType {
559        let minimum = {
560            let guard = self.mmap.read().unwrap();
561            guard.size()
562        };
563        self.config.ty(minimum)
564    }
565
566    /// Returns the size of the memory in pages
567    fn size(&self) -> Pages {
568        let guard = self.mmap.read().unwrap();
569        guard.size()
570    }
571
572    /// Returns the memory style for this memory.
573    fn style(&self) -> MemoryStyle {
574        self.config.style()
575    }
576
577    /// Grow memory by the specified amount of wasm pages.
578    ///
579    /// Returns `None` if memory can't be grown by the specified amount
580    /// of wasm pages.
581    fn grow(&mut self, delta: Pages) -> Result<Pages, MemoryError> {
582        let mut guard = self.mmap.write().unwrap();
583        guard.grow(delta, self.config.clone())
584    }
585
586    /// Grows the memory to at least a minimum size. If the memory is already big enough
587    /// for the min size then this function does nothing
588    fn grow_at_least(&mut self, min_size: u64) -> Result<(), MemoryError> {
589        let mut guard = self.mmap.write().unwrap();
590        guard.grow_at_least(min_size, self.config.clone())
591    }
592
593    /// Resets the memory down to a zero size
594    fn reset(&mut self) -> Result<(), MemoryError> {
595        let mut guard = self.mmap.write().unwrap();
596        guard.reset()?;
597        Ok(())
598    }
599
600    /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code.
601    fn vmmemory(&self) -> NonNull<VMMemoryDefinition> {
602        let guard = self.mmap.read().unwrap();
603        guard.vm_memory_definition.as_ptr()
604    }
605
606    /// Shared memory can always be cloned
607    fn try_clone(&self) -> Result<Box<dyn LinearMemory + Send + Sync + 'static>, MemoryError> {
608        Ok(Box::new(self.clone()))
609    }
610
611    /// Copies this memory to a new memory
612    fn copy(&self) -> Result<Box<dyn LinearMemory + Send + Sync + 'static>, MemoryError> {
613        let forked = Self::copy(self)?;
614        Ok(Box::new(forked))
615    }
616
617    /// Return a concrete shared memory handle for detached API sharing.
618    fn as_shared(&self) -> Result<VMSharedMemory, MemoryError> {
619        Ok(self.clone())
620    }
621
622    // Add current thread to waiter list
623    unsafe fn do_wait(
624        &mut self,
625        dst: u32,
626        expected: ExpectedValue,
627        timeout: Option<Duration>,
628    ) -> Result<u32, WaiterError> {
629        let dst = NotifyLocation {
630            address: dst,
631            memory_base: self.mmap.read().unwrap().alloc.as_ptr() as *mut _,
632        };
633        unsafe { self.conditions.do_wait(dst, expected, timeout) }
634    }
635
636    /// Notify waiters from the wait list. Return the number of waiters notified
637    fn do_notify(&mut self, dst: u32, count: u32) -> u32 {
638        self.conditions.do_notify(dst, count)
639    }
640
641    fn thread_conditions(&self) -> Option<&ThreadConditions> {
642        Some(&self.conditions)
643    }
644}
645
646impl From<VMOwnedMemory> for VMMemory {
647    fn from(mem: VMOwnedMemory) -> Self {
648        Self(Box::new(mem))
649    }
650}
651
652impl From<VMSharedMemory> for VMMemory {
653    fn from(mem: VMSharedMemory) -> Self {
654        Self(Box::new(mem))
655    }
656}
657
658/// Represents linear memory that can be either owned or shared
659#[derive(Debug)]
660pub struct VMMemory(pub Box<dyn LinearMemory + Send + Sync + 'static>);
661
662impl From<Box<dyn LinearMemory + Send + Sync + 'static>> for VMMemory {
663    fn from(mem: Box<dyn LinearMemory + Send + Sync + 'static>) -> Self {
664        Self(mem)
665    }
666}
667
668impl LinearMemory for VMMemory {
669    /// Returns the type for this memory.
670    fn ty(&self) -> MemoryType {
671        self.0.ty()
672    }
673
674    /// Returns the size of the memory in pages
675    fn size(&self) -> Pages {
676        self.0.size()
677    }
678
679    /// Grow memory by the specified amount of wasm pages.
680    ///
681    /// Returns `None` if memory can't be grown by the specified amount
682    /// of wasm pages.
683    fn grow(&mut self, delta: Pages) -> Result<Pages, MemoryError> {
684        self.0.grow(delta)
685    }
686
687    /// Grows the memory to at least a minimum size. If the memory is already big enough
688    /// for the min size then this function does nothing
689    fn grow_at_least(&mut self, min_size: u64) -> Result<(), MemoryError> {
690        self.0.grow_at_least(min_size)
691    }
692
693    /// Resets the memory down to a zero size
694    fn reset(&mut self) -> Result<(), MemoryError> {
695        self.0.reset()?;
696        Ok(())
697    }
698
699    /// Returns the memory style for this memory.
700    fn style(&self) -> MemoryStyle {
701        self.0.style()
702    }
703
704    /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code.
705    fn vmmemory(&self) -> NonNull<VMMemoryDefinition> {
706        self.0.vmmemory()
707    }
708
709    /// Attempts to clone this memory (if its cloneable)
710    fn try_clone(&self) -> Result<Box<dyn LinearMemory + Send + Sync + 'static>, MemoryError> {
711        self.0.try_clone()
712    }
713
714    /// Initialize memory with data
715    unsafe fn initialize_with_data(&self, start: usize, data: &[u8]) -> Result<(), Trap> {
716        unsafe { self.0.initialize_with_data(start, data) }
717    }
718
719    /// Copies this memory to a new memory
720    fn copy(&self) -> Result<Box<dyn LinearMemory + Send + Sync + 'static>, MemoryError> {
721        self.0.copy()
722    }
723
724    fn as_shared(&self) -> Result<VMSharedMemory, MemoryError> {
725        self.0.as_shared()
726    }
727
728    // Add current thread to waiter list
729    unsafe fn do_wait(
730        &mut self,
731        dst: u32,
732        expected: ExpectedValue,
733        timeout: Option<Duration>,
734    ) -> Result<u32, WaiterError> {
735        unsafe { self.0.do_wait(dst, expected, timeout) }
736    }
737
738    /// Notify waiters from the wait list. Return the number of waiters notified
739    fn do_notify(&mut self, dst: u32, count: u32) -> u32 {
740        self.0.do_notify(dst, count)
741    }
742
743    fn thread_conditions(&self) -> Option<&ThreadConditions> {
744        self.0.thread_conditions()
745    }
746}
747
748impl VMMemory {
749    /// Creates a new linear memory instance of the correct type with specified
750    /// minimum and maximum number of wasm pages.
751    ///
752    /// This creates a `Memory` with owned metadata: this can be used to create a memory
753    /// that will be imported into Wasm modules.
754    pub fn new(memory: &MemoryType, style: &MemoryStyle) -> Result<Self, MemoryError> {
755        Ok(if memory.shared {
756            Self(Box::new(VMSharedMemory::new(memory, style)?))
757        } else {
758            Self(Box::new(VMOwnedMemory::new(memory, style)?))
759        })
760    }
761
762    /// Returns the number of pages in the allocated memory block
763    pub fn get_runtime_size(&self) -> u32 {
764        self.0.size().0
765    }
766
767    /// Create a new linear memory instance with specified minimum and maximum number of wasm pages.
768    ///
769    /// This creates a `Memory` with metadata owned by a VM, pointed to by
770    /// `vm_memory_location`: this can be used to create a local memory.
771    ///
772    /// # Safety
773    /// - `vm_memory_location` must point to a valid location in VM memory.
774    pub unsafe fn from_definition(
775        memory: &MemoryType,
776        style: &MemoryStyle,
777        vm_memory_location: NonNull<VMMemoryDefinition>,
778    ) -> Result<Self, MemoryError> {
779        unsafe {
780            Ok(if memory.shared {
781                Self(Box::new(VMSharedMemory::from_definition(
782                    memory,
783                    style,
784                    vm_memory_location,
785                )?))
786            } else {
787                Self(Box::new(VMOwnedMemory::from_definition(
788                    memory,
789                    style,
790                    vm_memory_location,
791                )?))
792            })
793        }
794    }
795
796    /// Creates VMMemory from a custom implementation - the following into implementations
797    /// are natively supported
798    /// - VMOwnedMemory -> VMMemory
799    /// - Box<dyn LinearMemory + 'static> -> VMMemory
800    pub fn from_custom<IntoVMMemory>(memory: IntoVMMemory) -> Self
801    where
802        IntoVMMemory: Into<Self>,
803    {
804        memory.into()
805    }
806
807    /// Copies this memory to a new memory
808    pub fn copy(&self) -> Result<Box<dyn LinearMemory + Send + Sync + 'static>, MemoryError> {
809        LinearMemory::copy(self)
810    }
811
812    /// Attempts to clone this memory handle.
813    pub fn try_clone(&self) -> Result<Self, MemoryError> {
814        LinearMemory::try_clone(self).map(Self)
815    }
816}
817
818#[doc(hidden)]
819/// Default implementation to initialize memory with data
820pub unsafe fn initialize_memory_with_data(
821    memory: &VMMemoryDefinition,
822    start: usize,
823    data: &[u8],
824) -> Result<(), Trap> {
825    unsafe {
826        let mem_slice = slice::from_raw_parts_mut(memory.base, memory.current_length);
827        let end = start + data.len();
828        let to_init = &mut mem_slice[start..end];
829        to_init.copy_from_slice(data);
830
831        Ok(())
832    }
833}
834
835/// Represents memory that is used by the WebAssembly module
836pub trait LinearMemory
837where
838    Self: std::fmt::Debug + Send,
839{
840    /// Returns the type for this memory.
841    fn ty(&self) -> MemoryType;
842
843    /// Returns the size of the memory in pages
844    fn size(&self) -> Pages;
845
846    /// Returns the memory style for this memory.
847    fn style(&self) -> MemoryStyle;
848
849    /// Grow memory by the specified amount of wasm pages.
850    ///
851    /// Returns `None` if memory can't be grown by the specified amount
852    /// of wasm pages.
853    fn grow(&mut self, delta: Pages) -> Result<Pages, MemoryError>;
854
855    /// Grows the memory to at least a minimum size. If the memory is already big enough
856    /// for the min size then this function does nothing
857    fn grow_at_least(&mut self, _min_size: u64) -> Result<(), MemoryError> {
858        Err(MemoryError::UnsupportedOperation {
859            message: "grow_at_least() is not supported".to_string(),
860        })
861    }
862
863    /// Resets the memory back to zero length
864    fn reset(&mut self) -> Result<(), MemoryError> {
865        Err(MemoryError::UnsupportedOperation {
866            message: "reset() is not supported".to_string(),
867        })
868    }
869
870    /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code.
871    fn vmmemory(&self) -> NonNull<VMMemoryDefinition>;
872
873    /// Attempts to clone this memory (if its cloneable)
874    fn try_clone(&self) -> Result<Box<dyn LinearMemory + Send + Sync + 'static>, MemoryError>;
875
876    #[doc(hidden)]
877    /// # Safety
878    /// This function is unsafe because WebAssembly specification requires that data is always set at initialization time.
879    /// It should be the implementors responsibility to make sure this respects the spec
880    unsafe fn initialize_with_data(&self, start: usize, data: &[u8]) -> Result<(), Trap> {
881        unsafe {
882            let memory = self.vmmemory().as_ref();
883
884            initialize_memory_with_data(memory, start, data)
885        }
886    }
887
888    /// Copies this memory to a new memory
889    fn copy(&self) -> Result<Box<dyn LinearMemory + Send + Sync + 'static>, MemoryError>;
890
891    /// Returns a concrete shared memory handle if this memory is shared.
892    fn as_shared(&self) -> Result<VMSharedMemory, MemoryError> {
893        Err(MemoryError::MemoryNotShared)
894    }
895
896    /// Add current thread to the waiter hash, and wait until notified or timeout.
897    /// Return 0 if the waiter has been notified, 1 if there was a value mismatch,
898    /// or 2 if the timeout occurred.
899    ///
900    /// # Safety
901    /// the destination address must be a valid offset within this memory. It must also
902    /// be properly aligned for the expected value type; either 4-byte aligned for
903    /// `ExpectedValue::U32` or 8-byte aligned for `ExpectedValue::u64`.
904    unsafe fn do_wait(
905        &mut self,
906        _dst: u32,
907        _expected: ExpectedValue,
908        _timeout: Option<Duration>,
909    ) -> Result<u32, WaiterError> {
910        Err(WaiterError::Unimplemented)
911    }
912
913    /// Notify waiters from the wait list. Return the number of waiters notified
914    fn do_notify(&mut self, _dst: u32, _count: u32) -> u32 {
915        0
916    }
917
918    /// Access the internal atomics handler.
919    ///
920    /// Will be [`None`] if the memory does not support atomics.
921    fn thread_conditions(&self) -> Option<&ThreadConditions> {
922        None
923    }
924}