wasmprinter/
operand_stack.rs

1use crate::{Config, PrintTermcolor, Printer};
2use wasmparser::{
3    BinaryReader, Frame, OperatorsReader, Payload, Result, ValType, ValidPayload,
4    ValidatorResources, WasmFeatures,
5};
6
7pub struct Validator {
8    validator: wasmparser::Validator,
9    last_function: Option<FuncValidator>,
10}
11
12impl Validator {
13    pub fn new() -> Option<Validator> {
14        Some(Validator {
15            validator: wasmparser::Validator::new_with_features(WasmFeatures::all()),
16            last_function: None,
17        })
18    }
19
20    pub fn payload(&mut self, payload: &Payload<'_>) -> Result<()> {
21        match self.validator.payload(payload)? {
22            ValidPayload::Ok | ValidPayload::End(_) | ValidPayload::Parser(_) => Ok(()),
23            ValidPayload::Func(validator, _) => {
24                assert!(self.last_function.is_none());
25                self.last_function = Some(FuncValidator {
26                    push_count: 0,
27                    validator: validator.into_validator(Default::default()),
28                });
29                Ok(())
30            }
31        }
32    }
33
34    pub fn next_func(&mut self) -> Option<FuncValidator> {
35        Some(self.last_function.take().unwrap())
36    }
37}
38
39pub struct FuncValidator {
40    validator: wasmparser::FuncValidator<ValidatorResources>,
41    push_count: u32,
42}
43
44impl FuncValidator {
45    pub fn read_locals(&mut self, mut reader: BinaryReader<'_>) -> Result<()> {
46        self.validator.read_locals(&mut reader)
47    }
48
49    pub fn visit_operator(&mut self, reader: &OperatorsReader<'_>, is_end: bool) -> Result<()> {
50        let pos = reader.original_position();
51        reader
52            .clone()
53            .visit_operator(&mut self.validator.visitor(pos))??;
54
55        if !is_end {
56            let op = reader.clone().read()?;
57            let arity = op.operator_arity(&self.validator.visitor(pos));
58            if let Some((_pop_count, push_count)) = arity {
59                self.push_count = push_count;
60            }
61        }
62        Ok(())
63    }
64
65    pub fn visualize_operand_stack(&self, use_color: bool) -> crate::Result<String> {
66        let mut buf_color = PrintTermcolor(termcolor::Ansi::new(Vec::new()));
67        let mut buf_nocolor = PrintTermcolor(termcolor::NoColor::new(Vec::new()));
68
69        let stack_printer = Printer {
70            result: if use_color {
71                &mut buf_color
72            } else {
73                &mut buf_nocolor
74            },
75            config: &Config::new(),
76            nesting: 0,
77            line: 0,
78            group_lines: Vec::new(),
79            code_section_hints: Vec::new(),
80        };
81
82        if let Some(&Frame { height, .. }) = self.validator.get_control_frame(0) {
83            stack_printer.result.start_comment()?;
84            stack_printer.result.write_str("[")?;
85            let max_height = self.validator.operand_stack_height() as usize;
86            for depth in (height..max_height).rev() {
87                if depth + 1 < max_height {
88                    stack_printer.result.write_str(" ")?;
89                }
90                if depth + 1 == height + self.push_count as usize {
91                    stack_printer.result.start_type()?;
92                }
93                match self.validator.get_operand_type(depth) {
94                    Some(Some(ty)) => {
95                        stack_printer.result.write_str(&ty_to_str(ty))?;
96                    }
97                    Some(None) => {
98                        stack_printer.result.write_str("(unknown)")?;
99                    }
100                    None => {
101                        stack_printer.result.write_str("(invalid)")?;
102                    }
103                }
104            }
105            stack_printer.result.start_comment()?;
106            stack_printer.result.write_str("]")?;
107            stack_printer.result.reset_color()?;
108        }
109
110        let ret = String::from_utf8(if use_color {
111            buf_color.0.into_inner()
112        } else {
113            buf_nocolor.0.into_inner()
114        })?;
115        return Ok(ret);
116
117        fn ty_to_str(ty: ValType) -> String {
118            match ty {
119                ValType::I32 => String::from("i32"),
120                ValType::I64 => String::from("i64"),
121                ValType::F32 => String::from("f32"),
122                ValType::F64 => String::from("f64"),
123                ValType::V128 => String::from("v128"),
124                ValType::Ref(r) => format!("{r}"),
125            }
126        }
127    }
128}