tinywasm_wasmparser/
validator.rs

1/* Copyright 2018 Mozilla Foundation
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16use crate::std::mem;
17use crate::std::ops::Range;
18use crate::{
19    limits::*, BinaryReaderError, Encoding, FromReader, FunctionBody, HeapType, Parser, Payload,
20    RefType, Result, SectionLimited, ValType, WASM_COMPONENT_VERSION, WASM_MODULE_VERSION,
21};
22use alloc::sync::Arc;
23use alloc::vec::Vec;
24
25/// Test whether the given buffer contains a valid WebAssembly module or component,
26/// analogous to [`WebAssembly.validate`][js] in the JS API.
27///
28/// This functions requires the bytes to validate are entirely resident in memory.
29/// Additionally this validates the given bytes with the default set of WebAssembly
30/// features implemented by `wasmparser`.
31///
32/// For more fine-tuned control over validation it's recommended to review the
33/// documentation of [`Validator`].
34///
35/// Upon success, the type information for the top-level module or component will
36/// be returned.
37///
38/// [js]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/validate
39pub fn validate(bytes: &[u8]) -> Result<Types> {
40    Validator::new().validate_all(bytes)
41}
42
43#[test]
44fn test_validate() {
45    assert!(validate(&[0x0, 0x61, 0x73, 0x6d, 0x1, 0x0, 0x0, 0x0]).is_ok());
46    assert!(validate(&[0x0, 0x61, 0x73, 0x6d, 0x2, 0x0, 0x0, 0x0]).is_err());
47}
48
49mod component;
50mod core;
51mod func;
52pub mod names;
53mod operators;
54pub mod types;
55
56use self::component::*;
57pub use self::core::ValidatorResources;
58use self::core::*;
59use self::types::{TypeAlloc, Types, TypesRef};
60pub use func::{FuncToValidate, FuncValidator, FuncValidatorAllocations};
61pub use operators::{Frame, FrameKind};
62
63fn check_max(cur_len: usize, amt_added: u32, max: usize, desc: &str, offset: usize) -> Result<()> {
64    if max
65        .checked_sub(cur_len)
66        .and_then(|amt| amt.checked_sub(amt_added as usize))
67        .is_none()
68    {
69        if max == 1 {
70            bail!(offset, "multiple {desc}");
71        }
72
73        bail!(offset, "{desc} count exceeds limit of {max}");
74    }
75
76    Ok(())
77}
78
79fn combine_type_sizes(a: u32, b: u32, offset: usize) -> Result<u32> {
80    match a.checked_add(b) {
81        Some(sum) if sum < MAX_WASM_TYPE_SIZE => Ok(sum),
82        _ => Err(format_err!(
83            offset,
84            "effective type size exceeds the limit of {MAX_WASM_TYPE_SIZE}",
85        )),
86    }
87}
88
89/// Validator for a WebAssembly binary module or component.
90///
91/// This structure encapsulates state necessary to validate a WebAssembly
92/// binary. This implements validation as defined by the [core
93/// specification][core]. A `Validator` is designed, like
94/// [`Parser`], to accept incremental input over time.
95/// Additionally a `Validator` is also designed for parallel validation of
96/// functions as they are received.
97///
98/// It's expected that you'll be using a [`Parser`] in tandem with a
99/// `Validator`. As each [`Payload`](crate::Payload) is received from a
100/// [`Parser`] you'll pass it into a `Validator` to test the validity of the
101/// payload. Note that all payloads received from a [`Parser`] are expected to
102/// be passed to a [`Validator`]. For example if you receive
103/// [`Payload::TypeSection`](crate::Payload) you'll call
104/// [`Validator::type_section`] to validate this.
105///
106/// The design of [`Validator`] is intended that you'll interleave, in your own
107/// application's processing, calls to validation. Each variant, after it's
108/// received, will be validated and then your application would proceed as
109/// usual. At all times, however, you'll have access to the [`Validator`] and
110/// the validation context up to that point. This enables applications to check
111/// the types of functions and learn how many globals there are, for example.
112///
113/// [core]: https://webassembly.github.io/spec/core/valid/index.html
114#[derive(Default)]
115pub struct Validator {
116    /// The current state of the validator.
117    state: State,
118
119    /// The global type space used by the validator and any sub-validators.
120    types: TypeAlloc,
121
122    /// The module state when parsing a WebAssembly module.
123    module: Option<ModuleState>,
124
125    /// With the component model enabled, this stores the pushed component states.
126    /// The top of the stack is the current component state.
127    components: Vec<ComponentState>,
128
129    /// Enabled WebAssembly feature flags, dictating what's valid and what
130    /// isn't.
131    features: WasmFeatures,
132}
133
134#[derive(Debug, Clone, Copy, Eq, PartialEq)]
135enum State {
136    /// A header has not yet been parsed.
137    ///
138    /// The value is the expected encoding for the header.
139    Unparsed(Option<Encoding>),
140    /// A module header has been parsed.
141    ///
142    /// The associated module state is available via [`Validator::module`].
143    Module,
144    /// A component header has been parsed.
145    ///
146    /// The associated component state exists at the top of the
147    /// validator's [`Validator::components`] stack.
148    Component,
149    /// The parse has completed and no more data is expected.
150    End,
151}
152
153impl State {
154    fn ensure_parsable(&self, offset: usize) -> Result<()> {
155        match self {
156            Self::Module | Self::Component => Ok(()),
157            Self::Unparsed(_) => Err(BinaryReaderError::new(
158                "unexpected section before header was parsed",
159                offset,
160            )),
161            Self::End => Err(BinaryReaderError::new(
162                "unexpected section after parsing has completed",
163                offset,
164            )),
165        }
166    }
167
168    fn ensure_module(&self, section: &str, offset: usize) -> Result<()> {
169        self.ensure_parsable(offset)?;
170
171        match self {
172            Self::Module => Ok(()),
173            Self::Component => Err(format_err!(
174                offset,
175                "unexpected module {section} section while parsing a component",
176            )),
177            _ => unreachable!(),
178        }
179    }
180
181    fn ensure_component(&self, section: &str, offset: usize) -> Result<()> {
182        self.ensure_parsable(offset)?;
183
184        match self {
185            Self::Component => Ok(()),
186            Self::Module => Err(format_err!(
187                offset,
188                "unexpected component {section} section while parsing a module",
189            )),
190            _ => unreachable!(),
191        }
192    }
193}
194
195impl Default for State {
196    fn default() -> Self {
197        Self::Unparsed(None)
198    }
199}
200
201/// Flags for features that are enabled for validation.
202#[derive(Hash, Debug, Copy, Clone)]
203pub struct WasmFeatures {
204    /// The WebAssembly `mutable-global` proposal (enabled by default)
205    pub mutable_global: bool,
206    /// The WebAssembly `nontrapping-float-to-int-conversions` proposal (enabled by default)
207    pub saturating_float_to_int: bool,
208    /// The WebAssembly `sign-extension-ops` proposal (enabled by default)
209    pub sign_extension: bool,
210    /// The WebAssembly reference types proposal (enabled by default)
211    pub reference_types: bool,
212    /// The WebAssembly multi-value proposal (enabled by default)
213    pub multi_value: bool,
214    /// The WebAssembly bulk memory operations proposal (enabled by default)
215    pub bulk_memory: bool,
216    /// The WebAssembly SIMD proposal (enabled by default)
217    pub simd: bool,
218    /// The WebAssembly Relaxed SIMD proposal (enabled by default)
219    pub relaxed_simd: bool,
220    /// The WebAssembly threads proposal (enabled by default)
221    pub threads: bool,
222    /// The WebAssembly tail-call proposal (enabled by default)
223    pub tail_call: bool,
224    /// Whether or not floating-point instructions are enabled.
225    ///
226    /// This is enabled by default can be used to disallow floating-point
227    /// operators and types.
228    ///
229    /// This does not correspond to a WebAssembly proposal but is instead
230    /// intended for embeddings which have stricter-than-usual requirements
231    /// about execution. Floats in WebAssembly can have different NaN patterns
232    /// across hosts which can lead to host-dependent execution which some
233    /// runtimes may not desire.
234    pub floats: bool,
235    /// The WebAssembly multi memory proposal (enabled by default)
236    pub multi_memory: bool,
237    /// The WebAssembly exception handling proposal
238    pub exceptions: bool,
239    /// The WebAssembly memory64 proposal
240    pub memory64: bool,
241    /// The WebAssembly extended_const proposal
242    pub extended_const: bool,
243    /// The WebAssembly component model proposal.
244    pub component_model: bool,
245    /// The WebAssembly typed function references proposal
246    pub function_references: bool,
247    /// The WebAssembly memory control proposal
248    pub memory_control: bool,
249    /// The WebAssembly gc proposal
250    pub gc: bool,
251    /// Support for the `value` type in the component model proposal.
252    pub component_model_values: bool,
253    /// Support for the nested namespaces and projects in component model names.
254    pub component_model_nested_names: bool,
255}
256
257impl WasmFeatures {
258    /// Returns [`WasmFeatures`] with all features enabled.
259    pub fn all() -> Self {
260        WasmFeatures {
261            mutable_global: true,
262            saturating_float_to_int: true,
263            sign_extension: true,
264            reference_types: true,
265            multi_value: true,
266            bulk_memory: true,
267            simd: true,
268            relaxed_simd: true,
269            threads: true,
270            tail_call: true,
271            floats: true,
272            multi_memory: true,
273            exceptions: true,
274            memory64: true,
275            extended_const: true,
276            component_model: true,
277            function_references: true,
278            memory_control: true,
279            gc: true,
280            component_model_values: true,
281            component_model_nested_names: true,
282        }
283    }
284
285    /// NOTE: This only checks that the value type corresponds to the feature set!!
286    ///
287    /// To check that reference types are valid, we need access to the module
288    /// types. Use module.check_value_type.
289    pub(crate) fn check_value_type(&self, ty: ValType) -> Result<(), &'static str> {
290        match ty {
291            ValType::I32 | ValType::I64 => Ok(()),
292            ValType::F32 | ValType::F64 => {
293                if self.floats {
294                    Ok(())
295                } else {
296                    Err("floating-point support is disabled")
297                }
298            }
299            ValType::Ref(r) => self.check_ref_type(r),
300            ValType::V128 => {
301                if self.simd {
302                    Ok(())
303                } else {
304                    Err("SIMD support is not enabled")
305                }
306            }
307        }
308    }
309
310    pub(crate) fn check_ref_type(&self, r: RefType) -> Result<(), &'static str> {
311        if !self.reference_types {
312            return Err("reference types support is not enabled");
313        }
314        match (r.heap_type(), r.is_nullable()) {
315            // funcref/externref only require `reference-types`.
316            (HeapType::Func, true) | (HeapType::Extern, true) => Ok(()),
317
318            // Non-nullable func/extern references requires the
319            // `function-references` proposal.
320            (HeapType::Func | HeapType::Extern, false) => {
321                if self.function_references {
322                    Ok(())
323                } else {
324                    Err("function references required for non-nullable types")
325                }
326            }
327
328            // Indexed types require either the function-references or gc
329            // proposal as gc implies function references here.
330            (HeapType::Concrete(_), _) => {
331                if self.function_references || self.gc {
332                    Ok(())
333                } else {
334                    Err("function references required for index reference types")
335                }
336            }
337
338            // These types were added in the gc proposal.
339            (
340                HeapType::Any
341                | HeapType::None
342                | HeapType::Eq
343                | HeapType::Struct
344                | HeapType::Array
345                | HeapType::I31
346                | HeapType::NoExtern
347                | HeapType::NoFunc,
348                _,
349            ) => {
350                if self.gc {
351                    Ok(())
352                } else {
353                    Err("heap types not supported without the gc feature")
354                }
355            }
356
357            // These types were added in the exception-handling proposal.
358            (HeapType::Exn, _) => {
359                if self.exceptions {
360                    Ok(())
361                } else {
362                    Err("exception refs not supported without the exception handling feature")
363                }
364            }
365        }
366    }
367}
368
369impl Default for WasmFeatures {
370    fn default() -> WasmFeatures {
371        WasmFeatures {
372            // Off-by-default features.
373            exceptions: false,
374            memory64: false,
375            extended_const: false,
376            function_references: false,
377            memory_control: false,
378            gc: false,
379            component_model_values: false,
380            component_model_nested_names: false,
381
382            // On-by-default features (phase 4 or greater).
383            mutable_global: true,
384            saturating_float_to_int: true,
385            sign_extension: true,
386            bulk_memory: true,
387            multi_value: true,
388            reference_types: true,
389            tail_call: true,
390            simd: true,
391            floats: true,
392            relaxed_simd: true,
393            threads: true,
394            multi_memory: true,
395            component_model: true,
396        }
397    }
398}
399
400/// Possible return values from [`Validator::payload`].
401#[allow(clippy::large_enum_variant)]
402pub enum ValidPayload<'a> {
403    /// The payload validated, no further action need be taken.
404    Ok,
405    /// The payload validated, but it started a nested module or component.
406    ///
407    /// This result indicates that the specified parser should be used instead
408    /// of the currently-used parser until this returned one ends.
409    Parser(Parser),
410    /// A function was found to be validate.
411    Func(FuncToValidate<ValidatorResources>, FunctionBody<'a>),
412    /// The end payload was validated and the types known to the validator
413    /// are provided.
414    End(Types),
415}
416
417impl Validator {
418    /// Creates a new [`Validator`] ready to validate a WebAssembly module
419    /// or component.
420    ///
421    /// The new validator will receive payloads parsed from
422    /// [`Parser`], and expects the first payload received to be
423    /// the version header from the parser.
424    pub fn new() -> Validator {
425        Validator::default()
426    }
427
428    /// Creates a new [`Validator`] which has the specified set of wasm
429    /// features activated for validation.
430    ///
431    /// This function is the same as [`Validator::new`] except it also allows
432    /// you to customize the active wasm features in use for validation. This
433    /// can allow enabling experimental proposals or also turning off
434    /// on-by-default wasm proposals.
435    pub fn new_with_features(features: WasmFeatures) -> Validator {
436        let mut ret = Validator::new();
437        ret.features = features;
438        ret
439    }
440
441    /// Returns the wasm features used for this validator.
442    pub fn features(&self) -> &WasmFeatures {
443        &self.features
444    }
445
446    /// Validates an entire in-memory module or component with this validator.
447    ///
448    /// This function will internally create a [`Parser`] to parse the `bytes`
449    /// provided. The entire module or component specified by `bytes` will be
450    /// parsed and validated.
451    ///
452    /// Upon success, the type information for the top-level module or component
453    /// will be returned.
454    pub fn validate_all(&mut self, bytes: &[u8]) -> Result<Types> {
455        let mut functions_to_validate = Vec::new();
456        let mut last_types = None;
457        for payload in Parser::new(0).parse_all(bytes) {
458            match self.payload(&payload?)? {
459                ValidPayload::Func(a, b) => {
460                    functions_to_validate.push((a, b));
461                }
462                ValidPayload::End(types) => {
463                    // Only the last (top-level) type information will be returned
464                    last_types = Some(types);
465                }
466                _ => {}
467            }
468        }
469
470        let mut allocs = FuncValidatorAllocations::default();
471        for (func, body) in functions_to_validate {
472            let mut validator = func.into_validator(allocs);
473            validator.validate(&body)?;
474            allocs = validator.into_allocations();
475        }
476
477        Ok(last_types.unwrap())
478    }
479
480    /// Gets the types known by the validator so far within the
481    /// module/component `level` modules/components up from the
482    /// module/component currently being parsed.
483    ///
484    /// For instance, calling `validator.types(0)` will get the types of the
485    /// module/component currently being parsed, and `validator.types(1)` will
486    /// get the types of the component containing that module/component.
487    ///
488    /// Returns `None` if there is no module/component that many levels up.
489    pub fn types(&self, mut level: usize) -> Option<TypesRef> {
490        if let Some(module) = &self.module {
491            if level == 0 {
492                return Some(TypesRef::from_module(&self.types, &module.module));
493            } else {
494                level -= 1;
495            }
496        }
497
498        self.components
499            .iter()
500            .nth_back(level)
501            .map(|component| TypesRef::from_component(&self.types, component))
502    }
503
504    /// Convenience function to validate a single [`Payload`].
505    ///
506    /// This function is intended to be used as a convenience. It will
507    /// internally perform any validation necessary to validate the [`Payload`]
508    /// provided. The convenience part is that you're likely already going to
509    /// be matching on [`Payload`] in your application, at which point it's more
510    /// appropriate to call the individual methods on [`Validator`] per-variant
511    /// in [`Payload`], such as [`Validator::type_section`].
512    ///
513    /// This function returns a [`ValidPayload`] variant on success, indicating
514    /// one of a few possible actions that need to be taken after a payload is
515    /// validated. For example function contents are not validated here, they're
516    /// returned through [`ValidPayload`] for validation by the caller.
517    pub fn payload<'a>(&mut self, payload: &Payload<'a>) -> Result<ValidPayload<'a>> {
518        use crate::Payload::*;
519        match payload {
520            Version {
521                num,
522                encoding,
523                range,
524            } => self.version(*num, *encoding, range)?,
525
526            // Module sections
527            TypeSection(s) => self.type_section(s)?,
528            ImportSection(s) => self.import_section(s)?,
529            FunctionSection(s) => self.function_section(s)?,
530            TableSection(s) => self.table_section(s)?,
531            MemorySection(s) => self.memory_section(s)?,
532            TagSection(s) => self.tag_section(s)?,
533            GlobalSection(s) => self.global_section(s)?,
534            ExportSection(s) => self.export_section(s)?,
535            StartSection { func, range } => self.start_section(*func, range)?,
536            ElementSection(s) => self.element_section(s)?,
537            DataCountSection { count, range } => self.data_count_section(*count, range)?,
538            CodeSectionStart {
539                count,
540                range,
541                size: _,
542            } => self.code_section_start(*count, range)?,
543            CodeSectionEntry(body) => {
544                let func_validator = self.code_section_entry(body)?;
545                return Ok(ValidPayload::Func(func_validator, body.clone()));
546            }
547            DataSection(s) => self.data_section(s)?,
548
549            // Component sections
550            ModuleSection { parser, range, .. } => {
551                self.module_section(range)?;
552                return Ok(ValidPayload::Parser(parser.clone()));
553            }
554            InstanceSection(s) => self.instance_section(s)?,
555            CoreTypeSection(s) => self.core_type_section(s)?,
556            ComponentSection { parser, range, .. } => {
557                self.component_section(range)?;
558                return Ok(ValidPayload::Parser(parser.clone()));
559            }
560            ComponentInstanceSection(s) => self.component_instance_section(s)?,
561            ComponentAliasSection(s) => self.component_alias_section(s)?,
562            ComponentTypeSection(s) => self.component_type_section(s)?,
563            ComponentCanonicalSection(s) => self.component_canonical_section(s)?,
564            ComponentStartSection { start, range } => self.component_start_section(start, range)?,
565            ComponentImportSection(s) => self.component_import_section(s)?,
566            ComponentExportSection(s) => self.component_export_section(s)?,
567
568            End(offset) => return Ok(ValidPayload::End(self.end(*offset)?)),
569
570            CustomSection { .. } => {} // no validation for custom sections
571            UnknownSection { id, range, .. } => self.unknown_section(*id, range)?,
572        }
573        Ok(ValidPayload::Ok)
574    }
575
576    /// Validates [`Payload::Version`](crate::Payload).
577    pub fn version(&mut self, num: u16, encoding: Encoding, range: &Range<usize>) -> Result<()> {
578        match &self.state {
579            State::Unparsed(expected) => {
580                if let Some(expected) = expected {
581                    if *expected != encoding {
582                        bail!(
583                            range.start,
584                            "expected a version header for a {}",
585                            match expected {
586                                Encoding::Module => "module",
587                                Encoding::Component => "component",
588                            }
589                        );
590                    }
591                }
592            }
593            _ => {
594                return Err(BinaryReaderError::new(
595                    "wasm version header out of order",
596                    range.start,
597                ))
598            }
599        }
600
601        self.state = match encoding {
602            Encoding::Module => {
603                if num == WASM_MODULE_VERSION {
604                    assert!(self.module.is_none());
605                    self.module = Some(ModuleState::default());
606                    State::Module
607                } else {
608                    bail!(range.start, "unknown binary version: {num:#x}");
609                }
610            }
611            Encoding::Component => {
612                if !self.features.component_model {
613                    bail!(
614                        range.start,
615                        "unknown binary version and encoding combination: {num:#x} and 0x1, \
616                        note: encoded as a component but the WebAssembly component model feature \
617                        is not enabled - enable the feature to allow component validation",
618                    );
619                }
620                if num == WASM_COMPONENT_VERSION {
621                    self.components
622                        .push(ComponentState::new(ComponentKind::Component));
623                    State::Component
624                } else if num < WASM_COMPONENT_VERSION {
625                    bail!(range.start, "unsupported component version: {num:#x}");
626                } else {
627                    bail!(range.start, "unknown component version: {num:#x}");
628                }
629            }
630        };
631
632        Ok(())
633    }
634
635    /// Validates [`Payload::TypeSection`](crate::Payload).
636    pub fn type_section(&mut self, section: &crate::TypeSectionReader<'_>) -> Result<()> {
637        self.process_module_section(
638            Order::Type,
639            section,
640            "type",
641            |state, _, _types, count, offset| {
642                check_max(
643                    state.module.types.len(),
644                    count,
645                    MAX_WASM_TYPES,
646                    "types",
647                    offset,
648                )?;
649                state.module.assert_mut().types.reserve(count as usize);
650                Ok(())
651            },
652            |state, features, types, rec_group, offset| {
653                state
654                    .module
655                    .assert_mut()
656                    .add_types(rec_group, features, types, offset, true)?;
657                Ok(())
658            },
659        )
660    }
661
662    /// Validates [`Payload::ImportSection`](crate::Payload).
663    ///
664    /// This method should only be called when parsing a module.
665    pub fn import_section(&mut self, section: &crate::ImportSectionReader<'_>) -> Result<()> {
666        self.process_module_section(
667            Order::Import,
668            section,
669            "import",
670            |_, _, _, _, _| Ok(()), // add_import will check limits
671            |state, features, types, import, offset| {
672                state
673                    .module
674                    .assert_mut()
675                    .add_import(import, features, types, offset)
676            },
677        )
678    }
679
680    /// Validates [`Payload::FunctionSection`](crate::Payload).
681    ///
682    /// This method should only be called when parsing a module.
683    pub fn function_section(&mut self, section: &crate::FunctionSectionReader<'_>) -> Result<()> {
684        self.process_module_section(
685            Order::Function,
686            section,
687            "function",
688            |state, _, _, count, offset| {
689                check_max(
690                    state.module.functions.len(),
691                    count,
692                    MAX_WASM_FUNCTIONS,
693                    "functions",
694                    offset,
695                )?;
696                state.module.assert_mut().functions.reserve(count as usize);
697                debug_assert!(state.expected_code_bodies.is_none());
698                state.expected_code_bodies = Some(count);
699                Ok(())
700            },
701            |state, _, types, ty, offset| state.module.assert_mut().add_function(ty, types, offset),
702        )
703    }
704
705    /// Validates [`Payload::TableSection`](crate::Payload).
706    ///
707    /// This method should only be called when parsing a module.
708    pub fn table_section(&mut self, section: &crate::TableSectionReader<'_>) -> Result<()> {
709        let features = self.features;
710        self.process_module_section(
711            Order::Table,
712            section,
713            "table",
714            |state, _, _, count, offset| {
715                check_max(
716                    state.module.tables.len(),
717                    count,
718                    state.module.max_tables(&features),
719                    "tables",
720                    offset,
721                )?;
722                state.module.assert_mut().tables.reserve(count as usize);
723                Ok(())
724            },
725            |state, features, types, table, offset| state.add_table(table, features, types, offset),
726        )
727    }
728
729    /// Validates [`Payload::MemorySection`](crate::Payload).
730    ///
731    /// This method should only be called when parsing a module.
732    pub fn memory_section(&mut self, section: &crate::MemorySectionReader<'_>) -> Result<()> {
733        self.process_module_section(
734            Order::Memory,
735            section,
736            "memory",
737            |state, features, _, count, offset| {
738                check_max(
739                    state.module.memories.len(),
740                    count,
741                    state.module.max_memories(features),
742                    "memories",
743                    offset,
744                )?;
745                state.module.assert_mut().memories.reserve(count as usize);
746                Ok(())
747            },
748            |state, features, _, ty, offset| {
749                state.module.assert_mut().add_memory(ty, features, offset)
750            },
751        )
752    }
753
754    /// Validates [`Payload::TagSection`](crate::Payload).
755    ///
756    /// This method should only be called when parsing a module.
757    pub fn tag_section(&mut self, section: &crate::TagSectionReader<'_>) -> Result<()> {
758        if !self.features.exceptions {
759            return Err(BinaryReaderError::new(
760                "exceptions proposal not enabled",
761                section.range().start,
762            ));
763        }
764
765        self.process_module_section(
766            Order::Tag,
767            section,
768            "tag",
769            |state, _, _, count, offset| {
770                check_max(
771                    state.module.tags.len(),
772                    count,
773                    MAX_WASM_TAGS,
774                    "tags",
775                    offset,
776                )?;
777                state.module.assert_mut().tags.reserve(count as usize);
778                Ok(())
779            },
780            |state, features, types, ty, offset| {
781                state
782                    .module
783                    .assert_mut()
784                    .add_tag(ty, features, types, offset)
785            },
786        )
787    }
788
789    /// Validates [`Payload::GlobalSection`](crate::Payload).
790    ///
791    /// This method should only be called when parsing a module.
792    pub fn global_section(&mut self, section: &crate::GlobalSectionReader<'_>) -> Result<()> {
793        self.process_module_section(
794            Order::Global,
795            section,
796            "global",
797            |state, _, _, count, offset| {
798                check_max(
799                    state.module.globals.len(),
800                    count,
801                    MAX_WASM_GLOBALS,
802                    "globals",
803                    offset,
804                )?;
805                state.module.assert_mut().globals.reserve(count as usize);
806                Ok(())
807            },
808            |state, features, types, global, offset| {
809                state.add_global(global, features, types, offset)
810            },
811        )
812    }
813
814    /// Validates [`Payload::ExportSection`](crate::Payload).
815    ///
816    /// This method should only be called when parsing a module.
817    pub fn export_section(&mut self, section: &crate::ExportSectionReader<'_>) -> Result<()> {
818        self.process_module_section(
819            Order::Export,
820            section,
821            "export",
822            |state, _, _, count, offset| {
823                check_max(
824                    state.module.exports.len(),
825                    count,
826                    MAX_WASM_EXPORTS,
827                    "exports",
828                    offset,
829                )?;
830                state.module.assert_mut().exports.reserve(count as usize);
831                Ok(())
832            },
833            |state, features, types, e, offset| {
834                let state = state.module.assert_mut();
835                let ty = state.export_to_entity_type(&e, offset)?;
836                state.add_export(
837                    e.name, ty, features, offset, false, /* checked above */
838                    types,
839                )
840            },
841        )
842    }
843
844    /// Validates [`Payload::StartSection`](crate::Payload).
845    ///
846    /// This method should only be called when parsing a module.
847    pub fn start_section(&mut self, func: u32, range: &Range<usize>) -> Result<()> {
848        let offset = range.start;
849        self.state.ensure_module("start", offset)?;
850        let state = self.module.as_mut().unwrap();
851        state.update_order(Order::Start, offset)?;
852
853        let ty = state.module.get_func_type(func, &self.types, offset)?;
854        if !ty.params().is_empty() || !ty.results().is_empty() {
855            return Err(BinaryReaderError::new(
856                "invalid start function type",
857                offset,
858            ));
859        }
860
861        Ok(())
862    }
863
864    /// Validates [`Payload::ElementSection`](crate::Payload).
865    ///
866    /// This method should only be called when parsing a module.
867    pub fn element_section(&mut self, section: &crate::ElementSectionReader<'_>) -> Result<()> {
868        self.process_module_section(
869            Order::Element,
870            section,
871            "element",
872            |state, _, _, count, offset| {
873                check_max(
874                    state.module.element_types.len(),
875                    count,
876                    MAX_WASM_ELEMENT_SEGMENTS,
877                    "element segments",
878                    offset,
879                )?;
880                state
881                    .module
882                    .assert_mut()
883                    .element_types
884                    .reserve(count as usize);
885                Ok(())
886            },
887            |state, features, types, e, offset| {
888                state.add_element_segment(e, features, types, offset)
889            },
890        )
891    }
892
893    /// Validates [`Payload::DataCountSection`](crate::Payload).
894    ///
895    /// This method should only be called when parsing a module.
896    pub fn data_count_section(&mut self, count: u32, range: &Range<usize>) -> Result<()> {
897        let offset = range.start;
898        self.state.ensure_module("data count", offset)?;
899
900        let state = self.module.as_mut().unwrap();
901        state.update_order(Order::DataCount, offset)?;
902
903        if count > MAX_WASM_DATA_SEGMENTS as u32 {
904            return Err(BinaryReaderError::new(
905                "data count section specifies too many data segments",
906                offset,
907            ));
908        }
909
910        state.module.assert_mut().data_count = Some(count);
911        Ok(())
912    }
913
914    /// Validates [`Payload::CodeSectionStart`](crate::Payload).
915    ///
916    /// This method should only be called when parsing a module.
917    pub fn code_section_start(&mut self, count: u32, range: &Range<usize>) -> Result<()> {
918        let offset = range.start;
919        self.state.ensure_module("code", offset)?;
920
921        let state = self.module.as_mut().unwrap();
922        state.update_order(Order::Code, offset)?;
923
924        match state.expected_code_bodies.take() {
925            Some(n) if n == count => {}
926            Some(_) => {
927                return Err(BinaryReaderError::new(
928                    "function and code section have inconsistent lengths",
929                    offset,
930                ));
931            }
932            // empty code sections are allowed even if the function section is
933            // missing
934            None if count == 0 => {}
935            None => {
936                return Err(BinaryReaderError::new(
937                    "code section without function section",
938                    offset,
939                ))
940            }
941        }
942
943        // Take a snapshot of the types when we start the code section.
944        state.module.assert_mut().snapshot = Some(Arc::new(self.types.commit()));
945
946        Ok(())
947    }
948
949    /// Validates [`Payload::CodeSectionEntry`](crate::Payload).
950    ///
951    /// This function will prepare a [`FuncToValidate`] which can be used to
952    /// create a [`FuncValidator`] to validate the function. The function body
953    /// provided will not be parsed or validated by this function.
954    ///
955    /// Note that the returned [`FuncToValidate`] is "connected" to this
956    /// [`Validator`] in that it uses the internal context of this validator for
957    /// validating the function. The [`FuncToValidate`] can be sent to another
958    /// thread, for example, to offload actual processing of functions
959    /// elsewhere.
960    ///
961    /// This method should only be called when parsing a module.
962    pub fn code_section_entry(
963        &mut self,
964        body: &crate::FunctionBody,
965    ) -> Result<FuncToValidate<ValidatorResources>> {
966        let offset = body.range().start;
967        self.state.ensure_module("code", offset)?;
968
969        let state = self.module.as_mut().unwrap();
970
971        let (index, ty) = state.next_code_index_and_type(offset)?;
972        Ok(FuncToValidate::new(
973            index,
974            ty,
975            ValidatorResources(state.module.arc().clone()),
976            &self.features,
977        ))
978    }
979
980    /// Validates [`Payload::DataSection`](crate::Payload).
981    ///
982    /// This method should only be called when parsing a module.
983    pub fn data_section(&mut self, section: &crate::DataSectionReader<'_>) -> Result<()> {
984        self.process_module_section(
985            Order::Data,
986            section,
987            "data",
988            |state, _, _, count, offset| {
989                state.data_segment_count = count;
990                check_max(0, count, MAX_WASM_DATA_SEGMENTS, "data segments", offset)
991            },
992            |state, features, types, d, offset| state.add_data_segment(d, features, types, offset),
993        )
994    }
995
996    /// Validates [`Payload::ModuleSection`](crate::Payload).
997    ///
998    /// This method should only be called when parsing a component.
999    pub fn module_section(&mut self, range: &Range<usize>) -> Result<()> {
1000        self.state.ensure_component("module", range.start)?;
1001
1002        let current = self.components.last_mut().unwrap();
1003        check_max(
1004            current.core_modules.len(),
1005            1,
1006            MAX_WASM_MODULES,
1007            "modules",
1008            range.start,
1009        )?;
1010
1011        match mem::replace(&mut self.state, State::Unparsed(Some(Encoding::Module))) {
1012            State::Component => {}
1013            _ => unreachable!(),
1014        }
1015
1016        Ok(())
1017    }
1018
1019    /// Validates [`Payload::InstanceSection`](crate::Payload).
1020    ///
1021    /// This method should only be called when parsing a component.
1022    pub fn instance_section(&mut self, section: &crate::InstanceSectionReader) -> Result<()> {
1023        self.process_component_section(
1024            section,
1025            "core instance",
1026            |components, _, count, offset| {
1027                let current = components.last_mut().unwrap();
1028                check_max(
1029                    current.instance_count(),
1030                    count,
1031                    MAX_WASM_INSTANCES,
1032                    "instances",
1033                    offset,
1034                )?;
1035                current.core_instances.reserve(count as usize);
1036                Ok(())
1037            },
1038            |components, types, _, instance, offset| {
1039                components
1040                    .last_mut()
1041                    .unwrap()
1042                    .add_core_instance(instance, types, offset)
1043            },
1044        )
1045    }
1046
1047    /// Validates [`Payload::CoreTypeSection`](crate::Payload).
1048    ///
1049    /// This method should only be called when parsing a component.
1050    pub fn core_type_section(&mut self, section: &crate::CoreTypeSectionReader<'_>) -> Result<()> {
1051        self.process_component_section(
1052            section,
1053            "core type",
1054            |components, _types, count, offset| {
1055                let current = components.last_mut().unwrap();
1056                check_max(current.type_count(), count, MAX_WASM_TYPES, "types", offset)?;
1057                current.core_types.reserve(count as usize);
1058                Ok(())
1059            },
1060            |components, types, features, ty, offset| {
1061                ComponentState::add_core_type(
1062                    components, ty, features, types, offset, false, /* checked above */
1063                )
1064            },
1065        )
1066    }
1067
1068    /// Validates [`Payload::ComponentSection`](crate::Payload).
1069    ///
1070    /// This method should only be called when parsing a component.
1071    pub fn component_section(&mut self, range: &Range<usize>) -> Result<()> {
1072        self.state.ensure_component("component", range.start)?;
1073
1074        let current = self.components.last_mut().unwrap();
1075        check_max(
1076            current.components.len(),
1077            1,
1078            MAX_WASM_COMPONENTS,
1079            "components",
1080            range.start,
1081        )?;
1082
1083        match mem::replace(&mut self.state, State::Unparsed(Some(Encoding::Component))) {
1084            State::Component => {}
1085            _ => unreachable!(),
1086        }
1087
1088        Ok(())
1089    }
1090
1091    /// Validates [`Payload::ComponentInstanceSection`](crate::Payload).
1092    ///
1093    /// This method should only be called when parsing a component.
1094    pub fn component_instance_section(
1095        &mut self,
1096        section: &crate::ComponentInstanceSectionReader,
1097    ) -> Result<()> {
1098        self.process_component_section(
1099            section,
1100            "instance",
1101            |components, _, count, offset| {
1102                let current = components.last_mut().unwrap();
1103                check_max(
1104                    current.instance_count(),
1105                    count,
1106                    MAX_WASM_INSTANCES,
1107                    "instances",
1108                    offset,
1109                )?;
1110                current.instances.reserve(count as usize);
1111                Ok(())
1112            },
1113            |components, types, features, instance, offset| {
1114                components
1115                    .last_mut()
1116                    .unwrap()
1117                    .add_instance(instance, features, types, offset)
1118            },
1119        )
1120    }
1121
1122    /// Validates [`Payload::ComponentAliasSection`](crate::Payload).
1123    ///
1124    /// This method should only be called when parsing a component.
1125    pub fn component_alias_section(
1126        &mut self,
1127        section: &crate::ComponentAliasSectionReader<'_>,
1128    ) -> Result<()> {
1129        self.process_component_section(
1130            section,
1131            "alias",
1132            |_, _, _, _| Ok(()), // maximums checked via `add_alias`
1133            |components, types, features, alias, offset| -> Result<(), BinaryReaderError> {
1134                ComponentState::add_alias(components, alias, features, types, offset)
1135            },
1136        )
1137    }
1138
1139    /// Validates [`Payload::ComponentTypeSection`](crate::Payload).
1140    ///
1141    /// This method should only be called when parsing a component.
1142    pub fn component_type_section(
1143        &mut self,
1144        section: &crate::ComponentTypeSectionReader,
1145    ) -> Result<()> {
1146        self.process_component_section(
1147            section,
1148            "type",
1149            |components, _types, count, offset| {
1150                let current = components.last_mut().unwrap();
1151                check_max(current.type_count(), count, MAX_WASM_TYPES, "types", offset)?;
1152                current.types.reserve(count as usize);
1153                Ok(())
1154            },
1155            |components, types, features, ty, offset| {
1156                ComponentState::add_type(
1157                    components, ty, features, types, offset, false, /* checked above */
1158                )
1159            },
1160        )
1161    }
1162
1163    /// Validates [`Payload::ComponentCanonicalSection`](crate::Payload).
1164    ///
1165    /// This method should only be called when parsing a component.
1166    pub fn component_canonical_section(
1167        &mut self,
1168        section: &crate::ComponentCanonicalSectionReader,
1169    ) -> Result<()> {
1170        self.process_component_section(
1171            section,
1172            "function",
1173            |components, _, count, offset| {
1174                let current = components.last_mut().unwrap();
1175                check_max(
1176                    current.function_count(),
1177                    count,
1178                    MAX_WASM_FUNCTIONS,
1179                    "functions",
1180                    offset,
1181                )?;
1182                current.funcs.reserve(count as usize);
1183                Ok(())
1184            },
1185            |components, types, _, func, offset| {
1186                let current = components.last_mut().unwrap();
1187                match func {
1188                    crate::CanonicalFunction::Lift {
1189                        core_func_index,
1190                        type_index,
1191                        options,
1192                    } => current.lift_function(
1193                        core_func_index,
1194                        type_index,
1195                        options.into_vec(),
1196                        types,
1197                        offset,
1198                    ),
1199                    crate::CanonicalFunction::Lower {
1200                        func_index,
1201                        options,
1202                    } => current.lower_function(func_index, options.into_vec(), types, offset),
1203                    crate::CanonicalFunction::ResourceNew { resource } => {
1204                        current.resource_new(resource, types, offset)
1205                    }
1206                    crate::CanonicalFunction::ResourceDrop { resource } => {
1207                        current.resource_drop(resource, types, offset)
1208                    }
1209                    crate::CanonicalFunction::ResourceRep { resource } => {
1210                        current.resource_rep(resource, types, offset)
1211                    }
1212                }
1213            },
1214        )
1215    }
1216
1217    /// Validates [`Payload::ComponentStartSection`](crate::Payload).
1218    ///
1219    /// This method should only be called when parsing a component.
1220    pub fn component_start_section(
1221        &mut self,
1222        f: &crate::ComponentStartFunction,
1223        range: &Range<usize>,
1224    ) -> Result<()> {
1225        self.state.ensure_component("start", range.start)?;
1226
1227        self.components.last_mut().unwrap().add_start(
1228            f.func_index,
1229            &f.arguments,
1230            f.results,
1231            &self.features,
1232            &mut self.types,
1233            range.start,
1234        )
1235    }
1236
1237    /// Validates [`Payload::ComponentImportSection`](crate::Payload).
1238    ///
1239    /// This method should only be called when parsing a component.
1240    pub fn component_import_section(
1241        &mut self,
1242        section: &crate::ComponentImportSectionReader,
1243    ) -> Result<()> {
1244        self.process_component_section(
1245            section,
1246            "import",
1247            |_, _, _, _| Ok(()), // add_import will check limits
1248            |components, types, features, import, offset| {
1249                components
1250                    .last_mut()
1251                    .unwrap()
1252                    .add_import(import, features, types, offset)
1253            },
1254        )
1255    }
1256
1257    /// Validates [`Payload::ComponentExportSection`](crate::Payload).
1258    ///
1259    /// This method should only be called when parsing a component.
1260    pub fn component_export_section(
1261        &mut self,
1262        section: &crate::ComponentExportSectionReader,
1263    ) -> Result<()> {
1264        self.process_component_section(
1265            section,
1266            "export",
1267            |components, _, count, offset| {
1268                let current = components.last_mut().unwrap();
1269                check_max(
1270                    current.exports.len(),
1271                    count,
1272                    MAX_WASM_EXPORTS,
1273                    "exports",
1274                    offset,
1275                )?;
1276                current.exports.reserve(count as usize);
1277                Ok(())
1278            },
1279            |components, types, features, export, offset| {
1280                let current = components.last_mut().unwrap();
1281                let ty = current.export_to_entity_type(&export, features, types, offset)?;
1282                current.add_export(
1283                    export.name,
1284                    ty,
1285                    features,
1286                    types,
1287                    offset,
1288                    false, /* checked above */
1289                )
1290            },
1291        )
1292    }
1293
1294    /// Validates [`Payload::UnknownSection`](crate::Payload).
1295    ///
1296    /// Currently always returns an error.
1297    pub fn unknown_section(&mut self, id: u8, range: &Range<usize>) -> Result<()> {
1298        Err(format_err!(range.start, "malformed section id: {id}"))
1299    }
1300
1301    /// Validates [`Payload::End`](crate::Payload).
1302    ///
1303    /// Returns the types known to the validator for the module or component.
1304    pub fn end(&mut self, offset: usize) -> Result<Types> {
1305        match crate::std::mem::replace(&mut self.state, State::End) {
1306            State::Unparsed(_) => Err(BinaryReaderError::new(
1307                "cannot call `end` before a header has been parsed",
1308                offset,
1309            )),
1310            State::End => Err(BinaryReaderError::new(
1311                "cannot call `end` after parsing has completed",
1312                offset,
1313            )),
1314            State::Module => {
1315                let mut state = self.module.take().unwrap();
1316                state.validate_end(offset)?;
1317
1318                // If there's a parent component, we'll add a module to the parent state
1319                // and continue to validate the component
1320                if let Some(parent) = self.components.last_mut() {
1321                    parent.add_core_module(&state.module, &mut self.types, offset)?;
1322                    self.state = State::Component;
1323                }
1324
1325                Ok(Types::from_module(
1326                    self.types.commit(),
1327                    state.module.arc().clone(),
1328                ))
1329            }
1330            State::Component => {
1331                let mut component = self.components.pop().unwrap();
1332
1333                // Validate that all values were used for the component
1334                if let Some(index) = component.values.iter().position(|(_, used)| !*used) {
1335                    bail!(
1336                        offset,
1337                        "value index {index} was not used as part of an \
1338                         instantiation, start function, or export"
1339                    );
1340                }
1341
1342                // If there's a parent component, pop the stack, add it to the parent,
1343                // and continue to validate the component
1344                let ty = component.finish(&mut self.types, offset)?;
1345                if let Some(parent) = self.components.last_mut() {
1346                    parent.add_component(ty, &mut self.types)?;
1347                    self.state = State::Component;
1348                }
1349
1350                Ok(Types::from_component(self.types.commit(), component))
1351            }
1352        }
1353    }
1354
1355    fn process_module_section<'a, T>(
1356        &mut self,
1357        order: Order,
1358        section: &SectionLimited<'a, T>,
1359        name: &str,
1360        validate_section: impl FnOnce(
1361            &mut ModuleState,
1362            &WasmFeatures,
1363            &mut TypeAlloc,
1364            u32,
1365            usize,
1366        ) -> Result<()>,
1367        mut validate_item: impl FnMut(
1368            &mut ModuleState,
1369            &WasmFeatures,
1370            &mut TypeAlloc,
1371            T,
1372            usize,
1373        ) -> Result<()>,
1374    ) -> Result<()>
1375    where
1376        T: FromReader<'a>,
1377    {
1378        let offset = section.range().start;
1379        self.state.ensure_module(name, offset)?;
1380
1381        let state = self.module.as_mut().unwrap();
1382        state.update_order(order, offset)?;
1383
1384        validate_section(
1385            state,
1386            &self.features,
1387            &mut self.types,
1388            section.count(),
1389            offset,
1390        )?;
1391
1392        for item in section.clone().into_iter_with_offsets() {
1393            let (offset, item) = item?;
1394            validate_item(state, &self.features, &mut self.types, item, offset)?;
1395        }
1396
1397        Ok(())
1398    }
1399
1400    fn process_component_section<'a, T>(
1401        &mut self,
1402        section: &SectionLimited<'a, T>,
1403        name: &str,
1404        validate_section: impl FnOnce(
1405            &mut Vec<ComponentState>,
1406            &mut TypeAlloc,
1407            u32,
1408            usize,
1409        ) -> Result<()>,
1410        mut validate_item: impl FnMut(
1411            &mut Vec<ComponentState>,
1412            &mut TypeAlloc,
1413            &WasmFeatures,
1414            T,
1415            usize,
1416        ) -> Result<()>,
1417    ) -> Result<()>
1418    where
1419        T: FromReader<'a>,
1420    {
1421        let offset = section.range().start;
1422
1423        if !self.features.component_model {
1424            return Err(BinaryReaderError::new(
1425                "component model feature is not enabled",
1426                offset,
1427            ));
1428        }
1429
1430        self.state.ensure_component(name, offset)?;
1431        validate_section(
1432            &mut self.components,
1433            &mut self.types,
1434            section.count(),
1435            offset,
1436        )?;
1437
1438        for item in section.clone().into_iter_with_offsets() {
1439            let (offset, item) = item?;
1440            validate_item(
1441                &mut self.components,
1442                &mut self.types,
1443                &self.features,
1444                item,
1445                offset,
1446            )?;
1447        }
1448
1449        Ok(())
1450    }
1451}
1452
1453#[cfg(test)]
1454mod tests {
1455    use crate::{GlobalType, MemoryType, RefType, TableType, ValType, Validator, WasmFeatures};
1456    use anyhow::Result;
1457
1458    #[test]
1459    fn test_module_type_information() -> Result<()> {
1460        let bytes = wat::parse_str(
1461            r#"
1462            (module
1463                (type (func (param i32 i64) (result i32)))
1464                (memory 1 5)
1465                (table 10 funcref)
1466                (global (mut i32) (i32.const 0))
1467                (func (type 0) (i32.const 0))
1468                (tag (param i64 i32))
1469                (elem funcref (ref.func 0))
1470            )
1471        "#,
1472        )?;
1473
1474        let mut validator = Validator::new_with_features(WasmFeatures {
1475            exceptions: true,
1476            ..Default::default()
1477        });
1478
1479        let types = validator.validate_all(&bytes).unwrap();
1480
1481        assert_eq!(types.type_count(), 2);
1482        assert_eq!(types.memory_count(), 1);
1483        assert_eq!(types.table_count(), 1);
1484        assert_eq!(types.global_count(), 1);
1485        assert_eq!(types.core_function_count(), 1);
1486        assert_eq!(types.tag_count(), 1);
1487        assert_eq!(types.element_count(), 1);
1488        assert_eq!(types.module_count(), 0);
1489        assert_eq!(types.component_count(), 0);
1490        assert_eq!(types.core_instance_count(), 0);
1491        assert_eq!(types.value_count(), 0);
1492
1493        let id = match types.core_type_at(0) {
1494            crate::types::ComponentCoreTypeId::Sub(s) => s,
1495            crate::types::ComponentCoreTypeId::Module(_) => panic!(),
1496        };
1497        let ty = types[id].unwrap_func();
1498        assert_eq!(ty.params(), [ValType::I32, ValType::I64]);
1499        assert_eq!(ty.results(), [ValType::I32]);
1500
1501        let id = match types.core_type_at(1) {
1502            crate::types::ComponentCoreTypeId::Sub(s) => s,
1503            crate::types::ComponentCoreTypeId::Module(_) => panic!(),
1504        };
1505        let ty = types[id].unwrap_func();
1506        assert_eq!(ty.params(), [ValType::I64, ValType::I32]);
1507        assert_eq!(ty.results(), []);
1508
1509        assert_eq!(
1510            types.memory_at(0),
1511            MemoryType {
1512                memory64: false,
1513                shared: false,
1514                initial: 1,
1515                maximum: Some(5)
1516            }
1517        );
1518
1519        assert_eq!(
1520            types.table_at(0),
1521            TableType {
1522                initial: 10,
1523                maximum: None,
1524                element_type: RefType::FUNCREF,
1525            }
1526        );
1527
1528        assert_eq!(
1529            types.global_at(0),
1530            GlobalType {
1531                content_type: ValType::I32,
1532                mutable: true
1533            }
1534        );
1535
1536        let id = types.core_function_at(0);
1537        let ty = types[id].unwrap_func();
1538        assert_eq!(ty.params(), [ValType::I32, ValType::I64]);
1539        assert_eq!(ty.results(), [ValType::I32]);
1540
1541        let ty = types.tag_at(0);
1542        let ty = types[ty].unwrap_func();
1543        assert_eq!(ty.params(), [ValType::I64, ValType::I32]);
1544        assert_eq!(ty.results(), []);
1545
1546        assert_eq!(types.element_at(0), RefType::FUNCREF);
1547
1548        Ok(())
1549    }
1550
1551    #[test]
1552    fn test_type_id_aliasing() -> Result<()> {
1553        let bytes = wat::parse_str(
1554            r#"
1555            (component
1556              (type $T (list string))
1557              (alias outer 0 $T (type $A1))
1558              (alias outer 0 $T (type $A2))
1559            )
1560        "#,
1561        )?;
1562
1563        let mut validator = Validator::new_with_features(WasmFeatures {
1564            component_model: true,
1565            ..Default::default()
1566        });
1567
1568        let types = validator.validate_all(&bytes).unwrap();
1569
1570        let t_id = types.component_defined_type_at(0);
1571        let a1_id = types.component_defined_type_at(1);
1572        let a2_id = types.component_defined_type_at(2);
1573
1574        // The ids should all be the same
1575        assert!(t_id == a1_id);
1576        assert!(t_id == a2_id);
1577        assert!(a1_id == a2_id);
1578
1579        // However, they should all point to the same type
1580        assert!(crate::std::ptr::eq(&types[t_id], &types[a1_id],));
1581        assert!(crate::std::ptr::eq(&types[t_id], &types[a2_id],));
1582
1583        Ok(())
1584    }
1585
1586    #[test]
1587    fn test_type_id_exports() -> Result<()> {
1588        let bytes = wat::parse_str(
1589            r#"
1590            (component
1591              (type $T (list string))
1592              (export $A1 "A1" (type $T))
1593              (export $A2 "A2" (type $T))
1594            )
1595        "#,
1596        )?;
1597
1598        let mut validator = Validator::new_with_features(WasmFeatures {
1599            component_model: true,
1600            ..Default::default()
1601        });
1602
1603        let types = validator.validate_all(&bytes).unwrap();
1604
1605        let t_id = types.component_defined_type_at(0);
1606        let a1_id = types.component_defined_type_at(1);
1607        let a2_id = types.component_defined_type_at(2);
1608
1609        // The ids should all be the same
1610        assert!(t_id != a1_id);
1611        assert!(t_id != a2_id);
1612        assert!(a1_id != a2_id);
1613
1614        // However, they should all point to the same type
1615        assert!(crate::std::ptr::eq(&types[t_id], &types[a1_id],));
1616        assert!(crate::std::ptr::eq(&types[t_id], &types[a2_id],));
1617
1618        Ok(())
1619    }
1620}