orca_wasm/iterator/
component_iterator.rs

1//! Iterator to traverse a Component
2
3use crate::ir::component::Component;
4use crate::ir::id::{FunctionID, GlobalID, LocalID, ModuleID};
5use crate::ir::module::module_functions::FuncKind;
6use crate::ir::module::module_globals::Global;
7use crate::ir::types::{DataType, FuncInstrMode, InstrumentationMode, Location};
8use crate::iterator::iterator_trait::{IteratingInstrumenter, Iterator};
9use crate::module_builder::AddLocal;
10use crate::opcode::{Inject, InjectAt, Instrumenter, MacroOpcode, Opcode};
11use crate::subiterator::component_subiterator::ComponentSubIterator;
12use std::collections::HashMap;
13use std::iter::Iterator as StdIter;
14use wasmparser::Operator;
15
16/// Iterator for a Component.
17pub struct ComponentIterator<'a, 'b> {
18    /// The Component to iterate
19    pub comp: &'a mut Component<'b>,
20    /// The SubIterator for this Component
21    comp_iterator: ComponentSubIterator,
22}
23
24fn print_metadata(metadata: &HashMap<ModuleID, Vec<(FunctionID, usize)>>) {
25    for c in metadata.keys() {
26        println!("Module: {:?}", c);
27        for (m, i) in metadata.get(c).unwrap().iter() {
28            println!("Function: {:?} Instr: {:?}", m, i);
29        }
30    }
31}
32
33#[allow(dead_code)]
34impl<'a, 'b> ComponentIterator<'a, 'b> {
35    /// Creates a new Component Iterator
36    pub fn new(
37        comp: &'a mut Component<'b>,
38        skip_funcs: HashMap<ModuleID, Vec<FunctionID>>,
39    ) -> Self {
40        // Creates Module -> Function -> Number of Instructions
41        let mut metadata = HashMap::new();
42        for (mod_idx, m) in comp.modules.iter().enumerate() {
43            metadata.insert(ModuleID(mod_idx as u32), m.get_func_metadata());
44        }
45        print_metadata(&metadata);
46        let num_modules = comp.num_modules;
47        ComponentIterator {
48            comp,
49            comp_iterator: ComponentSubIterator::new(
50                ModuleID(0),
51                num_modules,
52                metadata,
53                skip_funcs,
54            ),
55        }
56    }
57
58    /// Returns the current module the component iterator is in
59    pub fn curr_module(&self) -> ModuleID {
60        if let (
61            Location::Component {
62                mod_idx,
63                func_idx: _func_idx,
64                instr_idx: _instr_idx,
65                ..
66            },
67            ..,
68        ) = self.curr_loc()
69        {
70            mod_idx
71        } else {
72            panic!("Should have gotten component location");
73        }
74    }
75
76    pub fn curr_op_owned(&self) -> Option<Operator<'b>> {
77        if self.comp_iterator.end() {
78            None
79        } else if let (
80            Location::Component {
81                mod_idx,
82                func_idx,
83                instr_idx,
84                ..
85            },
86            ..,
87        ) = self.comp_iterator.curr_loc()
88        {
89            match &self.comp.modules[*mod_idx as usize]
90                .functions
91                .get(func_idx)
92                .kind
93            {
94                FuncKind::Import(_) => None,
95                FuncKind::Local(l) => Some(l.body.instructions[instr_idx].op.clone()),
96            }
97        } else {
98            panic!("Should have gotten Component Location!")
99        }
100    }
101}
102
103impl<'b> Inject<'b> for ComponentIterator<'_, 'b> {
104    /// Injects an Operator at the current location
105    ///
106    /// # Example
107    /// ```no_run
108    /// use std::collections::HashMap;
109    /// use orca_wasm::ir::component::Component;
110    /// use orca_wasm::iterator::component_iterator::ComponentIterator;
111    /// use wasmparser::Operator;
112    /// use orca_wasm::ir::types::{Location};
113    /// use orca_wasm::iterator::iterator_trait::{IteratingInstrumenter, Iterator};
114    /// use orca_wasm::opcode::{Instrumenter, Opcode};
115    ///
116    /// let file = "path_to_file";
117    /// let buff = wat::parse_file(file).expect("couldn't convert the input wat to Wasm");
118    /// let mut component = Component::parse(&buff, false).expect("Unable to parse");
119    /// let mut comp_it = ComponentIterator::new(&mut component, HashMap::new());
120    ///
121    /// // Everytime there is a `call 1` instruction we want to inject an `i32.const 0`
122    /// let interested = Operator::Call { function_index: 1 };
123    ///
124    /// loop {
125    ///     let op = comp_it.curr_op();
126    ///     let instr_mode = comp_it.curr_instrument_mode();
127    ///
128    ///     if let Location::Component {
129    ///         mod_idx,
130    ///         func_idx,
131    ///         instr_idx,
132    ///         ..
133    ///     } = comp_it.curr_loc().0
134    ///     {
135    ///         if *comp_it.curr_op().unwrap() == interested {
136    ///             comp_it.before().i32_const(1);
137    ///         }
138    ///         if comp_it.next().is_none() {
139    ///             break;
140    ///         };
141    ///     } else {
142    ///         // Ensures we only get the location of a module while parsing a component
143    ///         panic!("Should've gotten Component Location!");
144    ///     }
145    /// }
146    /// ```
147    fn inject(&mut self, instr: Operator<'b>) {
148        if let (
149            Location::Component {
150                mod_idx,
151                func_idx,
152                instr_idx,
153                ..
154            },
155            ..,
156        ) = self.curr_loc()
157        {
158            match self.comp.modules[*mod_idx as usize]
159                .functions
160                .get_mut(func_idx)
161                .kind
162            {
163                FuncKind::Import(_) => panic!("Can't inject into an imported function!"),
164                FuncKind::Local(ref mut l) => l.add_instr(instr, instr_idx),
165            }
166        } else {
167            panic!("Should have gotten component location!")
168        }
169    }
170}
171impl<'b> InjectAt<'b> for ComponentIterator<'_, 'b> {
172    fn inject_at(&mut self, idx: usize, mode: InstrumentationMode, instr: Operator<'b>) {
173        if let (
174            Location::Component {
175                mod_idx, func_idx, ..
176            },
177            ..,
178        ) = self.curr_loc()
179        {
180            let loc = Location::Component {
181                mod_idx,
182                func_idx,
183                instr_idx: idx,
184            };
185            self.set_instrument_mode_at(mode, loc);
186            self.add_instr_at(loc, instr);
187        } else {
188            panic!("Should have gotten Component Location!")
189        }
190    }
191}
192impl<'b> Opcode<'b> for ComponentIterator<'_, 'b> {}
193impl<'b> MacroOpcode<'b> for ComponentIterator<'_, 'b> {}
194impl<'b> Instrumenter<'b> for ComponentIterator<'_, 'b> {
195    /// Returns the Instrumentation at the current Location
196    fn curr_instrument_mode(&self) -> &Option<InstrumentationMode> {
197        if let (
198            Location::Component {
199                mod_idx,
200                func_idx,
201                instr_idx,
202                ..
203            },
204            ..,
205        ) = self.comp_iterator.curr_loc()
206        {
207            match &self.comp.modules[*mod_idx as usize]
208                .functions
209                .get(func_idx)
210                .kind
211            {
212                FuncKind::Import(_) => {
213                    panic!("Can't get instrumentation from an imported function!")
214                }
215                FuncKind::Local(l) => &l.body.instructions[instr_idx].instr_flag.current_mode,
216            }
217        } else {
218            panic!("Should have gotten Component Location and not Module Location!")
219        }
220    }
221
222    fn set_instrument_mode_at(&mut self, mode: InstrumentationMode, loc: Location) {
223        if let Location::Component {
224            mod_idx,
225            func_idx,
226            instr_idx,
227            ..
228        } = loc
229        {
230            match self.comp.modules[*mod_idx as usize]
231                .functions
232                .get_mut(func_idx)
233                .kind
234            {
235                FuncKind::Import(_) => panic!("Can't instrument into an imported function!"),
236                FuncKind::Local(ref mut l) => {
237                    l.body.instructions[instr_idx].instr_flag.current_mode = Some(mode)
238                }
239            }
240        } else {
241            panic!("Should have gotten component location!")
242        }
243    }
244
245    fn curr_func_instrument_mode(&self) -> &Option<FuncInstrMode> {
246        if let (
247            Location::Component {
248                mod_idx, func_idx, ..
249            },
250            ..,
251        ) = self.comp_iterator.curr_loc()
252        {
253            match &self.comp.modules[*mod_idx as usize]
254                .functions
255                .get(func_idx)
256                .kind
257            {
258                FuncKind::Import(_) => {
259                    panic!("Can't get instrumentation from an imported function!")
260                }
261                FuncKind::Local(l) => &l.instr_flag.current_mode,
262            }
263        } else {
264            panic!("Should have gotten Component Location and not Module Location!")
265        }
266    }
267
268    fn set_func_instrument_mode(&mut self, mode: FuncInstrMode) {
269        if let (
270            Location::Component {
271                mod_idx, func_idx, ..
272            },
273            ..,
274        ) = self.curr_loc()
275        {
276            match self.comp.modules[*mod_idx as usize]
277                .functions
278                .get_mut(func_idx)
279                .kind
280            {
281                FuncKind::Import(_) => panic!("Can't instrument into an imported function!"),
282                FuncKind::Local(ref mut l) => l.instr_flag.current_mode = Some(mode),
283            }
284        } else {
285            panic!("Should have gotten component location!")
286        }
287    }
288
289    fn clear_instr_at(&mut self, loc: Location, mode: InstrumentationMode) {
290        if let Location::Component {
291            mod_idx,
292            func_idx,
293            instr_idx,
294            ..
295        } = loc
296        {
297            match self.comp.modules[*mod_idx as usize]
298                .functions
299                .get_mut(func_idx)
300                .kind
301            {
302                FuncKind::Import(_) => panic!("Can't instrument into an imported function!"),
303                FuncKind::Local(ref mut l) => l.clear_instr_at(instr_idx, mode),
304            }
305        } else {
306            panic!("Should have gotten component location!")
307        }
308    }
309
310    fn add_instr_at(&mut self, loc: Location, instr: Operator<'b>) {
311        if let Location::Component {
312            mod_idx,
313            func_idx,
314            instr_idx,
315            ..
316        } = loc
317        {
318            match self.comp.modules[*mod_idx as usize]
319                .functions
320                .get_mut(func_idx)
321                .kind
322            {
323                FuncKind::Import(_) => panic!("Can't instrument into an imported function!"),
324                FuncKind::Local(ref mut l) => {
325                    l.add_instr(instr, instr_idx);
326                }
327            }
328        } else {
329            panic!("Should have gotten Component Location and not Module Location!")
330        }
331    }
332
333    fn empty_alternate_at(&mut self, loc: Location) -> &mut Self {
334        if let Location::Component {
335            mod_idx,
336            func_idx,
337            instr_idx,
338            ..
339        } = loc
340        {
341            match self.comp.modules[*mod_idx as usize]
342                .functions
343                .get_mut(func_idx)
344                .kind
345            {
346                FuncKind::Import(_) => panic!("Can't instrument into an imported function!"),
347                FuncKind::Local(ref mut l) => {
348                    l.body.instructions[instr_idx].instr_flag.alternate = Some(vec![]);
349                }
350            }
351        } else {
352            panic!("Should have gotten Component Location and not Module Location!")
353        }
354
355        self
356    }
357
358    fn empty_block_alt_at(&mut self, loc: Location) -> &mut Self {
359        if let Location::Component {
360            mod_idx,
361            func_idx,
362            instr_idx,
363            ..
364        } = loc
365        {
366            match self.comp.modules[*mod_idx as usize]
367                .functions
368                .get_mut(func_idx)
369                .kind
370            {
371                FuncKind::Import(_) => panic!("Can't instrument into an imported function!"),
372                FuncKind::Local(ref mut l) => {
373                    l.body.instructions[instr_idx].instr_flag.block_alt = Some(vec![]);
374                    l.instr_flag.has_special_instr |= true;
375                }
376            }
377        } else {
378            panic!("Should have gotten Component Location and not Module Location!")
379        }
380
381        self
382    }
383
384    /// Gets the injected instruction at the current location by index
385    fn get_injected_val(&self, idx: usize) -> &Operator {
386        if let (
387            Location::Component {
388                mod_idx,
389                func_idx,
390                instr_idx,
391                ..
392            },
393            ..,
394        ) = self.comp_iterator.curr_loc()
395        {
396            match &self.comp.modules[*mod_idx as usize]
397                .functions
398                .get(func_idx)
399                .kind
400            {
401                FuncKind::Import(_) => panic!("Can't inject into an imported function!"),
402                FuncKind::Local(l) => l.body.instructions[instr_idx].instr_flag.get_instr(idx),
403            }
404        } else {
405            panic!("Should have gotten Component Location and not Module Location!")
406        }
407    }
408
409    ///Can be called after finishing some instrumentation to reset the mode.
410    fn finish_instr(&mut self) {
411        if let (
412            Location::Component {
413                mod_idx,
414                func_idx,
415                instr_idx,
416                ..
417            },
418            ..,
419        ) = self.comp_iterator.curr_loc()
420        {
421            match &mut self.comp.modules[*mod_idx as usize]
422                .functions
423                .get_mut(func_idx)
424                .kind
425            {
426                FuncKind::Import(_) => panic!("Can't inject into an imported function!"),
427                FuncKind::Local(l) => l.body.instructions[instr_idx].instr_flag.finish_instr(),
428            }
429        } else {
430            panic!("Should have gotten Component Location and not Module Location!")
431        }
432    }
433}
434impl<'b> IteratingInstrumenter<'b> for ComponentIterator<'_, 'b> {
435    /// Sets the type of Instrumentation Mode of the current location
436    fn set_instrument_mode(&mut self, mode: InstrumentationMode) {
437        if let (
438            Location::Component {
439                mod_idx: _mod_idx,
440                func_idx: _func_idx,
441                instr_idx: _instr_idx,
442                ..
443            },
444            ..,
445        ) = self.curr_loc()
446        {
447            self.set_instrument_mode_at(mode, self.curr_loc().0);
448        } else {
449            panic!("Should have gotten component location!")
450        }
451    }
452
453    fn add_global(&mut self, global: Global) -> GlobalID {
454        let curr_mod = *self.curr_module() as usize;
455        self.comp.modules[curr_mod].globals.add(global)
456    }
457}
458
459// Note: Marked Trait as the same lifetime as component
460impl Iterator for ComponentIterator<'_, '_> {
461    /// Resets the Component Iterator
462    fn reset(&mut self) {
463        self.comp_iterator.reset();
464    }
465
466    /// Goes to the next instruction
467    fn next(&mut self) -> Option<&Operator> {
468        match self.comp_iterator.next() {
469            false => None,
470            true => self.curr_op(),
471        }
472    }
473
474    /// Returns the Current Location as a Location and a bool value that
475    /// says whether the location is at the end of the function.
476    fn curr_loc(&self) -> (Location, bool) {
477        self.comp_iterator.curr_loc()
478    }
479
480    /// Returns the instruction at the current location
481    fn curr_op(&self) -> Option<&Operator> {
482        if self.comp_iterator.end() {
483            None
484        } else if let (
485            Location::Component {
486                mod_idx,
487                func_idx,
488                instr_idx,
489                ..
490            },
491            ..,
492        ) = self.comp_iterator.curr_loc()
493        {
494            match &self.comp.modules[*mod_idx as usize]
495                .functions
496                .get(func_idx)
497                .kind
498            {
499                FuncKind::Import(_) => panic!("Can't inject into an imported function!"),
500                FuncKind::Local(l) => Some(&l.body.instructions[instr_idx].op),
501            }
502        } else {
503            panic!("Should have gotten Component Location and not Module Location!")
504        }
505    }
506}
507
508impl AddLocal for ComponentIterator<'_, '_> {
509    fn add_local(&mut self, val_type: DataType) -> LocalID {
510        let curr_loc = self.curr_loc().0;
511        if let Location::Component {
512            mod_idx, func_idx, ..
513        } = curr_loc
514        {
515            {
516                self.comp.modules[*mod_idx as usize]
517                    .functions
518                    .add_local(func_idx, val_type)
519            }
520        } else {
521            panic!("Should have gotten Component Location and not Module Location!")
522        }
523    }
524}