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