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