/src/wasm-tools/crates/wit-component/src/lib.rs
Line | Count | Source |
1 | | //! The WebAssembly component tooling. |
2 | | |
3 | | #![deny(missing_docs)] |
4 | | #![cfg_attr(docsrs, feature(doc_cfg))] |
5 | | |
6 | | use std::str::FromStr; |
7 | | use std::{borrow::Cow, fmt::Display}; |
8 | | |
9 | | use anyhow::{Result, bail}; |
10 | | use wasm_encoder::{CanonicalOption, Encode, Section}; |
11 | | use wit_parser::{Resolve, WorldId}; |
12 | | |
13 | | mod encoding; |
14 | | mod gc; |
15 | | mod linking; |
16 | | mod printing; |
17 | | mod targets; |
18 | | mod validation; |
19 | | |
20 | | pub use encoding::{ComponentEncoder, LibraryInfo, encode}; |
21 | | pub use linking::Linker; |
22 | | pub use printing::*; |
23 | | pub use targets::*; |
24 | | pub use validation::AdapterModuleDidNotExport; |
25 | | pub use wit_parser::decoding::{DecodedWasm, decode, decode_reader}; |
26 | | |
27 | | pub mod metadata; |
28 | | |
29 | | #[cfg(feature = "dummy-module")] |
30 | | pub use dummy::dummy_module; |
31 | | #[cfg(feature = "dummy-module")] |
32 | | mod dummy; |
33 | | |
34 | | #[cfg(feature = "semver-check")] |
35 | | mod semver_check; |
36 | | #[cfg(feature = "semver-check")] |
37 | | pub use semver_check::*; |
38 | | |
39 | | /// Supported string encoding formats. |
40 | | #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)] |
41 | | pub enum StringEncoding { |
42 | | /// Strings are encoded with UTF-8. |
43 | | #[default] |
44 | | UTF8, |
45 | | /// Strings are encoded with UTF-16. |
46 | | UTF16, |
47 | | /// Strings are encoded with compact UTF-16 (i.e. Latin1+UTF-16). |
48 | | CompactUTF16, |
49 | | } |
50 | | |
51 | | impl Display for StringEncoding { |
52 | 0 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
53 | 0 | match self { |
54 | 0 | StringEncoding::UTF8 => write!(f, "utf8"), |
55 | 0 | StringEncoding::UTF16 => write!(f, "utf16"), |
56 | 0 | StringEncoding::CompactUTF16 => write!(f, "compact-utf16"), |
57 | | } |
58 | 0 | } |
59 | | } |
60 | | |
61 | | impl FromStr for StringEncoding { |
62 | | type Err = anyhow::Error; |
63 | | |
64 | 0 | fn from_str(s: &str) -> Result<Self> { |
65 | 0 | match s { |
66 | 0 | "utf8" => Ok(StringEncoding::UTF8), |
67 | 0 | "utf16" => Ok(StringEncoding::UTF16), |
68 | 0 | "compact-utf16" => Ok(StringEncoding::CompactUTF16), |
69 | 0 | _ => bail!("unknown string encoding `{}`", s), |
70 | | } |
71 | 0 | } |
72 | | } |
73 | | |
74 | | impl From<StringEncoding> for wasm_encoder::CanonicalOption { |
75 | 132 | fn from(e: StringEncoding) -> wasm_encoder::CanonicalOption { |
76 | 132 | match e { |
77 | 132 | StringEncoding::UTF8 => CanonicalOption::UTF8, |
78 | 0 | StringEncoding::UTF16 => CanonicalOption::UTF16, |
79 | 0 | StringEncoding::CompactUTF16 => CanonicalOption::CompactUTF16, |
80 | | } |
81 | 132 | } |
82 | | } |
83 | | |
84 | | /// A producer section to be added to all modules and components synthesized by |
85 | | /// this crate |
86 | 22.2k | pub(crate) fn base_producers() -> wasm_metadata::Producers { |
87 | 22.2k | let mut producer = wasm_metadata::Producers::empty(); |
88 | 22.2k | producer.add("processed-by", "wit-component", env!("CARGO_PKG_VERSION")); |
89 | 22.2k | producer |
90 | 22.2k | } |
91 | | |
92 | | /// Embed component metadata in a buffer of bytes that contains a Wasm module |
93 | 4.07k | pub fn embed_component_metadata( |
94 | 4.07k | bytes: &mut Vec<u8>, |
95 | 4.07k | wit_resolver: &Resolve, |
96 | 4.07k | world: WorldId, |
97 | 4.07k | encoding: StringEncoding, |
98 | 4.07k | ) -> Result<()> { |
99 | 4.07k | let encoded = metadata::encode(&wit_resolver, world, encoding, None)?; |
100 | | |
101 | 4.07k | let section = wasm_encoder::CustomSection { |
102 | 4.07k | name: "component-type".into(), |
103 | 4.07k | data: Cow::Borrowed(&encoded), |
104 | 4.07k | }; |
105 | 4.07k | bytes.push(section.id()); |
106 | 4.07k | section.encode(bytes); |
107 | | |
108 | 4.07k | Ok(()) |
109 | 4.07k | } |
110 | | |
111 | | #[cfg(test)] |
112 | | mod tests { |
113 | | use anyhow::Result; |
114 | | use wasmparser::Payload; |
115 | | use wit_parser::Resolve; |
116 | | |
117 | | use super::{StringEncoding, embed_component_metadata}; |
118 | | |
119 | | const MODULE_WAT: &str = r#" |
120 | | (module |
121 | | (type (;0;) (func)) |
122 | | (func (;0;) (type 0) |
123 | | nop |
124 | | ) |
125 | | ) |
126 | | "#; |
127 | | |
128 | | const COMPONENT_WIT: &str = r#" |
129 | | package test:foo; |
130 | | world test-world {} |
131 | | "#; |
132 | | |
133 | | #[test] |
134 | | fn component_metadata_embedding_works() -> Result<()> { |
135 | | let mut bytes = wat::parse_str(MODULE_WAT)?; |
136 | | |
137 | | // Get original len & custom section count |
138 | | let original_len = bytes.len(); |
139 | | let payloads = wasmparser::Parser::new(0).parse_all(&bytes); |
140 | | let original_custom_section_count = payloads.fold(0, |acc, payload| { |
141 | | if let Ok(Payload::CustomSection { .. }) = payload { |
142 | | acc + 1 |
143 | | } else { |
144 | | acc |
145 | | } |
146 | | }); |
147 | | |
148 | | // Parse pre-canned WIT to build resolver |
149 | | let mut resolver = Resolve::default(); |
150 | | let pkg = resolver.push_str("in-code.wit", COMPONENT_WIT)?; |
151 | | let world = resolver.select_world(&[pkg], Some("test-world"))?; |
152 | | |
153 | | // Embed component metadata |
154 | | embed_component_metadata(&mut bytes, &resolver, world, StringEncoding::UTF8)?; |
155 | | |
156 | | // Re-retrieve custom section count, and search for the component-type custom section along the way |
157 | | let mut found_component_section = false; |
158 | | let new_custom_section_count = |
159 | | wasmparser::Parser::new(0) |
160 | | .parse_all(&bytes) |
161 | | .fold(0, |acc, payload| { |
162 | | if let Ok(Payload::CustomSection(reader)) = payload { |
163 | | if reader.name() == "component-type" { |
164 | | found_component_section = true; |
165 | | } |
166 | | acc + 1 |
167 | | } else { |
168 | | acc |
169 | | } |
170 | | }); |
171 | | |
172 | | assert!(original_len < bytes.len()); |
173 | | assert_eq!(original_custom_section_count + 1, new_custom_section_count); |
174 | | assert!(found_component_section); |
175 | | |
176 | | Ok(()) |
177 | | } |
178 | | } |