walrus/module/
globals.rs

1//! Globals within a wasm module.
2use crate::emit::{Emit, EmitContext};
3use crate::parse::IndicesToIds;
4use crate::tombstone_arena::{Id, Tombstone, TombstoneArena};
5use crate::{ConstExpr, ImportId, Module, Result, ValType};
6
7/// The id of a global.
8pub type GlobalId = Id<Global>;
9
10/// A wasm global.
11#[derive(Debug)]
12pub struct Global {
13    // NB: Not public so that it can't get out of sync with the arena this is
14    // contained within.
15    id: GlobalId,
16    /// This global's type.
17    pub ty: ValType,
18    /// Whether this global is mutable or not.
19    pub mutable: bool,
20    /// Whether this global is shared or not.
21    pub shared: bool,
22    /// The kind of global this is
23    pub kind: GlobalKind,
24    /// The name of this data, used for debugging purposes in the `name`
25    /// custom section.
26    pub name: Option<String>,
27}
28
29impl Tombstone for Global {}
30
31/// The different kinds of globals a wasm module can have
32#[derive(Debug)]
33pub enum GlobalKind {
34    /// An imported global without a known initializer
35    Import(ImportId),
36    /// A locally declare global with the specified identifier
37    Local(ConstExpr),
38}
39
40impl Global {
41    /// Get this global's id.
42    pub fn id(&self) -> GlobalId {
43        self.id
44    }
45}
46
47/// The set of globals in each function in this module.
48#[derive(Debug, Default)]
49pub struct ModuleGlobals {
50    /// The arena where the globals are stored.
51    arena: TombstoneArena<Global>,
52}
53
54impl ModuleGlobals {
55    /// Adds a new imported global to this list.
56    pub fn add_import(
57        &mut self,
58        ty: ValType,
59        mutable: bool,
60        shared: bool,
61        import_id: ImportId,
62    ) -> GlobalId {
63        self.arena.alloc_with_id(|id| Global {
64            id,
65            ty,
66            mutable,
67            shared,
68            kind: GlobalKind::Import(import_id),
69            name: None,
70        })
71    }
72
73    /// Construct a new global, that does not originate from any of the input
74    /// wasm globals.
75    pub fn add_local(
76        &mut self,
77        ty: ValType,
78        mutable: bool,
79        shared: bool,
80        init: ConstExpr,
81    ) -> GlobalId {
82        self.arena.alloc_with_id(|id| Global {
83            id,
84            ty,
85            mutable,
86            shared,
87            kind: GlobalKind::Local(init),
88            name: None,
89        })
90    }
91
92    /// Gets a reference to a global given its id
93    pub fn get(&self, id: GlobalId) -> &Global {
94        &self.arena[id]
95    }
96
97    /// Gets a reference to a global given its id
98    pub fn get_mut(&mut self, id: GlobalId) -> &mut Global {
99        &mut self.arena[id]
100    }
101
102    /// Removes a global from this module.
103    ///
104    /// It is up to you to ensure that any potential references to the deleted
105    /// global are also removed, eg `get_global` expressions.
106    pub fn delete(&mut self, id: GlobalId) {
107        self.arena.delete(id);
108    }
109
110    /// Get a shared reference to this module's globals.
111    pub fn iter(&self) -> impl Iterator<Item = &Global> {
112        self.arena.iter().map(|(_, f)| f)
113    }
114}
115
116impl Module {
117    /// Construct a new, empty set of globals for a module.
118    pub(crate) fn parse_globals(
119        &mut self,
120        section: wasmparser::GlobalSectionReader,
121        ids: &mut IndicesToIds,
122    ) -> Result<()> {
123        log::debug!("parse global section");
124        for g in section {
125            let g = g?;
126            let init_expr = ConstExpr::eval(&g.init_expr, ids)?;
127            let id = self.globals.add_local(
128                ValType::parse(&g.ty.content_type)?,
129                g.ty.mutable,
130                g.ty.shared,
131                init_expr,
132            );
133            ids.push_global(id);
134        }
135        Ok(())
136    }
137}
138
139impl Emit for ModuleGlobals {
140    fn emit(&self, cx: &mut EmitContext) {
141        log::debug!("emit global section");
142        let mut wasm_global_section = wasm_encoder::GlobalSection::new();
143
144        fn get_local(global: &Global) -> Option<(&Global, &ConstExpr)> {
145            match &global.kind {
146                GlobalKind::Import(_) => None,
147                GlobalKind::Local(local) => Some((global, local)),
148            }
149        }
150
151        // All imported globals emitted earlier during the import section, so
152        // filter those out.
153        let globals = self.iter().filter_map(get_local).count();
154        if globals == 0 {
155            return;
156        }
157
158        for (global, local) in self.iter().filter_map(get_local) {
159            cx.indices.push_global(global.id());
160
161            wasm_global_section.global(
162                wasm_encoder::GlobalType {
163                    val_type: global.ty.to_wasmencoder_type(),
164                    mutable: global.mutable,
165                    shared: global.shared,
166                },
167                &local.to_wasmencoder_type(cx),
168            );
169        }
170
171        cx.wasm_module.section(&wasm_global_section);
172    }
173}