Skip to main content

walrus/module/
memories.rs

1//! Memories used in a wasm module.
2
3use crate::emit::{Emit, EmitContext};
4use crate::map::IdHashSet;
5use crate::parse::IndicesToIds;
6use crate::tombstone_arena::{Id, Tombstone, TombstoneArena};
7use crate::{Data, ImportId, Module, Result};
8
9/// The id of a memory.
10pub type MemoryId = Id<Memory>;
11
12/// A memory in the wasm.
13#[derive(Debug)]
14pub struct Memory {
15    id: MemoryId,
16    /// Whether or not this is a “shared” memory
17    ///
18    /// This is part of the threads proposal.
19    pub shared: bool,
20    /// Whether or not this is a 64-bit memory.
21    ///
22    /// This is part of the memory64 proposal.
23    pub memory64: bool,
24    /// Initial size of this memory, in wasm pages.
25    pub initial: u64,
26    /// Optional maximum size of this memory, in wasm pages.
27    pub maximum: Option<u64>,
28    ///The log base 2 of the memory’s custom page size.
29    ///
30    /// Memory pages are, by default, 64KiB large (i.e. 216 or 65536).
31    ///
32    /// The custom-page-sizes proposal allows changing it to other values.
33    pub page_size_log2: Option<u32>,
34    /// Whether or not this memory is imported, and if so from where.
35    pub import: Option<ImportId>,
36    /// Active data segments that will be used to initialize this memory.
37    pub data_segments: IdHashSet<Data>,
38    /// The name of this memory, used for debugging purposes in the `name`
39    /// custom section.
40    pub name: Option<String>,
41}
42
43impl Tombstone for Memory {
44    fn on_delete(&mut self) {
45        self.data_segments = Default::default();
46    }
47}
48
49impl Memory {
50    /// Return the id of this memory
51    pub fn id(&self) -> MemoryId {
52        self.id
53    }
54}
55
56/// The set of memories in this module.
57#[derive(Debug, Default)]
58pub struct ModuleMemories {
59    arena: TombstoneArena<Memory>,
60}
61
62impl ModuleMemories {
63    /// Add an imported memory
64    pub fn add_import(
65        &mut self,
66        shared: bool,
67        memory64: bool,
68        initial: u64,
69        maximum: Option<u64>,
70        page_size_log2: Option<u32>,
71        import: ImportId,
72    ) -> MemoryId {
73        let id = self.arena.next_id();
74        let id2 = self.arena.alloc(Memory {
75            id,
76            shared,
77            memory64,
78            initial,
79            maximum,
80            page_size_log2,
81            import: Some(import),
82            data_segments: Default::default(),
83            name: None,
84        });
85        debug_assert_eq!(id, id2);
86        id
87    }
88
89    /// Construct a new memory, that does not originate from any of the input
90    /// wasm memories.
91    pub fn add_local(
92        &mut self,
93        shared: bool,
94        memory64: bool,
95        initial: u64,
96        maximum: Option<u64>,
97        page_size_log2: Option<u32>,
98    ) -> MemoryId {
99        let id = self.arena.next_id();
100        let id2 = self.arena.alloc(Memory {
101            id,
102            shared,
103            memory64,
104            initial,
105            maximum,
106            page_size_log2,
107            import: None,
108            data_segments: Default::default(),
109            name: None,
110        });
111        debug_assert_eq!(id, id2);
112        id
113    }
114
115    /// Gets a reference to a memory given its id
116    pub fn get(&self, id: MemoryId) -> &Memory {
117        &self.arena[id]
118    }
119
120    /// Gets a reference to a memory given its id
121    pub fn get_mut(&mut self, id: MemoryId) -> &mut Memory {
122        &mut self.arena[id]
123    }
124
125    /// Removes a memory from this module.
126    ///
127    /// It is up to you to ensure that any potential references to the deleted
128    /// memory are also removed, eg `mem.load` expressions and exports.
129    pub fn delete(&mut self, id: MemoryId) {
130        self.arena.delete(id);
131    }
132
133    /// Get a shared reference to this module's memories.
134    pub fn iter(&self) -> impl Iterator<Item = &Memory> {
135        self.arena.iter().map(|(_, f)| f)
136    }
137
138    /// Get a mutable reference to this module's memories.
139    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Memory> {
140        self.arena.iter_mut().map(|(_, f)| f)
141    }
142
143    /// Get the number of memories in this module
144    pub fn len(&self) -> usize {
145        self.arena.len()
146    }
147
148    /// Checks if there are no memories in this module
149    pub fn is_empty(&self) -> bool {
150        self.arena.len() == 0
151    }
152}
153
154impl Module {
155    /// Construct a new, empty set of memories for a module.
156    pub(crate) fn parse_memories(
157        &mut self,
158        section: wasmparser::MemorySectionReader,
159        ids: &mut IndicesToIds,
160    ) -> Result<()> {
161        log::debug!("parse memory section");
162        for m in section {
163            let m = m?;
164            let id = self.memories.add_local(
165                m.shared,
166                m.memory64,
167                m.initial,
168                m.maximum,
169                m.page_size_log2,
170            );
171            ids.push_memory(id);
172        }
173        Ok(())
174    }
175}
176
177impl Emit for ModuleMemories {
178    fn emit(&self, cx: &mut EmitContext) {
179        log::debug!("emit memory section");
180
181        let mut wasm_memory_section = wasm_encoder::MemorySection::new();
182
183        // imported memories are emitted earlier
184        let memories = self.iter().filter(|m| m.import.is_none()).count();
185        if memories == 0 {
186            return;
187        }
188
189        for memory in self.iter().filter(|m| m.import.is_none()) {
190            cx.indices.push_memory(memory.id());
191
192            wasm_memory_section.memory(wasm_encoder::MemoryType {
193                minimum: memory.initial,
194                maximum: memory.maximum,
195                memory64: memory.memory64,
196                shared: memory.shared,
197                page_size_log2: memory.page_size_log2,
198            });
199        }
200
201        cx.wasm_module.section(&wasm_memory_section);
202    }
203}
204
205#[cfg(test)]
206mod tests {
207    use crate::Module;
208
209    #[test]
210    fn memories_len() {
211        let mut module = Module::default();
212        assert_eq!(module.memories.len(), 0);
213
214        module.memories.add_local(false, false, 0, Some(1024), None);
215        assert_eq!(module.memories.len(), 1);
216
217        module
218            .memories
219            .add_local(true, true, 1024, Some(2048), Some(16));
220        assert_eq!(module.memories.len(), 2);
221    }
222}