|
2 | 2 |
|
3 | 3 | #![deny(missing_docs)]
|
4 | 4 |
|
5 |
| -use anyhow::{bail, Context, Result}; |
6 | 5 | use std::str::FromStr;
|
7 |
| -use std::{fmt::Display, path::Path}; |
8 |
| -use wasm_encoder::CanonicalOption; |
9 |
| -use wit_parser::{PackageId, Resolve, UnresolvedPackage}; |
| 6 | +use std::{borrow::Cow, fmt::Display, path::Path}; |
| 7 | + |
| 8 | +use anyhow::{bail, Context, Result}; |
| 9 | +use wasm_encoder::{CanonicalOption, Encode, Section}; |
| 10 | +use wit_parser::{PackageId, Resolve, UnresolvedPackage, WorldId}; |
10 | 11 |
|
11 | 12 | mod decoding;
|
12 | 13 | mod encoding;
|
@@ -178,3 +179,94 @@ pub fn is_wasm_binary_or_wat(bytes: impl AsRef<[u8]>) -> bool {
|
178 | 179 |
|
179 | 180 | false
|
180 | 181 | }
|
| 182 | + |
| 183 | +/// Embed component metadata in a buffer of bytes that contains a Wasm module |
| 184 | +pub fn embed_component_metadata( |
| 185 | + bytes: &mut Vec<u8>, |
| 186 | + wit_resolver: &Resolve, |
| 187 | + world: WorldId, |
| 188 | + encoding: StringEncoding, |
| 189 | +) -> Result<()> { |
| 190 | + let encoded = metadata::encode(&wit_resolver, world, encoding, None)?; |
| 191 | + |
| 192 | + let section = wasm_encoder::CustomSection { |
| 193 | + name: "component-type".into(), |
| 194 | + data: Cow::Borrowed(&encoded), |
| 195 | + }; |
| 196 | + bytes.push(section.id()); |
| 197 | + section.encode(bytes); |
| 198 | + |
| 199 | + Ok(()) |
| 200 | +} |
| 201 | + |
| 202 | +#[cfg(test)] |
| 203 | +mod tests { |
| 204 | + use std::path::Path; |
| 205 | + |
| 206 | + use anyhow::Result; |
| 207 | + use wasmparser::Payload; |
| 208 | + use wit_parser::{Resolve, UnresolvedPackage}; |
| 209 | + |
| 210 | + use super::{embed_component_metadata, StringEncoding}; |
| 211 | + |
| 212 | + const MODULE_WAT: &str = r#" |
| 213 | +(module |
| 214 | + (type (;0;) (func)) |
| 215 | + (func (;0;) (type 0) |
| 216 | + nop |
| 217 | + ) |
| 218 | +) |
| 219 | +"#; |
| 220 | + |
| 221 | + const COMPONENT_WIT: &str = r#" |
| 222 | +package test:foo; |
| 223 | +world test-world {} |
| 224 | +"#; |
| 225 | + |
| 226 | + #[test] |
| 227 | + fn component_metadata_embedding_works() -> Result<()> { |
| 228 | + let mut bytes = wat::parse_str(MODULE_WAT)?; |
| 229 | + |
| 230 | + // Get original len & custom section count |
| 231 | + let original_len = bytes.len(); |
| 232 | + let payloads = wasmparser::Parser::new(0).parse_all(&bytes); |
| 233 | + let original_custom_section_count = payloads.fold(0, |acc, payload| { |
| 234 | + if let Ok(Payload::CustomSection { .. }) = payload { |
| 235 | + acc + 1 |
| 236 | + } else { |
| 237 | + acc |
| 238 | + } |
| 239 | + }); |
| 240 | + |
| 241 | + // Parse pre-canned WIT to build resolver |
| 242 | + let mut resolver = Resolve::default(); |
| 243 | + let pkg = UnresolvedPackage::parse(&Path::new("in-code.wit"), COMPONENT_WIT)?; |
| 244 | + let pkg_id = resolver.push(pkg)?; |
| 245 | + let world = resolver.select_world(pkg_id, Some("test-world").into())?; |
| 246 | + |
| 247 | + // Embed component metadata |
| 248 | + embed_component_metadata(&mut bytes, &resolver, world, StringEncoding::UTF8)?; |
| 249 | + |
| 250 | + // Re-retrieve custom section count, and search for the component-type custom section along the way |
| 251 | + let mut found_component_section = false; |
| 252 | + let new_custom_section_count = |
| 253 | + wasmparser::Parser::new(0) |
| 254 | + .parse_all(&bytes) |
| 255 | + .fold(0, |acc, payload| { |
| 256 | + if let Ok(Payload::CustomSection(reader)) = payload { |
| 257 | + if reader.name() == "component-type" { |
| 258 | + found_component_section = true; |
| 259 | + } |
| 260 | + acc + 1 |
| 261 | + } else { |
| 262 | + acc |
| 263 | + } |
| 264 | + }); |
| 265 | + |
| 266 | + assert!(original_len < bytes.len()); |
| 267 | + assert_eq!(original_custom_section_count + 1, new_custom_section_count); |
| 268 | + assert!(found_component_section); |
| 269 | + |
| 270 | + Ok(()) |
| 271 | + } |
| 272 | +} |
0 commit comments