1use std::ops::Range;
2
3use anyhow::Result;
4use serde_derive::Serialize;
5use wasmparser::{KnownCustom, Parser, Payload::*};
6
7use 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")]
19pub 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
31impl 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