1 | use std::ops::Range; |
2 | |
3 | use anyhow::Result; |
4 | use serde_derive::Serialize; |
5 | use wasmparser::{KnownCustom, Parser, Payload::*}; |
6 | |
7 | use crate::{ |
8 | Author, ComponentNames, Description, Homepage, Licenses, Metadata, ModuleNames, Producers, |
9 | Revision, Source, |
10 | }; |
11 | |
12 | /// Data representing either a Wasm Component or module |
13 | /// |
14 | /// Each payload has additional [`Metadata`] associated with it, |
15 | /// but if it's a Component it may have also additional `Payloads` associated |
16 | /// with it. |
17 | #[derive (Debug, Serialize)] |
18 | #[serde(rename_all = "lowercase" )] |
19 | pub enum Payload { |
20 | /// A representation of a Wasm Component |
21 | Component { |
22 | /// The metadata associated with the Component |
23 | metadata: Metadata, |
24 | /// The metadata of nested Components or Modules |
25 | children: Vec<Payload>, |
26 | }, |
27 | /// A representation of a Wasm Module |
28 | Module(Metadata), |
29 | } |
30 | |
31 | impl Payload { |
32 | /// Parse metadata from a WebAssembly binary. Supports both core WebAssembly modules, and |
33 | /// WebAssembly components. |
34 | pub fn from_binary(input: &[u8]) -> Result<Self> { |
35 | let mut output = Vec::new(); |
36 | |
37 | for payload in Parser::new(0).parse_all(&input) { |
38 | match payload? { |
39 | Version { encoding, .. } => { |
40 | if output.is_empty() { |
41 | match encoding { |
42 | wasmparser::Encoding::Module => { |
43 | output.push(Self::empty_module(0..input.len())) |
44 | } |
45 | wasmparser::Encoding::Component => { |
46 | output.push(Self::empty_component(0..input.len())) |
47 | } |
48 | } |
49 | } |
50 | } |
51 | ModuleSection { |
52 | unchecked_range: range, |
53 | .. |
54 | } => output.push(Self::empty_module(range)), |
55 | ComponentSection { |
56 | unchecked_range: range, |
57 | .. |
58 | } => output.push(Self::empty_component(range)), |
59 | End { .. } => { |
60 | let finished = output.pop().expect("non-empty metadata stack" ); |
61 | if output.is_empty() { |
62 | return Ok(finished); |
63 | } else { |
64 | output.last_mut().unwrap().push_child(finished); |
65 | } |
66 | } |
67 | CustomSection(c) => match c.as_known() { |
68 | KnownCustom::Name(_) => { |
69 | let names = ModuleNames::from_bytes(c.data(), c.data_offset())?; |
70 | if let Some(name) = names.get_name() { |
71 | output |
72 | .last_mut() |
73 | .expect("non-empty metadata stack" ) |
74 | .metadata_mut() |
75 | .name = Some(name.clone()); |
76 | } |
77 | } |
78 | KnownCustom::ComponentName(_) => { |
79 | let names = ComponentNames::from_bytes(c.data(), c.data_offset())?; |
80 | if let Some(name) = names.get_name() { |
81 | output |
82 | .last_mut() |
83 | .expect("non-empty metadata stack" ) |
84 | .metadata_mut() |
85 | .name = Some(name.clone()); |
86 | } |
87 | } |
88 | KnownCustom::Producers(_) => { |
89 | let producers = Producers::from_bytes(c.data(), c.data_offset())?; |
90 | output |
91 | .last_mut() |
92 | .expect("non-empty metadata stack" ) |
93 | .metadata_mut() |
94 | .producers = Some(producers); |
95 | } |
96 | KnownCustom::Unknown if c.name() == "author" => { |
97 | let a = Author::parse_custom_section(&c)?; |
98 | let Metadata { author, .. } = output |
99 | .last_mut() |
100 | .expect("non-empty metadata stack" ) |
101 | .metadata_mut(); |
102 | *author = Some(a); |
103 | } |
104 | KnownCustom::Unknown if c.name() == "description" => { |
105 | let a = Description::parse_custom_section(&c)?; |
106 | let Metadata { description, .. } = output |
107 | .last_mut() |
108 | .expect("non-empty metadata stack" ) |
109 | .metadata_mut(); |
110 | *description = Some(a); |
111 | } |
112 | KnownCustom::Unknown if c.name() == "licenses" => { |
113 | let a = Licenses::parse_custom_section(&c)?; |
114 | let Metadata { licenses, .. } = output |
115 | .last_mut() |
116 | .expect("non-empty metadata stack" ) |
117 | .metadata_mut(); |
118 | *licenses = Some(a); |
119 | } |
120 | KnownCustom::Unknown if c.name() == "source" => { |
121 | let a = Source::parse_custom_section(&c)?; |
122 | let Metadata { source, .. } = output |
123 | .last_mut() |
124 | .expect("non-empty metadata stack" ) |
125 | .metadata_mut(); |
126 | *source = Some(a); |
127 | } |
128 | KnownCustom::Unknown if c.name() == "homepage" => { |
129 | let a = Homepage::parse_custom_section(&c)?; |
130 | let Metadata { homepage, .. } = output |
131 | .last_mut() |
132 | .expect("non-empty metadata stack" ) |
133 | .metadata_mut(); |
134 | *homepage = Some(a); |
135 | } |
136 | KnownCustom::Unknown if c.name() == "revision" => { |
137 | let a = Revision::parse_custom_section(&c)?; |
138 | let Metadata { revision, .. } = output |
139 | .last_mut() |
140 | .expect("non-empty metadata stack" ) |
141 | .metadata_mut(); |
142 | *revision = Some(a); |
143 | } |
144 | KnownCustom::Unknown if c.name() == "version" => { |
145 | let a = crate::Version::parse_custom_section(&c)?; |
146 | let Metadata { version, .. } = output |
147 | .last_mut() |
148 | .expect("non-empty metadata stack" ) |
149 | .metadata_mut(); |
150 | *version = Some(a); |
151 | } |
152 | _ => {} |
153 | }, |
154 | _ => {} |
155 | } |
156 | } |
157 | Err(anyhow::anyhow!( |
158 | "malformed wasm binary, should have reached end" |
159 | )) |
160 | } |
161 | |
162 | /// Get a reference te the metadata |
163 | pub fn metadata(&self) -> &Metadata { |
164 | match self { |
165 | Payload::Component { metadata, .. } => metadata, |
166 | Payload::Module(metadata) => metadata, |
167 | } |
168 | } |
169 | |
170 | /// Get a mutable reference te the metadata |
171 | pub fn metadata_mut(&mut self) -> &mut Metadata { |
172 | match self { |
173 | Payload::Component { metadata, .. } => metadata, |
174 | Payload::Module(metadata) => metadata, |
175 | } |
176 | } |
177 | |
178 | fn empty_component(range: Range<usize>) -> Self { |
179 | let mut this = Self::Component { |
180 | metadata: Metadata::default(), |
181 | children: vec![], |
182 | }; |
183 | this.metadata_mut().range = range; |
184 | this |
185 | } |
186 | |
187 | fn empty_module(range: Range<usize>) -> Self { |
188 | let mut this = Self::Module(Metadata::default()); |
189 | this.metadata_mut().range = range; |
190 | this |
191 | } |
192 | |
193 | fn push_child(&mut self, child: Self) { |
194 | match self { |
195 | Self::Module { .. } => panic!("module shouldnt have children" ), |
196 | Self::Component { children, .. } => children.push(child), |
197 | } |
198 | } |
199 | } |
200 | |