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