xref: /wasmtime-44.0.1/crates/wizer/src/parse.rs (revision 5d13bb1d)
1 use crate::info::{
2     types_interner::{EntityType, TypeId},
3     Module, ModuleContext,
4 };
5 use crate::stack_ext::StackExt;
6 use anyhow::{Context, Result};
7 use std::convert::TryFrom;
8 use wasm_encoder::SectionId;
9 
10 struct StackEntry {
11     parser: wasmparser::Parser,
12     module: Module,
13 }
14 
15 /// Parse the given Wasm bytes into a `ModuleInfo` tree.
16 pub(crate) fn parse<'a>(full_wasm: &'a [u8]) -> anyhow::Result<ModuleContext<'a>> {
17     log::debug!("Parsing the input Wasm");
18 
19     let mut cx = ModuleContext::new();
20 
21     // The wasm we are currently parsing. This is advanced as the parser
22     // consumes input.
23     let mut wasm = full_wasm;
24 
25     let mut stack = vec![StackEntry {
26         parser: wasmparser::Parser::new(0),
27         module: cx.root(),
28     }];
29 
30     loop {
31         let (payload, consumed) = match stack
32             .top_mut()
33             .parser
34             .parse(wasm, true)
35             .context("failed to parse Wasm")?
36         {
37             wasmparser::Chunk::NeedMoreData(_) => unreachable!(),
38             wasmparser::Chunk::Parsed { payload, consumed } => (payload, consumed),
39         };
40         wasm = &wasm[consumed..];
41 
42         use wasmparser::Payload::*;
43         match payload {
44             Version { .. } => {}
45             TypeSection(types) => type_section(&mut cx, &mut stack, full_wasm, types)?,
46             ImportSection(imports) => import_section(&mut cx, &mut stack, full_wasm, imports)?,
47             FunctionSection(funcs) => function_section(&mut cx, &mut stack, full_wasm, funcs)?,
48             TableSection(tables) => table_section(&mut cx, &mut stack, full_wasm, tables)?,
49             MemorySection(mems) => memory_section(&mut cx, &mut stack, full_wasm, mems)?,
50             GlobalSection(globals) => global_section(&mut cx, &mut stack, full_wasm, globals)?,
51             ExportSection(exports) => export_section(&mut cx, &mut stack, full_wasm, exports)?,
52             StartSection { func: _, range } => {
53                 stack
54                     .top_mut()
55                     .module
56                     .add_raw_section(&mut cx, SectionId::Start, range, full_wasm)
57             }
58             ElementSection(elems) => stack.top_mut().module.add_raw_section(
59                 &mut cx,
60                 SectionId::Element,
61                 elems.range(),
62                 full_wasm,
63             ),
64             DataCountSection { range, .. } => stack.top_mut().module.add_raw_section(
65                 &mut cx,
66                 SectionId::DataCount,
67                 range,
68                 full_wasm,
69             ),
70             DataSection(data) => stack.top_mut().module.add_raw_section(
71                 &mut cx,
72                 SectionId::Data,
73                 data.range(),
74                 full_wasm,
75             ),
76             CustomSection(c) => stack.top_mut().module.add_raw_section(
77                 &mut cx,
78                 SectionId::Custom,
79                 c.range(),
80                 full_wasm,
81             ),
82             CodeSectionStart {
83                 range,
84                 count: _,
85                 size,
86             } => {
87                 wasm = &wasm[usize::try_from(size).unwrap()..];
88                 let entry = stack.top_mut();
89                 entry.parser.skip_section();
90                 entry
91                     .module
92                     .add_raw_section(&mut cx, SectionId::Code, range, full_wasm)
93             }
94             CodeSectionEntry(_) => unreachable!(),
95             UnknownSection { .. } => anyhow::bail!("unknown section"),
96             TagSection(_) => anyhow::bail!("exceptions are not supported yet"),
97             End(_) => {
98                 let entry = stack.pop().unwrap();
99 
100                 // If we finished parsing the root Wasm module, then we're done.
101                 assert!(entry.module.is_root());
102                 assert!(stack.is_empty());
103                 return Ok(cx);
104             }
105 
106             ComponentTypeSection(_)
107             | ComponentImportSection(_)
108             | ComponentExportSection(_)
109             | ComponentStartSection { .. }
110             | ComponentAliasSection(_)
111             | CoreTypeSection(_)
112             | InstanceSection(_)
113             | ComponentInstanceSection(_)
114             | ComponentCanonicalSection(_)
115             | ModuleSection { .. }
116             | ComponentSection { .. } => {
117                 unreachable!()
118             }
119         }
120     }
121 }
122 
123 fn type_section<'a>(
124     cx: &mut ModuleContext<'a>,
125     stack: &mut Vec<StackEntry>,
126     full_wasm: &'a [u8],
127     types: wasmparser::TypeSectionReader<'a>,
128 ) -> anyhow::Result<()> {
129     let module = stack.top().module;
130     module.add_raw_section(cx, SectionId::Type, types.range(), full_wasm);
131 
132     // Parse out types, as we will need them later when processing
133     // instance imports.
134     for group in types {
135         for ty in group?.into_types() {
136             match ty.composite_type {
137                 ty @ wasmparser::CompositeType::Func(_) => {
138                     module.push_type(cx, ty);
139                 }
140                 wasmparser::CompositeType::Array(_) => todo!(),
141                 wasmparser::CompositeType::Struct(_) => todo!(),
142             }
143         }
144     }
145 
146     Ok(())
147 }
148 
149 fn import_section<'a>(
150     cx: &mut ModuleContext<'a>,
151     stack: &mut Vec<StackEntry>,
152     full_wasm: &'a [u8],
153     imports: wasmparser::ImportSectionReader<'a>,
154 ) -> anyhow::Result<()> {
155     let module = stack.top().module;
156     stack
157         .top_mut()
158         .module
159         .add_raw_section(cx, SectionId::Import, imports.range(), full_wasm);
160 
161     // Check that we can properly handle all imports.
162     for imp in imports {
163         let imp = imp?;
164 
165         if imp.module.starts_with("__wizer_") || imp.name.starts_with("__wizer_") {
166             anyhow::bail!(
167                 "input Wasm module already imports entities named with the `__wizer_*` prefix"
168             );
169         }
170 
171         check_import_type(
172             cx,
173             stack.top().module.types(cx),
174             stack.top().module.is_root(),
175             &module.entity_type(cx, imp.ty),
176         )?;
177         module.push_import(cx, imp);
178     }
179     Ok(())
180 }
181 
182 fn check_import_type(
183     _cx: &ModuleContext,
184     _types: &[TypeId],
185     is_root: bool,
186     ty: &EntityType,
187 ) -> Result<()> {
188     match ty {
189         EntityType::Function(_) => Ok(()),
190         EntityType::Memory(mem_ty) => {
191             anyhow::ensure!(
192                 !mem_ty.shared,
193                 "shared memories are not supported by Wizer yet"
194             );
195             anyhow::ensure!(
196                 !mem_ty.memory64,
197                 "the memory64 proposal is not supported by Wizer yet"
198             );
199             anyhow::ensure!(
200                 !is_root,
201                 "memory imports are not allowed in the root Wasm module"
202             );
203             Ok(())
204         }
205         EntityType::Table(_) | EntityType::Global(_) => {
206             anyhow::ensure!(
207                 !is_root,
208                 "table and global imports are not allowed in the root Wasm module"
209             );
210             Ok(())
211         }
212     }
213 }
214 
215 fn function_section<'a>(
216     cx: &mut ModuleContext<'a>,
217     stack: &mut Vec<StackEntry>,
218     full_wasm: &'a [u8],
219     funcs: wasmparser::FunctionSectionReader<'a>,
220 ) -> anyhow::Result<()> {
221     let module = stack.top().module;
222     module.add_raw_section(cx, SectionId::Function, funcs.range(), full_wasm);
223 
224     for ty_idx in funcs {
225         let ty = module.type_id_at(cx, ty_idx?);
226         module.push_function(cx, ty);
227     }
228     Ok(())
229 }
230 
231 fn table_section<'a>(
232     cx: &mut ModuleContext<'a>,
233     stack: &mut Vec<StackEntry>,
234     full_wasm: &'a [u8],
235     tables: wasmparser::TableSectionReader<'a>,
236 ) -> anyhow::Result<()> {
237     let module = stack.top().module;
238     module.add_raw_section(cx, SectionId::Table, tables.range(), full_wasm);
239 
240     for table in tables {
241         module.push_table(cx, table?.ty);
242     }
243     Ok(())
244 }
245 
246 fn memory_section<'a>(
247     cx: &mut ModuleContext<'a>,
248     stack: &mut Vec<StackEntry>,
249     full_wasm: &'a [u8],
250     mems: wasmparser::MemorySectionReader<'a>,
251 ) -> anyhow::Result<()> {
252     let module = stack.top().module;
253     module.add_raw_section(cx, SectionId::Memory, mems.range(), full_wasm);
254 
255     for m in mems {
256         module.push_defined_memory(cx, m?);
257     }
258     Ok(())
259 }
260 
261 fn global_section<'a>(
262     cx: &mut ModuleContext<'a>,
263     stack: &mut Vec<StackEntry>,
264     full_wasm: &'a [u8],
265     globals: wasmparser::GlobalSectionReader<'a>,
266 ) -> anyhow::Result<()> {
267     let module = stack.top().module;
268     module.add_raw_section(cx, SectionId::Global, globals.range(), full_wasm);
269 
270     for g in globals {
271         module.push_defined_global(cx, g?.ty);
272     }
273     Ok(())
274 }
275 
276 fn export_section<'a>(
277     cx: &mut ModuleContext<'a>,
278     stack: &mut Vec<StackEntry>,
279     full_wasm: &'a [u8],
280     exports: wasmparser::ExportSectionReader<'a>,
281 ) -> anyhow::Result<()> {
282     let module = stack.top().module;
283     module.add_raw_section(cx, SectionId::Export, exports.range(), full_wasm);
284 
285     for export in exports {
286         let export = export?;
287 
288         if export.name.starts_with("__wizer_") {
289             anyhow::bail!(
290                 "input Wasm module already exports entities named with the `__wizer_*` prefix"
291             );
292         }
293 
294         match export.kind {
295             wasmparser::ExternalKind::Tag => {
296                 unreachable!("checked in validation")
297             }
298             wasmparser::ExternalKind::Func
299             | wasmparser::ExternalKind::Table
300             | wasmparser::ExternalKind::Memory
301             | wasmparser::ExternalKind::Global => {
302                 module.push_export(cx, export);
303             }
304         }
305     }
306     Ok(())
307 }
308