wasmparser-nostd 0.91.0

A simple event-driven library for parsing WebAssembly binary files.
Documentation
//! State relating to validating a WebAssembly component.

use super::{
    check_max, combine_type_sizes,
    core::Module,
    types::{
        ComponentFuncType, ComponentInstanceType, ComponentInstanceTypeKind, ComponentType,
        ComponentValType, EntityType, InstanceType, ModuleType, RecordType, Type, TypeId, TypeList,
        VariantCase,
    },
};
use crate::{
    limits::*,
    types::{
        ComponentDefinedType, ComponentEntityType, InstanceTypeKind, LoweringInfo, TupleType,
        UnionType, VariantType,
    },
    BinaryReaderError, CanonicalOption, ComponentExternalKind, ComponentOuterAliasKind,
    ComponentTypeRef, ExternalKind, FuncType, GlobalType, InstantiationArgKind, MemoryType, Result,
    TableType, TypeBounds, ValType, WasmFeatures,
};
use ::alloc::borrow::ToOwned;
use ::alloc::collections::BTreeSet;
use ::alloc::string::String;
use ::alloc::string::ToString;
use ::alloc::vec::Vec;
use ::core::mem;
use indexmap::{IndexMap, IndexSet};

pub(crate) struct ComponentState {
    // Core index spaces
    pub core_types: Vec<TypeId>,
    pub core_modules: Vec<TypeId>,
    pub core_instances: Vec<TypeId>,
    pub core_funcs: Vec<TypeId>,
    pub core_memories: Vec<MemoryType>,
    pub core_tables: Vec<TableType>,
    pub core_globals: Vec<GlobalType>,
    pub core_tags: Vec<TypeId>,

    // Component index spaces
    pub types: Vec<TypeId>,
    pub funcs: Vec<TypeId>,
    pub values: Vec<(ComponentValType, bool)>,
    pub instances: Vec<TypeId>,
    pub components: Vec<TypeId>,

    pub imports: IndexMap<String, ComponentEntityType>,
    pub exports: IndexMap<String, ComponentEntityType>,
    has_start: bool,
    type_size: usize,
}

impl ComponentState {
    pub fn type_count(&self) -> usize {
        self.core_types.len() + self.types.len()
    }

    pub fn instance_count(&self) -> usize {
        self.core_instances.len() + self.instances.len()
    }

    pub fn function_count(&self) -> usize {
        self.core_funcs.len() + self.funcs.len()
    }

    pub fn add_core_type(
        components: &mut [Self],
        ty: crate::CoreType,
        features: &WasmFeatures,
        types: &mut TypeList,
        offset: usize,
        check_limit: bool,
    ) -> Result<()> {
        let ty = match ty {
            crate::CoreType::Func(ty) => Type::Func(ty),
            crate::CoreType::Module(decls) => Type::Module(Self::create_module_type(
                components,
                decls.into_vec(),
                features,
                types,
                offset,
            )?),
        };

        let current = components.last_mut().unwrap();

        if check_limit {
            check_max(current.type_count(), 1, MAX_WASM_TYPES, "types", offset)?;
        }

        current.core_types.push(TypeId {
            type_size: ty.type_size(),
            index: types.len(),
        });
        types.push(ty);

        Ok(())
    }

    pub fn add_core_module(
        &mut self,
        module: &Module,
        types: &mut TypeList,
        offset: usize,
    ) -> Result<()> {
        let imports = module.imports_for_module_type(offset)?;

        // We have to clone the module's imports and exports here
        // because we cannot take the data out of the `MaybeOwned`
        // as it might be shared with a function validator.
        let ty = Type::Module(ModuleType {
            type_size: module.type_size,
            imports,
            exports: module.exports.clone(),
        });

        self.core_modules.push(TypeId {
            type_size: ty.type_size(),
            index: types.len(),
        });

        types.push(ty);

        Ok(())
    }

    pub fn add_core_instance(
        &mut self,
        instance: crate::Instance,
        types: &mut TypeList,
        offset: usize,
    ) -> Result<()> {
        let instance = match instance {
            crate::Instance::Instantiate { module_index, args } => {
                self.instantiate_module(module_index, args.into_vec(), types, offset)?
            }
            crate::Instance::FromExports(exports) => {
                self.instantiate_core_exports(exports.into_vec(), types, offset)?
            }
        };

        self.core_instances.push(instance);

        Ok(())
    }

    pub fn add_type(
        components: &mut Vec<Self>,
        ty: crate::ComponentType,
        features: &WasmFeatures,
        types: &mut TypeList,
        offset: usize,
        check_limit: bool,
    ) -> Result<()> {
        assert!(!components.is_empty());
        let ty = match ty {
            crate::ComponentType::Defined(ty) => Type::Defined(
                components
                    .last_mut()
                    .unwrap()
                    .create_defined_type(ty, types, offset)?,
            ),
            crate::ComponentType::Func(ty) => Type::ComponentFunc(
                components
                    .last_mut()
                    .unwrap()
                    .create_function_type(ty, types, offset)?,
            ),
            crate::ComponentType::Component(decls) => Type::Component(Self::create_component_type(
                components,
                decls.into_vec(),
                features,
                types,
                offset,
            )?),
            crate::ComponentType::Instance(decls) => Type::ComponentInstance(
                Self::create_instance_type(components, decls.into_vec(), features, types, offset)?,
            ),
        };

        let current = components.last_mut().unwrap();
        if check_limit {
            check_max(current.type_count(), 1, MAX_WASM_TYPES, "types", offset)?;
        }

        current.types.push(TypeId {
            type_size: ty.type_size(),
            index: types.len(),
        });
        types.push(ty);

        Ok(())
    }

    pub fn add_import(
        &mut self,
        import: crate::ComponentImport,
        types: &TypeList,
        offset: usize,
    ) -> Result<()> {
        let entity = self.check_type_ref(&import.ty, types, offset)?;

        let (len, max, desc) = match entity {
            ComponentEntityType::Module(id) => {
                self.core_modules.push(id);
                (self.core_modules.len(), MAX_WASM_MODULES, "modules")
            }
            ComponentEntityType::Component(id) => {
                self.components.push(id);
                (self.components.len(), MAX_WASM_COMPONENTS, "components")
            }
            ComponentEntityType::Instance(id) => {
                self.instances.push(id);
                (self.instance_count(), MAX_WASM_INSTANCES, "instances")
            }
            ComponentEntityType::Func(id) => {
                self.funcs.push(id);
                (self.function_count(), MAX_WASM_FUNCTIONS, "functions")
            }
            ComponentEntityType::Value(ty) => {
                self.values.push((ty, false));
                (self.values.len(), MAX_WASM_VALUES, "values")
            }
            ComponentEntityType::Type(id) => {
                self.types.push(id);
                (self.types.len(), MAX_WASM_TYPES, "types")
            }
        };

        check_max(len, 0, max, desc, offset)?;

        self.type_size = combine_type_sizes(self.type_size, entity.type_size(), offset)?;

        if self
            .imports
            .insert(import.name.to_string(), entity)
            .is_some()
        {
            bail!(
                offset,
                "duplicate import name `{}` already defined",
                import.name,
            );
        }

        Ok(())
    }

    pub fn add_export(
        &mut self,
        name: &str,
        ty: ComponentEntityType,
        offset: usize,
        check_limit: bool,
    ) -> Result<()> {
        if check_limit {
            check_max(self.exports.len(), 1, MAX_WASM_EXPORTS, "exports", offset)?;
        }

        self.type_size = combine_type_sizes(self.type_size, ty.type_size(), offset)?;

        if self.exports.insert(name.to_string(), ty).is_some() {
            bail!(offset, "duplicate export name `{name}` already defined");
        }

        Ok(())
    }

    pub fn lift_function(
        &mut self,
        core_func_index: u32,
        type_index: u32,
        options: Vec<CanonicalOption>,
        types: &TypeList,
        offset: usize,
    ) -> Result<()> {
        let ty = self.function_type_at(type_index, types, offset)?;
        let core_ty = types[self.core_function_at(core_func_index, offset)?]
            .as_func_type()
            .unwrap();

        // Lifting a function is for an export, so match the expected canonical ABI
        // export signature
        let info = ty.lower(types, false);
        self.check_options(Some(core_ty), &info, &options, types, offset)?;

        if core_ty.params() != info.params.as_slice() {
            bail!(
                offset,
                "lowered parameter types `{:?}` do not match parameter types \
                 `{:?}` of core function {core_func_index}",
                info.params.as_slice(),
                core_ty.params(),
            );
        }

        if core_ty.results() != info.results.as_slice() {
            bail!(
                offset,
                "lowered result types `{:?}` do not match result types \
                 `{:?}` of core function {core_func_index}",
                info.results.as_slice(),
                core_ty.results()
            );
        }

        self.funcs.push(self.types[type_index as usize]);

        Ok(())
    }

    pub fn lower_function(
        &mut self,
        func_index: u32,
        options: Vec<CanonicalOption>,
        types: &mut TypeList,
        offset: usize,
    ) -> Result<()> {
        let ty = types[self.function_at(func_index, offset)?]
            .as_component_func_type()
            .unwrap();

        // Lowering a function is for an import, so use a function type that matches
        // the expected canonical ABI import signature.
        let info = ty.lower(types, true);

        self.check_options(None, &info, &options, types, offset)?;

        let lowered_ty = Type::Func(info.into_func_type());

        self.core_funcs.push(TypeId {
            type_size: lowered_ty.type_size(),
            index: types.len(),
        });

        types.push(lowered_ty);

        Ok(())
    }

    pub fn add_component(&mut self, component: &mut Self, types: &mut TypeList) {
        let ty = Type::Component(ComponentType {
            type_size: component.type_size,
            imports: mem::take(&mut component.imports),
            exports: mem::take(&mut component.exports),
        });

        self.components.push(TypeId {
            type_size: ty.type_size(),
            index: types.len(),
        });

        types.push(ty);
    }

    pub fn add_instance(
        &mut self,
        instance: crate::ComponentInstance,
        types: &mut TypeList,
        offset: usize,
    ) -> Result<()> {
        let instance = match instance {
            crate::ComponentInstance::Instantiate {
                component_index,
                args,
            } => self.instantiate_component(component_index, args.into_vec(), types, offset)?,
            crate::ComponentInstance::FromExports(exports) => {
                self.instantiate_exports(exports.into_vec(), types, offset)?
            }
        };

        self.instances.push(instance);

        Ok(())
    }

    pub fn add_alias(
        components: &mut [Self],
        alias: crate::ComponentAlias,
        types: &TypeList,
        offset: usize,
    ) -> Result<()> {
        match alias {
            crate::ComponentAlias::InstanceExport {
                instance_index,
                kind,
                name,
            } => components.last_mut().unwrap().alias_instance_export(
                instance_index,
                kind,
                name,
                types,
                offset,
            ),
            crate::ComponentAlias::CoreInstanceExport {
                instance_index,
                kind,
                name,
            } => components.last_mut().unwrap().alias_core_instance_export(
                instance_index,
                kind,
                name,
                types,
                offset,
            ),
            crate::ComponentAlias::Outer { kind, count, index } => match kind {
                ComponentOuterAliasKind::CoreModule => {
                    Self::alias_module(components, count, index, offset)
                }
                ComponentOuterAliasKind::CoreType => {
                    Self::alias_core_type(components, count, index, offset)
                }
                ComponentOuterAliasKind::Type => Self::alias_type(components, count, index, offset),
                ComponentOuterAliasKind::Component => {
                    Self::alias_component(components, count, index, offset)
                }
            },
        }
    }

    pub fn add_start(
        &mut self,
        func_index: u32,
        args: &[u32],
        results: u32,
        types: &TypeList,
        offset: usize,
    ) -> Result<()> {
        if self.has_start {
            return Err(BinaryReaderError::new(
                "component cannot have more than one start function",
                offset,
            ));
        }

        let ft = types[self.function_at(func_index, offset)?]
            .as_component_func_type()
            .unwrap();

        if ft.params.len() != args.len() {
            bail!(
                offset,
                "component start function requires {} arguments but was given {}",
                ft.params.len(),
                args.len()
            );
        }

        if ft.results.len() as u32 != results {
            bail!(
                offset,
                "component start function has a result count of {results} \
                 but the function type has a result count of {type_results}",
                type_results = ft.results.len(),
            );
        }

        for (i, ((_, ty), arg)) in ft.params.iter().zip(args).enumerate() {
            // Ensure the value's type is a subtype of the parameter type
            if !ComponentValType::internal_is_subtype_of(
                self.value_at(*arg, offset)?,
                types,
                ty,
                types,
            ) {
                bail!(
                    offset,
                    "value type mismatch for component start function argument {i}"
                );
            }
        }

        for (_, ty) in ft.results.iter() {
            self.values.push((*ty, false));
        }

        self.has_start = true;

        Ok(())
    }

    fn check_options(
        &self,
        core_ty: Option<&FuncType>,
        info: &LoweringInfo,
        options: &[CanonicalOption],
        types: &TypeList,
        offset: usize,
    ) -> Result<()> {
        fn display(option: CanonicalOption) -> &'static str {
            match option {
                CanonicalOption::UTF8 => "utf8",
                CanonicalOption::UTF16 => "utf16",
                CanonicalOption::CompactUTF16 => "latin1-utf16",
                CanonicalOption::Memory(_) => "memory",
                CanonicalOption::Realloc(_) => "realloc",
                CanonicalOption::PostReturn(_) => "post-return",
            }
        }

        let mut encoding = None;
        let mut memory = None;
        let mut realloc = None;
        let mut post_return = None;

        for option in options {
            match option {
                CanonicalOption::UTF8 | CanonicalOption::UTF16 | CanonicalOption::CompactUTF16 => {
                    match encoding {
                        Some(existing) => {
                            bail!(
                                offset,
                                "canonical encoding option `{}` conflicts with option `{}`",
                                display(existing),
                                display(*option),
                            )
                        }
                        None => encoding = Some(*option),
                    }
                }
                CanonicalOption::Memory(idx) => {
                    memory = match memory {
                        None => {
                            self.memory_at(*idx, offset)?;
                            Some(*idx)
                        }
                        Some(_) => {
                            return Err(BinaryReaderError::new(
                                "canonical option `memory` is specified more than once",
                                offset,
                            ))
                        }
                    }
                }
                CanonicalOption::Realloc(idx) => {
                    realloc = match realloc {
                        None => {
                            let ty = types[self.core_function_at(*idx, offset)?]
                                .as_func_type()
                                .unwrap();
                            if ty.params()
                                != [ValType::I32, ValType::I32, ValType::I32, ValType::I32]
                                || ty.results() != [ValType::I32]
                            {
                                return Err(BinaryReaderError::new(
                                    "canonical option `realloc` uses a core function with an incorrect signature",
                                    offset,
                                ));
                            }
                            Some(*idx)
                        }
                        Some(_) => {
                            return Err(BinaryReaderError::new(
                                "canonical option `realloc` is specified more than once",
                                offset,
                            ))
                        }
                    }
                }
                CanonicalOption::PostReturn(idx) => {
                    post_return = match post_return {
                        None => {
                            let core_ty = core_ty.ok_or_else(|| {
                                BinaryReaderError::new(
                                    "canonical option `post-return` cannot be specified for lowerings",
                                    offset,
                                )
                            })?;

                            let ty = types[self.core_function_at(*idx, offset)?]
                                .as_func_type()
                                .unwrap();

                            if ty.params() != core_ty.results() || !ty.results().is_empty() {
                                return Err(BinaryReaderError::new(
                                    "canonical option `post-return` uses a core function with an incorrect signature",
                                    offset,
                                ));
                            }
                            Some(*idx)
                        }
                        Some(_) => {
                            return Err(BinaryReaderError::new(
                                "canonical option `post-return` is specified more than once",
                                offset,
                            ))
                        }
                    }
                }
            }
        }

        if info.requires_memory && memory.is_none() {
            return Err(BinaryReaderError::new(
                "canonical option `memory` is required",
                offset,
            ));
        }

        if info.requires_realloc && realloc.is_none() {
            return Err(BinaryReaderError::new(
                "canonical option `realloc` is required",
                offset,
            ));
        }

        Ok(())
    }

    pub fn check_type_ref(
        &self,
        ty: &ComponentTypeRef,
        types: &TypeList,
        offset: usize,
    ) -> Result<ComponentEntityType> {
        Ok(match ty {
            ComponentTypeRef::Module(index) => {
                let id = self.type_at(*index, true, offset)?;
                types[id].as_module_type().ok_or_else(|| {
                    format_err!(offset, "core type index {index} is not a module type")
                })?;
                ComponentEntityType::Module(id)
            }
            ComponentTypeRef::Func(index) => {
                let id = self.type_at(*index, false, offset)?;
                types[id].as_component_func_type().ok_or_else(|| {
                    format_err!(offset, "type index {index} is not a function type")
                })?;
                ComponentEntityType::Func(id)
            }
            ComponentTypeRef::Value(ty) => {
                let ty = match ty {
                    crate::ComponentValType::Primitive(ty) => ComponentValType::Primitive(*ty),
                    crate::ComponentValType::Type(index) => {
                        ComponentValType::Type(self.defined_type_at(*index, types, offset)?)
                    }
                };
                ComponentEntityType::Value(ty)
            }
            ComponentTypeRef::Type(TypeBounds::Eq, index) => {
                ComponentEntityType::Type(self.type_at(*index, false, offset)?)
            }
            ComponentTypeRef::Instance(index) => {
                let id = self.type_at(*index, false, offset)?;
                types[id].as_component_instance_type().ok_or_else(|| {
                    format_err!(offset, "type index {index} is not an instance type")
                })?;
                ComponentEntityType::Instance(id)
            }
            ComponentTypeRef::Component(index) => {
                let id = self.type_at(*index, false, offset)?;
                types[id].as_component_type().ok_or_else(|| {
                    format_err!(offset, "type index {index} is not a component type")
                })?;
                ComponentEntityType::Component(id)
            }
        })
    }

    pub fn export_to_entity_type(
        &mut self,
        export: &crate::ComponentExport,
        offset: usize,
    ) -> Result<ComponentEntityType> {
        Ok(match export.kind {
            ComponentExternalKind::Module => {
                ComponentEntityType::Module(self.module_at(export.index, offset)?)
            }
            ComponentExternalKind::Func => {
                ComponentEntityType::Func(self.function_at(export.index, offset)?)
            }
            ComponentExternalKind::Value => {
                ComponentEntityType::Value(*self.value_at(export.index, offset)?)
            }
            ComponentExternalKind::Type => {
                ComponentEntityType::Type(self.type_at(export.index, false, offset)?)
            }
            ComponentExternalKind::Instance => {
                ComponentEntityType::Instance(self.instance_at(export.index, offset)?)
            }
            ComponentExternalKind::Component => {
                ComponentEntityType::Component(self.component_at(export.index, offset)?)
            }
        })
    }

    fn create_module_type(
        components: &[Self],
        decls: Vec<crate::ModuleTypeDeclaration>,
        features: &WasmFeatures,
        types: &mut TypeList,
        offset: usize,
    ) -> Result<ModuleType> {
        let mut state = Module::default();

        for decl in decls {
            match decl {
                crate::ModuleTypeDeclaration::Type(ty) => {
                    state.add_type(ty, features, types, offset, true)?;
                }
                crate::ModuleTypeDeclaration::Export { name, ty } => {
                    let ty = state.check_type_ref(&ty, features, types, offset)?;
                    state.add_export(name, ty, features, offset, true)?;
                }
                crate::ModuleTypeDeclaration::OuterAlias { kind, count, index } => {
                    if count > 1 {
                        return Err(BinaryReaderError::new(
                                    "outer type aliases in module type declarations are limited to a maximum count of 1",
                                    offset,
                                ));
                    }
                    match kind {
                        crate::OuterAliasKind::Type => {
                            let ty = if count == 0 {
                                // Local alias, check the local module state
                                state.type_at(index, offset)?
                            } else {
                                // Otherwise, check the enclosing component state
                                let component =
                                    Self::check_alias_count(components, count - 1, offset)?;
                                component.type_at(index, true, offset)?
                            };

                            check_max(state.types.len(), 1, MAX_WASM_TYPES, "types", offset)?;

                            state.types.push(ty);
                        }
                    }
                }
                crate::ModuleTypeDeclaration::Import(import) => {
                    state.add_import(import, features, types, offset)?;
                }
            }
        }

        let imports = state.imports_for_module_type(offset)?;

        Ok(ModuleType {
            type_size: state.type_size,
            imports,
            exports: state.exports,
        })
    }

    fn create_component_type(
        components: &mut Vec<Self>,
        decls: Vec<crate::ComponentTypeDeclaration>,
        features: &WasmFeatures,
        types: &mut TypeList,
        offset: usize,
    ) -> Result<ComponentType> {
        components.push(ComponentState::default());

        for decl in decls {
            match decl {
                crate::ComponentTypeDeclaration::CoreType(ty) => {
                    Self::add_core_type(components, ty, features, types, offset, true)?;
                }
                crate::ComponentTypeDeclaration::Type(ty) => {
                    Self::add_type(components, ty, features, types, offset, true)?;
                }
                crate::ComponentTypeDeclaration::Export { name, ty } => {
                    let current = components.last_mut().unwrap();
                    let ty = current.check_type_ref(&ty, types, offset)?;
                    current.add_export(name, ty, offset, true)?;
                }
                crate::ComponentTypeDeclaration::Import(import) => {
                    components
                        .last_mut()
                        .unwrap()
                        .add_import(import, types, offset)?;
                }
                crate::ComponentTypeDeclaration::Alias(alias) => {
                    match alias {
                        crate::ComponentAlias::Outer { kind, count, index }
                            if kind == ComponentOuterAliasKind::CoreType
                                || kind == ComponentOuterAliasKind::Type =>
                        {
                            match kind {
                                ComponentOuterAliasKind::CoreType => {
                                    Self::alias_core_type(components, count, index, offset)?
                                }
                                ComponentOuterAliasKind::Type => {
                                    Self::alias_type(components, count, index, offset)?
                                }
                                _ => unreachable!(),
                            }
                        }
                        _ => return Err(BinaryReaderError::new(
                            "only outer type aliases are allowed in component type declarations",
                            offset,
                        )),
                    }
                }
            };
        }

        let state = components.pop().unwrap();

        Ok(ComponentType {
            type_size: state.type_size,
            imports: state.imports,
            exports: state.exports,
        })
    }

    fn create_instance_type(
        components: &mut Vec<Self>,
        decls: Vec<crate::InstanceTypeDeclaration>,
        features: &WasmFeatures,
        types: &mut TypeList,
        offset: usize,
    ) -> Result<ComponentInstanceType> {
        components.push(ComponentState::default());

        for decl in decls {
            match decl {
                crate::InstanceTypeDeclaration::CoreType(ty) => {
                    Self::add_core_type(components, ty, features, types, offset, true)?;
                }
                crate::InstanceTypeDeclaration::Type(ty) => {
                    Self::add_type(components, ty, features, types, offset, true)?;
                }
                crate::InstanceTypeDeclaration::Export { name, ty } => {
                    let current = components.last_mut().unwrap();
                    let ty = current.check_type_ref(&ty, types, offset)?;
                    current.add_export(name, ty, offset, true)?;
                }
                crate::InstanceTypeDeclaration::Alias(alias) => match alias {
                    crate::ComponentAlias::Outer { kind, count, index }
                        if kind == ComponentOuterAliasKind::CoreType
                            || kind == ComponentOuterAliasKind::Type =>
                    {
                        match kind {
                            ComponentOuterAliasKind::CoreType => {
                                Self::alias_core_type(components, count, index, offset)?
                            }
                            ComponentOuterAliasKind::Type => {
                                Self::alias_type(components, count, index, offset)?
                            }
                            _ => unreachable!(),
                        }
                    }
                    _ => {
                        return Err(BinaryReaderError::new(
                            "only outer type aliases are allowed in instance type declarations",
                            offset,
                        ))
                    }
                },
            };
        }

        let state = components.pop().unwrap();

        Ok(ComponentInstanceType {
            type_size: state.type_size,
            kind: ComponentInstanceTypeKind::Defined(state.exports),
        })
    }

    fn create_function_type(
        &self,
        ty: crate::ComponentFuncType,
        types: &TypeList,
        offset: usize,
    ) -> Result<ComponentFuncType> {
        let mut type_size = 1;

        let mut set = BTreeSet::new();

        let params = ty
            .params
            .iter()
            .map(|(name, ty)| {
                Self::check_name(name, "function parameter", offset)?;
                if !set.insert(*name) {
                    return Err(BinaryReaderError::new("duplicate parameter name", offset));
                }
                let ty = self.create_component_val_type(*ty, types, offset)?;
                type_size = combine_type_sizes(type_size, ty.type_size(), offset)?;
                Ok((name.to_string(), ty))
            })
            .collect::<Result<_>>()?;

        set.clear();

        let results = ty
            .results
            .iter()
            .map(|(name, ty)| {
                if let Some(name) = name {
                    Self::check_name(name, "function result", offset)?;
                    if !set.insert(name) {
                        return Err(BinaryReaderError::new("duplicate result name", offset));
                    }
                }
                let ty = self.create_component_val_type(*ty, types, offset)?;
                type_size = combine_type_sizes(type_size, ty.type_size(), offset)?;
                Ok((name.map(ToOwned::to_owned), ty))
            })
            .collect::<Result<_>>()?;

        Ok(ComponentFuncType {
            type_size,
            params,
            results,
        })
    }

    fn check_name(name: &str, desc: &str, offset: usize) -> Result<()> {
        if name.is_empty() {
            bail!(offset, "{desc} name cannot be empty");
        }

        Ok(())
    }

    fn instantiate_module(
        &self,
        module_index: u32,
        module_args: Vec<crate::InstantiationArg>,
        types: &mut TypeList,
        offset: usize,
    ) -> Result<TypeId> {
        fn insert_arg<'a>(
            name: &'a str,
            arg: &'a InstanceType,
            args: &mut IndexMap<&'a str, &'a InstanceType>,
            offset: usize,
        ) -> Result<()> {
            if args.insert(name, arg).is_some() {
                bail!(
                    offset,
                    "duplicate module instantiation argument named `{name}`"
                );
            }

            Ok(())
        }

        let module_type_id = self.module_at(module_index, offset)?;
        let mut args = IndexMap::new();

        // Populate the arguments
        for module_arg in module_args {
            match module_arg.kind {
                InstantiationArgKind::Instance => {
                    let instance_type = types[self.core_instance_at(module_arg.index, offset)?]
                        .as_instance_type()
                        .unwrap();
                    insert_arg(module_arg.name, instance_type, &mut args, offset)?;
                }
            }
        }

        // Validate the arguments
        let module_type = types[module_type_id].as_module_type().unwrap();
        for ((module, name), expected) in module_type.imports.iter() {
            let instance = args.get(module.as_str()).ok_or_else(|| {
                format_err!(
                    offset,
                    "missing module instantiation argument named `{module}`"
                )
            })?;

            let arg = instance
                .internal_exports(types)
                .get(name.as_str())
                .ok_or_else(|| {
                    format_err!(
                        offset,
                        "module instantiation argument `{module}` does not \
                         export an item named `{name}`",
                    )
                })?;

            match (arg, expected) {
                (EntityType::Func(_), EntityType::Func(_))
                | (EntityType::Table(_), EntityType::Table(_))
                | (EntityType::Memory(_), EntityType::Memory(_))
                | (EntityType::Global(_), EntityType::Global(_))
                | (EntityType::Tag(_), EntityType::Tag(_)) => {}
                _ => {
                    bail!(
                        offset,
                        "module instantiation argument `{module}` exports \
                         an item named `{name}` but it is not a {}",
                        expected.desc()
                    )
                }
            }

            if !EntityType::internal_is_subtype_of(arg, types, expected, types) {
                bail!(
                    offset,
                    "{} type mismatch for export `{name}` of module \
                     instantiation argument `{module}`",
                    expected.desc(),
                );
            }
        }

        let ty = Type::Instance(InstanceType {
            type_size: module_type
                .exports
                .iter()
                .fold(1, |acc, (_, ty)| acc + ty.type_size()),
            kind: InstanceTypeKind::Instantiated(module_type_id),
        });

        let id = TypeId {
            type_size: ty.type_size(),
            index: types.len(),
        };

        types.push(ty);

        Ok(id)
    }

    fn instantiate_component(
        &mut self,
        component_index: u32,
        component_args: Vec<crate::ComponentInstantiationArg>,
        types: &mut TypeList,
        offset: usize,
    ) -> Result<TypeId> {
        fn insert_arg<'a>(
            name: &'a str,
            arg: ComponentEntityType,
            args: &mut IndexMap<&'a str, ComponentEntityType>,
            offset: usize,
        ) -> Result<()> {
            if args.insert(name, arg).is_some() {
                bail!(
                    offset,
                    "duplicate component instantiation argument named `{name}`"
                );
            }

            Ok(())
        }

        let component_type_id = self.component_at(component_index, offset)?;
        let mut args = IndexMap::new();

        // Populate the arguments
        for component_arg in component_args {
            match component_arg.kind {
                ComponentExternalKind::Module => {
                    insert_arg(
                        component_arg.name,
                        ComponentEntityType::Module(self.module_at(component_arg.index, offset)?),
                        &mut args,
                        offset,
                    )?;
                }
                ComponentExternalKind::Component => {
                    insert_arg(
                        component_arg.name,
                        ComponentEntityType::Component(
                            self.component_at(component_arg.index, offset)?,
                        ),
                        &mut args,
                        offset,
                    )?;
                }
                ComponentExternalKind::Instance => {
                    insert_arg(
                        component_arg.name,
                        ComponentEntityType::Instance(
                            self.instance_at(component_arg.index, offset)?,
                        ),
                        &mut args,
                        offset,
                    )?;
                }
                ComponentExternalKind::Func => {
                    insert_arg(
                        component_arg.name,
                        ComponentEntityType::Func(self.function_at(component_arg.index, offset)?),
                        &mut args,
                        offset,
                    )?;
                }
                ComponentExternalKind::Value => {
                    insert_arg(
                        component_arg.name,
                        ComponentEntityType::Value(*self.value_at(component_arg.index, offset)?),
                        &mut args,
                        offset,
                    )?;
                }
                ComponentExternalKind::Type => {
                    // Type arguments are ignored
                }
            }
        }

        // Validate the arguments
        let component_type = types[component_type_id].as_component_type().unwrap();
        for (name, expected) in component_type.imports.iter() {
            match args.get(name.as_str()) {
                Some(arg) => {
                    match (arg, expected) {
                        (ComponentEntityType::Module(_), ComponentEntityType::Module(_))
                        | (ComponentEntityType::Component(_), ComponentEntityType::Component(_))
                        | (ComponentEntityType::Instance(_), ComponentEntityType::Instance(_))
                        | (ComponentEntityType::Func(_), ComponentEntityType::Func(_))
                        | (ComponentEntityType::Value(_), ComponentEntityType::Value(_)) => {}
                        _ => {
                            bail!(
                                offset,
                                "expected component instantiation argument \
                                 `{name}` to be of type `{}`",
                                expected.desc()
                            )
                        }
                    };

                    if !ComponentEntityType::internal_is_subtype_of(arg, types, expected, types) {
                        bail!(
                            offset,
                            "{} type mismatch for component instantiation argument `{name}`",
                            expected.desc(),
                        );
                    }
                }
                None => {
                    bail!(
                        offset,
                        "missing component instantiation argument named `{name}`"
                    );
                }
            }
        }

        let ty = Type::ComponentInstance(ComponentInstanceType {
            type_size: component_type
                .exports
                .iter()
                .fold(1, |acc, (_, ty)| acc + ty.type_size()),
            kind: ComponentInstanceTypeKind::Instantiated(component_type_id),
        });

        let id = TypeId {
            type_size: ty.type_size(),
            index: types.len(),
        };

        types.push(ty);

        Ok(id)
    }

    fn instantiate_exports(
        &mut self,
        exports: Vec<crate::ComponentExport>,
        types: &mut TypeList,
        offset: usize,
    ) -> Result<TypeId> {
        fn insert_export(
            name: &str,
            export: ComponentEntityType,
            exports: &mut IndexMap<String, ComponentEntityType>,
            type_size: &mut usize,
            offset: usize,
        ) -> Result<()> {
            *type_size = combine_type_sizes(*type_size, export.type_size(), offset)?;

            if exports.insert(name.to_string(), export).is_some() {
                bail!(
                    offset,
                    "duplicate instantiation export name `{name}` already defined",
                )
            }

            Ok(())
        }

        let mut type_size = 1;
        let mut inst_exports = IndexMap::new();
        for export in exports {
            match export.kind {
                ComponentExternalKind::Module => {
                    insert_export(
                        export.name,
                        ComponentEntityType::Module(self.module_at(export.index, offset)?),
                        &mut inst_exports,
                        &mut type_size,
                        offset,
                    )?;
                }
                ComponentExternalKind::Component => {
                    insert_export(
                        export.name,
                        ComponentEntityType::Component(self.component_at(export.index, offset)?),
                        &mut inst_exports,
                        &mut type_size,
                        offset,
                    )?;
                }
                ComponentExternalKind::Instance => {
                    insert_export(
                        export.name,
                        ComponentEntityType::Instance(self.instance_at(export.index, offset)?),
                        &mut inst_exports,
                        &mut type_size,
                        offset,
                    )?;
                }
                ComponentExternalKind::Func => {
                    insert_export(
                        export.name,
                        ComponentEntityType::Func(self.function_at(export.index, offset)?),
                        &mut inst_exports,
                        &mut type_size,
                        offset,
                    )?;
                }
                ComponentExternalKind::Value => {
                    insert_export(
                        export.name,
                        ComponentEntityType::Value(*self.value_at(export.index, offset)?),
                        &mut inst_exports,
                        &mut type_size,
                        offset,
                    )?;
                }
                ComponentExternalKind::Type => {
                    insert_export(
                        export.name,
                        ComponentEntityType::Type(self.type_at(export.index, false, offset)?),
                        &mut inst_exports,
                        &mut type_size,
                        offset,
                    )?;
                }
            }
        }

        let ty = Type::ComponentInstance(ComponentInstanceType {
            type_size,
            kind: ComponentInstanceTypeKind::Exports(inst_exports),
        });

        let id = TypeId {
            type_size: ty.type_size(),
            index: types.len(),
        };

        types.push(ty);

        Ok(id)
    }

    fn instantiate_core_exports(
        &mut self,
        exports: Vec<crate::Export>,
        types: &mut TypeList,
        offset: usize,
    ) -> Result<TypeId> {
        fn insert_export(
            name: &str,
            export: EntityType,
            exports: &mut IndexMap<String, EntityType>,
            type_size: &mut usize,
            offset: usize,
        ) -> Result<()> {
            *type_size = combine_type_sizes(*type_size, export.type_size(), offset)?;

            if exports.insert(name.to_string(), export).is_some() {
                bail!(
                    offset,
                    "duplicate instantiation export name `{name}` already defined",
                )
            }

            Ok(())
        }

        let mut type_size = 1;
        let mut inst_exports = IndexMap::new();
        for export in exports {
            match export.kind {
                ExternalKind::Func => {
                    insert_export(
                        export.name,
                        EntityType::Func(self.core_function_at(export.index, offset)?),
                        &mut inst_exports,
                        &mut type_size,
                        offset,
                    )?;
                }
                ExternalKind::Table => insert_export(
                    export.name,
                    EntityType::Table(*self.table_at(export.index, offset)?),
                    &mut inst_exports,
                    &mut type_size,
                    offset,
                )?,
                ExternalKind::Memory => insert_export(
                    export.name,
                    EntityType::Memory(*self.memory_at(export.index, offset)?),
                    &mut inst_exports,
                    &mut type_size,
                    offset,
                )?,
                ExternalKind::Global => {
                    insert_export(
                        export.name,
                        EntityType::Global(*self.global_at(export.index, offset)?),
                        &mut inst_exports,
                        &mut type_size,
                        offset,
                    )?;
                }
                ExternalKind::Tag => insert_export(
                    export.name,
                    EntityType::Tag(self.core_function_at(export.index, offset)?),
                    &mut inst_exports,
                    &mut type_size,
                    offset,
                )?,
            }
        }

        let ty = Type::Instance(InstanceType {
            type_size,
            kind: InstanceTypeKind::Exports(inst_exports),
        });

        let id = TypeId {
            type_size: ty.type_size(),
            index: types.len(),
        };

        types.push(ty);

        Ok(id)
    }

    fn alias_core_instance_export(
        &mut self,
        instance_index: u32,
        kind: ExternalKind,
        name: &str,
        types: &TypeList,
        offset: usize,
    ) -> Result<()> {
        macro_rules! push_module_export {
            ($expected:path, $collection:ident, $ty:literal) => {{
                match self.core_instance_export(instance_index, name, types, offset)? {
                    $expected(ty) => {
                        self.$collection.push(*ty);
                        Ok(())
                    }
                    _ => {
                        bail!(
                            offset,
                            "export `{name}` for core instance {instance_index} is not a {}",
                            $ty
                        )
                    }
                }
            }};
        }

        match kind {
            ExternalKind::Func => {
                check_max(
                    self.function_count(),
                    1,
                    MAX_WASM_FUNCTIONS,
                    "functions",
                    offset,
                )?;
                push_module_export!(EntityType::Func, core_funcs, "function")
            }
            ExternalKind::Table => {
                check_max(self.core_tables.len(), 1, MAX_WASM_TABLES, "tables", offset)?;
                push_module_export!(EntityType::Table, core_tables, "table")
            }
            ExternalKind::Memory => {
                check_max(
                    self.core_memories.len(),
                    1,
                    MAX_WASM_MEMORIES,
                    "memories",
                    offset,
                )?;
                push_module_export!(EntityType::Memory, core_memories, "memory")
            }
            ExternalKind::Global => {
                check_max(
                    self.core_globals.len(),
                    1,
                    MAX_WASM_GLOBALS,
                    "globals",
                    offset,
                )?;
                push_module_export!(EntityType::Global, core_globals, "global")
            }
            ExternalKind::Tag => {
                check_max(self.core_tags.len(), 1, MAX_WASM_TAGS, "tags", offset)?;
                push_module_export!(EntityType::Tag, core_tags, "tag")
            }
        }
    }

    fn alias_instance_export(
        &mut self,
        instance_index: u32,
        kind: ComponentExternalKind,
        name: &str,
        types: &TypeList,
        offset: usize,
    ) -> Result<()> {
        macro_rules! push_component_export {
            ($expected:path, $collection:ident, $ty:literal) => {{
                match self.instance_export(instance_index, name, types, offset)? {
                    $expected(ty) => {
                        self.$collection.push(*ty);
                        Ok(())
                    }
                    _ => {
                        bail!(
                            offset,
                            "export `{name}` for instance {instance_index} is not a {}",
                            $ty
                        )
                    }
                }
            }};
        }

        match kind {
            ComponentExternalKind::Module => {
                check_max(
                    self.core_modules.len(),
                    1,
                    MAX_WASM_MODULES,
                    "modules",
                    offset,
                )?;
                push_component_export!(ComponentEntityType::Module, core_modules, "module")
            }
            ComponentExternalKind::Component => {
                check_max(
                    self.components.len(),
                    1,
                    MAX_WASM_COMPONENTS,
                    "components",
                    offset,
                )?;
                push_component_export!(ComponentEntityType::Component, components, "component")
            }
            ComponentExternalKind::Instance => {
                check_max(
                    self.instance_count(),
                    1,
                    MAX_WASM_INSTANCES,
                    "instances",
                    offset,
                )?;
                push_component_export!(ComponentEntityType::Instance, instances, "instance")
            }
            ComponentExternalKind::Func => {
                check_max(
                    self.function_count(),
                    1,
                    MAX_WASM_FUNCTIONS,
                    "functions",
                    offset,
                )?;
                push_component_export!(ComponentEntityType::Func, funcs, "function")
            }
            ComponentExternalKind::Value => {
                check_max(self.values.len(), 1, MAX_WASM_VALUES, "values", offset)?;
                match self.instance_export(instance_index, name, types, offset)? {
                    ComponentEntityType::Value(ty) => {
                        self.values.push((*ty, false));
                        Ok(())
                    }
                    _ => bail!(
                        offset,
                        "export `{name}` for instance {instance_index} is not a value",
                    ),
                }
            }
            ComponentExternalKind::Type => {
                check_max(self.type_count(), 1, MAX_WASM_TYPES, "types", offset)?;
                push_component_export!(ComponentEntityType::Type, types, "type")
            }
        }
    }

    fn alias_module(components: &mut [Self], count: u32, index: u32, offset: usize) -> Result<()> {
        let component = Self::check_alias_count(components, count, offset)?;
        let ty = component.module_at(index, offset)?;

        let current = components.last_mut().unwrap();
        check_max(
            current.core_modules.len(),
            1,
            MAX_WASM_MODULES,
            "modules",
            offset,
        )?;

        current.core_modules.push(ty);
        Ok(())
    }

    fn alias_component(
        components: &mut [Self],
        count: u32,
        index: u32,
        offset: usize,
    ) -> Result<()> {
        let component = Self::check_alias_count(components, count, offset)?;
        let ty = component.component_at(index, offset)?;

        let current = components.last_mut().unwrap();
        check_max(
            current.components.len(),
            1,
            MAX_WASM_COMPONENTS,
            "components",
            offset,
        )?;

        current.components.push(ty);
        Ok(())
    }

    fn alias_core_type(
        components: &mut [Self],
        count: u32,
        index: u32,
        offset: usize,
    ) -> Result<()> {
        let component = Self::check_alias_count(components, count, offset)?;
        let ty = component.type_at(index, true, offset)?;

        let current = components.last_mut().unwrap();
        check_max(current.type_count(), 1, MAX_WASM_TYPES, "types", offset)?;

        current.core_types.push(ty);
        Ok(())
    }

    fn alias_type(components: &mut [Self], count: u32, index: u32, offset: usize) -> Result<()> {
        let component = Self::check_alias_count(components, count, offset)?;
        let ty = component.type_at(index, false, offset)?;

        let current = components.last_mut().unwrap();
        check_max(current.type_count(), 1, MAX_WASM_TYPES, "types", offset)?;

        current.types.push(ty);
        Ok(())
    }

    fn check_alias_count(components: &[Self], count: u32, offset: usize) -> Result<&Self> {
        let count = count as usize;
        if count >= components.len() {
            bail!(offset, "invalid outer alias count of {count}");
        }

        Ok(&components[components.len() - count - 1])
    }

    fn create_defined_type(
        &self,
        ty: crate::ComponentDefinedType,
        types: &TypeList,
        offset: usize,
    ) -> Result<ComponentDefinedType> {
        match ty {
            crate::ComponentDefinedType::Primitive(ty) => Ok(ComponentDefinedType::Primitive(ty)),
            crate::ComponentDefinedType::Record(fields) => {
                self.create_record_type(fields.as_ref(), types, offset)
            }
            crate::ComponentDefinedType::Variant(cases) => {
                self.create_variant_type(cases.as_ref(), types, offset)
            }
            crate::ComponentDefinedType::List(ty) => Ok(ComponentDefinedType::List(
                self.create_component_val_type(ty, types, offset)?,
            )),
            crate::ComponentDefinedType::Tuple(tys) => {
                self.create_tuple_type(tys.as_ref(), types, offset)
            }
            crate::ComponentDefinedType::Flags(names) => {
                self.create_flags_type(names.as_ref(), offset)
            }
            crate::ComponentDefinedType::Enum(cases) => {
                self.create_enum_type(cases.as_ref(), offset)
            }
            crate::ComponentDefinedType::Union(tys) => {
                self.create_union_type(tys.as_ref(), types, offset)
            }
            crate::ComponentDefinedType::Option(ty) => Ok(ComponentDefinedType::Option(
                self.create_component_val_type(ty, types, offset)?,
            )),
            crate::ComponentDefinedType::Result { ok, err } => Ok(ComponentDefinedType::Result {
                ok: ok
                    .map(|ty| self.create_component_val_type(ty, types, offset))
                    .transpose()?,
                err: err
                    .map(|ty| self.create_component_val_type(ty, types, offset))
                    .transpose()?,
            }),
        }
    }

    fn create_record_type(
        &self,
        fields: &[(&str, crate::ComponentValType)],
        types: &TypeList,
        offset: usize,
    ) -> Result<ComponentDefinedType> {
        let mut type_size = 1;
        let mut field_map = IndexMap::with_capacity(fields.len());

        for (name, ty) in fields {
            Self::check_name(name, "record field", offset)?;
            let ty = self.create_component_val_type(*ty, types, offset)?;
            type_size = combine_type_sizes(type_size, ty.type_size(), offset)?;

            if field_map.insert(name.to_string(), ty).is_some() {
                bail!(offset, "duplicate field named `{name}` in record type");
            }
        }

        Ok(ComponentDefinedType::Record(RecordType {
            type_size,
            fields: field_map,
        }))
    }

    fn create_variant_type(
        &self,
        cases: &[crate::VariantCase],
        types: &TypeList,
        offset: usize,
    ) -> Result<ComponentDefinedType> {
        let mut type_size = 1;
        let mut case_map = IndexMap::with_capacity(cases.len());

        if cases.is_empty() {
            return Err(BinaryReaderError::new(
                "variant type must have at least one case",
                offset,
            ));
        }

        if cases.len() > u32::MAX as usize {
            return Err(BinaryReaderError::new(
                "variant type cannot be represented with a 32-bit discriminant value",
                offset,
            ));
        }

        for (i, case) in cases.iter().enumerate() {
            Self::check_name(case.name, "variant case", offset)?;
            if let Some(refines) = case.refines {
                if refines >= i as u32 {
                    return Err(BinaryReaderError::new(
                        "variant case can only refine a previously defined case",
                        offset,
                    ));
                }
            }
            let ty = case
                .ty
                .map(|ty| self.create_component_val_type(ty, types, offset))
                .transpose()?;

            type_size =
                combine_type_sizes(type_size, ty.map(|ty| ty.type_size()).unwrap_or(1), offset)?;

            if case_map
                .insert(
                    case.name.to_string(),
                    VariantCase {
                        ty,
                        refines: case.refines.map(|i| cases[i as usize].name.to_string()),
                    },
                )
                .is_some()
            {
                bail!(
                    offset,
                    "duplicate case named `{}` in variant type",
                    case.name,
                );
            }
        }

        Ok(ComponentDefinedType::Variant(VariantType {
            type_size,
            cases: case_map,
        }))
    }

    fn create_tuple_type(
        &self,
        tys: &[crate::ComponentValType],
        types: &TypeList,
        offset: usize,
    ) -> Result<ComponentDefinedType> {
        let mut type_size = 1;
        let types = tys
            .iter()
            .map(|ty| {
                let ty = self.create_component_val_type(*ty, types, offset)?;
                type_size = combine_type_sizes(type_size, ty.type_size(), offset)?;
                Ok(ty)
            })
            .collect::<Result<_>>()?;

        Ok(ComponentDefinedType::Tuple(TupleType { type_size, types }))
    }

    fn create_flags_type(&self, names: &[&str], offset: usize) -> Result<ComponentDefinedType> {
        let mut names_set = IndexSet::with_capacity(names.len());

        for name in names {
            Self::check_name(name, "flag", offset)?;
            if !names_set.insert(name.to_string()) {
                bail!(offset, "duplicate flag named `{name}`");
            }
        }

        Ok(ComponentDefinedType::Flags(names_set))
    }

    fn create_enum_type(&self, cases: &[&str], offset: usize) -> Result<ComponentDefinedType> {
        if cases.len() > u32::MAX as usize {
            return Err(BinaryReaderError::new(
                "enumeration type cannot be represented with a 32-bit discriminant value",
                offset,
            ));
        }

        let mut tags = IndexSet::with_capacity(cases.len());

        for tag in cases {
            Self::check_name(tag, "enum tag", offset)?;
            if !tags.insert(tag.to_string()) {
                bail!(offset, "duplicate enum tag named `{tag}`");
            }
        }

        Ok(ComponentDefinedType::Enum(tags))
    }

    fn create_union_type(
        &self,
        tys: &[crate::ComponentValType],
        types: &TypeList,
        offset: usize,
    ) -> Result<ComponentDefinedType> {
        let mut type_size = 1;
        let types = tys
            .iter()
            .map(|ty| {
                let ty = self.create_component_val_type(*ty, types, offset)?;
                type_size = combine_type_sizes(type_size, ty.type_size(), offset)?;
                Ok(ty)
            })
            .collect::<Result<_>>()?;

        Ok(ComponentDefinedType::Union(UnionType { type_size, types }))
    }

    fn create_component_val_type(
        &self,
        ty: crate::ComponentValType,
        types: &TypeList,
        offset: usize,
    ) -> Result<ComponentValType> {
        Ok(match ty {
            crate::ComponentValType::Primitive(pt) => ComponentValType::Primitive(pt),
            crate::ComponentValType::Type(idx) => {
                ComponentValType::Type(self.defined_type_at(idx, types, offset)?)
            }
        })
    }

    pub fn type_at(&self, idx: u32, core: bool, offset: usize) -> Result<TypeId> {
        let types = if core { &self.core_types } else { &self.types };
        types
            .get(idx as usize)
            .copied()
            .ok_or_else(|| format_err!(offset, "unknown type {idx}: type index out of bounds"))
    }

    fn function_type_at<'a>(
        &self,
        idx: u32,
        types: &'a TypeList,
        offset: usize,
    ) -> Result<&'a ComponentFuncType> {
        types[self.type_at(idx, false, offset)?]
            .as_component_func_type()
            .ok_or_else(|| format_err!(offset, "type index {idx} is not a function type"))
    }

    fn function_at(&self, idx: u32, offset: usize) -> Result<TypeId> {
        self.funcs.get(idx as usize).copied().ok_or_else(|| {
            format_err!(
                offset,
                "unknown function {idx}: function index out of bounds"
            )
        })
    }

    fn component_at(&self, idx: u32, offset: usize) -> Result<TypeId> {
        self.components.get(idx as usize).copied().ok_or_else(|| {
            format_err!(
                offset,
                "unknown component {idx}: component index out of bounds"
            )
        })
    }

    fn instance_at(&self, idx: u32, offset: usize) -> Result<TypeId> {
        self.instances.get(idx as usize).copied().ok_or_else(|| {
            format_err!(
                offset,
                "unknown instance {idx}: instance index out of bounds"
            )
        })
    }

    fn instance_export<'a>(
        &self,
        instance_index: u32,
        name: &str,
        types: &'a TypeList,
        offset: usize,
    ) -> Result<&'a ComponentEntityType> {
        match types[self.instance_at(instance_index, offset)?]
            .as_component_instance_type()
            .unwrap()
            .internal_exports(types)
            .get(name)
        {
            Some(export) => Ok(export),
            None => bail!(
                offset,
                "instance {instance_index} has no export named `{name}`"
            ),
        }
    }

    fn value_at(&mut self, idx: u32, offset: usize) -> Result<&ComponentValType> {
        match self.values.get_mut(idx as usize) {
            Some((ty, used)) if !*used => {
                *used = true;
                Ok(ty)
            }
            Some(_) => bail!(offset, "value {idx} cannot be used more than once"),
            None => bail!(offset, "unknown value {idx}: value index out of bounds"),
        }
    }

    fn defined_type_at(&self, idx: u32, types: &TypeList, offset: usize) -> Result<TypeId> {
        let id = self.type_at(idx, false, offset)?;
        match &types[id] {
            Type::Defined(_) => Ok(id),
            _ => bail!(offset, "type index {} is not a defined type", id.index),
        }
    }

    fn core_function_at(&self, idx: u32, offset: usize) -> Result<TypeId> {
        match self.core_funcs.get(idx as usize) {
            Some(id) => Ok(*id),
            None => bail!(
                offset,
                "unknown core function {idx}: function index out of bounds"
            ),
        }
    }

    fn module_at(&self, idx: u32, offset: usize) -> Result<TypeId> {
        match self.core_modules.get(idx as usize) {
            Some(id) => Ok(*id),
            None => bail!(offset, "unknown module {idx}: module index out of bounds"),
        }
    }

    fn core_instance_at(&self, idx: u32, offset: usize) -> Result<TypeId> {
        match self.core_instances.get(idx as usize) {
            Some(id) => Ok(*id),
            None => bail!(
                offset,
                "unknown core instance {idx}: instance index out of bounds"
            ),
        }
    }

    fn core_instance_export<'a>(
        &self,
        instance_index: u32,
        name: &str,
        types: &'a TypeList,
        offset: usize,
    ) -> Result<&'a EntityType> {
        match types[self.core_instance_at(instance_index, offset)?]
            .as_instance_type()
            .unwrap()
            .internal_exports(types)
            .get(name)
        {
            Some(export) => Ok(export),
            None => bail!(
                offset,
                "core instance {instance_index} has no export named `{name}`"
            ),
        }
    }

    fn global_at(&self, idx: u32, offset: usize) -> Result<&GlobalType> {
        match self.core_globals.get(idx as usize) {
            Some(t) => Ok(t),
            None => bail!(offset, "unknown global {idx}: global index out of bounds"),
        }
    }

    fn table_at(&self, idx: u32, offset: usize) -> Result<&TableType> {
        match self.core_tables.get(idx as usize) {
            Some(t) => Ok(t),
            None => bail!(offset, "unknown table {idx}: table index out of bounds"),
        }
    }

    fn memory_at(&self, idx: u32, offset: usize) -> Result<&MemoryType> {
        match self.core_memories.get(idx as usize) {
            Some(t) => Ok(t),
            None => bail!(offset, "unknown memory {idx}: memory index out of bounds"),
        }
    }
}

impl Default for ComponentState {
    fn default() -> Self {
        Self {
            core_types: Default::default(),
            core_modules: Default::default(),
            core_instances: Default::default(),
            core_funcs: Default::default(),
            core_memories: Default::default(),
            core_tables: Default::default(),
            core_globals: Default::default(),
            core_tags: Default::default(),
            types: Default::default(),
            funcs: Default::default(),
            values: Default::default(),
            instances: Default::default(),
            components: Default::default(),
            imports: Default::default(),
            exports: Default::default(),
            has_start: Default::default(),
            type_size: 1,
        }
    }
}