| 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 | |