Skip to main content

wasm_encoder/component/
imports.rs

1use crate::{
2    ComponentExportKind, ComponentSection, ComponentSectionId, ComponentValType, Encode,
3    encode_section,
4};
5use alloc::borrow::Cow;
6use alloc::string::String;
7use alloc::vec::Vec;
8
9/// Represents the possible type bounds for type references.
10#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
11pub 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
18impl Encode for TypeBounds {
19    fn encode(&self, sink: &mut Vec<u8>) {
20        match self {
21            Self::Eq(i) => {
22                sink.push(0x00);
23                i.encode(sink);
24            }
25            Self::SubResource => sink.push(0x01),
26        }
27    }
28}
29
30/// Represents a reference to a type.
31#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
32pub 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
55impl ComponentTypeRef {
56    /// Gets the export kind of the reference.
57    pub fn kind(&self) -> ComponentExportKind {
58        match self {
59            Self::Module(_) => ComponentExportKind::Module,
60            Self::Func(_) => ComponentExportKind::Func,
61            Self::Value(_) => ComponentExportKind::Value,
62            Self::Type(..) => ComponentExportKind::Type,
63            Self::Instance(_) => ComponentExportKind::Instance,
64            Self::Component(_) => ComponentExportKind::Component,
65        }
66    }
67}
68
69impl Encode for ComponentTypeRef {
70    fn encode(&self, sink: &mut Vec<u8>) {
71        self.kind().encode(sink);
72
73        match self {
74            Self::Module(idx) | Self::Func(idx) | Self::Instance(idx) | Self::Component(idx) => {
75                idx.encode(sink);
76            }
77            Self::Value(ty) => ty.encode(sink),
78            Self::Type(bounds) => bounds.encode(sink),
79        }
80    }
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)]
114pub struct ComponentImportSection {
115    bytes: Vec<u8>,
116    num_added: u32,
117}
118
119impl ComponentImportSection {
120    /// Create a new component import section encoder.
121    pub fn new() -> Self {
122        Self::default()
123    }
124
125    /// The number of imports in the section.
126    pub fn len(&self) -> u32 {
127        self.num_added
128    }
129
130    /// Determines if the section is empty.
131    pub fn is_empty(&self) -> bool {
132        self.num_added == 0
133    }
134
135    /// Define an import in the component import section.
136    pub fn import<'a>(
137        &mut self,
138        name: impl Into<ComponentExternName<'a>>,
139        ty: ComponentTypeRef,
140    ) -> &mut Self {
141        name.into().encode(&mut self.bytes);
142        ty.encode(&mut self.bytes);
143        self.num_added += 1;
144        self
145    }
146}
147
148impl Encode for ComponentImportSection {
149    fn encode(&self, sink: &mut Vec<u8>) {
150        encode_section(sink, self.num_added, &self.bytes);
151    }
152}
153
154impl ComponentSection for ComponentImportSection {
155    fn id(&self) -> u8 {
156        ComponentSectionId::Import.into()
157    }
158}
159
160/// Full options for encoding a component name.
161#[derive(Debug, Clone)]
162pub 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
176impl Encode for ComponentExternName<'_> {
177    fn encode(&self, bytes: &mut Vec<u8>) {
178        let mut options = Vec::new();
179
180        let ComponentExternName {
181            name: _,
182            implements,
183            version_suffix,
184            external_id,
185        } = self;
186
187        if let Some(s) = implements {
188            options.push((0x00, s.as_bytes()));
189        }
190        if let Some(s) = version_suffix {
191            options.push((0x01, s.as_bytes()));
192        }
193        if let Some(s) = external_id {
194            options.push((0x02, s.as_bytes()));
195        }
196
197        if options.is_empty() {
198            // Prior to WebAssembly/component-model#263 import and export names
199            // were discriminated with a leading byte indicating what kind of
200            // import they are.  After that PR though names are always prefixed
201            // with a 0x00 byte.
202            //
203            // On 2023-10-28 in bytecodealliance/wasm-tools#1262 was landed to
204            // start transitioning to "always lead with 0x00". That updated the
205            // validator/parser to accept either 0x00 or 0x01 but the encoder
206            // wasn't updated at the time.
207            //
208            // On 2024-09-03 in bytecodealliance/wasm-tools#TODO this encoder
209            // was updated to always emit 0x00 as a leading byte.
210            //
211            // This corresponds with the `importname'` production in the
212            // specification.
213            bytes.push(0x00);
214        } else {
215            bytes.push(0x02);
216        }
217
218        self.name.encode(bytes);
219
220        if !options.is_empty() {
221            options.len().encode(bytes);
222            for (kind, val) in options {
223                bytes.push(kind);
224                val.encode(bytes);
225            }
226        }
227    }
228}
229
230impl<'a> From<&'a str> for ComponentExternName<'a> {
231    fn from(name: &'a str) -> Self {
232        ComponentExternName {
233            name: Cow::Borrowed(name),
234            implements: None,
235            external_id: None,
236            version_suffix: None,
237        }
238    }
239}
240
241impl<'a> From<&'a String> for ComponentExternName<'a> {
242    fn from(name: &'a String) -> Self {
243        ComponentExternName::from(name.as_str())
244    }
245}
246
247impl<'a> From<String> for ComponentExternName<'a> {
248    fn from(name: String) -> Self {
249        ComponentExternName {
250            name: Cow::Owned(name),
251            implements: None,
252            external_id: None,
253            version_suffix: None,
254        }
255    }
256}
257
258#[cfg(feature = "wasmparser")]
259impl<'a> From<wasmparser::ComponentExternName<'a>> for ComponentExternName<'a> {
260    fn from(name: wasmparser::ComponentExternName<'a>) -> Self {
261        let wasmparser::ComponentExternName {
262            name,
263            implements,
264            external_id,
265            version_suffix,
266        } = name;
267        ComponentExternName {
268            name: name.into(),
269            implements: implements.map(|s| s.into()),
270            external_id: external_id.map(|s| s.into()),
271            version_suffix: version_suffix.map(|s| s.into()),
272        }
273    }
274}