wasmparser/readers/component/
types.rs

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