Skip to content

Commit de36f6c

Browse files
author
Pat Hickey
authored
Support wit bindgens adding producer information via component-type metadata (#930)
* wasm-metadata: refactor to make it easier to manipulate producers sections previously the only api for updating a producers section was with AddMetadata, this allows you to rewrite a wasm using that or just a method on your own Producers * wit-component: add facility for bindgen (metadata::encode) to plumb through additional producers info * ComponentBuilder keeps track of a Producers section added to a component * metadata::encode gets an additional argument for an optional Producers * ComponentEncoder::module decodes producers out of type metadata section, and rewrites them into the core module * fix * fix fuzz target
1 parent 89c3be6 commit de36f6c

File tree

9 files changed

+372
-127
lines changed

9 files changed

+372
-127
lines changed

crates/wasm-metadata/src/lib.rs

Lines changed: 273 additions & 115 deletions
Large diffs are not rendered by default.

crates/wit-component/src/builder.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::mem;
22
use wasm_encoder::*;
3+
use wasm_metadata::Producers;
34

45
/// Helper type used when encoding a component to have helpers that
56
/// simultaneously encode an item while returning its corresponding index in the
@@ -29,11 +30,18 @@ pub struct ComponentBuilder {
2930
instances: u32,
3031
types: u32,
3132
components: u32,
33+
34+
producers: Producers,
3235
}
3336

3437
impl ComponentBuilder {
3538
pub fn finish(mut self) -> Vec<u8> {
36-
self.component.section(&crate::producer_section());
39+
// Ideally the Default for self.producers would be crate::base_producers,
40+
// but in lieu we stick it in now:
41+
let mut base = crate::base_producers();
42+
base.merge(&self.producers);
43+
// Write producers section as last section:
44+
self.component.section(&base.section());
3745
self.flush();
3846
self.component.finish()
3947
}
@@ -197,6 +205,10 @@ impl ComponentBuilder {
197205
.instantiate(component_index, args);
198206
inc(&mut self.instances)
199207
}
208+
209+
pub fn add_producers(&mut self, producers: &Producers) {
210+
self.producers.merge(producers)
211+
}
200212
}
201213

202214
// Helper macro to generate methods on `ComponentBuilder` to get specific

crates/wit-component/src/encoding.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ use wit_parser::{
8989
const INDIRECT_TABLE_NAME: &str = "$imports";
9090

9191
mod wit;
92-
pub use wit::encode;
92+
pub use wit::{encode, encode_component};
9393

9494
mod types;
9595
use types::{InstanceTypeEncoder, RootTypeEncoder, ValtypeEncoder};
@@ -982,14 +982,14 @@ impl<'a> EncodingState<'a> {
982982
shim.section(&tables);
983983
shim.section(&exports);
984984
shim.section(&code);
985-
shim.section(&crate::producer_section());
985+
shim.section(&crate::base_producers().section());
986986
shim.section(&names);
987987

988988
let mut fixups = Module::default();
989989
fixups.section(&types);
990990
fixups.section(&imports_section);
991991
fixups.section(&elements);
992-
fixups.section(&crate::producer_section());
992+
fixups.section(&crate::base_producers().section());
993993
let mut names = NameSection::new();
994994
names.module("wit-component:fixups");
995995
fixups.section(&names);
@@ -1351,10 +1351,16 @@ impl ComponentEncoder {
13511351
/// Set the core module to encode as a component.
13521352
/// This method will also parse any component type information stored in custom sections
13531353
/// inside the module, and add them as the interface, imports, and exports.
1354+
/// It will also add any producers information inside the component type information to the
1355+
/// core module.
13541356
pub fn module(mut self, module: &[u8]) -> Result<Self> {
13551357
let (wasm, metadata) = metadata::decode(module)?;
1356-
self.module = wasm;
13571358
self.metadata.merge(metadata)?;
1359+
self.module = if let Some(producers) = &self.metadata.producers {
1360+
producers.add_to_wasm(&wasm)?
1361+
} else {
1362+
wasm
1363+
};
13581364
Ok(self)
13591365
}
13601366

crates/wit-component/src/encoding/wit.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,19 @@ use wit_parser::*;
2727
/// The binary returned can be [`decode`d](crate::decode) to recover the WIT
2828
/// package provided.
2929
pub fn encode(resolve: &Resolve, package: PackageId) -> Result<Vec<u8>> {
30+
Ok(encode_component(resolve, package)?.finish())
31+
}
32+
33+
/// Exactly like `encode`, except gives an unfinished `ComponentBuilder` in case you need
34+
/// to append anything else before finishing.
35+
pub fn encode_component(resolve: &Resolve, package: PackageId) -> Result<ComponentBuilder> {
3036
let mut encoder = Encoder {
3137
component: ComponentBuilder::default(),
3238
resolve,
3339
package,
3440
};
3541
encoder.run()?;
36-
Ok(encoder.component.finish())
42+
Ok(encoder.component)
3743
}
3844

3945
struct Encoder<'a> {

crates/wit-component/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ impl From<StringEncoding> for wasm_encoder::CanonicalOption {
7777

7878
/// A producer section to be added to all modules and components synthesized by
7979
/// this crate
80-
pub(crate) fn producer_section() -> wasm_encoder::ProducersSection {
80+
pub(crate) fn base_producers() -> wasm_metadata::Producers {
8181
let mut producer = wasm_metadata::Producers::empty();
8282
producer.add("processed-by", "wit-component", env!("CARGO_PKG_VERSION"));
83-
producer.section()
83+
producer
8484
}

crates/wit-component/src/metadata.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use crate::{DecodedWasm, StringEncoding};
3737
use anyhow::{bail, Context, Result};
3838
use indexmap::IndexMap;
3939
use wasm_encoder::Encode;
40+
use wasm_metadata::Producers;
4041
use wasmparser::BinaryReader;
4142
use wit_parser::{Document, Package, Resolve, World, WorldId, WorldItem};
4243

@@ -53,6 +54,8 @@ pub struct Bindgen {
5354
pub world: WorldId,
5455
/// Metadata about this specific module that was bound.
5556
pub metadata: ModuleMetadata,
57+
/// Producer information about tools used to produce this specific module.
58+
pub producers: Option<Producers>,
5659
}
5760

5861
impl Default for Bindgen {
@@ -82,6 +85,7 @@ impl Default for Bindgen {
8285
resolve,
8386
world,
8487
metadata: ModuleMetadata::default(),
88+
producers: None,
8589
}
8690
}
8791
}
@@ -143,7 +147,12 @@ pub fn decode(wasm: &[u8]) -> Result<(Vec<u8>, Bindgen)> {
143147
/// into the final core wasm binary. The core wasm binary is later fed
144148
/// through `wit-component` to produce the actual component where this returned
145149
/// section will be decoded.
146-
pub fn encode(resolve: &Resolve, world: WorldId, encoding: StringEncoding) -> Result<Vec<u8>> {
150+
pub fn encode(
151+
resolve: &Resolve,
152+
world: WorldId,
153+
encoding: StringEncoding,
154+
producers: Option<&Producers>,
155+
) -> Result<Vec<u8>> {
147156
let world = &resolve.worlds[world];
148157
let doc = &resolve.documents[world.document];
149158
let pkg = &resolve.packages[doc.package.unwrap()];
@@ -167,7 +176,13 @@ pub fn encode(resolve: &Resolve, world: WorldId, encoding: StringEncoding) -> Re
167176
pkg.name.encode(&mut ret);
168177
doc.name.encode(&mut ret);
169178
world.name.encode(&mut ret);
170-
ret.extend(crate::encoding::encode(resolve, doc.package.unwrap())?);
179+
// This appends a wasm binary encoded Component to the ret:
180+
let mut component_builder = crate::encoding::encode_component(resolve, doc.package.unwrap())?;
181+
182+
if let Some(p) = producers {
183+
component_builder.add_producers(p);
184+
}
185+
ret.extend(component_builder.finish());
171186
Ok(ret)
172187
}
173188

@@ -195,10 +210,12 @@ impl Bindgen {
195210
let doc = resolve.packages[pkg].documents[doc_name];
196211
let world = resolve.documents[doc].worlds[world_name];
197212
let metadata = ModuleMetadata::new(&resolve, world, encoding);
213+
let producers = wasm_metadata::Producers::from_wasm(&data[reader.original_position()..])?;
198214
Ok(Bindgen {
199215
resolve,
200216
world,
201217
metadata,
218+
producers,
202219
})
203220
}
204221

@@ -220,6 +237,7 @@ impl Bindgen {
220237
import_encodings,
221238
export_encodings,
222239
},
240+
producers,
223241
} = other;
224242

225243
let world = self.resolve.merge(resolve).worlds[world.index()];
@@ -249,6 +267,13 @@ impl Bindgen {
249267
}
250268
}
251269
}
270+
if let Some(producers) = producers {
271+
if let Some(mine) = &mut self.producers {
272+
mine.merge(&producers);
273+
} else {
274+
self.producers = Some(producers);
275+
}
276+
}
252277

253278
Ok(())
254279
}

crates/wit-component/tests/components.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ fn component_encoding_via_flags() -> Result<()> {
6363
continue;
6464
}
6565
};
66+
6667
let wat = wasmprinter::print_bytes(&bytes)?;
6768
assert_output(&wat, &component_path)?;
6869
let (doc, resolve) = match wit_component::decode("component", &bytes)? {
@@ -71,6 +72,34 @@ fn component_encoding_via_flags() -> Result<()> {
7172
};
7273
let wit = DocumentPrinter::default().print(&resolve, doc)?;
7374
assert_output(&wit, &component_wit_path)?;
75+
76+
// Check that the producer data got piped through properly
77+
let metadata = wasm_metadata::Metadata::from_binary(&bytes)?;
78+
match metadata {
79+
// Depends on the ComponentEncoder always putting the first module as the 0th child:
80+
wasm_metadata::Metadata::Component { children, .. } => match children[0].as_ref() {
81+
wasm_metadata::Metadata::Module { producers, .. } => {
82+
let producers = producers.as_ref().expect("child module has producers");
83+
let processed_by = producers
84+
.get("processed-by")
85+
.expect("child has processed-by section");
86+
assert_eq!(
87+
processed_by
88+
.get("wit-component")
89+
.expect("wit-component producer present"),
90+
env!("CARGO_PKG_VERSION")
91+
);
92+
assert_eq!(
93+
processed_by
94+
.get("my-fake-bindgen")
95+
.expect("added bindgen field present"),
96+
"123.45"
97+
);
98+
}
99+
_ => panic!("expected child to be a module"),
100+
},
101+
_ => panic!("expected top level metadata of component"),
102+
}
74103
}
75104

76105
Ok(())
@@ -100,7 +129,14 @@ fn read_core_module(path: &Path) -> Result<Vec<u8>> {
100129
&Default::default(),
101130
)?;
102131
let world = resolve.select_world(pkg, None)?;
103-
let encoded = wit_component::metadata::encode(&resolve, world, StringEncoding::UTF8)?;
132+
133+
// Add this producer data to the wit-component metadata so we can make sure it gets through the
134+
// translation:
135+
let mut producers = wasm_metadata::Producers::empty();
136+
producers.add("processed-by", "my-fake-bindgen", "123.45");
137+
138+
let encoded =
139+
wit_component::metadata::encode(&resolve, world, StringEncoding::UTF8, Some(&producers))?;
104140

105141
let section = wasm_encoder::CustomSection {
106142
name: "component-type",

fuzz/fuzz_targets/roundtrip-wit.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ fuzz_target!(|data: &[u8]| {
5454

5555
for (id, _world) in resolve.worlds.iter() {
5656
let mut dummy = wit_component::dummy_module(&resolve, id);
57-
let metadata = wit_component::metadata::encode(&resolve, id, StringEncoding::UTF8).unwrap();
57+
let metadata =
58+
wit_component::metadata::encode(&resolve, id, StringEncoding::UTF8, None).unwrap();
5859
let section = CustomSection {
5960
name: "component-type",
6061
data: &metadata,

src/bin/wasm-tools/component.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ impl EmbedOpts {
186186
&resolve,
187187
world,
188188
self.encoding.unwrap_or(StringEncoding::UTF8),
189+
None,
189190
)?;
190191

191192
let section = wasm_encoder::CustomSection {

0 commit comments

Comments
 (0)