1 use crate::component::ComponentContext;
2 use anyhow::{Result, bail};
3 use std::collections::hash_map::Entry;
4 use wasmparser::{
5     CanonicalFunction, ComponentAlias, ComponentExternalKind, ComponentOuterAliasKind, Encoding,
6     Instance, Parser, Payload,
7 };
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 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.
22 pub(crate) fn parse<'a>(full_wasm: &'a [u8]) -> anyhow::Result<ComponentContext<'a>> {
23     let mut component = ComponentContext::default();
24     let mut parser = Parser::new(0).parse_all(full_wasm);
25 
26     while let Some(payload) = parser.next() {
27         let payload = payload?;
28 
29         match &payload {
30             // Module sections get parsed with wizer's core wasm support.
31             Payload::ModuleSection { .. } => {
32                 let info = crate::parse::parse_with(&full_wasm, &mut parser)?;
33                 component.push_module_section(info);
34             }
35 
36             // All other sections get pushed raw as-is into the component.
37             _ => {
38                 if let Some((id, range)) = payload.as_section() {
39                     component.push_raw_section(wasm_encoder::RawSection {
40                         id,
41                         data: &full_wasm[range],
42                     });
43                 }
44             }
45         }
46 
47         // Further validation/handling of each section, mostly about maintaining
48         // index spaces so we know what indices are used when the instrumented
49         // component is produced.
50         match payload {
51             Payload::Version {
52                 encoding: Encoding::Module,
53                 ..
54             } => {
55                 bail!("expected a component, found a core module");
56             }
57 
58             Payload::ComponentSection { .. } => {
59                 bail!("wizer does not currently support nested components");
60             }
61 
62             Payload::InstanceSection(reader) => {
63                 for instance in reader {
64                     let instance_index = component.inc_core_instances();
65 
66                     if let Instance::Instantiate { module_index, .. } = instance? {
67                         match component.core_instantiations.entry(module_index) {
68                             Entry::Vacant(entry) => {
69                                 entry.insert(instance_index);
70                             }
71                             Entry::Occupied(_) => bail!("modules may be instantiated at most once"),
72                         }
73                     }
74                 }
75             }
76             Payload::ComponentInstanceSection(reader) => {
77                 for _ in reader {
78                     component.inc_instances();
79                 }
80             }
81 
82             Payload::ComponentAliasSection(reader) => {
83                 for alias in reader {
84                     match alias? {
85                         ComponentAlias::CoreInstanceExport { kind, .. } => {
86                             component.inc_core(kind);
87                         }
88                         ComponentAlias::InstanceExport { kind, .. } => {
89                             validate_item_kind(kind, "aliases")?;
90                             component.inc(kind);
91                         }
92                         ComponentAlias::Outer { kind, .. } => match kind {
93                             ComponentOuterAliasKind::CoreType => {}
94                             ComponentOuterAliasKind::Type => {
95                                 component.inc_types();
96                             }
97                             ComponentOuterAliasKind::CoreModule => {
98                                 bail!("wizer does not currently support module aliases");
99                             }
100                             ComponentOuterAliasKind::Component => {
101                                 bail!("wizer does not currently support component aliases");
102                             }
103                         },
104                     }
105                 }
106             }
107 
108             Payload::ComponentCanonicalSection(reader) => {
109                 for function in reader {
110                     match function? {
111                         CanonicalFunction::Lift { .. } => {
112                             component.inc_funcs();
113                         }
114                         _ => {
115                             component.inc_core_funcs();
116                         }
117                     }
118                 }
119             }
120 
121             Payload::ComponentImportSection(reader) => {
122                 for import in reader {
123                     let kind = import?.ty.kind();
124                     validate_item_kind(kind, "imports")?;
125                     component.inc(kind);
126                 }
127             }
128 
129             Payload::ComponentExportSection(reader) => {
130                 for export in reader {
131                     let kind = export?.kind;
132                     validate_item_kind(kind, "exports")?;
133                     component.inc(kind);
134                 }
135             }
136 
137             Payload::ComponentTypeSection(reader) => {
138                 for _ in reader {
139                     component.inc_types();
140                 }
141             }
142 
143             // The `start` section for components is itself not stable, so not
144             // super urgent to handle. A simplifying assumption is made to just
145             // reject it outright for now if it shows up.
146             Payload::ComponentStartSection { .. } => {
147                 bail!("wizer does not currently support component start functions");
148             }
149 
150             _ => {}
151         }
152     }
153 
154     Ok(component)
155 }
156 
157 fn validate_item_kind(kind: ComponentExternalKind, msg: &str) -> Result<()> {
158     match kind {
159         // Aliasing modules would require keeping track of where a module's
160         // original definition is. There's not much usage of this in the wild
161         // so reject it for now as a simplifying assumption.
162         ComponentExternalKind::Module => {
163             bail!("wizer does not currently support module {msg}");
164         }
165 
166         // Aliasing components deals with nested instantiations or similar,
167         // where a simplifying assumption is made to not worry about that for now.
168         ComponentExternalKind::Component => {
169             bail!("wizer does not currently support component {msg}");
170         }
171 
172         // Like start sections, support for this is just deferred to some other
173         // time, if ever.
174         ComponentExternalKind::Value => {
175             bail!("wizer does not currently support value {msg}");
176         }
177 
178         ComponentExternalKind::Func
179         | ComponentExternalKind::Type
180         | ComponentExternalKind::Instance => Ok(()),
181     }
182 }
183