wit_printer/
lib.rs

1//! A crate to print out the WebAssembly Interface Types textual format.
2//!
3//! This crate converts a full WebAssembly file to its textual format, including
4//! the necessary pieces to print out the WebAssembly Interface Types section,
5//! if present.
6
7#![deny(missing_docs)]
8
9use anyhow::Context;
10use std::fmt::Write;
11use std::path::Path;
12use wasmprinter::Printer;
13use wit_parser::*;
14
15/// Prints an entire wasm file to its textual representation, returning the
16/// in-memory `String` of the textual representation.
17///
18/// # Errors
19///
20/// Returns an error if the file cannot be read or it wasn't a valid wasm file.
21pub fn print_file(file: impl AsRef<Path>) -> anyhow::Result<String> {
22    _print_file(file.as_ref())
23}
24
25fn _print_file(file: &Path) -> anyhow::Result<String> {
26    let contents = std::fs::read(file).context(format!("failed to read `{}`", file.display()))?;
27    _print_bytes(&contents)
28}
29
30/// Prints an entire in-memory wasm module to its textual representation,
31/// returning the in-memory `String` of the textual representation.
32///
33/// # Errors
34///
35/// Returns an error if the bytes weren't a valid wasm blob.
36pub fn print_bytes(wasm: impl AsRef<[u8]>) -> anyhow::Result<String> {
37    _print_bytes(wasm.as_ref())
38}
39
40fn _print_bytes(wasm: &[u8]) -> anyhow::Result<String> {
41    let mut printer = Printer::new();
42    printer.add_custom_section_printer(wit_schema_version::SECTION_NAME, print_wit);
43    printer.print(wasm)
44}
45
46fn print_wit(printer: &mut Printer, offset: usize, bytes: &[u8]) -> anyhow::Result<()> {
47    let mut parser = Parser::new(offset, bytes).context("failed to parse header")?;
48    let mut func = 0;
49    while !parser.is_empty() {
50        match parser.section().context("failed to parse section")? {
51            Section::Type(types) => {
52                let ret = printer.result_mut();
53                for (i, ty) in types.into_iter().enumerate() {
54                    let ty = ty.context("failed to parse type")?;
55                    write!(ret, "\n  (@interface type (;{};) (func", i)?;
56                    for param in ty.params.iter() {
57                        ret.push_str(" (param ");
58                        push_ty(ret, param);
59                        ret.push_str(")");
60                    }
61                    for result in ty.results.iter() {
62                        ret.push_str(" (result ");
63                        push_ty(ret, result);
64                        ret.push_str(")");
65                    }
66                    ret.push_str("))");
67                }
68            }
69            Section::Import(imports) => {
70                let ret = printer.result_mut();
71                for i in imports {
72                    let i = i.context("failed to parse import")?;
73                    write!(
74                        ret,
75                        "\n  (@interface import \"{}\" \"{}\" \
76                         (func (;{};) (type {})))",
77                        i.module, i.name, func, i.ty,
78                    )?;
79                    func += 1;
80                }
81            }
82            Section::Export(exports) => {
83                let ret = printer.result_mut();
84                for e in exports {
85                    let e = e.context("failed to parse export")?;
86                    ret.push_str("\n  (@interface export ");
87                    ret.push_str("\"");
88                    ret.push_str(e.name);
89                    ret.push_str("\" (func ");
90                    ret.push_str(&format!("{}", e.func));
91                    ret.push_str("))");
92                }
93            }
94            Section::Func(funcs) => {
95                for f in funcs {
96                    let f = f.context("failed to parse func")?;
97                    write!(
98                        printer.result_mut(),
99                        "\n  (@interface func (;{};) (type {})",
100                        func,
101                        f.ty
102                    )?;
103                    for instr in f.instrs() {
104                        let instr = instr.context("failed to parse instruction")?;
105                        printer.result_mut().push_str("\n    ");
106                        push_instr(printer, &instr)?;
107                    }
108                    printer.result_mut().push_str(")");
109                    func += 1;
110                }
111            }
112            Section::Implement(implements) => {
113                for i in implements {
114                    let i = i.context("failed to parse implement")?;
115                    printer
116                        .result_mut()
117                        .push_str("\n  (@interface implement (func ");
118                    printer.print_func_idx(i.core_func)?;
119                    printer.result_mut().push_str(") (func ");
120                    printer
121                        .result_mut()
122                        .push_str(&format!("{}", i.adapter_func));
123                    printer.result_mut().push_str("))");
124                }
125            }
126        }
127    }
128    return Ok(());
129
130    fn push_ty(ret: &mut String, param: &ValType) {
131        match param {
132            ValType::S8 => ret.push_str("s8"),
133            ValType::S16 => ret.push_str("s16"),
134            ValType::S32 => ret.push_str("s32"),
135            ValType::S64 => ret.push_str("s64"),
136            ValType::U8 => ret.push_str("u8"),
137            ValType::U16 => ret.push_str("u16"),
138            ValType::U32 => ret.push_str("u32"),
139            ValType::U64 => ret.push_str("u64"),
140            ValType::F32 => ret.push_str("f32"),
141            ValType::F64 => ret.push_str("f64"),
142            ValType::String => ret.push_str("string"),
143            ValType::Externref => ret.push_str("externref"),
144            ValType::I32 => ret.push_str("i32"),
145            ValType::I64 => ret.push_str("i64"),
146        }
147    }
148
149    fn push_instr(ret: &mut Printer, instr: &Instruction) -> anyhow::Result<()> {
150        use Instruction::*;
151
152        match instr {
153            ArgGet(i) => write!(ret.result_mut(), "arg.get {}", i)?,
154            CallCore(i) => {
155                ret.result_mut().push_str("call-core ");
156                ret.print_func_idx(*i)?;
157            }
158            End => ret.result_mut().push_str("end"),
159            MemoryToString(mem) => {
160                ret.result_mut().push_str("memory-to-string");
161                if *mem != 0 {
162                    write!(ret.result_mut(), " {}", mem)?;
163                }
164            }
165            StringToMemory(payload) => {
166                ret.result_mut().push_str("string-to-memory ");
167                ret.print_func_idx(payload.malloc)?;
168                if payload.mem != 0 {
169                    write!(ret.result_mut(), " {}", payload.mem)?;
170                }
171            }
172            CallAdapter(f) => write!(ret.result_mut(), "call-adapter {}", f)?,
173            DeferCallCore(f) => {
174                ret.result_mut().push_str("defer-call-core ");
175                ret.print_func_idx(*f)?;
176            }
177
178            I32ToS8 => ret.result_mut().push_str("i32-to-s8"),
179            I32ToS8X => ret.result_mut().push_str("i32-to-s8x"),
180            I32ToU8 => ret.result_mut().push_str("i32-to-u8"),
181            I32ToS16 => ret.result_mut().push_str("i32-to-s16"),
182            I32ToS16X => ret.result_mut().push_str("i32-to-s16x"),
183            I32ToU16 => ret.result_mut().push_str("i32-to-u16"),
184            I32ToS32 => ret.result_mut().push_str("i32-to-s32"),
185            I32ToU32 => ret.result_mut().push_str("i32-to-u32"),
186            I32ToS64 => ret.result_mut().push_str("i32-to-s64"),
187            I32ToU64 => ret.result_mut().push_str("i32-to-u64"),
188
189            I64ToS8 => ret.result_mut().push_str("i64-to-s8"),
190            I64ToS8X => ret.result_mut().push_str("i64-to-s8x"),
191            I64ToU8 => ret.result_mut().push_str("i64-to-u8"),
192            I64ToS16 => ret.result_mut().push_str("i64-to-s16"),
193            I64ToS16X => ret.result_mut().push_str("i64-to-s16x"),
194            I64ToU16 => ret.result_mut().push_str("i64-to-u16"),
195            I64ToS32 => ret.result_mut().push_str("i64-to-s32"),
196            I64ToS32X => ret.result_mut().push_str("i64-to-s32x"),
197            I64ToU32 => ret.result_mut().push_str("i64-to-u32"),
198            I64ToS64 => ret.result_mut().push_str("i64-to-s64"),
199            I64ToU64 => ret.result_mut().push_str("i64-to-u64"),
200
201            S8ToI32 => ret.result_mut().push_str("s8-to-i32"),
202            U8ToI32 => ret.result_mut().push_str("u8-to-i32"),
203            S16ToI32 => ret.result_mut().push_str("s16-to-i32"),
204            U16ToI32 => ret.result_mut().push_str("u16-to-i32"),
205            S32ToI32 => ret.result_mut().push_str("s32-to-i32"),
206            U32ToI32 => ret.result_mut().push_str("u32-to-i32"),
207            S64ToI32 => ret.result_mut().push_str("s64-to-i32"),
208            S64ToI32X => ret.result_mut().push_str("s64-to-i32x"),
209            U64ToI32 => ret.result_mut().push_str("u64-to-i32"),
210            U64ToI32X => ret.result_mut().push_str("u64-to-i32x"),
211
212            S8ToI64 => ret.result_mut().push_str("s8-to-i64"),
213            U8ToI64 => ret.result_mut().push_str("u8-to-i64"),
214            S16ToI64 => ret.result_mut().push_str("s16-to-i64"),
215            U16ToI64 => ret.result_mut().push_str("u16-to-i64"),
216            S32ToI64 => ret.result_mut().push_str("s32-to-i64"),
217            U32ToI64 => ret.result_mut().push_str("u32-to-i64"),
218            S64ToI64 => ret.result_mut().push_str("s64-to-i64"),
219            U64ToI64 => ret.result_mut().push_str("u64-to-i64"),
220        }
221
222        Ok(())
223    }
224}