Coverage Report

Created: 2026-08-15 07:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasm-tools/crates/wasm-encoder/src/component/imports.rs
Line
Count
Source
1
use crate::{
2
    ComponentExportKind, ComponentSection, ComponentSectionId, ComponentValType, Encode,
3
    encode_section,
4
};
5
use alloc::borrow::Cow;
6
use alloc::string::String;
7
use alloc::vec::Vec;
8
9
/// Represents the possible type bounds for type references.
10
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
11
pub enum TypeBounds {
12
    /// The type is bounded by equality to the type index specified.
13
    Eq(u32),
14
    /// This type is a fresh resource type,
15
    SubResource,
16
}
17
18
impl Encode for TypeBounds {
19
101k
    fn encode(&self, sink: &mut Vec<u8>) {
20
101k
        match self {
21
97.9k
            Self::Eq(i) => {
22
97.9k
                sink.push(0x00);
23
97.9k
                i.encode(sink);
24
97.9k
            }
25
3.94k
            Self::SubResource => sink.push(0x01),
26
        }
27
101k
    }
28
}
29
30
/// Represents a reference to a type.
31
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
32
pub enum ComponentTypeRef {
33
    /// The reference is to a core module type.
34
    ///
35
    /// The index is expected to be core type index to a core module type.
36
    Module(u32),
37
    /// The reference is to a function type.
38
    ///
39
    /// The index is expected to be a type index to a function type.
40
    Func(u32),
41
    /// The reference is to a value type.
42
    Value(ComponentValType),
43
    /// The reference is to a bounded type.
44
    Type(TypeBounds),
45
    /// The reference is to an instance type.
46
    ///
47
    /// The index is expected to be a type index to an instance type.
48
    Instance(u32),
49
    /// The reference is to a component type.
50
    ///
51
    /// The index is expected to be a type index to a component type.
52
    Component(u32),
53
}
54
55
impl ComponentTypeRef {
56
    /// Gets the export kind of the reference.
57
221k
    pub fn kind(&self) -> ComponentExportKind {
58
221k
        match self {
59
0
            Self::Module(_) => ComponentExportKind::Module,
60
43.9k
            Self::Func(_) => ComponentExportKind::Func,
61
0
            Self::Value(_) => ComponentExportKind::Value,
62
101k
            Self::Type(..) => ComponentExportKind::Type,
63
59.5k
            Self::Instance(_) => ComponentExportKind::Instance,
64
15.7k
            Self::Component(_) => ComponentExportKind::Component,
65
        }
66
221k
    }
67
}
68
69
impl Encode for ComponentTypeRef {
70
221k
    fn encode(&self, sink: &mut Vec<u8>) {
71
221k
        self.kind().encode(sink);
72
73
221k
        match self {
74
119k
            Self::Module(idx) | Self::Func(idx) | Self::Instance(idx) | Self::Component(idx) => {
75
119k
                idx.encode(sink);
76
119k
            }
77
0
            Self::Value(ty) => ty.encode(sink),
78
101k
            Self::Type(bounds) => bounds.encode(sink),
79
        }
80
221k
    }
81
}
82
83
/// An encoder for the import section of WebAssembly components.
84
///
85
/// # Example
86
///
87
/// ```rust
88
/// use wasm_encoder::{Component, ComponentTypeSection, PrimitiveValType, ComponentImportSection, ComponentTypeRef};
89
///
90
/// let mut types = ComponentTypeSection::new();
91
///
92
/// // Define a function type of `[string, string] -> string`.
93
/// types
94
///   .function()
95
///   .params(
96
///     [
97
///       ("a", PrimitiveValType::String),
98
///       ("b", PrimitiveValType::String)
99
///     ]
100
///   )
101
///   .result(Some(PrimitiveValType::String.into()));
102
///
103
/// // This imports a function named `f` with the type defined above
104
/// let mut imports = ComponentImportSection::new();
105
/// imports.import("f", ComponentTypeRef::Func(0));
106
///
107
/// let mut component = Component::new();
108
/// component.section(&types);
109
/// component.section(&imports);
110
///
111
/// let bytes = component.finish();
112
/// ```
113
#[derive(Clone, Debug, Default)]
114
pub struct ComponentImportSection {
115
    bytes: Vec<u8>,
116
    num_added: u32,
117
}
118
119
impl ComponentImportSection {
120
    /// Create a new component import section encoder.
121
4.11k
    pub fn new() -> Self {
122
4.11k
        Self::default()
123
4.11k
    }
124
125
    /// The number of imports in the section.
126
0
    pub fn len(&self) -> u32 {
127
0
        self.num_added
128
0
    }
129
130
    /// Determines if the section is empty.
131
0
    pub fn is_empty(&self) -> bool {
132
0
        self.num_added == 0
133
0
    }
134
135
    /// Define an import in the component import section.
136
5.37k
    pub fn import<'a>(
137
5.37k
        &mut self,
138
5.37k
        name: impl Into<ComponentExternName<'a>>,
139
5.37k
        ty: ComponentTypeRef,
140
5.37k
    ) -> &mut Self {
141
5.37k
        name.into().encode(&mut self.bytes);
142
5.37k
        ty.encode(&mut self.bytes);
143
5.37k
        self.num_added += 1;
144
5.37k
        self
145
5.37k
    }
<wasm_encoder::component::imports::ComponentImportSection>::import::<wasm_encoder::component::imports::ComponentExternName>
Line
Count
Source
136
5.37k
    pub fn import<'a>(
137
5.37k
        &mut self,
138
5.37k
        name: impl Into<ComponentExternName<'a>>,
139
5.37k
        ty: ComponentTypeRef,
140
5.37k
    ) -> &mut Self {
141
5.37k
        name.into().encode(&mut self.bytes);
142
5.37k
        ty.encode(&mut self.bytes);
143
5.37k
        self.num_added += 1;
144
5.37k
        self
145
5.37k
    }
Unexecuted instantiation: <wasm_encoder::component::imports::ComponentImportSection>::import::<&alloc::string::String>
Unexecuted instantiation: <wasm_encoder::component::imports::ComponentImportSection>::import::<_>
146
}
147
148
impl Encode for ComponentImportSection {
149
4.11k
    fn encode(&self, sink: &mut Vec<u8>) {
150
4.11k
        encode_section(sink, self.num_added, &self.bytes);
151
4.11k
    }
152
}
153
154
impl ComponentSection for ComponentImportSection {
155
4.11k
    fn id(&self) -> u8 {
156
4.11k
        ComponentSectionId::Import.into()
157
4.11k
    }
158
}
159
160
/// Full options for encoding a component name.
161
#[derive(Debug, Clone)]
162
pub struct ComponentExternName<'a> {
163
    /// The name to encode.
164
    pub name: Cow<'a, str>,
165
    /// An optional `(implements ...)` directive (See 🏷️ in the component model
166
    /// explainer).
167
    pub implements: Option<Cow<'a, str>>,
168
    /// An optional `(versionsuffix ...)` directive (See 🔗 in the component
169
    /// model explainer).
170
    pub version_suffix: Option<Cow<'a, str>>,
171
    /// An optional `(external-id ...)` directive (See 🏷️ in the component model
172
    /// explainer).
173
    pub external_id: Option<Cow<'a, str>>,
174
}
175
176
impl Encode for ComponentExternName<'_> {
177
288k
    fn encode(&self, bytes: &mut Vec<u8>) {
178
288k
        let mut options = Vec::new();
179
180
        let ComponentExternName {
181
            name: _,
182
288k
            implements,
183
288k
            version_suffix,
184
288k
            external_id,
185
288k
        } = self;
186
187
288k
        if let Some(s) = implements {
188
9.47k
            options.push((0x00, s.as_bytes()));
189
279k
        }
190
288k
        if let Some(s) = version_suffix {
191
0
            options.push((0x01, s.as_bytes()));
192
288k
        }
193
288k
        if let Some(s) = external_id {
194
139k
            options.push((0x02, s.as_bytes()));
195
149k
        }
196
197
288k
        if options.is_empty() {
198
148k
            // Prior to WebAssembly/component-model#263 import and export names
199
148k
            // were discriminated with a leading byte indicating what kind of
200
148k
            // import they are.  After that PR though names are always prefixed
201
148k
            // with a 0x00 byte.
202
148k
            //
203
148k
            // On 2023-10-28 in bytecodealliance/wasm-tools#1262 was landed to
204
148k
            // start transitioning to "always lead with 0x00". That updated the
205
148k
            // validator/parser to accept either 0x00 or 0x01 but the encoder
206
148k
            // wasn't updated at the time.
207
148k
            //
208
148k
            // On 2024-09-03 in bytecodealliance/wasm-tools#TODO this encoder
209
148k
            // was updated to always emit 0x00 as a leading byte.
210
148k
            //
211
148k
            // This corresponds with the `importname'` production in the
212
148k
            // specification.
213
148k
            bytes.push(0x00);
214
148k
        } else {
215
139k
            bytes.push(0x02);
216
139k
        }
217
218
288k
        self.name.encode(bytes);
219
220
288k
        if !options.is_empty() {
221
139k
            options.len().encode(bytes);
222
148k
            for (kind, val) in options {
223
148k
                bytes.push(kind);
224
148k
                val.encode(bytes);
225
148k
            }
226
148k
        }
227
288k
    }
228
}
229
230
impl<'a> From<&'a str> for ComponentExternName<'a> {
231
61.3k
    fn from(name: &'a str) -> Self {
232
61.3k
        ComponentExternName {
233
61.3k
            name: Cow::Borrowed(name),
234
61.3k
            implements: None,
235
61.3k
            external_id: None,
236
61.3k
            version_suffix: None,
237
61.3k
        }
238
61.3k
    }
239
}
240
241
impl<'a> From<&'a String> for ComponentExternName<'a> {
242
61.3k
    fn from(name: &'a String) -> Self {
243
61.3k
        ComponentExternName::from(name.as_str())
244
61.3k
    }
245
}
246
247
impl<'a> From<String> for ComponentExternName<'a> {
248
63.7k
    fn from(name: String) -> Self {
249
63.7k
        ComponentExternName {
250
63.7k
            name: Cow::Owned(name),
251
63.7k
            implements: None,
252
63.7k
            external_id: None,
253
63.7k
            version_suffix: None,
254
63.7k
        }
255
63.7k
    }
256
}
257
258
#[cfg(feature = "wasmparser")]
259
impl<'a> From<wasmparser::ComponentExternName<'a>> for ComponentExternName<'a> {
260
0
    fn from(name: wasmparser::ComponentExternName<'a>) -> Self {
261
        let wasmparser::ComponentExternName {
262
0
            name,
263
0
            implements,
264
0
            external_id,
265
0
            version_suffix,
266
0
        } = name;
267
        ComponentExternName {
268
0
            name: name.into(),
269
0
            implements: implements.map(|s| s.into()),
270
0
            external_id: external_id.map(|s| s.into()),
271
0
            version_suffix: version_suffix.map(|s| s.into()),
272
        }
273
0
    }
274
}