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
//! Pre-visitor for parsing WASM.

use crate::{Function, Result};
use wasmparser::{Operator, VisitOperator};

/// A pre-visitor that validates the WASM and then visits it.
pub struct ValidateThenVisit<'a, T>(pub T, pub &'a mut Function);

macro_rules! validate_then_visit {
    ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => {
        $(
            fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output {
                self.0.$visit($($($arg.clone()),*)?)?;
                // Only visit operators if the compiler is in a reachable code state. If
                // the compiler is in an unreachable code state, most of the operators are
                // ignored except for If, Block, Loop, Else and End. These operators need
                // to be observed in order to keep the control stack frames balanced and to
                // determine if reachability should be restored.
                let visit_when_unreachable = visit_op_when_unreachable(Operator::$op $({ $($arg: $arg.clone()),* })?);
                if true || visit_when_unreachable  {
                    Ok(self.1.$visit($($($arg),*)?))
                } else {
                    Ok(Ok(()))
                }
            }
        )*
    };
}

fn visit_op_when_unreachable(op: Operator) -> bool {
    use Operator::*;
    matches!(op, If { .. } | Block { .. } | Loop { .. } | Else | End)
}

// /// Trait to handle reachability state.
// trait ReachableState {
//     /// Returns true if the current state of the program is reachable.
//     fn is_reachable(&self) -> bool;
// }
//
// impl ReachableState for Function {
//     fn is_reachable(&self) -> bool {
//         true
//     }
// }

impl<'a, T> VisitOperator<'a> for ValidateThenVisit<'_, T>
where
    T: VisitOperator<'a, Output = wasmparser::Result<()>>,
{
    type Output = Result<Result<()>>;

    wasmparser::for_each_operator!(validate_then_visit);
}