Skip to content

Commit 30cc3ae

Browse files
refactor: expose fn for embedding component metadata (#1233)
The logic for embedding component metadata as a custom section was used only in the course of `wasm-tools component embed`, which made it difficult to use from outside wasm-tools. This commit refeactors the code that actually writes out the metadata to a function that can be re-used and is exposed by wit-component. Signed-off-by: Victor Adossi <[email protected]>
1 parent 67fb7b0 commit 30cc3ae

File tree

2 files changed

+106
-20
lines changed

2 files changed

+106
-20
lines changed

crates/wit-component/src/lib.rs

Lines changed: 96 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
33
#![deny(missing_docs)]
44

5-
use anyhow::{bail, Context, Result};
65
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};
1011

1112
mod decoding;
1213
mod encoding;
@@ -178,3 +179,94 @@ pub fn is_wasm_binary_or_wat(bytes: impl AsRef<[u8]>) -> bool {
178179

179180
false
180181
}
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+
}

src/bin/wasm-tools/component.rs

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
//! The WebAssembly component tool command line interface.
22
3-
use anyhow::{bail, Context, Result};
4-
use clap::Parser;
5-
use std::borrow::Cow;
63
use std::collections::HashMap;
74
use std::io::Read;
85
use std::path::{Path, PathBuf};
9-
use wasm_encoder::{Encode, Section};
6+
7+
use anyhow::{bail, Context, Result};
8+
use clap::Parser;
9+
1010
use wasm_tools::Output;
1111
use wit_component::{
12-
is_wasm_binary_or_wat, parse_wit_from_path, ComponentEncoder, DecodedWasm, Linker,
13-
StringEncoding, WitPrinter,
12+
embed_component_metadata, is_wasm_binary_or_wat, parse_wit_from_path, ComponentEncoder,
13+
DecodedWasm, Linker, StringEncoding, WitPrinter,
1414
};
1515
use wit_parser::{Resolve, UnresolvedPackage};
1616

@@ -216,21 +216,15 @@ impl EmbedOpts {
216216
let (resolve, id) = parse_wit_from_path(self.wit)?;
217217
let world = resolve.select_world(id, self.world.as_deref())?;
218218

219-
let encoded = wit_component::metadata::encode(
219+
let mut wasm = wasm.unwrap_or_else(|| wit_component::dummy_module(&resolve, world));
220+
221+
embed_component_metadata(
222+
&mut wasm,
220223
&resolve,
221224
world,
222225
self.encoding.unwrap_or(StringEncoding::UTF8),
223-
None,
224226
)?;
225227

226-
let section = wasm_encoder::CustomSection {
227-
name: "component-type".into(),
228-
data: Cow::Borrowed(&encoded),
229-
};
230-
let mut wasm = wasm.unwrap_or_else(|| wit_component::dummy_module(&resolve, world));
231-
wasm.push(section.id());
232-
section.encode(&mut wasm);
233-
234228
self.io.output(Output::Wasm {
235229
bytes: &wasm,
236230
wat: self.wat,

0 commit comments

Comments
 (0)