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