1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
//! Intermediate Representation of a Module's Imports

use crate::ir::id::{FunctionID, ImportsID};
use wasmparser::TypeRef;

// TODO: Need to handle the relationship between Functions and Imports
/// Represents an import in a WebAssembly module.
#[derive(Debug, Clone)]
pub struct Import<'a> {
    /// The module being imported from.
    pub module: &'a str,
    /// The name of the imported item.
    pub name: &'a str,
    /// The type of the imported item.
    pub ty: TypeRef,
    /// The name (in the custom section) of the imported item.
    pub custom_name: Option<String>,
    pub(crate) deleted: bool,
}

impl<'a> From<wasmparser::Import<'a>> for Import<'a> {
    fn from(import: wasmparser::Import<'a>) -> Self {
        Import {
            module: import.module,
            name: import.name,
            ty: import.ty,
            custom_name: None,
            deleted: false,
        }
    }
}

impl Import<'_> {
    /// Check whether this is an import for a function.
    pub fn is_function(&self) -> bool {
        matches!(self.ty, TypeRef::Func(_))
    }
    /// Check whether this is an import for a global.
    pub fn is_global(&self) -> bool {
        matches!(self.ty, TypeRef::Global(_))
    }
    /// Check whether this is an import for a table.
    pub fn is_table(&self) -> bool {
        matches!(self.ty, TypeRef::Table(_))
    }
    /// Check whether this is an import for a tag.
    pub fn is_tag(&self) -> bool {
        matches!(self.ty, TypeRef::Tag(_))
    }
    /// Check whether this is an import for a memory.
    pub fn is_memory(&self) -> bool {
        matches!(self.ty, TypeRef::Memory(_))
    }
}

/// Represents the Imports Section of a WASM Module
#[derive(Clone, Debug, Default)]
pub struct ModuleImports<'a> {
    /// The imports.
    imports: Vec<Import<'a>>,

    pub(crate) num_funcs: u32,
    pub(crate) num_funcs_added: u32,
    pub(crate) num_globals: u32,
    pub(crate) num_globals_added: u32,
    pub(crate) num_tables: u32,
    pub(crate) num_tables_added: u32,
    pub(crate) num_tags: u32,
    pub(crate) num_tags_added: u32,
    pub(crate) num_memories: u32,
    pub(crate) num_memories_added: u32,
}

impl<'a> ModuleImports<'a> {
    /// Creates a new `ModuleImports` struct given a Vec of Imports
    pub fn new(imports: Vec<Import<'a>>) -> Self {
        let mut def = Self::default();
        for import in imports.iter() {
            if import.is_function() {
                def.num_funcs += 1;
            } else if import.is_global() {
                def.num_globals += 1;
            } else if import.is_table() {
                def.num_tables += 1;
            } else if import.is_tag() {
                def.num_tags += 1;
            } else if import.is_memory() {
                def.num_memories += 1;
            }
        }
        def.imports = imports;
        def
    }

    /// Checks if there are no Imports
    pub fn is_empty(&self) -> bool {
        self.imports.is_empty()
    }

    /// Get an iterator over the imports.
    pub fn iter(&self) -> std::slice::Iter<'_, Import<'a>> {
        self.imports.iter()
    }

    /// Get a mutable iterator over the imports.
    pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, Import<'a>> {
        self.imports.iter_mut()
    }

    /// Set the name of a given import using the ImportsID.
    pub fn set_name(&mut self, name: String, imports_id: ImportsID) {
        self.imports[*imports_id as usize].custom_name = Some(name)
    }

    /// Set the name of an imported function, using the FunctionID rather
    /// than the ImportsID. Note that these are not necessarily equal if
    /// the module has non-function imports! (It is more efficient to
    /// do this operation using the ImportsID.)
    pub fn set_fn_name(&mut self, name: String, func_id: FunctionID) {
        for (curr_fn_id, import) in (0_u32..).zip(self.imports.iter_mut()) {
            if import.is_function() && curr_fn_id == *func_id {
                import.custom_name = Some(name);
                return;
            }
        }
    }

    /// Returns the number of imports
    pub fn len(&self) -> usize {
        self.imports.len()
    }

    /// Add a new import to the module.
    pub(crate) fn add<'b>(&'b mut self, import: Import<'a>) -> ImportsID {
        // using a match instead of import.is_*() to make sure that we're
        // exhaustive due to the compiler guarantees.
        match import.ty {
            TypeRef::Func(..) => {
                self.num_funcs += 1;
                self.num_funcs_added += 1;
            }
            TypeRef::Global(..) => {
                self.num_globals += 1;
                self.num_globals_added += 1;
            }
            TypeRef::Table(..) => {
                self.num_tables += 1;
                self.num_tables_added += 1;
            }
            TypeRef::Tag(..) => {
                self.num_tags += 1;
                self.num_tags_added += 1;
            }
            TypeRef::Memory(..) => {
                self.num_memories += 1;
                self.num_memories_added += 1;
            }
        }
        self.imports.push(import);
        ImportsID((self.imports.len() - 1) as u32)
    }

    pub(crate) fn delete(&mut self, imports_id: ImportsID) {
        self.imports[*imports_id as usize].deleted = true;
    }

    /// Find an import by the `module` and `name` and return its `ImportsID` if found
    pub fn find(&self, module: String, name: String) -> Option<ImportsID> {
        for (id, imp) in self.imports.iter().enumerate() {
            if imp.module == module.as_str() && imp.name == name.as_str() {
                return Some(ImportsID(id as u32));
            }
        }
        None
    }

    /// Get the function ID of an Imported Function
    pub fn get_func(&self, module: String, name: String) -> Option<FunctionID> {
        for (idx, imp) in self.imports.iter().enumerate() {
            if imp.is_function() && imp.module == module.as_str() && *imp.name == name {
                return Some(FunctionID(idx as u32));
            }
        }
        None
    }

    /// Get an Import by its `ImportsID`
    pub fn get(&self, id: ImportsID) -> &Import {
        &self.imports[*id as usize]
    }

    /// Get the name of an Import
    pub fn get_import_name(&self, imports_id: ImportsID) -> &Option<String> {
        &self.imports[*imports_id as usize].custom_name
    }
}