//! Generation of Wasm
//! [components](https://github.com/WebAssembly/component-model).

// FIXME(#1000): component support in `wasm-smith` is a work in progress.
#![allow(unused_variables, dead_code)]

use crate::{Config, arbitrary_loop};
use arbitrary::{Arbitrary, Result, Unstructured};
use std::collections::BTreeMap;
use std::{
    collections::{HashMap, HashSet},
    rc::Rc,
};
use wasm_encoder::{
    ComponentTypeRef, ComponentValType, HeapType, PrimitiveValType, RefType, TypeBounds, ValType,
};

mod encode;

/// A pseudo-random WebAssembly [component].
///
/// Construct instances of this type with [the `Arbitrary`
/// trait](https://docs.rs/arbitrary/*/arbitrary/trait.Arbitrary.html).
///
/// [component]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md
///
/// ## Configured Generated Components
///
/// The `Arbitrary` implementation uses the [`Config::default()`][crate::Config]
/// configuration. If you want to customize the shape of generated components,
/// create your own [`Config`][crate::Config] instance and pass it to
/// [`Component::new`][crate::Component::new].
#[derive(Debug)]
pub struct Component {
    sections: Vec<Section>,
}

/// A builder to create a component (and possibly a whole tree of nested
/// components).
///
/// Maintains a stack of components we are currently building, as well as
/// metadata about them. The split between `Component` and `ComponentBuilder` is
/// that the builder contains metadata that is purely used when generating
/// components and is unnecessary after we are done generating the structure of
/// the components and only need to encode an already-generated component to
/// bytes.
#[derive(Debug)]
struct ComponentBuilder {
    config: Config,

    // The set of core `valtype`s that we are configured to generate.
    core_valtypes: Vec<ValType>,

    // Stack of types scopes that are currently available.
    //
    // There is an entry in this stack for each component, but there can also be
    // additional entries for module/component/instance types, each of which
    // have their own scope.
    //
    // This stack is always non-empty and the last entry is always the current
    // scope.
    //
    // When a particular scope can alias outer types, it can alias from any
    // scope that is older than it (i.e. `types_scope[i]` can alias from
    // `types_scope[j]` when `j <= i`).
    types: Vec<TypesScope>,

    // The set of components we are currently building and their associated
    // metadata.
    components: Vec<ComponentContext>,

    // Whether we are in the final bits of generating this component and we just
    // need to ensure that the minimum number of entities configured have all
    // been generated. This changes the behavior of various
    // `arbitrary_<section>` methods to always fill in their minimums.
    fill_minimums: bool,

    // Our maximums for these entities are applied across the whole component
    // tree, not per-component.
    total_components: usize,
    total_modules: usize,
    total_instances: usize,
    total_values: usize,
}

#[derive(Debug, Clone)]
enum ComponentOrCoreFuncType {
    Component(Rc<FuncType>),
    Core(Rc<crate::core::FuncType>),
}

impl ComponentOrCoreFuncType {
    fn as_core(&self) -> &Rc<crate::core::FuncType> {
        match self {
            ComponentOrCoreFuncType::Core(t) => t,
            ComponentOrCoreFuncType::Component(_) => panic!("not a core func type"),
        }
    }

    fn as_component(&self) -> &Rc<FuncType> {
        match self {
            ComponentOrCoreFuncType::Core(_) => panic!("not a component func type"),
            ComponentOrCoreFuncType::Component(t) => t,
        }
    }
}

#[derive(Debug, Clone)]
enum ComponentOrCoreInstanceType {
    Component(Rc<InstanceType>),
    Core(BTreeMap<String, crate::core::EntityType>),
}

/// Metadata (e.g. contents of various index spaces) we keep track of on a
/// per-component basis.
#[derive(Debug)]
struct ComponentContext {
    // The actual component itself.
    component: Component,

    // The number of imports we have generated thus far.
    num_imports: usize,

    // The set of names of imports we've generated thus far.
    import_names: HashSet<String>,

    // The set of URLs of imports we've generated thus far.
    import_urls: HashSet<String>,

    // This component's function index space.
    funcs: Vec<ComponentOrCoreFuncType>,

    // Which entries in `funcs` are component functions?
    component_funcs: Vec<u32>,

    // Which entries in `component_funcs` are component functions that only use scalar
    // types?
    scalar_component_funcs: Vec<u32>,

    // Which entries in `funcs` are core Wasm functions?
    //
    // Note that a component can't import core functions, so these entries will
    // never point to a `Section::Import`.
    core_funcs: Vec<u32>,

    // This component's component index space.
    //
    // An indirect list of all directly-nested (not transitive) components
    // inside this component.
    //
    // Each entry is of the form `(i, j)` where `component.sections[i]` is
    // guaranteed to be either
    //
    // * a `Section::Component` and we are referencing the component defined in
    //   that section (in this case `j` must also be `0`, since a component
    //   section can only contain a single nested component), or
    //
    // * a `Section::Import` and we are referencing the `j`th import in that
    //   section, which is guaranteed to be a component import.
    components: Vec<(usize, usize)>,

    // This component's module index space.
    //
    // An indirect list of all directly-nested (not transitive) modules
    // inside this component.
    //
    // Each entry is of the form `(i, j)` where `component.sections[i]` is
    // guaranteed to be either
    //
    // * a `Section::Core` and we are referencing the module defined in that
    //   section (in this case `j` must also be `0`, since a core section can
    //   only contain a single nested module), or
    //
    // * a `Section::Import` and we are referencing the `j`th import in that
    //   section, which is guaranteed to be a module import.
    modules: Vec<(usize, usize)>,

    // This component's instance index space.
    instances: Vec<ComponentOrCoreInstanceType>,

    // This component's value index space.
    values: Vec<ComponentValType>,
}

impl ComponentContext {
    fn empty() -> Self {
        ComponentContext {
            component: Component::empty(),
            num_imports: 0,
            import_names: HashSet::default(),
            import_urls: HashSet::default(),
            funcs: vec![],
            component_funcs: vec![],
            scalar_component_funcs: vec![],
            core_funcs: vec![],
            components: vec![],
            modules: vec![],
            instances: vec![],
            values: vec![],
        }
    }

    fn num_modules(&self) -> usize {
        self.modules.len()
    }

    fn num_components(&self) -> usize {
        self.components.len()
    }

    fn num_instances(&self) -> usize {
        self.instances.len()
    }

    fn num_funcs(&self) -> usize {
        self.funcs.len()
    }

    fn num_values(&self) -> usize {
        self.values.len()
    }
}

#[derive(Debug, Default)]
struct TypesScope {
    // All core types in this scope, regardless of kind.
    core_types: Vec<Rc<CoreType>>,

    // The indices of all the entries in `core_types` that are core function types.
    core_func_types: Vec<u32>,

    // The indices of all the entries in `core_types` that are module types.
    module_types: Vec<u32>,

    // All component types in this index space, regardless of kind.
    types: Vec<Rc<Type>>,

    // The indices of all the entries in `types` that are defined value types.
    defined_types: Vec<u32>,

    // The indices of all the entries in `types` that are func types.
    func_types: Vec<u32>,

    // A map from function types to their indices in the types space.
    func_type_to_indices: HashMap<Rc<FuncType>, Vec<u32>>,

    // The indices of all the entries in `types` that are component types.
    component_types: Vec<u32>,

    // The indices of all the entries in `types` that are instance types.
    instance_types: Vec<u32>,
}

impl TypesScope {
    fn push(&mut self, ty: Rc<Type>) -> u32 {
        let ty_idx = u32::try_from(self.types.len()).unwrap();

        let kind_list = match &*ty {
            Type::Defined(_) => &mut self.defined_types,
            Type::Func(func_ty) => {
                self.func_type_to_indices
                    .entry(func_ty.clone())
                    .or_default()
                    .push(ty_idx);
                &mut self.func_types
            }
            Type::Component(_) => &mut self.component_types,
            Type::Instance(_) => &mut self.instance_types,
        };
        kind_list.push(ty_idx);

        self.types.push(ty);
        ty_idx
    }

    fn push_core(&mut self, ty: Rc<CoreType>) -> u32 {
        let ty_idx = u32::try_from(self.core_types.len()).unwrap();

        let kind_list = match &*ty {
            CoreType::Func(_) => &mut self.core_func_types,
            CoreType::Module(_) => &mut self.module_types,
        };
        kind_list.push(ty_idx);

        self.core_types.push(ty);
        ty_idx
    }

    fn get(&self, index: u32) -> &Rc<Type> {
        &self.types[index as usize]
    }

    fn get_core(&self, index: u32) -> &Rc<CoreType> {
        &self.core_types[index as usize]
    }

    fn get_func(&self, index: u32) -> &Rc<FuncType> {
        match &**self.get(index) {
            Type::Func(f) => f,
            _ => panic!("get_func on non-function type"),
        }
    }

    fn can_ref_type(&self) -> bool {
        // All component types and core module types may be referenced
        !self.types.is_empty() || !self.module_types.is_empty()
    }
}

impl<'a> Arbitrary<'a> for Component {
    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
        Component::new(Config::default(), u)
    }
}

#[derive(Default)]
struct EntityCounts {
    globals: usize,
    tables: usize,
    memories: usize,
    tags: usize,
    funcs: usize,
}

impl Component {
    /// Construct a new `Component` using the given configuration.
    pub fn new(config: Config, u: &mut Unstructured) -> Result<Self> {
        let mut builder = ComponentBuilder::new(config);
        builder.build(u)
    }

    fn empty() -> Self {
        Component { sections: vec![] }
    }
}

#[must_use]
enum Step {
    Finished(Component),
    StillBuilding,
}

impl Step {
    fn unwrap_still_building(self) {
        match self {
            Step::Finished(_) => panic!(
                "`Step::unwrap_still_building` called on a `Step` that is not `StillBuilding`"
            ),
            Step::StillBuilding => {}
        }
    }
}

impl ComponentBuilder {
    fn new(config: Config) -> Self {
        ComponentBuilder {
            config,
            core_valtypes: vec![],
            types: vec![Default::default()],
            components: vec![ComponentContext::empty()],
            fill_minimums: false,
            total_components: 0,
            total_modules: 0,
            total_instances: 0,
            total_values: 0,
        }
    }

    fn build(&mut self, u: &mut Unstructured) -> Result<Component> {
        self.core_valtypes = crate::core::configured_valtypes(&self.config);

        let mut choices: Vec<fn(&mut ComponentBuilder, &mut Unstructured) -> Result<Step>> = vec![];

        loop {
            choices.clear();
            choices.push(Self::finish_component);

            // Only add any choice other than "finish what we've generated thus
            // far" when there is more arbitrary fuzzer data for us to consume.
            if !u.is_empty() {
                choices.push(Self::arbitrary_custom_section);

                // NB: we add each section as a choice even if we've already
                // generated our maximum number of entities in that section so that
                // we can exercise adding empty sections to the end of the module.
                choices.push(Self::arbitrary_core_type_section);
                choices.push(Self::arbitrary_type_section);
                choices.push(Self::arbitrary_import_section);
                choices.push(Self::arbitrary_canonical_section);

                if self.total_modules < self.config.max_modules {
                    choices.push(Self::arbitrary_core_module_section);
                }

                if self.components.len() < self.config.max_nesting_depth
                    && self.total_components < self.config.max_components
                {
                    choices.push(Self::arbitrary_component_section);
                }

                // FIXME(#1000)
                //
                // choices.push(Self::arbitrary_instance_section);
                // choices.push(Self::arbitrary_export_section);
                // choices.push(Self::arbitrary_start_section);
                // choices.push(Self::arbitrary_alias_section);
            }

            let f = u.choose(&choices)?;
            match f(self, u)? {
                Step::StillBuilding => {}
                Step::Finished(component) => {
                    if self.components.is_empty() {
                        // If we just finished the root component, then return it.
                        return Ok(component);
                    } else {
                        // Otherwise, add it as a nested component in the parent.
                        self.push_section(Section::Component(component));
                    }
                }
            }
        }
    }

    fn finish_component(&mut self, u: &mut Unstructured) -> Result<Step> {
        // Ensure we've generated all of our minimums.
        self.fill_minimums = true;
        {
            if self.current_type_scope().types.len() < self.config.min_types {
                self.arbitrary_type_section(u)?.unwrap_still_building();
            }
            if self.component().num_imports < self.config.min_imports {
                self.arbitrary_import_section(u)?.unwrap_still_building();
            }
            if self.component().funcs.len() < self.config.min_funcs {
                self.arbitrary_canonical_section(u)?.unwrap_still_building();
            }
        }
        self.fill_minimums = false;

        self.types
            .pop()
            .expect("should have a types scope for the component we are finishing");
        Ok(Step::Finished(self.components.pop().unwrap().component))
    }

    fn component(&self) -> &ComponentContext {
        self.components.last().unwrap()
    }

    fn component_mut(&mut self) -> &mut ComponentContext {
        self.components.last_mut().unwrap()
    }

    fn last_section(&self) -> Option<&Section> {
        self.component().component.sections.last()
    }

    fn last_section_mut(&mut self) -> Option<&mut Section> {
        self.component_mut().component.sections.last_mut()
    }

    fn push_section(&mut self, section: Section) {
        self.component_mut().component.sections.push(section);
    }

    fn ensure_section(
        &mut self,
        mut predicate: impl FnMut(&Section) -> bool,
        mut make_section: impl FnMut() -> Section,
    ) -> &mut Section {
        match self.last_section() {
            Some(sec) if predicate(sec) => {}
            _ => self.push_section(make_section()),
        }
        self.last_section_mut().unwrap()
    }

    fn arbitrary_custom_section(&mut self, u: &mut Unstructured) -> Result<Step> {
        self.push_section(Section::Custom(u.arbitrary()?));
        Ok(Step::StillBuilding)
    }

    fn push_type(&mut self, ty: Rc<Type>) -> u32 {
        match self.ensure_section(
            |s| matches!(s, Section::Type(_)),
            || Section::Type(TypeSection { types: vec![] }),
        ) {
            Section::Type(TypeSection { types }) => {
                types.push(ty.clone());
                self.current_type_scope_mut().push(ty)
            }
            _ => unreachable!(),
        }
    }

    fn push_core_type(&mut self, ty: Rc<CoreType>) -> u32 {
        match self.ensure_section(
            |s| matches!(s, Section::CoreType(_)),
            || Section::CoreType(CoreTypeSection { types: vec![] }),
        ) {
            Section::CoreType(CoreTypeSection { types }) => {
                types.push(ty.clone());
                self.current_type_scope_mut().push_core(ty)
            }
            _ => unreachable!(),
        }
    }

    fn arbitrary_core_type_section(&mut self, u: &mut Unstructured) -> Result<Step> {
        self.push_section(Section::CoreType(CoreTypeSection { types: vec![] }));

        let min = if self.fill_minimums {
            self.config
                .min_types
                .saturating_sub(self.current_type_scope().types.len())
        } else {
            0
        };

        let max = self.config.max_types - self.current_type_scope().types.len();

        arbitrary_loop(u, min, max, |u| {
            let mut type_fuel = self.config.max_type_size;
            let ty = self.arbitrary_core_type(u, &mut type_fuel)?;
            self.push_core_type(ty);
            Ok(true)
        })?;

        Ok(Step::StillBuilding)
    }

    fn arbitrary_core_type(
        &self,
        u: &mut Unstructured,
        type_fuel: &mut u32,
    ) -> Result<Rc<CoreType>> {
        *type_fuel = type_fuel.saturating_sub(1);
        if *type_fuel == 0 {
            return Ok(Rc::new(CoreType::Module(Rc::new(ModuleType::default()))));
        }

        let ty = match u.int_in_range::<u8>(0..=1)? {
            0 => CoreType::Func(arbitrary_func_type(
                u,
                &self.config,
                &self.core_valtypes,
                if self.config.multi_value_enabled {
                    None
                } else {
                    Some(1)
                },
                0,
            )?),
            1 => CoreType::Module(self.arbitrary_module_type(u, type_fuel)?),
            _ => unreachable!(),
        };
        Ok(Rc::new(ty))
    }

    fn arbitrary_type_section(&mut self, u: &mut Unstructured) -> Result<Step> {
        self.push_section(Section::Type(TypeSection { types: vec![] }));

        let min = if self.fill_minimums {
            self.config
                .min_types
                .saturating_sub(self.current_type_scope().types.len())
        } else {
            0
        };

        let max = self.config.max_types - self.current_type_scope().types.len();

        arbitrary_loop(u, min, max, |u| {
            let mut type_fuel = self.config.max_type_size;
            let ty = self.arbitrary_type(u, &mut type_fuel)?;
            self.push_type(ty);
            Ok(true)
        })?;

        Ok(Step::StillBuilding)
    }

    fn arbitrary_type_ref<'a>(
        &self,
        u: &mut Unstructured<'a>,
        for_import: bool,
        for_type_def: bool,
    ) -> Result<Option<ComponentTypeRef>> {
        let mut choices: Vec<fn(&Self, &mut Unstructured) -> Result<ComponentTypeRef>> = Vec::new();
        let scope = self.current_type_scope();

        if !scope.module_types.is_empty()
            && (for_type_def || !for_import || self.total_modules < self.config.max_modules)
        {
            choices.push(|me, u| {
                Ok(ComponentTypeRef::Module(
                    *u.choose(&me.current_type_scope().module_types)?,
                ))
            });
        }

        // Types cannot be imported currently
        if !for_import
            && !scope.types.is_empty()
            && (for_type_def || scope.types.len() < self.config.max_types)
        {
            choices.push(|me, u| {
                Ok(ComponentTypeRef::Type(TypeBounds::Eq(u.int_in_range(
                    0..=u32::try_from(me.current_type_scope().types.len() - 1).unwrap(),
                )?)))
            });
        }

        // TODO: wasm-smith needs to ensure that every arbitrary value gets used exactly once.
        //       until that time, don't import values
        // if for_type_def || !for_import || self.total_values < self.config.max_values() {
        //     choices.push(|me, u| Ok(ComponentTypeRef::Value(me.arbitrary_component_val_type(u)?)));
        // }

        if !scope.func_types.is_empty()
            && (for_type_def || !for_import || self.component().num_funcs() < self.config.max_funcs)
        {
            choices.push(|me, u| {
                Ok(ComponentTypeRef::Func(
                    *u.choose(&me.current_type_scope().func_types)?,
                ))
            });
        }

        if !scope.component_types.is_empty()
            && (for_type_def || !for_import || self.total_components < self.config.max_components)
        {
            choices.push(|me, u| {
                Ok(ComponentTypeRef::Component(
                    *u.choose(&me.current_type_scope().component_types)?,
                ))
            });
        }

        if !scope.instance_types.is_empty()
            && (for_type_def || !for_import || self.total_instances < self.config.max_instances)
        {
            choices.push(|me, u| {
                Ok(ComponentTypeRef::Instance(
                    *u.choose(&me.current_type_scope().instance_types)?,
                ))
            });
        }

        if choices.is_empty() {
            return Ok(None);
        }

        let f = u.choose(&choices)?;
        f(self, u).map(Option::Some)
    }

    fn arbitrary_type(&mut self, u: &mut Unstructured, type_fuel: &mut u32) -> Result<Rc<Type>> {
        *type_fuel = type_fuel.saturating_sub(1);
        if *type_fuel == 0 {
            return Ok(Rc::new(Type::Defined(
                self.arbitrary_defined_type(u, type_fuel)?,
            )));
        }

        let ty = match u.int_in_range::<u8>(0..=3)? {
            0 => Type::Defined(self.arbitrary_defined_type(u, type_fuel)?),
            1 => Type::Func(self.arbitrary_func_type(u, type_fuel)?),
            2 => Type::Component(self.arbitrary_component_type(u, type_fuel)?),
            3 => Type::Instance(self.arbitrary_instance_type(u, type_fuel)?),
            _ => unreachable!(),
        };
        Ok(Rc::new(ty))
    }

    fn arbitrary_module_type(
        &self,
        u: &mut Unstructured,
        type_fuel: &mut u32,
    ) -> Result<Rc<ModuleType>> {
        let mut defs = vec![];
        let mut has_memory = false;
        let mut has_canonical_abi_realloc = false;
        let mut has_canonical_abi_free = false;
        let mut types: Vec<Rc<crate::core::FuncType>> = vec![];
        let mut imports = HashMap::new();
        let mut exports = HashSet::new();
        let mut counts = EntityCounts::default();

        // Special case the canonical ABI functions since certain types can only
        // be passed across the component boundary if they exist and
        // randomly generating them is extremely unlikely.

        // `memory`
        if counts.memories < self.config.max_memories && u.ratio::<u8>(99, 100)? {
            defs.push(ModuleTypeDef::Export(
                "memory".into(),
                crate::core::EntityType::Memory(self.arbitrary_core_memory_type(u)?),
            ));
            exports.insert("memory".into());
            counts.memories += 1;
            has_memory = true;
        }

        // `canonical_abi_realloc`
        if counts.funcs < self.config.max_funcs
            && types.len() < self.config.max_types
            && u.ratio::<u8>(99, 100)?
        {
            let realloc_ty = Rc::new(crate::core::FuncType {
                params: vec![ValType::I32, ValType::I32, ValType::I32, ValType::I32],
                results: vec![ValType::I32],
            });
            let ty_idx = u32::try_from(types.len()).unwrap();
            types.push(realloc_ty.clone());
            defs.push(ModuleTypeDef::TypeDef(
                crate::core::CompositeType::new_func(
                    realloc_ty.clone(),
                    false, // TODO: handle shared
                ),
            ));
            defs.push(ModuleTypeDef::Export(
                "canonical_abi_realloc".into(),
                crate::core::EntityType::Func(ty_idx, realloc_ty),
            ));
            exports.insert("canonical_abi_realloc".into());
            counts.funcs += 1;
            has_canonical_abi_realloc = true;
        }

        // `canonical_abi_free`
        if counts.funcs < self.config.max_funcs
            && types.len() < self.config.max_types
            && u.ratio::<u8>(99, 100)?
        {
            let free_ty = Rc::new(crate::core::FuncType {
                params: vec![ValType::I32, ValType::I32, ValType::I32],
                results: vec![],
            });
            let ty_idx = u32::try_from(types.len()).unwrap();
            types.push(free_ty.clone());
            defs.push(ModuleTypeDef::TypeDef(
                crate::core::CompositeType::new_func(
                    free_ty.clone(),
                    false, // TODO: handle shared
                ),
            ));
            defs.push(ModuleTypeDef::Export(
                "canonical_abi_free".into(),
                crate::core::EntityType::Func(ty_idx, free_ty),
            ));
            exports.insert("canonical_abi_free".into());
            counts.funcs += 1;
            has_canonical_abi_free = true;
        }

        let mut entity_choices: Vec<
            fn(
                &ComponentBuilder,
                &mut Unstructured,
                &mut EntityCounts,
                &[Rc<crate::core::FuncType>],
            ) -> Result<crate::core::EntityType>,
        > = Vec::with_capacity(5);

        arbitrary_loop(u, 0, 100, |u| {
            *type_fuel = type_fuel.saturating_sub(1);
            if *type_fuel == 0 {
                return Ok(false);
            }

            let max_choice = if types.len() < self.config.max_types {
                // Check if the parent scope has core function types to alias
                if !types.is_empty()
                    || (!self.types.is_empty()
                        && !self.types.last().unwrap().core_func_types.is_empty())
                {
                    // Imports, exports, types, and aliases
                    3
                } else {
                    // Imports, exports, and types
                    2
                }
            } else {
                // Imports and exports
                1
            };

            match u.int_in_range::<u8>(0..=max_choice)? {
                // Import.
                0 => {
                    let module = crate::limited_string(100, u)?;
                    let existing_module_imports = imports.entry(module.clone()).or_default();
                    let field = crate::unique_string(100, existing_module_imports, u)?;
                    let entity_type = match self.arbitrary_core_entity_type(
                        u,
                        &types,
                        &mut entity_choices,
                        &mut counts,
                    )? {
                        None => return Ok(false),
                        Some(x) => x,
                    };
                    defs.push(ModuleTypeDef::Import(crate::core::Import {
                        module,
                        field,
                        entity_type,
                    }));
                }

                // Export.
                1 => {
                    let name = crate::unique_string(100, &mut exports, u)?;
                    let entity_ty = match self.arbitrary_core_entity_type(
                        u,
                        &types,
                        &mut entity_choices,
                        &mut counts,
                    )? {
                        None => return Ok(false),
                        Some(x) => x,
                    };
                    defs.push(ModuleTypeDef::Export(name, entity_ty));
                }

                // Type definition.
                2 => {
                    let ty = arbitrary_func_type(
                        u,
                        &self.config,
                        &self.core_valtypes,
                        if self.config.multi_value_enabled {
                            None
                        } else {
                            Some(1)
                        },
                        0,
                    )?;
                    types.push(ty.clone());
                    defs.push(ModuleTypeDef::TypeDef(
                        crate::core::CompositeType::new_func(ty, false),
                    )); // TODO: handle shared
                }

                // Alias
                3 => {
                    let (count, index, kind) = self.arbitrary_outer_core_type_alias(u, &types)?;
                    let ty = match &kind {
                        CoreOuterAliasKind::Type(ty) => ty.clone(),
                    };
                    types.push(ty);
                    defs.push(ModuleTypeDef::OuterAlias {
                        count,
                        i: index,
                        kind,
                    });
                }

                _ => unreachable!(),
            }

            Ok(true)
        })?;

        Ok(Rc::new(ModuleType {
            defs,
            has_memory,
            has_canonical_abi_realloc,
            has_canonical_abi_free,
        }))
    }

    fn arbitrary_core_entity_type(
        &self,
        u: &mut Unstructured,
        types: &[Rc<crate::core::FuncType>],
        choices: &mut Vec<
            fn(
                &ComponentBuilder,
                &mut Unstructured,
                &mut EntityCounts,
                &[Rc<crate::core::FuncType>],
            ) -> Result<crate::core::EntityType>,
        >,
        counts: &mut EntityCounts,
    ) -> Result<Option<crate::core::EntityType>> {
        choices.clear();

        if counts.globals < self.config.max_globals {
            choices.push(|c, u, counts, _types| {
                counts.globals += 1;
                Ok(crate::core::EntityType::Global(
                    c.arbitrary_core_global_type(u)?,
                ))
            });
        }

        if counts.tables < self.config.max_tables {
            choices.push(|c, u, counts, _types| {
                counts.tables += 1;
                Ok(crate::core::EntityType::Table(
                    c.arbitrary_core_table_type(u)?,
                ))
            });
        }

        if counts.memories < self.config.max_memories {
            choices.push(|c, u, counts, _types| {
                counts.memories += 1;
                Ok(crate::core::EntityType::Memory(
                    c.arbitrary_core_memory_type(u)?,
                ))
            });
        }

        if types.iter().any(|ty| ty.results.is_empty())
            && self.config.exceptions_enabled
            && counts.tags < self.config.max_tags
        {
            choices.push(|c, u, counts, types| {
                counts.tags += 1;
                let tag_func_types = types
                    .iter()
                    .enumerate()
                    .filter(|(_, ty)| ty.results.is_empty())
                    .map(|(i, _)| u32::try_from(i).unwrap())
                    .collect::<Vec<_>>();
                Ok(crate::core::EntityType::Tag(
                    crate::core::arbitrary_tag_type(u, &tag_func_types, |idx| {
                        types[usize::try_from(idx).unwrap()].clone()
                    })?,
                ))
            });
        }

        if !types.is_empty() && counts.funcs < self.config.max_funcs {
            choices.push(|c, u, counts, types| {
                counts.funcs += 1;
                let ty_idx = u.int_in_range(0..=u32::try_from(types.len() - 1).unwrap())?;
                let ty = types[ty_idx as usize].clone();
                Ok(crate::core::EntityType::Func(ty_idx, ty))
            });
        }

        if choices.is_empty() {
            return Ok(None);
        }

        let f = u.choose(choices)?;
        let ty = f(self, u, counts, types)?;
        Ok(Some(ty))
    }

    fn arbitrary_core_valtype(&self, u: &mut Unstructured) -> Result<ValType> {
        Ok(*u.choose(&self.core_valtypes)?)
    }

    fn arbitrary_core_global_type(&self, u: &mut Unstructured) -> Result<crate::core::GlobalType> {
        Ok(crate::core::GlobalType {
            val_type: self.arbitrary_core_valtype(u)?,
            mutable: u.arbitrary()?,
            shared: false,
        })
    }

    fn arbitrary_core_table_type(&self, u: &mut Unstructured) -> Result<crate::core::TableType> {
        crate::core::arbitrary_table_type(u, &self.config, None)
    }

    fn arbitrary_core_memory_type(&self, u: &mut Unstructured) -> Result<crate::core::MemoryType> {
        crate::core::arbitrary_memtype(u, &self.config)
    }

    fn with_types_scope<T>(&mut self, f: impl FnOnce(&mut Self) -> Result<T>) -> Result<T> {
        self.types.push(Default::default());
        let result = f(self);
        self.types.pop();
        result
    }

    fn current_type_scope(&self) -> &TypesScope {
        self.types.last().unwrap()
    }

    fn current_type_scope_mut(&mut self) -> &mut TypesScope {
        self.types.last_mut().unwrap()
    }

    fn outer_types_scope(&self, count: u32) -> &TypesScope {
        &self.types[self.types.len() - 1 - usize::try_from(count).unwrap()]
    }

    fn outer_type(&self, count: u32, i: u32) -> &Rc<Type> {
        &self.outer_types_scope(count).types[usize::try_from(i).unwrap()]
    }

    fn arbitrary_component_type(
        &mut self,
        u: &mut Unstructured,
        type_fuel: &mut u32,
    ) -> Result<Rc<ComponentType>> {
        let mut defs = vec![];
        let mut imports = HashSet::new();
        let mut import_urls = HashSet::new();
        let mut exports = HashSet::new();
        let mut export_urls = HashSet::new();

        self.with_types_scope(|me| {
            arbitrary_loop(u, 0, 100, |u| {
                *type_fuel = type_fuel.saturating_sub(1);
                if *type_fuel == 0 {
                    return Ok(false);
                }

                if me.current_type_scope().can_ref_type() && u.int_in_range::<u8>(0..=3)? == 0 {
                    if let Some(ty) = me.arbitrary_type_ref(u, true, true)? {
                        // Imports.
                        let name = crate::unique_kebab_string(100, &mut imports, u)?;
                        let url = if u.arbitrary()? {
                            Some(crate::unique_url(100, &mut import_urls, u)?)
                        } else {
                            None
                        };
                        defs.push(ComponentTypeDef::Import(Import { name, url, ty }));
                        return Ok(true);
                    }

                    // Can't reference an arbitrary type, fallback to another definition.
                }

                // Type definitions, exports, and aliases.
                let def =
                    me.arbitrary_instance_type_def(u, &mut exports, &mut export_urls, type_fuel)?;
                defs.push(def.into());
                Ok(true)
            })
        })?;

        Ok(Rc::new(ComponentType { defs }))
    }

    fn arbitrary_instance_type(
        &mut self,
        u: &mut Unstructured,
        type_fuel: &mut u32,
    ) -> Result<Rc<InstanceType>> {
        let mut defs = vec![];
        let mut exports = HashSet::new();
        let mut export_urls = HashSet::new();

        self.with_types_scope(|me| {
            arbitrary_loop(u, 0, 100, |u| {
                *type_fuel = type_fuel.saturating_sub(1);
                if *type_fuel == 0 {
                    return Ok(false);
                }

                defs.push(me.arbitrary_instance_type_def(
                    u,
                    &mut exports,
                    &mut export_urls,
                    type_fuel,
                )?);
                Ok(true)
            })
        })?;

        Ok(Rc::new(InstanceType { defs }))
    }

    fn arbitrary_instance_type_def(
        &mut self,
        u: &mut Unstructured,
        exports: &mut HashSet<String>,
        export_urls: &mut HashSet<String>,
        type_fuel: &mut u32,
    ) -> Result<InstanceTypeDecl> {
        let mut choices: Vec<
            fn(
                &mut ComponentBuilder,
                &mut HashSet<String>,
                &mut HashSet<String>,
                &mut Unstructured,
                &mut u32,
            ) -> Result<InstanceTypeDecl>,
        > = Vec::with_capacity(3);

        // Export.
        if self.current_type_scope().can_ref_type() {
            choices.push(|me, exports, export_urls, u, _type_fuel| {
                let ty = me.arbitrary_type_ref(u, false, true)?.unwrap();
                if let ComponentTypeRef::Type(TypeBounds::Eq(idx)) = ty {
                    let ty = me.current_type_scope().get(idx).clone();
                    me.current_type_scope_mut().push(ty);
                }
                Ok(InstanceTypeDecl::Export {
                    name: crate::unique_kebab_string(100, exports, u)?,
                    url: if u.arbitrary()? {
                        Some(crate::unique_url(100, export_urls, u)?)
                    } else {
                        None
                    },
                    ty,
                })
            });
        }

        // Outer type alias.
        if self
            .types
            .iter()
            .any(|scope| !scope.types.is_empty() || !scope.core_types.is_empty())
        {
            choices.push(|me, _exports, _export_urls, u, _type_fuel| {
                let alias = me.arbitrary_outer_type_alias(u)?;
                match &alias {
                    Alias::Outer {
                        kind: OuterAliasKind::Type(ty),
                        ..
                    } => me.current_type_scope_mut().push(ty.clone()),
                    Alias::Outer {
                        kind: OuterAliasKind::CoreType(ty),
                        ..
                    } => me.current_type_scope_mut().push_core(ty.clone()),
                    _ => unreachable!(),
                };
                Ok(InstanceTypeDecl::Alias(alias))
            });
        }

        // Core type definition.
        choices.push(|me, _exports, _export_urls, u, type_fuel| {
            let ty = me.arbitrary_core_type(u, type_fuel)?;
            me.current_type_scope_mut().push_core(ty.clone());
            Ok(InstanceTypeDecl::CoreType(ty))
        });

        // Type definition.
        if self.types.len() < self.config.max_nesting_depth {
            choices.push(|me, _exports, _export_urls, u, type_fuel| {
                let ty = me.arbitrary_type(u, type_fuel)?;
                me.current_type_scope_mut().push(ty.clone());
                Ok(InstanceTypeDecl::Type(ty))
            });
        }

        let f = u.choose(&choices)?;
        f(self, exports, export_urls, u, type_fuel)
    }

    fn arbitrary_outer_core_type_alias(
        &self,
        u: &mut Unstructured,
        local_types: &[Rc<crate::core::FuncType>],
    ) -> Result<(u32, u32, CoreOuterAliasKind)> {
        let enclosing_type_len = if !self.types.is_empty() {
            self.types.last().unwrap().core_func_types.len()
        } else {
            0
        };

        assert!(!local_types.is_empty() || enclosing_type_len > 0);

        let max = enclosing_type_len + local_types.len() - 1;
        let i = u.int_in_range(0..=max)?;
        let (count, index, ty) = if i < enclosing_type_len {
            let enclosing = self.types.last().unwrap();
            let index = enclosing.core_func_types[i];
            (
                1,
                index,
                match enclosing.get_core(index).as_ref() {
                    CoreType::Func(ty) => ty.clone(),
                    CoreType::Module(_) => unreachable!(),
                },
            )
        } else if i - enclosing_type_len < local_types.len() {
            let i = i - enclosing_type_len;
            (0, u32::try_from(i).unwrap(), local_types[i].clone())
        } else {
            unreachable!()
        };

        Ok((count, index, CoreOuterAliasKind::Type(ty)))
    }

    fn arbitrary_outer_type_alias(&self, u: &mut Unstructured) -> Result<Alias> {
        let non_empty_types_scopes: Vec<_> = self
            .types
            .iter()
            .rev()
            .enumerate()
            .filter(|(_, scope)| !scope.types.is_empty() || !scope.core_types.is_empty())
            .collect();
        assert!(
            !non_empty_types_scopes.is_empty(),
            "precondition: there are non-empty types scopes"
        );

        let (count, scope) = u.choose(&non_empty_types_scopes)?;
        let count = u32::try_from(*count).unwrap();
        assert!(!scope.types.is_empty() || !scope.core_types.is_empty());

        let max_type_in_scope = scope.types.len() + scope.core_types.len() - 1;
        let i = u.int_in_range(0..=max_type_in_scope)?;

        let (i, kind) = if i < scope.types.len() {
            let i = u32::try_from(i).unwrap();
            (i, OuterAliasKind::Type(Rc::clone(scope.get(i))))
        } else if i - scope.types.len() < scope.core_types.len() {
            let i = u32::try_from(i - scope.types.len()).unwrap();
            (i, OuterAliasKind::CoreType(Rc::clone(scope.get_core(i))))
        } else {
            unreachable!()
        };

        Ok(Alias::Outer { count, i, kind })
    }

    fn arbitrary_func_type(
        &self,
        u: &mut Unstructured,
        type_fuel: &mut u32,
    ) -> Result<Rc<FuncType>> {
        let mut params = Vec::new();
        let mut results = Vec::new();
        let mut names = HashSet::new();

        // Note: parameters are currently limited to a maximum of 16
        // because any additional parameters will require indirect access
        // via a pointer argument; when this occurs, validation of any
        // lowered function will fail because it will be missing a
        // memory option (not yet implemented).
        //
        // When options are correctly specified on canonical functions,
        // we should increase this maximum to test indirect parameter
        // passing.
        arbitrary_loop(u, 0, 16, |u| {
            *type_fuel = type_fuel.saturating_sub(1);
            if *type_fuel == 0 {
                return Ok(false);
            }

            let name = crate::unique_kebab_string(100, &mut names, u)?;
            let ty = self.arbitrary_component_val_type(u)?;

            params.push((name, ty));

            Ok(true)
        })?;

        names.clear();

        // Likewise, the limit for results is 1 before the memory option is
        // required. When the memory option is implemented, this restriction
        // should be relaxed.
        arbitrary_loop(u, 0, 1, |u| {
            *type_fuel = type_fuel.saturating_sub(1);
            if *type_fuel == 0 {
                return Ok(false);
            }

            // If the result list is empty (i.e. first push), then arbitrarily give
            // the result a name. Otherwise, all of the subsequent items must be named.
            let name = if results.is_empty() {
                // Most of the time we should have a single, unnamed result.
                u.ratio::<u8>(10, 100)?
                    .then(|| crate::unique_kebab_string(100, &mut names, u))
                    .transpose()?
            } else {
                Some(crate::unique_kebab_string(100, &mut names, u)?)
            };

            let ty = self.arbitrary_component_val_type(u)?;

            results.push((name, ty));

            // There can be only one unnamed result.
            if results.len() == 1 && results[0].0.is_none() {
                return Ok(false);
            }

            Ok(true)
        })?;

        Ok(Rc::new(FuncType { params, results }))
    }

    fn arbitrary_component_val_type(&self, u: &mut Unstructured) -> Result<ComponentValType> {
        let max_choices = if self.current_type_scope().defined_types.is_empty() {
            0
        } else {
            1
        };
        match u.int_in_range(0..=max_choices)? {
            0 => Ok(ComponentValType::Primitive(
                self.arbitrary_primitive_val_type(u)?,
            )),
            1 => {
                let index = *u.choose(&self.current_type_scope().defined_types)?;
                let ty = Rc::clone(self.current_type_scope().get(index));
                Ok(ComponentValType::Type(index))
            }
            _ => unreachable!(),
        }
    }

    fn arbitrary_primitive_val_type(&self, u: &mut Unstructured) -> Result<PrimitiveValType> {
        match u.int_in_range(0..=12)? {
            0 => Ok(PrimitiveValType::Bool),
            1 => Ok(PrimitiveValType::S8),
            2 => Ok(PrimitiveValType::U8),
            3 => Ok(PrimitiveValType::S16),
            4 => Ok(PrimitiveValType::U16),
            5 => Ok(PrimitiveValType::S32),
            6 => Ok(PrimitiveValType::U32),
            7 => Ok(PrimitiveValType::S64),
            8 => Ok(PrimitiveValType::U64),
            9 => Ok(PrimitiveValType::F32),
            10 => Ok(PrimitiveValType::F64),
            11 => Ok(PrimitiveValType::Char),
            12 => Ok(PrimitiveValType::String),
            _ => unreachable!(),
        }
    }

    fn arbitrary_record_type(
        &self,
        u: &mut Unstructured,
        type_fuel: &mut u32,
    ) -> Result<RecordType> {
        let mut fields = vec![];
        let mut field_names = HashSet::new();
        arbitrary_loop(u, 0, 100, |u| {
            *type_fuel = type_fuel.saturating_sub(1);
            if *type_fuel == 0 {
                return Ok(false);
            }

            let name = crate::unique_kebab_string(100, &mut field_names, u)?;
            let ty = self.arbitrary_component_val_type(u)?;

            fields.push((name, ty));
            Ok(true)
        })?;
        Ok(RecordType { fields })
    }

    fn arbitrary_variant_type(
        &self,
        u: &mut Unstructured,
        type_fuel: &mut u32,
    ) -> Result<VariantType> {
        let mut cases = vec![];
        let mut case_names = HashSet::new();
        arbitrary_loop(u, 1, 100, |u| {
            *type_fuel = type_fuel.saturating_sub(1);
            if *type_fuel == 0 {
                return Ok(false);
            }

            let name = crate::unique_kebab_string(100, &mut case_names, u)?;

            let ty = u
                .arbitrary::<bool>()?
                .then(|| self.arbitrary_component_val_type(u))
                .transpose()?;

            let refines = if !cases.is_empty() && u.arbitrary()? {
                let max_cases = u32::try_from(cases.len() - 1).unwrap();
                Some(u.int_in_range(0..=max_cases)?)
            } else {
                None
            };

            cases.push((name, ty, refines));
            Ok(true)
        })?;

        Ok(VariantType { cases })
    }

    fn arbitrary_list_type(&self, u: &mut Unstructured) -> Result<ListType> {
        Ok(ListType {
            elem_ty: self.arbitrary_component_val_type(u)?,
        })
    }

    fn arbitrary_tuple_type(&self, u: &mut Unstructured, type_fuel: &mut u32) -> Result<TupleType> {
        let mut fields = vec![];
        arbitrary_loop(u, 0, 100, |u| {
            *type_fuel = type_fuel.saturating_sub(1);
            if *type_fuel == 0 {
                return Ok(false);
            }

            fields.push(self.arbitrary_component_val_type(u)?);
            Ok(true)
        })?;
        Ok(TupleType { fields })
    }

    fn arbitrary_flags_type(&self, u: &mut Unstructured, type_fuel: &mut u32) -> Result<FlagsType> {
        let mut fields = vec![];
        let mut field_names = HashSet::new();
        arbitrary_loop(u, 0, 100, |u| {
            *type_fuel = type_fuel.saturating_sub(1);
            if *type_fuel == 0 {
                return Ok(false);
            }

            fields.push(crate::unique_kebab_string(100, &mut field_names, u)?);
            Ok(true)
        })?;
        Ok(FlagsType { fields })
    }

    fn arbitrary_enum_type(&self, u: &mut Unstructured, type_fuel: &mut u32) -> Result<EnumType> {
        let mut variants = vec![];
        let mut variant_names = HashSet::new();
        arbitrary_loop(u, 1, 100, |u| {
            *type_fuel = type_fuel.saturating_sub(1);
            if *type_fuel == 0 {
                return Ok(false);
            }

            variants.push(crate::unique_kebab_string(100, &mut variant_names, u)?);
            Ok(true)
        })?;
        Ok(EnumType { variants })
    }

    fn arbitrary_option_type(&self, u: &mut Unstructured) -> Result<OptionType> {
        Ok(OptionType {
            inner_ty: self.arbitrary_component_val_type(u)?,
        })
    }

    fn arbitrary_result_type(&self, u: &mut Unstructured) -> Result<ResultType> {
        Ok(ResultType {
            ok_ty: u
                .arbitrary::<bool>()?
                .then(|| self.arbitrary_component_val_type(u))
                .transpose()?,
            err_ty: u
                .arbitrary::<bool>()?
                .then(|| self.arbitrary_component_val_type(u))
                .transpose()?,
        })
    }

    fn arbitrary_defined_type(
        &self,
        u: &mut Unstructured,
        type_fuel: &mut u32,
    ) -> Result<DefinedType> {
        match u.int_in_range(0..=8)? {
            0 => Ok(DefinedType::Primitive(
                self.arbitrary_primitive_val_type(u)?,
            )),
            1 => Ok(DefinedType::Record(
                self.arbitrary_record_type(u, type_fuel)?,
            )),
            2 => Ok(DefinedType::Variant(
                self.arbitrary_variant_type(u, type_fuel)?,
            )),
            3 => Ok(DefinedType::List(self.arbitrary_list_type(u)?)),
            4 => Ok(DefinedType::Tuple(self.arbitrary_tuple_type(u, type_fuel)?)),
            5 => Ok(DefinedType::Flags(self.arbitrary_flags_type(u, type_fuel)?)),
            6 => Ok(DefinedType::Enum(self.arbitrary_enum_type(u, type_fuel)?)),
            7 => Ok(DefinedType::Option(self.arbitrary_option_type(u)?)),
            8 => Ok(DefinedType::Result(self.arbitrary_result_type(u)?)),
            _ => unreachable!(),
        }
    }

    fn push_import(&mut self, name: String, url: Option<String>, ty: ComponentTypeRef) {
        let nth = match self.ensure_section(
            |sec| matches!(sec, Section::Import(_)),
            || Section::Import(ImportSection { imports: vec![] }),
        ) {
            Section::Import(sec) => {
                sec.imports.push(Import { name, url, ty });
                sec.imports.len() - 1
            }
            _ => unreachable!(),
        };
        let section_index = self.component().component.sections.len() - 1;

        match ty {
            ComponentTypeRef::Module(_) => {
                self.total_modules += 1;
                self.component_mut().modules.push((section_index, nth));
            }
            ComponentTypeRef::Func(ty_index) => {
                let func_ty = match self.current_type_scope().get(ty_index).as_ref() {
                    Type::Func(ty) => ty.clone(),
                    _ => unreachable!(),
                };

                if func_ty.is_scalar() {
                    let func_index = u32::try_from(self.component().component_funcs.len()).unwrap();
                    self.component_mut().scalar_component_funcs.push(func_index);
                }

                let func_index = u32::try_from(self.component().funcs.len()).unwrap();
                self.component_mut()
                    .funcs
                    .push(ComponentOrCoreFuncType::Component(func_ty));

                self.component_mut().component_funcs.push(func_index);
            }
            ComponentTypeRef::Value(ty) => {
                self.total_values += 1;
                self.component_mut().values.push(ty);
            }
            ComponentTypeRef::Type(TypeBounds::Eq(ty_index)) => {
                let ty = self.current_type_scope().get(ty_index).clone();
                self.current_type_scope_mut().push(ty);
            }
            ComponentTypeRef::Type(TypeBounds::SubResource) => {
                unimplemented!()
            }
            ComponentTypeRef::Instance(ty_index) => {
                let instance_ty = match self.current_type_scope().get(ty_index).as_ref() {
                    Type::Instance(ty) => ty.clone(),
                    _ => unreachable!(),
                };

                self.total_instances += 1;
                self.component_mut()
                    .instances
                    .push(ComponentOrCoreInstanceType::Component(instance_ty));
            }
            ComponentTypeRef::Component(_) => {
                self.total_components += 1;
                self.component_mut().components.push((section_index, nth));
            }
        }
    }

    fn core_function_type(&self, core_func_index: u32) -> &Rc<crate::core::FuncType> {
        self.component().funcs[self.component().core_funcs[core_func_index as usize] as usize]
            .as_core()
    }

    fn component_function_type(&self, func_index: u32) -> &Rc<FuncType> {
        self.component().funcs[self.component().component_funcs[func_index as usize] as usize]
            .as_component()
    }

    fn push_func(&mut self, func: Func) {
        let nth = match self.component_mut().component.sections.last_mut() {
            Some(Section::Canonical(CanonicalSection { funcs })) => funcs.len(),
            _ => {
                self.push_section(Section::Canonical(CanonicalSection { funcs: vec![] }));
                0
            }
        };
        let section_index = self.component().component.sections.len() - 1;

        let func_index = u32::try_from(self.component().funcs.len()).unwrap();

        let ty = match &func {
            Func::CanonLift { func_ty, .. } => {
                let ty = Rc::clone(self.current_type_scope().get_func(*func_ty));
                if ty.is_scalar() {
                    let func_index = u32::try_from(self.component().component_funcs.len()).unwrap();
                    self.component_mut().scalar_component_funcs.push(func_index);
                }
                self.component_mut().component_funcs.push(func_index);
                ComponentOrCoreFuncType::Component(ty)
            }
            Func::CanonLower {
                func_index: comp_func_index,
                ..
            } => {
                let comp_func_ty = self.component_function_type(*comp_func_index);
                let core_func_ty = canonical_abi_for(comp_func_ty);
                self.component_mut().core_funcs.push(func_index);
                ComponentOrCoreFuncType::Core(core_func_ty)
            }
        };

        self.component_mut().funcs.push(ty);

        match self.component_mut().component.sections.last_mut() {
            Some(Section::Canonical(CanonicalSection { funcs })) => funcs.push(func),
            _ => unreachable!(),
        }
    }

    fn arbitrary_import_section(&mut self, u: &mut Unstructured) -> Result<Step> {
        self.push_section(Section::Import(ImportSection { imports: vec![] }));

        let min = if self.fill_minimums {
            self.config
                .min_imports
                .saturating_sub(self.component().num_imports)
        } else {
            // Allow generating empty sections. We can always fill in the required
            // minimum later.
            0
        };
        let max = self.config.max_imports - self.component().num_imports;

        crate::arbitrary_loop(u, min, max, |u| {
            match self.arbitrary_type_ref(u, true, false)? {
                Some(ty) => {
                    let name =
                        crate::unique_kebab_string(100, &mut self.component_mut().import_names, u)?;
                    let url = if u.arbitrary()? {
                        Some(crate::unique_url(
                            100,
                            &mut self.component_mut().import_urls,
                            u,
                        )?)
                    } else {
                        None
                    };
                    self.push_import(name, url, ty);
                    Ok(true)
                }
                None => Ok(false),
            }
        })?;

        Ok(Step::StillBuilding)
    }

    fn arbitrary_canonical_section(&mut self, u: &mut Unstructured) -> Result<Step> {
        self.push_section(Section::Canonical(CanonicalSection { funcs: vec![] }));

        let min = if self.fill_minimums {
            self.config
                .min_funcs
                .saturating_sub(self.component().funcs.len())
        } else {
            // Allow generating empty sections. We can always fill in the
            // required minimum later.
            0
        };
        let max = self.config.max_funcs - self.component().funcs.len();

        let mut choices: Vec<fn(&mut Unstructured, &mut ComponentBuilder) -> Result<Option<Func>>> =
            Vec::with_capacity(2);

        crate::arbitrary_loop(u, min, max, |u| {
            choices.clear();

            // NB: We only lift/lower scalar component functions.
            //
            // If we generated lifting and lowering of compound value types,
            // the probability of generating a corresponding Wasm module that
            // generates valid instances of the compound value types would
            // be vanishingly tiny (e.g. for `list<string>` we would have to
            // generate a core Wasm module that correctly produces a pointer and
            // length for a memory region that itself is a series of pointers
            // and lengths of valid strings, as well as `canonical_abi_realloc`
            // and `canonical_abi_free` functions that do the right thing).
            //
            // This is a pretty serious limitation of `wasm-smith`'s component
            // types support, but it is one we are intentionally
            // accepting. `wasm-smith` will focus on generating arbitrary
            // component sections, structures, and import/export topologies; not
            // component functions and core Wasm implementations of component
            // functions. In the future, we intend to build a new, distinct test
            // case generator specifically for exercising component functions
            // and the canonical ABI. This new generator won't emit arbitrary
            // component sections, structures, or import/export topologies, and
            // will instead leave that to `wasm-smith`.

            if !self.component().scalar_component_funcs.is_empty() {
                choices.push(|u, c| {
                    let func_index = *u.choose(&c.component().scalar_component_funcs)?;
                    Ok(Some(Func::CanonLower {
                        // Scalar component functions don't use any canonical options.
                        options: vec![],
                        func_index,
                    }))
                });
            }

            if !self.component().core_funcs.is_empty() {
                choices.push(|u, c| {
                    let core_func_index = u.int_in_range(
                        0..=u32::try_from(c.component().core_funcs.len() - 1).unwrap(),
                    )?;
                    let core_func_ty = c.core_function_type(core_func_index);
                    let comp_func_ty = inverse_scalar_canonical_abi_for(u, core_func_ty)?;

                    let func_ty = if let Some(indices) = c
                        .current_type_scope()
                        .func_type_to_indices
                        .get(&comp_func_ty)
                    {
                        // If we've already defined this component function type
                        // one or more times, then choose one of those
                        // definitions arbitrarily.
                        debug_assert!(!indices.is_empty());
                        *u.choose(indices)?
                    } else if c.current_type_scope().types.len() < c.config.max_types {
                        // If we haven't already defined this component function
                        // type, and we haven't defined the configured maximum
                        // amount of types yet, then just define this type.
                        let ty = Rc::new(Type::Func(Rc::new(comp_func_ty)));
                        c.push_type(ty)
                    } else {
                        // Otherwise, give up on lifting this function.
                        return Ok(None);
                    };

                    Ok(Some(Func::CanonLift {
                        func_ty,
                        // Scalar functions don't use any canonical options.
                        options: vec![],
                        core_func_index,
                    }))
                });
            }

            if choices.is_empty() {
                return Ok(false);
            }

            let f = u.choose(&choices)?;
            if let Some(func) = f(u, self)? {
                self.push_func(func);
            }

            Ok(true)
        })?;

        Ok(Step::StillBuilding)
    }

    fn arbitrary_core_module_section(&mut self, u: &mut Unstructured) -> Result<Step> {
        let module = crate::core::Module::new_internal(
            self.config.clone(),
            u,
            crate::core::DuplicateImportsBehavior::Disallowed,
        )?;
        self.push_section(Section::CoreModule(module));
        self.total_modules += 1;
        Ok(Step::StillBuilding)
    }

    fn arbitrary_component_section(&mut self, u: &mut Unstructured) -> Result<Step> {
        self.types.push(TypesScope::default());
        self.components.push(ComponentContext::empty());
        self.total_components += 1;
        Ok(Step::StillBuilding)
    }

    fn arbitrary_instance_section(&mut self, u: &mut Unstructured) -> Result<()> {
        todo!()
    }

    fn arbitrary_export_section(&mut self, u: &mut Unstructured) -> Result<()> {
        todo!()
    }

    fn arbitrary_start_section(&mut self, u: &mut Unstructured) -> Result<()> {
        todo!()
    }

    fn arbitrary_alias_section(&mut self, u: &mut Unstructured) -> Result<()> {
        todo!()
    }
}

fn canonical_abi_for(func_ty: &FuncType) -> Rc<crate::core::FuncType> {
    let to_core_ty = |ty| match ty {
        ComponentValType::Primitive(prim_ty) => match prim_ty {
            PrimitiveValType::Char
            | PrimitiveValType::Bool
            | PrimitiveValType::S8
            | PrimitiveValType::U8
            | PrimitiveValType::S16
            | PrimitiveValType::U16
            | PrimitiveValType::S32
            | PrimitiveValType::U32 => ValType::I32,
            PrimitiveValType::S64 | PrimitiveValType::U64 => ValType::I64,
            PrimitiveValType::F32 => ValType::F32,
            PrimitiveValType::F64 => ValType::F64,
            PrimitiveValType::String | PrimitiveValType::ErrorContext => {
                unimplemented!("non-scalar types are not supported yet")
            }
        },
        ComponentValType::Type(_) => unimplemented!("non-scalar types are not supported yet"),
    };

    Rc::new(crate::core::FuncType {
        params: func_ty
            .params
            .iter()
            .map(|(_, ty)| to_core_ty(*ty))
            .collect(),
        results: func_ty
            .results
            .iter()
            .map(|(_, ty)| to_core_ty(*ty))
            .collect(),
    })
}

fn inverse_scalar_canonical_abi_for(
    u: &mut Unstructured,
    core_func_ty: &crate::core::FuncType,
) -> Result<FuncType> {
    let from_core_ty = |u: &mut Unstructured, core_ty| match core_ty {
        ValType::I32 => u
            .choose(&[
                ComponentValType::Primitive(PrimitiveValType::Char),
                ComponentValType::Primitive(PrimitiveValType::Bool),
                ComponentValType::Primitive(PrimitiveValType::S8),
                ComponentValType::Primitive(PrimitiveValType::U8),
                ComponentValType::Primitive(PrimitiveValType::S16),
                ComponentValType::Primitive(PrimitiveValType::U16),
                ComponentValType::Primitive(PrimitiveValType::S32),
                ComponentValType::Primitive(PrimitiveValType::U32),
            ])
            .cloned(),
        ValType::I64 => u
            .choose(&[
                ComponentValType::Primitive(PrimitiveValType::S64),
                ComponentValType::Primitive(PrimitiveValType::U64),
            ])
            .cloned(),
        ValType::F32 => Ok(ComponentValType::Primitive(PrimitiveValType::F32)),
        ValType::F64 => Ok(ComponentValType::Primitive(PrimitiveValType::F64)),
        ValType::V128 | ValType::Ref(_) => {
            unreachable!("not used in canonical ABI")
        }
    };

    let mut names = HashSet::default();
    let mut params = vec![];

    for core_ty in &core_func_ty.params {
        params.push((
            crate::unique_kebab_string(100, &mut names, u)?,
            from_core_ty(u, *core_ty)?,
        ));
    }

    names.clear();

    let results = match core_func_ty.results.len() {
        0 => Vec::new(),
        1 => vec![(
            if u.arbitrary()? {
                Some(crate::unique_kebab_string(100, &mut names, u)?)
            } else {
                None
            },
            from_core_ty(u, core_func_ty.results[0])?,
        )],
        _ => unimplemented!("non-scalar types are not supported yet"),
    };

    Ok(FuncType { params, results })
}

#[derive(Debug)]
enum Section {
    Custom(CustomSection),
    CoreModule(crate::Module),
    CoreInstance(CoreInstanceSection),
    CoreType(CoreTypeSection),
    Component(Component),
    Instance(InstanceSection),
    Alias(AliasSection),
    Type(TypeSection),
    Canonical(CanonicalSection),
    Start(StartSection),
    Import(ImportSection),
    Export(ExportSection),
}

#[derive(Debug)]
struct CustomSection {
    name: String,
    data: Vec<u8>,
}

impl<'a> Arbitrary<'a> for CustomSection {
    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
        let name = crate::limited_string(1_000, u)?;
        let data = u.arbitrary()?;
        Ok(CustomSection { name, data })
    }
}

#[derive(Debug)]
struct TypeSection {
    types: Vec<Rc<Type>>,
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
enum CoreType {
    Func(Rc<crate::core::FuncType>),
    Module(Rc<ModuleType>),
}

#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
struct ModuleType {
    defs: Vec<ModuleTypeDef>,
    has_memory: bool,
    has_canonical_abi_realloc: bool,
    has_canonical_abi_free: bool,
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
enum ModuleTypeDef {
    TypeDef(crate::core::CompositeType),
    Import(crate::core::Import),
    OuterAlias {
        count: u32,
        i: u32,
        kind: CoreOuterAliasKind,
    },
    Export(String, crate::core::EntityType),
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
enum Type {
    Defined(DefinedType),
    Func(Rc<FuncType>),
    Component(Rc<ComponentType>),
    Instance(Rc<InstanceType>),
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
enum CoreInstanceExportAliasKind {
    Func,
    Table,
    Memory,
    Global,
    Tag,
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
enum CoreOuterAliasKind {
    Type(Rc<crate::core::FuncType>),
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
enum Alias {
    InstanceExport {
        instance: u32,
        name: String,
        kind: InstanceExportAliasKind,
    },
    CoreInstanceExport {
        instance: u32,
        name: String,
        kind: CoreInstanceExportAliasKind,
    },
    Outer {
        count: u32,
        i: u32,
        kind: OuterAliasKind,
    },
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
enum InstanceExportAliasKind {
    Module,
    Component,
    Instance,
    Func,
    Value,
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
enum OuterAliasKind {
    Module,
    Component,
    CoreType(Rc<CoreType>),
    Type(Rc<Type>),
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
struct ComponentType {
    defs: Vec<ComponentTypeDef>,
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
enum ComponentTypeDef {
    CoreType(Rc<CoreType>),
    Type(Rc<Type>),
    Alias(Alias),
    Import(Import),
    Export {
        name: String,
        url: Option<String>,
        ty: ComponentTypeRef,
    },
}

impl From<InstanceTypeDecl> for ComponentTypeDef {
    fn from(def: InstanceTypeDecl) -> Self {
        match def {
            InstanceTypeDecl::CoreType(t) => Self::CoreType(t),
            InstanceTypeDecl::Type(t) => Self::Type(t),
            InstanceTypeDecl::Export { name, url, ty } => Self::Export { name, url, ty },
            InstanceTypeDecl::Alias(a) => Self::Alias(a),
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
struct InstanceType {
    defs: Vec<InstanceTypeDecl>,
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
enum InstanceTypeDecl {
    CoreType(Rc<CoreType>),
    Type(Rc<Type>),
    Alias(Alias),
    Export {
        name: String,
        url: Option<String>,
        ty: ComponentTypeRef,
    },
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
struct FuncType {
    params: Vec<(String, ComponentValType)>,
    results: Vec<(Option<String>, ComponentValType)>,
}

impl FuncType {
    fn unnamed_result_ty(&self) -> Option<ComponentValType> {
        if self.results.len() == 1 {
            let (name, ty) = &self.results[0];
            if name.is_none() {
                return Some(*ty);
            }
        }
        None
    }

    fn is_scalar(&self) -> bool {
        self.params.iter().all(|(_, ty)| is_scalar(ty))
            && self.results.len() == 1
            && is_scalar(&self.results[0].1)
    }
}

fn is_scalar(ty: &ComponentValType) -> bool {
    match ty {
        ComponentValType::Primitive(prim) => match prim {
            PrimitiveValType::Bool
            | PrimitiveValType::S8
            | PrimitiveValType::U8
            | PrimitiveValType::S16
            | PrimitiveValType::U16
            | PrimitiveValType::S32
            | PrimitiveValType::U32
            | PrimitiveValType::S64
            | PrimitiveValType::U64
            | PrimitiveValType::F32
            | PrimitiveValType::F64
            | PrimitiveValType::Char => true,
            PrimitiveValType::String | PrimitiveValType::ErrorContext => false,
        },
        ComponentValType::Type(_) => false,
    }
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
enum DefinedType {
    Primitive(PrimitiveValType),
    Record(RecordType),
    Variant(VariantType),
    List(ListType),
    Tuple(TupleType),
    Flags(FlagsType),
    Enum(EnumType),
    Option(OptionType),
    Result(ResultType),
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
struct RecordType {
    fields: Vec<(String, ComponentValType)>,
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
struct VariantType {
    cases: Vec<(String, Option<ComponentValType>, Option<u32>)>,
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
struct ListType {
    elem_ty: ComponentValType,
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
struct TupleType {
    fields: Vec<ComponentValType>,
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
struct FlagsType {
    fields: Vec<String>,
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
struct EnumType {
    variants: Vec<String>,
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
struct OptionType {
    inner_ty: ComponentValType,
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
struct ResultType {
    ok_ty: Option<ComponentValType>,
    err_ty: Option<ComponentValType>,
}

#[derive(Debug)]
struct ImportSection {
    imports: Vec<Import>,
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
struct Import {
    name: String,
    url: Option<String>,
    ty: ComponentTypeRef,
}

#[derive(Debug)]
struct CanonicalSection {
    funcs: Vec<Func>,
}

#[derive(Debug)]
enum Func {
    CanonLift {
        func_ty: u32,
        options: Vec<CanonOpt>,
        core_func_index: u32,
    },
    CanonLower {
        options: Vec<CanonOpt>,
        func_index: u32,
    },
}

#[derive(Debug)]
enum CanonOpt {
    StringUtf8,
    StringUtf16,
    StringLatin1Utf16,
    Memory(u32),
    Realloc(u32),
    PostReturn(u32),
}

#[derive(Debug)]
struct InstanceSection {}

#[derive(Debug)]
struct ExportSection {}

#[derive(Debug)]
struct StartSection {}

#[derive(Debug)]
struct AliasSection {}

#[derive(Debug)]
struct CoreInstanceSection {}

#[derive(Debug)]
struct CoreTypeSection {
    types: Vec<Rc<CoreType>>,
}

fn arbitrary_func_type(
    u: &mut Unstructured,
    config: &Config,
    valtypes: &[ValType],
    max_results: Option<usize>,
    type_ref_limit: u32,
) -> Result<Rc<crate::core::FuncType>> {
    let mut params = vec![];
    let mut results = vec![];
    arbitrary_loop(u, 0, 20, |u| {
        params.push(arbitrary_valtype(u, config, valtypes, type_ref_limit)?);
        Ok(true)
    })?;
    arbitrary_loop(u, 0, max_results.unwrap_or(20), |u| {
        results.push(arbitrary_valtype(u, config, valtypes, type_ref_limit)?);
        Ok(true)
    })?;
    Ok(Rc::new(crate::core::FuncType { params, results }))
}

fn arbitrary_valtype(
    u: &mut Unstructured,
    config: &Config,
    valtypes: &[ValType],
    type_ref_limit: u32,
) -> Result<ValType> {
    if config.gc_enabled && type_ref_limit > 0 && u.ratio(1, 20)? {
        Ok(ValType::Ref(RefType {
            // TODO: For now, only create allow nullable reference
            // types. Eventually we should support non-nullable reference types,
            // but this means that we will also need to recognize when it is
            // impossible to create an instance of the reference (eg `(ref
            // nofunc)` has no instances, and self-referential types that
            // contain a non-null self-reference are also impossible to create).
            nullable: true,
            heap_type: HeapType::Concrete(u.int_in_range(0..=type_ref_limit - 1)?),
        }))
    } else {
        Ok(*u.choose(valtypes)?)
    }
}
