wasmparser/readers/component/
types.rs

1use crate::limits::*;
2use crate::prelude::*;
3use crate::RecGroup;
4use crate::{
5    BinaryReader, ComponentAlias, ComponentExportName, ComponentImport, ComponentTypeRef,
6    FromReader, Import, Result, SectionLimited, TypeRef, ValType,
7};
8use core::fmt;
9
10/// Represents the kind of an outer core alias in a WebAssembly component.
11#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12pub enum OuterAliasKind {
13    /// The alias is to a core type.
14    Type,
15}
16
17/// Represents a core type in a WebAssembly component.
18#[derive(Debug, Clone, Eq, PartialEq)]
19pub enum CoreType<'a> {
20    /// The type is for a core subtype.
21    Rec(RecGroup),
22    /// The type is for a core module.
23    Module(Box<[ModuleTypeDeclaration<'a>]>),
24}
25
26impl<'a> FromReader<'a> for CoreType<'a> {
27    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
28        // For the time being, this special logic handles an ambiguous encoding
29        // in the component model: the `0x50` opcode represents both a core
30        // module type as well as a GC non-final `sub` type. To avoid this, the
31        // component model specification requires us to prefix a non-final `sub`
32        // type with `0x00` when it is used as a top-level core type of a
33        // component. Eventually (prior to the component model's v1.0 release),
34        // a module type will get a new opcode and this special logic can go
35        // away.
36        Ok(match reader.peek()? {
37            0x00 => {
38                reader.read_u8()?;
39                let x = reader.peek()?;
40                if x != 0x50 {
41                    return reader.invalid_leading_byte(x, "non-final sub type");
42                }
43                CoreType::Rec(reader.read()?)
44            }
45            0x50 => {
46                reader.read_u8()?;
47                CoreType::Module(
48                    reader
49                        .read_iter(MAX_WASM_MODULE_TYPE_DECLS, "module type declaration")?
50                        .collect::<Result<_>>()?,
51                )
52            }
53            _ => CoreType::Rec(reader.read()?),
54        })
55    }
56}
57
58/// Represents a module type declaration in a WebAssembly component.
59#[derive(Debug, Clone, Eq, PartialEq)]
60pub enum ModuleTypeDeclaration<'a> {
61    /// The module type definition is for a type.
62    Type(RecGroup),
63    /// The module type definition is for an export.
64    Export {
65        /// The name of the exported item.
66        name: &'a str,
67        /// The type reference of the export.
68        ty: TypeRef,
69    },
70    /// The module type declaration is for an outer alias.
71    OuterAlias {
72        /// The alias kind.
73        kind: OuterAliasKind,
74        /// The outward count, starting at zero for the current type.
75        count: u32,
76        /// The index of the item within the outer type.
77        index: u32,
78    },
79    /// The module type definition is for an import.
80    Import(Import<'a>),
81}
82
83impl<'a> FromReader<'a> for ModuleTypeDeclaration<'a> {
84    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
85        Ok(match reader.read_u8()? {
86            0x00 => ModuleTypeDeclaration::Import(reader.read()?),
87            0x01 => ModuleTypeDeclaration::Type(reader.read()?),
88            0x02 => {
89                let kind = match reader.read_u8()? {
90                    0x10 => OuterAliasKind::Type,
91                    x => {
92                        return reader.invalid_leading_byte(x, "outer alias kind");
93                    }
94                };
95                match reader.read_u8()? {
96                    0x01 => ModuleTypeDeclaration::OuterAlias {
97                        kind,
98                        count: reader.read()?,
99                        index: reader.read()?,
100                    },
101                    x => {
102                        return reader.invalid_leading_byte(x, "outer alias target");
103                    }
104                }
105            }
106            0x03 => ModuleTypeDeclaration::Export {
107                name: reader.read()?,
108                ty: reader.read()?,
109            },
110            x => return reader.invalid_leading_byte(x, "type definition"),
111        })
112    }
113}
114
115/// A reader for the core type section of a WebAssembly component.
116///
117/// # Examples
118/// ```
119/// use wasmparser::{CoreTypeSectionReader, BinaryReader};
120/// # let data: &[u8] = &[0x01, 0x60, 0x00, 0x00];
121/// let reader = BinaryReader::new(data, 0);
122/// let mut reader = CoreTypeSectionReader::new(reader).unwrap();
123/// for ty in reader {
124///     println!("Type {:?}", ty.expect("type"));
125/// }
126/// ```
127pub type CoreTypeSectionReader<'a> = SectionLimited<'a, CoreType<'a>>;
128
129/// Represents a value type in a WebAssembly component.
130#[derive(Debug, Clone, Copy, PartialEq, Eq)]
131pub enum ComponentValType {
132    /// The value type is a primitive type.
133    Primitive(PrimitiveValType),
134    /// The value type is a reference to a defined type.
135    Type(u32),
136}
137
138impl<'a> FromReader<'a> for ComponentValType {
139    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
140        if let Some(ty) = PrimitiveValType::from_byte(reader.peek()?) {
141            reader.read_u8()?;
142            return Ok(ComponentValType::Primitive(ty));
143        }
144
145        Ok(ComponentValType::Type(reader.read_var_s33()? as u32))
146    }
147}
148
149impl<'a> FromReader<'a> for Option<ComponentValType> {
150    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
151        match reader.read_u8()? {
152            0x0 => Ok(None),
153            0x1 => Ok(Some(reader.read()?)),
154            x => reader.invalid_leading_byte(x, "optional component value type"),
155        }
156    }
157}
158
159/// Represents a primitive value type.
160#[derive(Debug, Clone, Copy, PartialEq, Eq)]
161pub enum PrimitiveValType {
162    /// The type is a boolean.
163    Bool,
164    /// The type is a signed 8-bit integer.
165    S8,
166    /// The type is an unsigned 8-bit integer.
167    U8,
168    /// The type is a signed 16-bit integer.
169    S16,
170    /// The type is an unsigned 16-bit integer.
171    U16,
172    /// The type is a signed 32-bit integer.
173    S32,
174    /// The type is an unsigned 32-bit integer.
175    U32,
176    /// The type is a signed 64-bit integer.
177    S64,
178    /// The type is an unsigned 64-bit integer.
179    U64,
180    /// The type is a 32-bit floating point number with only one NaN.
181    F32,
182    /// The type is a 64-bit floating point number with only one NaN.
183    F64,
184    /// The type is a Unicode character.
185    Char,
186    /// The type is a string.
187    String,
188    /// The error-context type. (added with the async proposal for the component
189    /// model)
190    ErrorContext,
191}
192
193impl PrimitiveValType {
194    fn from_byte(byte: u8) -> Option<PrimitiveValType> {
195        Some(match byte {
196            0x7f => PrimitiveValType::Bool,
197            0x7e => PrimitiveValType::S8,
198            0x7d => PrimitiveValType::U8,
199            0x7c => PrimitiveValType::S16,
200            0x7b => PrimitiveValType::U16,
201            0x7a => PrimitiveValType::S32,
202            0x79 => PrimitiveValType::U32,
203            0x78 => PrimitiveValType::S64,
204            0x77 => PrimitiveValType::U64,
205            0x76 => PrimitiveValType::F32,
206            0x75 => PrimitiveValType::F64,
207            0x74 => PrimitiveValType::Char,
208            0x73 => PrimitiveValType::String,
209            0x64 => PrimitiveValType::ErrorContext,
210            _ => return None,
211        })
212    }
213
214    #[cfg(feature = "validate")]
215    pub(crate) fn contains_ptr(&self) -> bool {
216        matches!(self, Self::String)
217    }
218
219    /// Determines if primitive value type `a` is a subtype of `b`.
220    pub fn is_subtype_of(a: Self, b: Self) -> bool {
221        // Note that this intentionally diverges from the upstream specification
222        // at this time and only considers exact equality for subtyping
223        // relationships.
224        //
225        // More information can be found in the subtyping implementation for
226        // component functions.
227        a == b
228    }
229}
230
231impl fmt::Display for PrimitiveValType {
232    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233        use PrimitiveValType::*;
234        let s = match self {
235            Bool => "bool",
236            S8 => "s8",
237            U8 => "u8",
238            S16 => "s16",
239            U16 => "u16",
240            S32 => "s32",
241            U32 => "u32",
242            S64 => "s64",
243            U64 => "u64",
244            F32 => "f32",
245            F64 => "f64",
246            Char => "char",
247            String => "string",
248            ErrorContext => "error-context",
249        };
250        s.fmt(f)
251    }
252}
253
254/// Represents a type in a WebAssembly component.
255#[derive(Debug, Clone, Eq, PartialEq)]
256pub enum ComponentType<'a> {
257    /// The type is a component defined type.
258    Defined(ComponentDefinedType<'a>),
259    /// The type is a function type.
260    Func(ComponentFuncType<'a>),
261    /// The type is a component type.
262    Component(Box<[ComponentTypeDeclaration<'a>]>),
263    /// The type is an instance type.
264    Instance(Box<[InstanceTypeDeclaration<'a>]>),
265    /// The type is a fresh new resource type.
266    Resource {
267        /// The representation of this resource type in core WebAssembly.
268        rep: ValType,
269        /// An optionally-specified destructor to use for when this resource is
270        /// no longer needed.
271        dtor: Option<u32>,
272    },
273}
274
275impl<'a> FromReader<'a> for ComponentType<'a> {
276    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
277        Ok(match reader.read_u8()? {
278            0x3f => ComponentType::Resource {
279                rep: reader.read()?,
280                dtor: match reader.read_u8()? {
281                    0x00 => None,
282                    0x01 => Some(reader.read()?),
283                    b => return reader.invalid_leading_byte(b, "resource destructor"),
284                },
285            },
286            0x40 => {
287                let params = reader
288                    .read_iter(MAX_WASM_FUNCTION_PARAMS, "component function parameters")?
289                    .collect::<Result<_>>()?;
290                let result = read_resultlist(reader)?;
291                ComponentType::Func(ComponentFuncType { params, result })
292            }
293            0x41 => ComponentType::Component(
294                reader
295                    .read_iter(MAX_WASM_COMPONENT_TYPE_DECLS, "component type declaration")?
296                    .collect::<Result<_>>()?,
297            ),
298            0x42 => ComponentType::Instance(
299                reader
300                    .read_iter(MAX_WASM_INSTANCE_TYPE_DECLS, "instance type declaration")?
301                    .collect::<Result<_>>()?,
302            ),
303            x => {
304                if let Some(ty) = PrimitiveValType::from_byte(x) {
305                    ComponentType::Defined(ComponentDefinedType::Primitive(ty))
306                } else {
307                    ComponentType::Defined(ComponentDefinedType::read(reader, x)?)
308                }
309            }
310        })
311    }
312}
313
314/// Represents part of a component type declaration in a WebAssembly component.
315#[derive(Debug, Clone, Eq, PartialEq)]
316pub enum ComponentTypeDeclaration<'a> {
317    /// The component type declaration is for a core type.
318    CoreType(CoreType<'a>),
319    /// The component type declaration is for a type.
320    Type(ComponentType<'a>),
321    /// The component type declaration is for an alias.
322    Alias(ComponentAlias<'a>),
323    /// The component type declaration is for an export.
324    Export {
325        /// The name of the export.
326        name: ComponentExportName<'a>,
327        /// The type reference for the export.
328        ty: ComponentTypeRef,
329    },
330    /// The component type declaration is for an import.
331    Import(ComponentImport<'a>),
332}
333
334impl<'a> FromReader<'a> for ComponentTypeDeclaration<'a> {
335    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
336        // Component types are effectively instance types with the additional
337        // variant of imports; check for imports here or delegate to
338        // `InstanceTypeDeclaration` with the appropriate conversions.
339        if reader.peek()? == 0x03 {
340            reader.read_u8()?;
341            return Ok(ComponentTypeDeclaration::Import(reader.read()?));
342        }
343
344        Ok(match reader.read()? {
345            InstanceTypeDeclaration::CoreType(t) => ComponentTypeDeclaration::CoreType(t),
346            InstanceTypeDeclaration::Type(t) => ComponentTypeDeclaration::Type(t),
347            InstanceTypeDeclaration::Alias(a) => ComponentTypeDeclaration::Alias(a),
348            InstanceTypeDeclaration::Export { name, ty } => {
349                ComponentTypeDeclaration::Export { name, ty }
350            }
351        })
352    }
353}
354
355/// Represents an instance type declaration in a WebAssembly component.
356#[derive(Debug, Clone, Eq, PartialEq)]
357pub enum InstanceTypeDeclaration<'a> {
358    /// The component type declaration is for a core type.
359    CoreType(CoreType<'a>),
360    /// The instance type declaration is for a type.
361    Type(ComponentType<'a>),
362    /// The instance type declaration is for an alias.
363    Alias(ComponentAlias<'a>),
364    /// The instance type declaration is for an export.
365    Export {
366        /// The name of the export.
367        name: ComponentExportName<'a>,
368        /// The type reference for the export.
369        ty: ComponentTypeRef,
370    },
371}
372
373impl<'a> FromReader<'a> for InstanceTypeDeclaration<'a> {
374    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
375        Ok(match reader.read_u8()? {
376            0x00 => InstanceTypeDeclaration::CoreType(reader.read()?),
377            0x01 => InstanceTypeDeclaration::Type(reader.read()?),
378            0x02 => InstanceTypeDeclaration::Alias(reader.read()?),
379            0x04 => InstanceTypeDeclaration::Export {
380                name: reader.read()?,
381                ty: reader.read()?,
382            },
383            x => return reader.invalid_leading_byte(x, "component or instance type declaration"),
384        })
385    }
386}
387
388/// Represents a type of a function in a WebAssembly component.
389#[derive(Debug, Clone, Eq, PartialEq)]
390pub struct ComponentFuncType<'a> {
391    /// The function parameters.
392    pub params: Box<[(&'a str, ComponentValType)]>,
393    /// The function result.
394    pub result: Option<ComponentValType>,
395}
396
397pub(crate) fn read_resultlist(reader: &mut BinaryReader<'_>) -> Result<Option<ComponentValType>> {
398    match reader.read_u8()? {
399        0x00 => Ok(Some(reader.read()?)),
400        0x01 => match reader.read_u8()? {
401            0x00 => Ok(None),
402            x => return reader.invalid_leading_byte(x, "number of results"),
403        },
404        x => return reader.invalid_leading_byte(x, "component function results"),
405    }
406}
407
408/// Represents a case in a variant type.
409#[derive(Debug, Clone, PartialEq, Eq)]
410pub struct VariantCase<'a> {
411    /// The name of the variant case.
412    pub name: &'a str,
413    /// The value type of the variant case.
414    pub ty: Option<ComponentValType>,
415    /// The index of the variant case that is refined by this one.
416    pub refines: Option<u32>,
417}
418
419impl<'a> FromReader<'a> for VariantCase<'a> {
420    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
421        Ok(VariantCase {
422            name: reader.read()?,
423            ty: reader.read()?,
424            refines: match reader.read_u8()? {
425                0x0 => None,
426                0x1 => Some(reader.read_var_u32()?),
427                x => return reader.invalid_leading_byte(x, "variant case refines"),
428            },
429        })
430    }
431}
432
433/// Represents a defined type in a WebAssembly component.
434#[derive(Debug, Clone, PartialEq, Eq)]
435pub enum ComponentDefinedType<'a> {
436    /// The type is one of the primitive value types.
437    Primitive(PrimitiveValType),
438    /// The type is a record with the given fields.
439    Record(Box<[(&'a str, ComponentValType)]>),
440    /// The type is a variant with the given cases.
441    Variant(Box<[VariantCase<'a>]>),
442    /// The type is a list of the given value type.
443    List(ComponentValType),
444    /// The type is a tuple of the given value types.
445    Tuple(Box<[ComponentValType]>),
446    /// The type is flags with the given names.
447    Flags(Box<[&'a str]>),
448    /// The type is an enum with the given tags.
449    Enum(Box<[&'a str]>),
450    /// The type is an option of the given value type.
451    Option(ComponentValType),
452    /// The type is a result type.
453    Result {
454        /// The type returned for success.
455        ok: Option<ComponentValType>,
456        /// The type returned for failure.
457        err: Option<ComponentValType>,
458    },
459    /// An owned handle to a resource.
460    Own(u32),
461    /// A borrowed handle to a resource.
462    Borrow(u32),
463    /// A future type with the specified payload type.
464    Future(Option<ComponentValType>),
465    /// A stream type with the specified payload type.
466    Stream(Option<ComponentValType>),
467}
468
469impl<'a> ComponentDefinedType<'a> {
470    fn read(reader: &mut BinaryReader<'a>, byte: u8) -> Result<ComponentDefinedType<'a>> {
471        Ok(match byte {
472            0x72 => ComponentDefinedType::Record(
473                reader
474                    .read_iter(MAX_WASM_RECORD_FIELDS, "record field")?
475                    .collect::<Result<_>>()?,
476            ),
477            0x71 => ComponentDefinedType::Variant(
478                reader
479                    .read_iter(MAX_WASM_VARIANT_CASES, "variant cases")?
480                    .collect::<Result<_>>()?,
481            ),
482            0x70 => ComponentDefinedType::List(reader.read()?),
483            0x6f => ComponentDefinedType::Tuple(
484                reader
485                    .read_iter(MAX_WASM_TUPLE_TYPES, "tuple types")?
486                    .collect::<Result<_>>()?,
487            ),
488            0x6e => ComponentDefinedType::Flags(
489                reader
490                    .read_iter(MAX_WASM_FLAG_NAMES, "flag names")?
491                    .collect::<Result<_>>()?,
492            ),
493            0x6d => ComponentDefinedType::Enum(
494                reader
495                    .read_iter(MAX_WASM_ENUM_CASES, "enum cases")?
496                    .collect::<Result<_>>()?,
497            ),
498            // NOTE: 0x6c (union) removed
499            0x6b => ComponentDefinedType::Option(reader.read()?),
500            0x6a => ComponentDefinedType::Result {
501                ok: reader.read()?,
502                err: reader.read()?,
503            },
504            0x69 => ComponentDefinedType::Own(reader.read()?),
505            0x68 => ComponentDefinedType::Borrow(reader.read()?),
506            0x65 => ComponentDefinedType::Future(reader.read()?),
507            0x66 => ComponentDefinedType::Stream(reader.read()?),
508            x => return reader.invalid_leading_byte(x, "component defined type"),
509        })
510    }
511}
512
513/// A reader for the type section of a WebAssembly component.
514///
515/// # Examples
516///
517/// ```
518/// use wasmparser::{ComponentTypeSectionReader, BinaryReader};
519/// let data: &[u8] = &[0x01, 0x40, 0x01, 0x03, b'f', b'o', b'o', 0x73, 0x00, 0x73];
520/// let reader = BinaryReader::new(data, 0);
521/// let mut reader = ComponentTypeSectionReader::new(reader).unwrap();
522/// for ty in reader {
523///     println!("Type {:?}", ty.expect("type"));
524/// }
525/// ```
526pub type ComponentTypeSectionReader<'a> = SectionLimited<'a, ComponentType<'a>>;