1 use crate::component::ComponentContext;
2 use std::collections::hash_map::Entry;
3 use wasmparser::{
4     CanonicalFunction, ComponentAlias, ComponentExternalKind, ComponentOuterAliasKind, Encoding,
5     Instance, Parser, Payload,
6 };
7 use wasmtime::{Result, bail};
8 
9 /// Parse the given Wasm bytes into a `ComponentContext` tree.
10 ///
11 /// This will parse the input component and build up metadata that Wizer needs
12 /// to know about the component. At this time there are limitations to this
13 /// parsing phase which in theory could be lifted in the future but serve as
14 /// simplifying assumptions for now:
15 ///
16 /// * Nested components with modules are not supported.
17 /// * Imported modules or components are not supported.
18 /// * Instantiating a module twice is not supported.
19 /// * Component-level start functions are not supported.
20 ///
21 /// Some of these restrictions are likely to be loosened over time, however.
parse<'a>(full_wasm: &'a [u8]) -> wasmtime::Result<ComponentContext<'a>>22 pub(crate) fn parse<'a>(full_wasm: &'a [u8]) -> wasmtime::Result<ComponentContext<'a>> {
23     let mut component = ComponentContext::default();
24     let parser = Parser::new(0).parse_all(full_wasm);
25     parse_into(Some(&mut component), full_wasm, parser)?;
26     Ok(component)
27 }
28 
parse_into<'a>( mut cx: Option<&mut ComponentContext<'a>>, full_wasm: &'a [u8], mut iter: impl Iterator<Item = wasmparser::Result<Payload<'a>>>, ) -> wasmtime::Result<()>29 fn parse_into<'a>(
30     mut cx: Option<&mut ComponentContext<'a>>,
31     full_wasm: &'a [u8],
32     mut iter: impl Iterator<Item = wasmparser::Result<Payload<'a>>>,
33 ) -> wasmtime::Result<()> {
34     let mut stack = Vec::new();
35     while let Some(payload) = iter.next() {
36         let payload = payload?;
37 
38         match &payload {
39             // Module sections get parsed with wizer's core wasm support.
40             Payload::ModuleSection { .. } => match &mut cx {
41                 Some(component) => {
42                     let info = crate::parse::parse_with(&full_wasm, &mut iter)?;
43                     component.push_module_section(info);
44                 }
45                 None => {
46                     bail!("nested components with modules not currently supported");
47                 }
48             },
49 
50             // All other sections get pushed raw as-is into the component.
51             _ => {
52                 if let Some((id, range)) = payload.as_section()
53                     && let Some(component) = &mut cx
54                 {
55                     component.push_raw_section(wasm_encoder::RawSection {
56                         id,
57                         data: &full_wasm[range],
58                     });
59                 }
60             }
61         }
62 
63         // Further validation/handling of each section, mostly about maintaining
64         // index spaces so we know what indices are used when the instrumented
65         // component is produced.
66         match payload {
67             Payload::Version {
68                 encoding: Encoding::Module,
69                 ..
70             } => {
71                 bail!("expected a component, found a core module");
72             }
73 
74             Payload::ComponentSection { .. } => {
75                 stack.push(cx.take());
76             }
77 
78             Payload::End(_) => {
79                 if stack.len() > 0 {
80                     cx = stack.pop().unwrap();
81                 }
82             }
83 
84             Payload::InstanceSection(reader) => {
85                 if let Some(component) = &mut cx {
86                     for instance in reader {
87                         let instance_index = component.inc_core_instances();
88 
89                         if let Instance::Instantiate { module_index, .. } = instance? {
90                             match component.core_instantiations.entry(module_index) {
91                                 Entry::Vacant(entry) => {
92                                     entry.insert(instance_index);
93                                 }
94                                 Entry::Occupied(_) => {
95                                     bail!("modules may be instantiated at most once")
96                                 }
97                             }
98                         }
99                     }
100                 }
101             }
102             Payload::ComponentInstanceSection(reader) => {
103                 if let Some(component) = &mut cx {
104                     for _ in reader {
105                         component.inc_instances();
106                     }
107                 }
108             }
109 
110             Payload::ComponentAliasSection(reader) => {
111                 for alias in reader {
112                     match alias? {
113                         ComponentAlias::CoreInstanceExport { kind, .. } => {
114                             if let Some(component) = &mut cx {
115                                 component.inc_core(kind);
116                             }
117                         }
118                         ComponentAlias::InstanceExport { kind, .. } => {
119                             validate_item_kind(kind, "aliases")?;
120                             if let Some(component) = &mut cx {
121                                 component.inc(kind);
122                             }
123                         }
124                         ComponentAlias::Outer { kind, .. } => match kind {
125                             ComponentOuterAliasKind::CoreType => {}
126                             ComponentOuterAliasKind::Type => {
127                                 if let Some(component) = &mut cx {
128                                     component.inc_types();
129                                 }
130                             }
131                             ComponentOuterAliasKind::CoreModule => {
132                                 bail!("wizer does not currently support module aliases");
133                             }
134                             ComponentOuterAliasKind::Component => {
135                                 bail!("wizer does not currently support component aliases");
136                             }
137                         },
138                     }
139                 }
140             }
141 
142             Payload::ComponentCanonicalSection(reader) => {
143                 for function in reader {
144                     match function? {
145                         CanonicalFunction::Lift { .. } => {
146                             if let Some(component) = &mut cx {
147                                 component.inc_funcs();
148                             }
149                         }
150                         _ => {
151                             if let Some(component) = &mut cx {
152                                 component.inc_core_funcs();
153                             }
154                         }
155                     }
156                 }
157             }
158 
159             Payload::ComponentImportSection(reader) => {
160                 for import in reader {
161                     let kind = import?.ty.kind();
162                     validate_item_kind(kind, "imports")?;
163                     if let Some(component) = &mut cx {
164                         component.inc(kind);
165                     }
166                 }
167             }
168 
169             Payload::ComponentExportSection(reader) => {
170                 for export in reader {
171                     let kind = export?.kind;
172                     validate_item_kind(kind, "exports")?;
173                     if let Some(component) = &mut cx {
174                         component.inc(kind);
175                     }
176                 }
177             }
178 
179             Payload::ComponentTypeSection(reader) => {
180                 for _ in reader {
181                     if let Some(component) = &mut cx {
182                         component.inc_types();
183                     }
184                 }
185             }
186 
187             // The `start` section for components is itself not stable, so not
188             // super urgent to handle. A simplifying assumption is made to just
189             // reject it outright for now if it shows up.
190             Payload::ComponentStartSection { .. } => {
191                 bail!("wizer does not currently support component start functions");
192             }
193 
194             _ => {}
195         }
196     }
197 
198     Ok(())
199 }
200 
validate_item_kind(kind: ComponentExternalKind, msg: &str) -> Result<()>201 fn validate_item_kind(kind: ComponentExternalKind, msg: &str) -> Result<()> {
202     match kind {
203         // Aliasing modules would require keeping track of where a module's
204         // original definition is. There's not much usage of this in the wild
205         // so reject it for now as a simplifying assumption.
206         ComponentExternalKind::Module => {
207             bail!("wizer does not currently support module {msg}");
208         }
209 
210         // Aliasing components deals with nested instantiations or similar,
211         // where a simplifying assumption is made to not worry about that for now.
212         ComponentExternalKind::Component => {
213             bail!("wizer does not currently support component {msg}");
214         }
215 
216         // Like start sections, support for this is just deferred to some other
217         // time, if ever.
218         ComponentExternalKind::Value => {
219             bail!("wizer does not currently support value {msg}");
220         }
221 
222         ComponentExternalKind::Func
223         | ComponentExternalKind::Type
224         | ComponentExternalKind::Instance => Ok(()),
225     }
226 }
227