Coverage Report

Created: 2025-12-04 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}