wasm_encoder/core/
types.rs

1use crate::{Encode, Section, SectionId, encode_section};
2use alloc::boxed::Box;
3use alloc::vec::Vec;
4
5/// Represents a subtype of possible other types in a WebAssembly module.
6#[derive(Debug, Clone)]
7pub struct SubType {
8    /// Is the subtype final.
9    pub is_final: bool,
10    /// The list of supertype indexes. As of GC MVP, there can be at most one
11    /// supertype.
12    pub supertype_idx: Option<u32>,
13    /// The composite type of the subtype.
14    pub composite_type: CompositeType,
15}
16
17/// Represents a composite type in a WebAssembly module.
18#[derive(Debug, Clone)]
19pub struct CompositeType {
20    /// The type defined inside the composite type.
21    pub inner: CompositeInnerType,
22    /// Whether the type is shared. This is part of the
23    /// shared-everything-threads proposal.
24    pub shared: bool,
25}
26
27/// A [`CompositeType`] can contain one of these types.
28#[derive(Debug, Clone)]
29pub enum CompositeInnerType {
30    /// The type is for a function.
31    Func(FuncType),
32    /// The type is for an array.
33    Array(ArrayType),
34    /// The type is for a struct.
35    Struct(StructType),
36    /// The type is for a continuation.
37    Cont(ContType),
38}
39
40/// Represents a type of a function in a WebAssembly module.
41#[derive(Debug, Clone, Eq, PartialEq, Hash)]
42pub struct FuncType {
43    /// The combined parameters and result types.
44    params_results: Box<[ValType]>,
45    /// The number of parameter types.
46    len_params: usize,
47}
48
49/// Represents a type of an array in a WebAssembly module.
50#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
51pub struct ArrayType(pub FieldType);
52
53/// Represents a type of a struct in a WebAssembly module.
54#[derive(Debug, Clone, Eq, PartialEq, Hash)]
55pub struct StructType {
56    /// Struct fields.
57    pub fields: Box<[FieldType]>,
58}
59
60/// Field type in composite types (structs, arrays).
61#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
62pub struct FieldType {
63    /// Storage type of the field.
64    pub element_type: StorageType,
65    /// Is the field mutable.
66    pub mutable: bool,
67}
68
69/// Storage type for composite type fields.
70#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
71pub enum StorageType {
72    /// The `i8` type.
73    I8,
74    /// The `i16` type.
75    I16,
76    /// A value type.
77    Val(ValType),
78}
79
80impl StorageType {
81    /// Is this storage type defaultable?
82    pub fn is_defaultable(&self) -> bool {
83        self.unpack().is_defaultable()
84    }
85
86    /// Unpack this storage type into a value type.
87    pub fn unpack(&self) -> ValType {
88        match self {
89            StorageType::I8 | StorageType::I16 => ValType::I32,
90            StorageType::Val(v) => *v,
91        }
92    }
93}
94
95/// Represents a type of a continuation in a WebAssembly module.
96#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
97pub struct ContType(pub u32);
98
99/// The type of a core WebAssembly value.
100#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
101pub enum ValType {
102    /// The `i32` type.
103    I32,
104    /// The `i64` type.
105    I64,
106    /// The `f32` type.
107    F32,
108    /// The `f64` type.
109    F64,
110    /// The `v128` type.
111    ///
112    /// Part of the SIMD proposal.
113    V128,
114    /// A reference type.
115    ///
116    /// The `funcref` and `externref` type fall into this category and the full
117    /// generalization here is due to the implementation of the
118    /// function-references proposal.
119    Ref(RefType),
120}
121
122impl ValType {
123    /// Is this a numeric value type?
124    pub fn is_numeric(&self) -> bool {
125        match self {
126            ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 => true,
127            ValType::V128 | ValType::Ref(_) => false,
128        }
129    }
130
131    /// Is this a vector type?
132    pub fn is_vector(&self) -> bool {
133        match self {
134            ValType::V128 => true,
135            ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::Ref(_) => false,
136        }
137    }
138
139    /// Is this a reference type?
140    pub fn is_reference(&self) -> bool {
141        match self {
142            ValType::Ref(_) => true,
143            ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => false,
144        }
145    }
146}
147
148impl FuncType {
149    /// Creates a new [`FuncType`] from the given `params` and `results`.
150    pub fn new<P, R>(params: P, results: R) -> Self
151    where
152        P: IntoIterator<Item = ValType>,
153        R: IntoIterator<Item = ValType>,
154    {
155        let mut buffer = params.into_iter().collect::<Vec<_>>();
156        let len_params = buffer.len();
157        buffer.extend(results);
158        Self::from_parts(buffer.into(), len_params)
159    }
160
161    #[inline]
162    pub(crate) fn from_parts(params_results: Box<[ValType]>, len_params: usize) -> Self {
163        Self {
164            params_results,
165            len_params,
166        }
167    }
168
169    /// Returns a shared slice to the parameter types of the [`FuncType`].
170    #[inline]
171    pub fn params(&self) -> &[ValType] {
172        &self.params_results[..self.len_params]
173    }
174
175    /// Returns a shared slice to the result types of the [`FuncType`].
176    #[inline]
177    pub fn results(&self) -> &[ValType] {
178        &self.params_results[self.len_params..]
179    }
180}
181
182impl ValType {
183    /// Alias for the `funcref` type in WebAssembly
184    pub const FUNCREF: ValType = ValType::Ref(RefType::FUNCREF);
185    /// Alias for the `externref` type in WebAssembly
186    pub const EXTERNREF: ValType = ValType::Ref(RefType::EXTERNREF);
187    /// Alias for the `exnref` type in WebAssembly
188    pub const EXNREF: ValType = ValType::Ref(RefType::EXNREF);
189
190    /// Is this value defaultable?
191    pub fn is_defaultable(&self) -> bool {
192        match self {
193            ValType::Ref(r) => r.nullable,
194            ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => true,
195        }
196    }
197}
198
199impl Encode for StorageType {
200    fn encode(&self, sink: &mut Vec<u8>) {
201        match self {
202            StorageType::I8 => sink.push(0x78),
203            StorageType::I16 => sink.push(0x77),
204            StorageType::Val(vt) => vt.encode(sink),
205        }
206    }
207}
208
209impl Encode for ValType {
210    fn encode(&self, sink: &mut Vec<u8>) {
211        match self {
212            ValType::I32 => sink.push(0x7F),
213            ValType::I64 => sink.push(0x7E),
214            ValType::F32 => sink.push(0x7D),
215            ValType::F64 => sink.push(0x7C),
216            ValType::V128 => sink.push(0x7B),
217            ValType::Ref(rt) => rt.encode(sink),
218        }
219    }
220}
221
222/// A reference type.
223///
224/// This is largely part of the function references proposal for WebAssembly but
225/// additionally is used by the `funcref` and `externref` types. The full
226/// generality of this type is only exercised with function-references.
227#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
228#[allow(missing_docs)]
229pub struct RefType {
230    pub nullable: bool,
231    pub heap_type: HeapType,
232}
233
234impl RefType {
235    /// Alias for the `anyref` type in WebAssembly.
236    pub const ANYREF: RefType = RefType {
237        nullable: true,
238        heap_type: HeapType::Abstract {
239            shared: false,
240            ty: AbstractHeapType::Any,
241        },
242    };
243
244    /// Alias for the `anyref` type in WebAssembly.
245    pub const EQREF: RefType = RefType {
246        nullable: true,
247        heap_type: HeapType::Abstract {
248            shared: false,
249            ty: AbstractHeapType::Eq,
250        },
251    };
252
253    /// Alias for the `funcref` type in WebAssembly.
254    pub const FUNCREF: RefType = RefType {
255        nullable: true,
256        heap_type: HeapType::Abstract {
257            shared: false,
258            ty: AbstractHeapType::Func,
259        },
260    };
261
262    /// Alias for the `externref` type in WebAssembly.
263    pub const EXTERNREF: RefType = RefType {
264        nullable: true,
265        heap_type: HeapType::Abstract {
266            shared: false,
267            ty: AbstractHeapType::Extern,
268        },
269    };
270
271    /// Alias for the `i31ref` type in WebAssembly.
272    pub const I31REF: RefType = RefType {
273        nullable: true,
274        heap_type: HeapType::Abstract {
275            shared: false,
276            ty: AbstractHeapType::I31,
277        },
278    };
279
280    /// Alias for the `arrayref` type in WebAssembly.
281    pub const ARRAYREF: RefType = RefType {
282        nullable: true,
283        heap_type: HeapType::Abstract {
284            shared: false,
285            ty: AbstractHeapType::Array,
286        },
287    };
288
289    /// Alias for the `exnref` type in WebAssembly.
290    pub const EXNREF: RefType = RefType {
291        nullable: true,
292        heap_type: HeapType::Abstract {
293            shared: false,
294            ty: AbstractHeapType::Exn,
295        },
296    };
297
298    /// Create a new abstract reference type.
299    pub fn new_abstract(ty: AbstractHeapType, nullable: bool, shared: bool) -> Self {
300        Self {
301            nullable,
302            heap_type: HeapType::Abstract { shared, ty },
303        }
304    }
305
306    /// Set the nullability of this reference type.
307    pub fn nullable(mut self, nullable: bool) -> Self {
308        self.nullable = nullable;
309        self
310    }
311}
312
313impl Encode for RefType {
314    fn encode(&self, sink: &mut Vec<u8>) {
315        match self {
316            // Binary abbreviations (i.e., short form), for when the ref is
317            // nullable.
318            RefType {
319                nullable: true,
320                heap_type: heap @ HeapType::Abstract { .. },
321            } => {
322                heap.encode(sink);
323            }
324
325            // Generic 'ref null <heaptype>' encoding (i.e., long form).
326            RefType {
327                nullable: true,
328                heap_type,
329            } => {
330                sink.push(0x63);
331                heap_type.encode(sink);
332            }
333
334            // Generic 'ref <heaptype>' encoding.
335            RefType {
336                nullable: false,
337                heap_type,
338            } => {
339                sink.push(0x64);
340                heap_type.encode(sink);
341            }
342        }
343    }
344}
345
346impl From<RefType> for ValType {
347    fn from(ty: RefType) -> ValType {
348        ValType::Ref(ty)
349    }
350}
351
352/// Part of the function references proposal.
353#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
354pub enum HeapType {
355    /// An abstract heap type; e.g., `anyref`.
356    Abstract {
357        /// Whether the type is shared.
358        shared: bool,
359        /// The actual heap type.
360        ty: AbstractHeapType,
361    },
362
363    /// A concrete Wasm-defined type at the given index.
364    Concrete(u32),
365}
366
367impl HeapType {
368    /// Alias for the unshared `any` heap type.
369    pub const ANY: Self = Self::Abstract {
370        shared: false,
371        ty: AbstractHeapType::Any,
372    };
373
374    /// Alias for the unshared `func` heap type.
375    pub const FUNC: Self = Self::Abstract {
376        shared: false,
377        ty: AbstractHeapType::Func,
378    };
379
380    /// Alias for the unshared `extern` heap type.
381    pub const EXTERN: Self = Self::Abstract {
382        shared: false,
383        ty: AbstractHeapType::Extern,
384    };
385
386    /// Alias for the unshared `i31` heap type.
387    pub const I31: Self = Self::Abstract {
388        shared: false,
389        ty: AbstractHeapType::I31,
390    };
391}
392
393impl Encode for HeapType {
394    fn encode(&self, sink: &mut Vec<u8>) {
395        match self {
396            HeapType::Abstract { shared, ty } => {
397                if *shared {
398                    sink.push(0x65);
399                }
400                ty.encode(sink);
401            }
402            // Note that this is encoded as a signed type rather than unsigned
403            // as it's decoded as an s33
404            HeapType::Concrete(i) => i64::from(*i).encode(sink),
405        }
406    }
407}
408
409/// An abstract heap type.
410#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
411pub enum AbstractHeapType {
412    /// Untyped (any) function.
413    Func,
414
415    /// The abstract external heap type.
416    Extern,
417
418    /// The abstract `any` heap type.
419    ///
420    /// The common supertype (a.k.a. top) of all internal types.
421    Any,
422
423    /// The abstract `none` heap type.
424    ///
425    /// The common subtype (a.k.a. bottom) of all internal types.
426    None,
427
428    /// The abstract `noextern` heap type.
429    ///
430    /// The common subtype (a.k.a. bottom) of all external types.
431    NoExtern,
432
433    /// The abstract `nofunc` heap type.
434    ///
435    /// The common subtype (a.k.a. bottom) of all function types.
436    NoFunc,
437
438    /// The abstract `eq` heap type.
439    ///
440    /// The common supertype of all referenceable types on which comparison
441    /// (ref.eq) is allowed.
442    Eq,
443
444    /// The abstract `struct` heap type.
445    ///
446    /// The common supertype of all struct types.
447    Struct,
448
449    /// The abstract `array` heap type.
450    ///
451    /// The common supertype of all array types.
452    Array,
453
454    /// The unboxed `i31` heap type.
455    I31,
456
457    /// The abstract `exception` heap type.
458    Exn,
459
460    /// The abstract `noexn` heap type.
461    NoExn,
462
463    /// The abstract `cont` heap type.
464    Cont,
465
466    /// The abstract `nocont` heap type.
467    NoCont,
468}
469
470impl Encode for AbstractHeapType {
471    fn encode(&self, sink: &mut Vec<u8>) {
472        use AbstractHeapType::*;
473        match self {
474            Func => sink.push(0x70),
475            Extern => sink.push(0x6F),
476            Any => sink.push(0x6E),
477            None => sink.push(0x71),
478            NoExtern => sink.push(0x72),
479            NoFunc => sink.push(0x73),
480            Eq => sink.push(0x6D),
481            Struct => sink.push(0x6B),
482            Array => sink.push(0x6A),
483            I31 => sink.push(0x6C),
484            Exn => sink.push(0x69),
485            NoExn => sink.push(0x74),
486            Cont => sink.push(0x68),
487            NoCont => sink.push(0x75),
488        }
489    }
490}
491
492/// An encoder for the type section of WebAssembly modules.
493///
494/// # Example
495///
496/// ```rust
497/// use wasm_encoder::{Module, TypeSection, ValType};
498///
499/// let mut types = TypeSection::new();
500///
501/// types.ty().function([ValType::I32, ValType::I32], [ValType::I64]);
502///
503/// let mut module = Module::new();
504/// module.section(&types);
505///
506/// let bytes = module.finish();
507/// ```
508#[derive(Clone, Debug, Default)]
509pub struct TypeSection {
510    bytes: Vec<u8>,
511    num_added: u32,
512}
513
514impl TypeSection {
515    /// Create a new module type section encoder.
516    pub fn new() -> Self {
517        Self::default()
518    }
519
520    /// The number of types in the section.
521    pub fn len(&self) -> u32 {
522        self.num_added
523    }
524
525    /// Determines if the section is empty.
526    pub fn is_empty(&self) -> bool {
527        self.num_added == 0
528    }
529
530    /// Encode a function type in this type section.
531    #[must_use = "the encoder must be used to encode the type"]
532    pub fn ty(&mut self) -> CoreTypeEncoder<'_> {
533        self.num_added += 1;
534        CoreTypeEncoder {
535            bytes: &mut self.bytes,
536            push_prefix_if_component_core_type: false,
537        }
538    }
539}
540
541impl Encode for TypeSection {
542    fn encode(&self, sink: &mut Vec<u8>) {
543        encode_section(sink, self.num_added, &self.bytes);
544    }
545}
546
547impl Section for TypeSection {
548    fn id(&self) -> u8 {
549        SectionId::Type.into()
550    }
551}
552
553/// A single-use encoder for encoding a type; this forces all encoding for a
554/// type to be done in a single shot.
555#[derive(Debug)]
556pub struct CoreTypeEncoder<'a> {
557    pub(crate) bytes: &'a mut Vec<u8>,
558    // For the time being, this flag handles an ambiguous encoding in the
559    // component model: the `0x50` opcode represents both a core module type as
560    // well as a GC non-final `sub` type. To avoid this, the component model
561    // specification requires us to prefix a non-final `sub` type with `0x00`
562    // when it is used as a top-level core type of a component. Eventually
563    // (prior to the component model's v1.0 release), a module type will get a
564    // new opcode and this special logic can go away.
565    pub(crate) push_prefix_if_component_core_type: bool,
566}
567impl<'a> CoreTypeEncoder<'a> {
568    /// Define a function type in this type section.
569    pub fn function<P, R>(mut self, params: P, results: R)
570    where
571        P: IntoIterator<Item = ValType>,
572        P::IntoIter: ExactSizeIterator,
573        R: IntoIterator<Item = ValType>,
574        R::IntoIter: ExactSizeIterator,
575    {
576        self.encode_function(params, results);
577    }
578
579    /// Define a function type in this type section.
580    pub fn func_type(mut self, ty: &FuncType) {
581        self.encode_function(ty.params().iter().cloned(), ty.results().iter().cloned());
582    }
583
584    fn encode_function<P, R>(&mut self, params: P, results: R)
585    where
586        P: IntoIterator<Item = ValType>,
587        P::IntoIter: ExactSizeIterator,
588        R: IntoIterator<Item = ValType>,
589        R::IntoIter: ExactSizeIterator,
590    {
591        let params = params.into_iter();
592        let results = results.into_iter();
593
594        self.bytes.push(0x60);
595        params.len().encode(self.bytes);
596        params.for_each(|p| p.encode(self.bytes));
597        results.len().encode(self.bytes);
598        results.for_each(|p| p.encode(self.bytes));
599    }
600
601    /// Define an array type in this type section.
602    pub fn array(mut self, ty: &StorageType, mutable: bool) {
603        self.encode_array(ty, mutable);
604    }
605
606    fn encode_array(&mut self, ty: &StorageType, mutable: bool) {
607        self.bytes.push(0x5e);
608        self.encode_field(ty, mutable);
609    }
610
611    fn encode_field(&mut self, ty: &StorageType, mutable: bool) {
612        ty.encode(self.bytes);
613        self.bytes.push(mutable as u8);
614    }
615
616    /// Define a struct type in this type section.
617    pub fn struct_<F>(mut self, fields: F)
618    where
619        F: IntoIterator<Item = FieldType>,
620        F::IntoIter: ExactSizeIterator,
621    {
622        self.encode_struct(fields);
623    }
624
625    fn encode_struct<F>(&mut self, fields: F)
626    where
627        F: IntoIterator<Item = FieldType>,
628        F::IntoIter: ExactSizeIterator,
629    {
630        let fields = fields.into_iter();
631        self.bytes.push(0x5f);
632        fields.len().encode(self.bytes);
633        for f in fields {
634            self.encode_field(&f.element_type, f.mutable);
635        }
636    }
637
638    /// Define a continuation type in this subsection
639    pub fn cont(mut self, ty: &ContType) {
640        self.encode_cont(ty)
641    }
642
643    fn encode_cont(&mut self, ty: &ContType) {
644        self.bytes.push(0x5d);
645        i64::from(ty.0).encode(self.bytes);
646    }
647
648    /// Define an explicit subtype in this type section.
649    pub fn subtype(mut self, ty: &SubType) {
650        self.encode_subtype(ty)
651    }
652
653    /// Define an explicit subtype in this type section.
654    fn encode_subtype(&mut self, ty: &SubType) {
655        // We only need to emit a prefix byte before the actual composite type
656        // when either the `sub` type is not final or it has a declared super
657        // type (see notes on `push_prefix_if_component_core_type`).
658        if ty.supertype_idx.is_some() || !ty.is_final {
659            if ty.is_final {
660                self.bytes.push(0x4f);
661            } else {
662                if self.push_prefix_if_component_core_type {
663                    self.bytes.push(0x00);
664                }
665                self.bytes.push(0x50);
666            }
667            ty.supertype_idx.encode(self.bytes);
668        }
669        if ty.composite_type.shared {
670            self.bytes.push(0x65);
671        }
672        match &ty.composite_type.inner {
673            CompositeInnerType::Func(ty) => {
674                self.encode_function(ty.params().iter().copied(), ty.results().iter().copied())
675            }
676            CompositeInnerType::Array(ArrayType(ty)) => {
677                self.encode_array(&ty.element_type, ty.mutable)
678            }
679            CompositeInnerType::Struct(ty) => self.encode_struct(ty.fields.iter().cloned()),
680            CompositeInnerType::Cont(ty) => self.encode_cont(ty),
681        }
682    }
683
684    /// Define an explicit recursion group in this type section.
685    pub fn rec<T>(mut self, types: T)
686    where
687        T: IntoIterator<Item = SubType>,
688        T::IntoIter: ExactSizeIterator,
689    {
690        // When emitting a `rec` group, we will never emit `sub`'s special
691        // `0x00` prefix; that is only necessary when `sub` is not wrapped by
692        // `rec` (see notes on `push_prefix_if_component_core_type`).
693        self.push_prefix_if_component_core_type = false;
694        let types = types.into_iter();
695        self.bytes.push(0x4e);
696        types.len().encode(self.bytes);
697        types.for_each(|t| {
698            self.encode_subtype(&t);
699        });
700    }
701}
702
703#[cfg(test)]
704mod tests {
705    use super::*;
706    use crate::Module;
707    use wasmparser::WasmFeatures;
708
709    #[test]
710    fn func_types_dont_require_wasm_gc() {
711        let mut types = TypeSection::new();
712        types.ty().subtype(&SubType {
713            is_final: true,
714            supertype_idx: None,
715            composite_type: CompositeType {
716                inner: CompositeInnerType::Func(FuncType::new([], [])),
717                shared: false,
718            },
719        });
720
721        let mut module = Module::new();
722        module.section(&types);
723        let wasm_bytes = module.finish();
724
725        let mut validator =
726            wasmparser::Validator::new_with_features(WasmFeatures::default() & !WasmFeatures::GC);
727
728        validator.validate_all(&wasm_bytes).expect(
729            "Encoding pre Wasm GC type should not accidentally use Wasm GC specific encoding",
730        );
731    }
732}