Skip to main content

sway_ir/
module.rs

1//! A scope containing a collection of [`Function`]s and constant values.
2//!
3//! A module also has a 'kind' corresponding to the different Sway module types.
4
5use std::collections::BTreeMap;
6
7use crate::{
8    context::Context,
9    function::{Function, FunctionIterator},
10    Config, ConfigContent, Constant, GlobalVar, StorageKey, Type,
11};
12
13/// A wrapper around an [ECS](https://github.com/orlp/slotmap) handle into the
14/// [`Context`].
15#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
16pub struct Module(pub slotmap::DefaultKey);
17
18#[doc(hidden)]
19pub struct ModuleContent {
20    pub kind: Kind,
21    pub functions: Vec<Function>,
22    pub global_variables: BTreeMap<Vec<String>, GlobalVar>,
23    pub configs: BTreeMap<String, Config>,
24    pub storage_keys: BTreeMap<String, StorageKey>,
25}
26
27/// The different 'kinds' of Sway module: `Contract`, `Library`, `Predicate` or `Script`.
28#[derive(Clone, Copy, Debug, Eq, PartialEq)]
29pub enum Kind {
30    Contract,
31    Library,
32    Predicate,
33    Script,
34}
35
36impl Module {
37    /// Return a new module of a specific kind.
38    pub fn new(context: &mut Context, kind: Kind) -> Module {
39        let content = ModuleContent {
40            kind,
41            functions: Vec::new(),
42            global_variables: BTreeMap::new(),
43            configs: BTreeMap::new(),
44            storage_keys: BTreeMap::new(),
45        };
46        Module(context.modules.insert(content))
47    }
48
49    /// Get this module's [`Kind`].
50    pub fn get_kind(&self, context: &Context) -> Kind {
51        context.modules[self.0].kind
52    }
53
54    /// Return an iterator over each of the [`Function`]s in this module.
55    pub fn function_iter(&self, context: &Context) -> FunctionIterator {
56        FunctionIterator::new(context, self)
57    }
58
59    /// Add a global variable value to this module.
60    pub fn add_global_variable(
61        &self,
62        context: &mut Context,
63        call_path: Vec<String>,
64        const_val: GlobalVar,
65    ) {
66        context.modules[self.0]
67            .global_variables
68            .insert(call_path, const_val);
69    }
70
71    /// Add a value to the module global storage, by forcing the name to be unique if needed.
72    ///
73    /// Will use the provided name as a hint and eventually rename it to guarantee insertion.
74    pub fn new_unique_global_var(
75        &self,
76        context: &mut Context,
77        name: String,
78        local_type: Type,
79        initializer: Option<Constant>,
80        mutable: bool,
81    ) -> GlobalVar {
82        let module = &context.modules[self.0];
83        let new_name = if module.global_variables.contains_key(&vec![name.clone()]) {
84            // Assuming that we'll eventually find a unique name by appending numbers to the old
85            // one...
86            (0..)
87                .find_map(|n| {
88                    let candidate = format!("{name}{n}");
89                    if module
90                        .global_variables
91                        .contains_key(&vec![candidate.clone()])
92                    {
93                        None
94                    } else {
95                        Some(candidate)
96                    }
97                })
98                .unwrap()
99        } else {
100            name
101        };
102        let gv = GlobalVar::new(context, local_type, initializer, mutable);
103        self.add_global_variable(context, vec![new_name], gv);
104        gv
105    }
106
107    /// Get a named global variable from this module, if found.
108    pub fn get_global_variable(
109        &self,
110        context: &Context,
111        call_path: &Vec<String>,
112    ) -> Option<GlobalVar> {
113        context.modules[self.0]
114            .global_variables
115            .get(call_path)
116            .copied()
117    }
118
119    /// Lookup global variable name.
120    pub fn lookup_global_variable_name(
121        &self,
122        context: &Context,
123        global: &GlobalVar,
124    ) -> Option<String> {
125        context.modules[self.0]
126            .global_variables
127            .iter()
128            .find(|(_key, val)| *val == global)
129            .map(|(key, _)| key.join("::"))
130    }
131
132    /// Add a config value to this module.
133    pub fn add_config(
134        &self,
135        context: &mut Context,
136        name: String,
137        content: ConfigContent,
138    ) -> Config {
139        let config = Config::new(context, content);
140        context.modules[self.0].configs.insert(name, config);
141        config
142    }
143
144    /// Get a named config from this module, if found.
145    pub fn get_config(&self, context: &Context, name: &str) -> Option<Config> {
146        context.modules[self.0].configs.get(name).copied()
147    }
148
149    /// Add a storage key value to this module.
150    pub fn add_storage_key(&self, context: &mut Context, path: String, storage_key: StorageKey) {
151        context.modules[self.0]
152            .storage_keys
153            .insert(path, storage_key);
154    }
155
156    /// Get a storage key with the given `path` from this module, if found.
157    pub fn get_storage_key<'a>(&self, context: &'a Context, path: &str) -> Option<&'a StorageKey> {
158        context.modules[self.0].storage_keys.get(path)
159    }
160
161    /// Lookup storage key path.
162    pub fn lookup_storage_key_path<'a>(
163        &self,
164        context: &'a Context,
165        storage_key: &StorageKey,
166    ) -> Option<&'a str> {
167        context.modules[self.0]
168            .storage_keys
169            .iter()
170            .find(|(_key, val)| *val == storage_key)
171            .map(|(key, _)| key.as_str())
172    }
173
174    /// Removes a function from the module.  Returns true if function was found and removed.
175    ///
176    /// **Use with care!  Be sure the function is not an entry point nor called at any stage.**
177    pub fn remove_function(&self, context: &mut Context, function: &Function) -> bool {
178        let fns = &mut context
179            .modules
180            .get_mut(self.0)
181            .expect("Module must exist in context.")
182            .functions;
183
184        let len_before = fns.len();
185        fns.retain(|mod_fn| mod_fn != function);
186        let len_after = fns.len();
187
188        len_before != len_after
189    }
190
191    pub fn iter_configs<'a>(&'a self, context: &'a Context) -> impl Iterator<Item = Config> + 'a {
192        context.modules[self.0].configs.values().copied()
193    }
194}
195
196/// An iterator over [`Module`]s within a [`Context`].
197pub struct ModuleIterator {
198    modules: Vec<slotmap::DefaultKey>,
199    next: usize,
200}
201
202impl ModuleIterator {
203    /// Return a new [`Module`] iterator.
204    pub fn new(context: &Context) -> ModuleIterator {
205        // Copy all the current modules indices, so they may be modified in the context during
206        // iteration.
207        ModuleIterator {
208            modules: context.modules.iter().map(|pair| pair.0).collect(),
209            next: 0,
210        }
211    }
212}
213
214impl Iterator for ModuleIterator {
215    type Item = Module;
216
217    fn next(&mut self) -> Option<Module> {
218        if self.next < self.modules.len() {
219            let idx = self.next;
220            self.next += 1;
221            Some(Module(self.modules[idx]))
222        } else {
223            None
224        }
225    }
226}