Coverage Report

Created: 2026-09-14 07:40

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
202k
    fn encode(&self, sink: &mut Vec<u8>) {
20
202k
        match self {
21
194k
            Self::Eq(i) => {
22
194k
                sink.push(0x00);
23
194k
                i.encode(sink);
24
194k
            }
25
8.24k
            Self::SubResource => sink.push(0x01),
26
        }
27
202k
    }
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
401k
    pub fn kind(&self) -> ComponentExportKind {
58
401k
        match self {
59
0
            Self::Module(_) => ComponentExportKind::Module,
60
80.9k
            Self::Func(_) => ComponentExportKind::Func,
61
0
            Self::Value(_) => ComponentExportKind::Value,
62
202k
            Self::Type(..) => ComponentExportKind::Type,
63
95.0k
            Self::Instance(_) => ComponentExportKind::Instance,
64
23.6k
            Self::Component(_) => ComponentExportKind::Component,
65
        }
66
401k
    }
67
}
68
69
impl Encode for ComponentTypeRef {
70
401k
    fn encode(&self, sink: &mut Vec<u8>) {
71
401k
        self.kind().encode(sink);
72
73
401k
        match self {
74
199k
            Self::Module(idx) | Self::Func(idx) | Self::Instance(idx) | Self::Component(idx) => {
75
199k
                idx.encode(sink);
76
199k
            }
77
0
            Self::Value(ty) => ty.encode(sink),
78
202k
            Self::Type(bounds) => bounds.encode(sink),
79
        }
80
401k
    }
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
8.47k
    pub fn new() -> Self {
122
8.47k
        Self::default()
123
8.47k
    }
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
10.5k
    pub fn import<'a>(
137
10.5k
        &mut self,
138
10.5k
        name: impl Into<ComponentExternName<'a>>,
139
10.5k
        ty: ComponentTypeRef,
140
10.5k
    ) -> &mut Self {
141
10.5k
        name.into().encode(&mut self.bytes);
142
10.5k
        ty.encode(&mut self.bytes);
143
10.5k
        self.num_added += 1;
144
10.5k
        self
145
10.5k
    }
<wasm_encoder::component::imports::ComponentImportSection>::import::<wasm_encoder::component::imports::ComponentExternName>
Line
Count
Source
136
10.5k
    pub fn import<'a>(
137
10.5k
        &mut self,
138
10.5k
        name: impl Into<ComponentExternName<'a>>,
139
10.5k
        ty: ComponentTypeRef,
140
10.5k
    ) -> &mut Self {
141
10.5k
        name.into().encode(&mut self.bytes);
142
10.5k
        ty.encode(&mut self.bytes);
143
10.5k
        self.num_added += 1;
144
10.5k
        self
145
10.5k
    }
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
8.47k
    fn encode(&self, sink: &mut Vec<u8>) {
150
8.47k
        encode_section(sink, self.num_added, &self.bytes);
151
8.47k
    }
152
}
153
154
impl ComponentSection for ComponentImportSection {
155
8.47k
    fn id(&self) -> u8 {
156
8.47k
        ComponentSectionId::Import.into()
157
8.47k
    }
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
507k
    fn encode(&self, bytes: &mut Vec<u8>) {
178
507k
        let mut options = Vec::new();
179
180
        let ComponentExternName {
181
            name: _,
182
507k
            implements,
183
507k
            version_suffix,
184
507k
            external_id,
185
507k
        } = self;
186
187
507k
        if let Some(s) = implements {
188
22.2k
            options.push((0x00, s.as_bytes()));
189
485k
        }
190
507k
        if let Some(s) = version_suffix {
191
0
            options.push((0x01, s.as_bytes()));
192
507k
        }
193
507k
        if let Some(s) = external_id {
194
274k
            options.push((0x02, s.as_bytes()));
195
274k
        }
196
197
507k
        if options.is_empty() {
198
231k
            // Prior to WebAssembly/component-model#263 import and export names
199
231k
            // were discriminated with a leading byte indicating what kind of
200
231k
            // import they are.  After that PR though names are always prefixed
201
231k
            // with a 0x00 byte.
202
231k
            //
203
231k
            // On 2023-10-28 in bytecodealliance/wasm-tools#1262 was landed to
204
231k
            // start transitioning to "always lead with 0x00". That updated the
205
231k
            // validator/parser to accept either 0x00 or 0x01 but the encoder
206
231k
            // wasn't updated at the time.
207
231k
            //
208
231k
            // On 2024-09-03 in bytecodealliance/wasm-tools#TODO this encoder
209
231k
            // was updated to always emit 0x00 as a leading byte.
210
231k
            //
211
231k
            // This corresponds with the `importname'` production in the
212
231k
            // specification.
213
231k
            bytes.push(0x00);
214
275k
        } else {
215
275k
            bytes.push(0x02);
216
275k
        }
217
218
507k
        self.name.encode(bytes);
219
220
507k
        if !options.is_empty() {
221
275k
            options.len().encode(bytes);
222
296k
            for (kind, val) in options {
223
296k
                bytes.push(kind);
224
296k
                val.encode(bytes);
225
296k
            }
226
231k
        }
227
507k
    }
228
}
229
230
impl<'a> From<&'a str> for ComponentExternName<'a> {
231
90.1k
    fn from(name: &'a str) -> Self {
232
90.1k
        ComponentExternName {
233
90.1k
            name: Cow::Borrowed(name),
234
90.1k
            implements: None,
235
90.1k
            external_id: None,
236
90.1k
            version_suffix: None,
237
90.1k
        }
238
90.1k
    }
239
}
240
241
impl<'a> From<&'a String> for ComponentExternName<'a> {
242
90.1k
    fn from(name: &'a String) -> Self {
243
90.1k
        ComponentExternName::from(name.as_str())
244
90.1k
    }
245
}
246
247
impl<'a> From<String> for ComponentExternName<'a> {
248
92.8k
    fn from(name: String) -> Self {
249
92.8k
        ComponentExternName {
250
92.8k
            name: Cow::Owned(name),
251
92.8k
            implements: None,
252
92.8k
            external_id: None,
253
92.8k
            version_suffix: None,
254
92.8k
        }
255
92.8k
    }
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
}