xref: /wasmtime-44.0.1/crates/wizer/src/parse.rs (revision 2f76c853)
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             ComponentTypeSection(_)
106             | ComponentImportSection(_)
107             | ComponentExportSection(_)
108             | ComponentStartSection { .. }
109             | ComponentAliasSection(_)
110             | CoreTypeSection(_)
111             | InstanceSection(_)
112             | ComponentInstanceSection(_)
113             | ComponentCanonicalSection(_)
114             | ModuleSection { .. }
115             | ComponentSection { .. } => {
116                 unreachable!()
117             }
118             _ => anyhow::bail!("unsupported wasmparser payload"),
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.inner {
137                 wasmparser::CompositeInnerType::Func(_) => {
138                     module.push_type(cx, ty.composite_type);
139                 }
140                 wasmparser::CompositeInnerType::Array(_) => todo!(),
141                 wasmparser::CompositeInnerType::Struct(_) => todo!(),
142                 wasmparser::CompositeInnerType::Cont(_) => todo!(),
143             }
144         }
145     }
146 
147     Ok(())
148 }
149 
150 fn import_section<'a>(
151     cx: &mut ModuleContext<'a>,
152     stack: &mut Vec<StackEntry>,
153     full_wasm: &'a [u8],
154     imports: wasmparser::ImportSectionReader<'a>,
155 ) -> anyhow::Result<()> {
156     let module = stack.top().module;
157     stack
158         .top_mut()
159         .module
160         .add_raw_section(cx, SectionId::Import, imports.range(), full_wasm);
161 
162     // Check that we can properly handle all imports.
163     for imp in imports {
164         let imp = imp?;
165 
166         if imp.module.starts_with("__wizer_") || imp.name.starts_with("__wizer_") {
167             anyhow::bail!(
168                 "input Wasm module already imports entities named with the `__wizer_*` prefix"
169             );
170         }
171 
172         check_import_type(
173             cx,
174             stack.top().module.types(cx),
175             stack.top().module.is_root(),
176             &module.entity_type(cx, imp.ty),
177         )?;
178         module.push_import(cx, imp);
179     }
180     Ok(())
181 }
182 
183 fn check_import_type(
184     _cx: &ModuleContext,
185     _types: &[TypeId],
186     is_root: bool,
187     ty: &EntityType,
188 ) -> Result<()> {
189     match ty {
190         EntityType::Function(_) => Ok(()),
191         EntityType::Memory(mem_ty) => {
192             anyhow::ensure!(
193                 !mem_ty.shared,
194                 "shared memories are not supported by Wizer yet"
195             );
196             anyhow::ensure!(
197                 !mem_ty.memory64,
198                 "the memory64 proposal is not supported by Wizer yet"
199             );
200             anyhow::ensure!(
201                 !is_root,
202                 "memory imports are not allowed in the root Wasm module"
203             );
204             Ok(())
205         }
206         EntityType::Table(_) | EntityType::Global(_) => {
207             anyhow::ensure!(
208                 !is_root,
209                 "table and global imports are not allowed in the root Wasm module"
210             );
211             Ok(())
212         }
213     }
214 }
215 
216 fn function_section<'a>(
217     cx: &mut ModuleContext<'a>,
218     stack: &mut Vec<StackEntry>,
219     full_wasm: &'a [u8],
220     funcs: wasmparser::FunctionSectionReader<'a>,
221 ) -> anyhow::Result<()> {
222     let module = stack.top().module;
223     module.add_raw_section(cx, SectionId::Function, funcs.range(), full_wasm);
224 
225     for ty_idx in funcs {
226         let ty = module.type_id_at(cx, ty_idx?);
227         module.push_function(cx, ty);
228     }
229     Ok(())
230 }
231 
232 fn table_section<'a>(
233     cx: &mut ModuleContext<'a>,
234     stack: &mut Vec<StackEntry>,
235     full_wasm: &'a [u8],
236     tables: wasmparser::TableSectionReader<'a>,
237 ) -> anyhow::Result<()> {
238     let module = stack.top().module;
239     module.add_raw_section(cx, SectionId::Table, tables.range(), full_wasm);
240 
241     for table in tables {
242         module.push_table(cx, table?.ty);
243     }
244     Ok(())
245 }
246 
247 fn memory_section<'a>(
248     cx: &mut ModuleContext<'a>,
249     stack: &mut Vec<StackEntry>,
250     full_wasm: &'a [u8],
251     mems: wasmparser::MemorySectionReader<'a>,
252 ) -> anyhow::Result<()> {
253     let module = stack.top().module;
254     module.add_raw_section(cx, SectionId::Memory, mems.range(), full_wasm);
255 
256     for m in mems {
257         module.push_defined_memory(cx, m?);
258     }
259     Ok(())
260 }
261 
262 fn global_section<'a>(
263     cx: &mut ModuleContext<'a>,
264     stack: &mut Vec<StackEntry>,
265     full_wasm: &'a [u8],
266     globals: wasmparser::GlobalSectionReader<'a>,
267 ) -> anyhow::Result<()> {
268     let module = stack.top().module;
269     module.add_raw_section(cx, SectionId::Global, globals.range(), full_wasm);
270 
271     for g in globals {
272         module.push_defined_global(cx, g?.ty);
273     }
274     Ok(())
275 }
276 
277 fn export_section<'a>(
278     cx: &mut ModuleContext<'a>,
279     stack: &mut Vec<StackEntry>,
280     full_wasm: &'a [u8],
281     exports: wasmparser::ExportSectionReader<'a>,
282 ) -> anyhow::Result<()> {
283     let module = stack.top().module;
284     module.add_raw_section(cx, SectionId::Export, exports.range(), full_wasm);
285 
286     for export in exports {
287         let export = export?;
288 
289         if export.name.starts_with("__wizer_") {
290             anyhow::bail!(
291                 "input Wasm module already exports entities named with the `__wizer_*` prefix"
292             );
293         }
294 
295         match export.kind {
296             wasmparser::ExternalKind::Tag => {
297                 unreachable!("checked in validation")
298             }
299             wasmparser::ExternalKind::Func
300             | wasmparser::ExternalKind::Table
301             | wasmparser::ExternalKind::Memory
302             | wasmparser::ExternalKind::Global => {
303                 module.push_export(cx, export);
304             }
305         }
306     }
307     Ok(())
308 }
309