wasm_metadata/
payload.rs

1use std::ops::Range;
2
3use anyhow::Result;
4use serde_derive::Serialize;
5use wasmparser::{KnownCustom, Parser, Payload::*};
6
7use crate::{
8    Authors, 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() == "authors" => {
97                        let a = Authors::parse_custom_section(&c)?;
98                        let Metadata {
99                            authors: author, ..
100                        } = output
101                            .last_mut()
102                            .expect("non-empty metadata stack")
103                            .metadata_mut();
104                        *author = Some(a);
105                    }
106                    KnownCustom::Unknown if c.name() == "description" => {
107                        let a = Description::parse_custom_section(&c)?;
108                        let Metadata { description, .. } = output
109                            .last_mut()
110                            .expect("non-empty metadata stack")
111                            .metadata_mut();
112                        *description = Some(a);
113                    }
114                    KnownCustom::Unknown if c.name() == "licenses" => {
115                        let a = Licenses::parse_custom_section(&c)?;
116                        let Metadata { licenses, .. } = output
117                            .last_mut()
118                            .expect("non-empty metadata stack")
119                            .metadata_mut();
120                        *licenses = Some(a);
121                    }
122                    KnownCustom::Unknown if c.name() == "source" => {
123                        let a = Source::parse_custom_section(&c)?;
124                        let Metadata { source, .. } = output
125                            .last_mut()
126                            .expect("non-empty metadata stack")
127                            .metadata_mut();
128                        *source = Some(a);
129                    }
130                    KnownCustom::Unknown if c.name() == "homepage" => {
131                        let a = Homepage::parse_custom_section(&c)?;
132                        let Metadata { homepage, .. } = output
133                            .last_mut()
134                            .expect("non-empty metadata stack")
135                            .metadata_mut();
136                        *homepage = Some(a);
137                    }
138                    KnownCustom::Unknown if c.name() == "revision" => {
139                        let a = Revision::parse_custom_section(&c)?;
140                        let Metadata { revision, .. } = output
141                            .last_mut()
142                            .expect("non-empty metadata stack")
143                            .metadata_mut();
144                        *revision = Some(a);
145                    }
146                    KnownCustom::Unknown if c.name() == "version" => {
147                        let a = crate::Version::parse_custom_section(&c)?;
148                        let Metadata { version, .. } = output
149                            .last_mut()
150                            .expect("non-empty metadata stack")
151                            .metadata_mut();
152                        *version = Some(a);
153                    }
154                    _ => {}
155                },
156                _ => {}
157            }
158        }
159        Err(anyhow::anyhow!(
160            "malformed wasm binary, should have reached end"
161        ))
162    }
163
164    /// Get a reference te the metadata
165    pub fn metadata(&self) -> &Metadata {
166        match self {
167            Payload::Component { metadata, .. } => metadata,
168            Payload::Module(metadata) => metadata,
169        }
170    }
171
172    /// Get a mutable reference te the metadata
173    pub fn metadata_mut(&mut self) -> &mut Metadata {
174        match self {
175            Payload::Component { metadata, .. } => metadata,
176            Payload::Module(metadata) => metadata,
177        }
178    }
179
180    fn empty_component(range: Range<usize>) -> Self {
181        let mut this = Self::Component {
182            metadata: Metadata::default(),
183            children: vec![],
184        };
185        this.metadata_mut().range = range;
186        this
187    }
188
189    fn empty_module(range: Range<usize>) -> Self {
190        let mut this = Self::Module(Metadata::default());
191        this.metadata_mut().range = range;
192        this
193    }
194
195    fn push_child(&mut self, child: Self) {
196        match self {
197            Self::Module { .. } => panic!("module shouldnt have children"),
198            Self::Component { children, .. } => children.push(child),
199        }
200    }
201}