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}
169
170impl Encode for ComponentExternName<'_> {
171    fn encode(&self, bytes: &mut Vec<u8>) {
172        let mut options = Vec::new();
173
174        if let Some(s) = &self.implements {
175            options.push((0x00, s.as_bytes()));
176        }
177
178        if options.is_empty() {
179            // Prior to WebAssembly/component-model#263 import and export names
180            // were discriminated with a leading byte indicating what kind of
181            // import they are.  After that PR though names are always prefixed
182            // with a 0x00 byte.
183            //
184            // On 2023-10-28 in bytecodealliance/wasm-tools#1262 was landed to
185            // start transitioning to "always lead with 0x00". That updated the
186            // validator/parser to accept either 0x00 or 0x01 but the encoder
187            // wasn't updated at the time.
188            //
189            // On 2024-09-03 in bytecodealliance/wasm-tools#TODO this encoder
190            // was updated to always emit 0x00 as a leading byte.
191            //
192            // This corresponds with the `importname'` production in the
193            // specification.
194            bytes.push(0x00);
195        } else {
196            bytes.push(0x02);
197        }
198
199        self.name.encode(bytes);
200
201        if !options.is_empty() {
202            options.len().encode(bytes);
203            for (kind, val) in options {
204                bytes.push(kind);
205                val.encode(bytes);
206            }
207        }
208    }
209}
210
211impl<'a> From<&'a str> for ComponentExternName<'a> {
212    fn from(name: &'a str) -> Self {
213        ComponentExternName {
214            name: Cow::Borrowed(name),
215            implements: None,
216        }
217    }
218}
219
220impl<'a> From<&'a String> for ComponentExternName<'a> {
221    fn from(name: &'a String) -> Self {
222        ComponentExternName {
223            name: Cow::Borrowed(name),
224            implements: None,
225        }
226    }
227}
228
229impl<'a> From<String> for ComponentExternName<'a> {
230    fn from(name: String) -> Self {
231        ComponentExternName {
232            name: Cow::Owned(name),
233            implements: None,
234        }
235    }
236}
237
238#[cfg(feature = "wasmparser")]
239impl<'a> From<wasmparser::ComponentExternName<'a>> for ComponentExternName<'a> {
240    fn from(name: wasmparser::ComponentExternName<'a>) -> Self {
241        let wasmparser::ComponentExternName { name, implements } = name;
242        ComponentExternName {
243            name: name.into(),
244            implements: implements.map(|s| s.into()),
245        }
246    }
247}