pub mod component_type_object;

use anyhow::Result;
use heck::*;
use indexmap::IndexSet;
use std::collections::{HashMap, HashSet};
use std::fmt::Write;
use std::mem;
use wit_bindgen_core::abi::{
    self, AbiVariant, Bindgen, Bitcast, Instruction, LiftLower, WasmSignature, WasmType,
};
use wit_bindgen_core::{
    AnonymousTypeGenerator, AsyncFilterSet, Direction, Files, InterfaceGenerator as _, Ns,
    WorldGenerator, dealias, uwrite, uwriteln, wit_parser::*,
};
use wit_component::StringEncoding;

#[derive(Default)]
struct C {
    src: Source,
    opts: Opts,
    h_includes: Vec<String>,
    c_includes: Vec<String>,
    return_pointer_area_size: ArchitectureSize,
    return_pointer_area_align: Alignment,
    names: Ns,
    needs_string: bool,
    needs_union_int32_float: bool,
    needs_union_float_int32: bool,
    needs_union_int64_double: bool,
    needs_union_double_int64: bool,
    needs_async: bool,
    prim_names: HashSet<String>,
    world: String,
    sizes: SizeAlign,
    renamed_interfaces: HashMap<WorldKey, String>,

    world_id: Option<WorldId>,
    dtor_funcs: HashMap<TypeId, String>,
    type_names: HashMap<TypeId, String>,
    resources: HashMap<TypeId, ResourceInfo>,
    futures: IndexSet<TypeId>,
}

#[derive(Default)]
pub struct ResourceInfo {
    pub direction: Direction,
    own: String,
    borrow: String,
    drop_fn: String,
}

#[derive(Default, Debug, Eq, PartialEq, Clone, Copy)]
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
pub enum Enabled {
    #[default]
    No,
    Yes,
}

impl std::fmt::Display for Enabled {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Yes => write!(f, "yes"),
            Self::No => write!(f, "no"),
        }
    }
}

#[derive(Default, Debug, Clone)]
#[cfg_attr(feature = "clap", derive(clap::Parser))]
pub struct Opts {
    /// Skip emitting component allocation helper functions
    #[cfg_attr(feature = "clap", arg(long))]
    pub no_helpers: bool,

    /// Set component string encoding
    #[cfg_attr(
        feature = "clap",
        arg(
            long,
            default_value_t = StringEncoding::default(),
            value_name = "ENCODING",
        ),
    )]
    pub string_encoding: StringEncoding,

    /// Skip optional null pointer and boolean result argument signature
    /// flattening
    #[cfg_attr(feature = "clap", arg(long, default_value_t = false))]
    pub no_sig_flattening: bool,

    /// Skip generating an object file which contains type information for the
    /// world that is being generated.
    #[cfg_attr(feature = "clap", arg(long, default_value_t = false))]
    pub no_object_file: bool,

    /// Rename the interface `K` to `V` in the generated source code.
    #[cfg_attr(feature = "clap", arg(long, name = "K=V", value_parser = parse_rename))]
    pub rename: Vec<(String, String)>,

    /// Rename the world in the generated source code and file names.
    #[cfg_attr(feature = "clap", arg(long, value_name = "NAME"))]
    pub rename_world: Option<String>,

    /// Add the specified suffix to the name of the custom section containing
    /// the component type.
    #[cfg_attr(feature = "clap", arg(long, value_name = "STRING"))]
    pub type_section_suffix: Option<String>,

    /// Configure the autodropping of borrows in exported functions.
    #[cfg_attr(
        feature = "clap",
        arg(
            long,
            default_value_t = Enabled::default(),
            value_name = "ENABLED",
        ),
    )]
    pub autodrop_borrows: Enabled,

    #[cfg_attr(feature = "clap", clap(flatten))]
    pub async_: AsyncFilterSet,

    /// Force generation of async helpers even if no async functions/futures are present.
    #[cfg_attr(feature = "clap", arg(long, default_value_t = false))]
    pub generate_async_helpers: bool,

    /// Generate helpers for threading builtins. Implies `--generate-async-helpers`.
    #[cfg_attr(feature = "clap", arg(long, default_value_t = false))]
    pub generate_threading_helpers: bool,
}

#[cfg(feature = "clap")]
fn parse_rename(name: &str) -> Result<(String, String)> {
    let mut parts = name.splitn(2, '=');
    let to_rename = parts.next().unwrap();
    match parts.next() {
        Some(part) => Ok((to_rename.to_string(), part.to_string())),
        None => anyhow::bail!("`--rename` option must have an `=` in it (e.g. `--rename a=b`)"),
    }
}

impl Opts {
    pub fn build(&self) -> Box<dyn WorldGenerator> {
        let mut r = C::default();
        r.opts = self.clone();
        Box::new(r)
    }
}

#[derive(Clone, Debug, Default)]
struct Return {
    scalar: Option<Scalar>,
    retptrs: Vec<Type>,
}

#[derive(Clone)]
struct CSig {
    name: String,
    sig: String,
    params: Vec<(bool, String)>,
    ret: Return,
    retptrs: Vec<String>,
}

#[derive(Clone, Debug)]
enum Scalar {
    Void,
    OptionBool(Type),
    ResultBool(Option<Type>, Option<Type>),
    Type(Type),
}

impl WorldGenerator for C {
    fn preprocess(&mut self, resolve: &Resolve, world: WorldId) -> Result<()> {
        self.world = self
            .opts
            .rename_world
            .clone()
            .unwrap_or_else(|| resolve.worlds[world].name.clone());
        self.sizes.fill(resolve);
        self.world_id = Some(world);

        let mut interfaces = HashMap::new();
        let world = &resolve.worlds[world];
        for (key, _item) in world.imports.iter().chain(world.exports.iter()) {
            let name = resolve.name_world_key(key);
            interfaces
                .entry(name)
                .or_insert(Vec::new())
                .push(key.clone());
        }

        for (from, to) in self.opts.rename.iter() {
            match interfaces.get(from) {
                Some(keys) => {
                    for key in keys {
                        self.renamed_interfaces.insert(key.clone(), to.clone());
                    }
                }
                None => {
                    eprintln!("warning: rename of `{from}` did not match any interfaces");
                }
            }
        }
        Ok(())
    }

    fn import_interface(
        &mut self,
        resolve: &Resolve,
        name: &WorldKey,
        id: InterfaceId,
        _files: &mut Files,
    ) -> Result<()> {
        let wasm_import_module = resolve.name_world_key(name);
        let mut r#gen = self.interface(resolve, true, Some(&wasm_import_module));
        r#gen.interface = Some((id, name));
        r#gen.define_interface_types(id);

        for (i, (_name, func)) in resolve.interfaces[id].functions.iter().enumerate() {
            if i == 0 {
                let name = resolve.name_world_key(name);
                uwriteln!(r#gen.src.h_fns, "\n// Imported Functions from `{name}`");
                uwriteln!(r#gen.src.c_fns, "\n// Imported Functions from `{name}`");
            }
            r#gen.import(Some(name), func);
        }

        r#gen.r#gen.src.append(&r#gen.src);

        Ok(())
    }

    fn import_funcs(
        &mut self,
        resolve: &Resolve,
        world: WorldId,
        funcs: &[(&str, &Function)],
        _files: &mut Files,
    ) {
        let name = &resolve.worlds[world].name;
        let mut r#gen = self.interface(resolve, true, Some("$root"));
        r#gen.define_function_types(funcs);

        for (i, (_name, func)) in funcs.iter().enumerate() {
            if i == 0 {
                uwriteln!(r#gen.src.h_fns, "\n// Imported Functions from `{name}`");
                uwriteln!(r#gen.src.c_fns, "\n// Imported Functions from `{name}`");
            }
            r#gen.import(None, func);
        }

        r#gen.r#gen.src.append(&r#gen.src);
    }

    fn export_interface(
        &mut self,
        resolve: &Resolve,
        name: &WorldKey,
        id: InterfaceId,
        _files: &mut Files,
    ) -> Result<()> {
        let mut r#gen = self.interface(resolve, false, None);
        r#gen.interface = Some((id, name));
        r#gen.define_interface_types(id);

        for (i, (_name, func)) in resolve.interfaces[id].functions.iter().enumerate() {
            if i == 0 {
                let name = resolve.name_world_key(name);
                uwriteln!(r#gen.src.h_fns, "\n// Exported Functions from `{name}`");
                uwriteln!(r#gen.src.c_fns, "\n// Exported Functions from `{name}`");
            }
            r#gen.export(func, Some(name));
        }

        r#gen.r#gen.src.append(&r#gen.src);
        Ok(())
    }

    fn export_funcs(
        &mut self,
        resolve: &Resolve,
        world: WorldId,
        funcs: &[(&str, &Function)],
        _files: &mut Files,
    ) -> Result<()> {
        let name = &resolve.worlds[world].name;
        let mut r#gen = self.interface(resolve, false, None);
        r#gen.define_function_types(funcs);

        for (i, (_name, func)) in funcs.iter().enumerate() {
            if i == 0 {
                uwriteln!(r#gen.src.h_fns, "\n// Exported Functions from `{name}`");
                uwriteln!(r#gen.src.c_fns, "\n// Exported Functions from `{name}`");
            }
            r#gen.export(func, None);
        }

        r#gen.r#gen.src.append(&r#gen.src);
        Ok(())
    }

    fn import_types(
        &mut self,
        resolve: &Resolve,
        _world: WorldId,
        types: &[(&str, TypeId)],
        _files: &mut Files,
    ) {
        let mut r#gen = self.interface(resolve, true, Some("$root"));
        let mut live = LiveTypes::default();
        for (_, id) in types {
            live.add_type_id(resolve, *id);
        }
        r#gen.define_live_types(live);
        r#gen.r#gen.src.append(&r#gen.src);
    }

    fn finish(&mut self, resolve: &Resolve, id: WorldId, files: &mut Files) -> Result<()> {
        let linking_symbol = component_type_object::linking_symbol(&self.world);
        self.c_include("<stdlib.h>");
        let snake = self.world.to_snake_case();
        uwriteln!(
            self.src.c_adapters,
            "\n// Ensure that the *_component_type.o object is linked in"
        );
        uwrite!(
            self.src.c_adapters,
            "
               extern void {linking_symbol}(void);
               __attribute__((used))
               void {linking_symbol}_public_use_in_this_compilation_unit(void) {{
                   {linking_symbol}();
               }}
           ",
        );

        self.print_intrinsics();

        if self.needs_string {
            self.c_include("<string.h>");
            let (strlen, size) = match self.opts.string_encoding {
                StringEncoding::UTF8 => (format!("strlen(s)"), 1),
                StringEncoding::UTF16 => {
                    self.h_include("<uchar.h>");
                    uwrite!(
                        self.src.h_helpers,
                        "
                            // Returns the length of the UTF-16 string `s` in code units
                            size_t {snake}_string_len(const char16_t* s);
                        ",
                    );
                    uwrite!(
                        self.src.c_helpers,
                        "
                            size_t {snake}_string_len(const char16_t* s) {{
                                char16_t* c = (char16_t*)s;
                                for (; *c; ++c);
                                return c-s;
                            }}
                        ",
                    );
                    (format!("{snake}_string_len(s)"), 2)
                }
                StringEncoding::CompactUTF16 => unimplemented!(),
            };
            let ty = self.char_type();
            let c_string_ty = match self.opts.string_encoding {
                StringEncoding::UTF8 => "char",
                StringEncoding::UTF16 => "char16_t",
                StringEncoding::CompactUTF16 => panic!("Compact UTF16 unsupported"),
            };
            uwrite!(
                self.src.h_helpers,
                "
                   // Sets the string `ret` to reference the input string `s` without copying it
                   void {snake}_string_set({snake}_string_t *ret, const {c_string_ty} *s);

                   // Creates a copy of the input nul-terminated string `s` and
                   // stores it into the component model string `ret`.
                   void {snake}_string_dup({snake}_string_t *ret, const {c_string_ty} *s);

                   // Creates a copy of the input string `s` with length `len` and
                   // stores it into the component model string `ret`.
                   // The length is specified in code units (bytes for UTF-8, 16-bit values for UTF-16).
                   void {snake}_string_dup_n({snake}_string_t *ret, const {c_string_ty} *s, size_t len);

                   // Deallocates the string pointed to by `ret`, deallocating
                   // the memory behind the string.
                   void {snake}_string_free({snake}_string_t *ret);\
               ",
            );
            uwrite!(
                self.src.c_helpers,
                "
                   void {snake}_string_set({snake}_string_t *ret, const {c_string_ty} *s) {{
                       ret->ptr = ({ty}*) s;
                       ret->len = {strlen};
                   }}

                   void {snake}_string_dup({snake}_string_t *ret, const {c_string_ty} *s) {{
                       ret->len = {strlen};
                       ret->ptr = ({ty}*) cabi_realloc(NULL, 0, {size}, ret->len * {size});
                       memcpy(ret->ptr, s, ret->len * {size});
                   }}

                   void {snake}_string_dup_n({snake}_string_t *ret, const {c_string_ty} *s, size_t len) {{
                       ret->len = len;
                       ret->ptr = ({ty}*) cabi_realloc(NULL, 0, {size}, ret->len * {size});
                       memcpy(ret->ptr, s, ret->len * {size});
                   }}

                   void {snake}_string_free({snake}_string_t *ret) {{
                       if (ret->len > 0) {{
                           free(ret->ptr);
                       }}
                       ret->ptr = NULL;
                       ret->len = 0;
                   }}
               ",
            );
        }
        if self.needs_union_int32_float {
            uwriteln!(
                self.src.c_defs,
                "\nunion int32_float {{ int32_t a; float b; }};"
            );
        }
        if self.needs_union_float_int32 {
            uwriteln!(
                self.src.c_defs,
                "\nunion float_int32 {{ float a; int32_t b; }};"
            );
        }
        if self.needs_union_int64_double {
            uwriteln!(
                self.src.c_defs,
                "\nunion int64_double {{ int64_t a; double b; }};"
            );
        }
        if self.needs_union_double_int64 {
            uwriteln!(
                self.src.c_defs,
                "\nunion double_int64 {{ double a; int64_t b; }};"
            );
        }
        if self.needs_async
            || self.futures.len() > 0
            || self.opts.generate_async_helpers
            || self.opts.generate_threading_helpers
        {
            self.generate_async_helpers();
        }
        if self.opts.generate_threading_helpers {
            self.generate_threading_helpers();
        }
        let version = env!("CARGO_PKG_VERSION");
        let mut h_str = wit_bindgen_core::Source::default();

        wit_bindgen_core::generated_preamble(&mut h_str, version);

        uwrite!(
            h_str,
            "#ifndef __BINDINGS_{0}_H
            #define __BINDINGS_{0}_H
            #ifdef __cplusplus
            extern \"C\" {{",
            self.world.to_shouty_snake_case(),
        );

        // Deindent the extern C { declaration
        h_str.deindent(1);
        uwriteln!(h_str, "\n#endif\n");

        uwriteln!(h_str, "#include <stdint.h>");
        uwriteln!(h_str, "#include <stdbool.h>");
        uwriteln!(h_str, "#include <stddef.h>");
        for include in self.h_includes.iter() {
            uwriteln!(h_str, "#include {include}");
        }

        let mut c_str = wit_bindgen_core::Source::default();
        wit_bindgen_core::generated_preamble(&mut c_str, version);
        uwriteln!(c_str, "#include \"{snake}.h\"");
        for include in self.c_includes.iter() {
            uwriteln!(c_str, "#include {include}");
        }
        c_str.push_str(&self.src.c_defs);
        c_str.push_str(&self.src.c_fns);

        // Declare a statically-allocated return area, if needed. We only do
        // this for export bindings, because import bindings allocate their
        // return-area on the stack.
        if !self.return_pointer_area_size.is_empty() {
            // Automatic indentation avoided due to `extern "C" {` declaration
            uwrite!(
                c_str,
                "
                __attribute__((__aligned__({})))
                static uint8_t RET_AREA[{}];
                ",
                self.return_pointer_area_align
                    .format(POINTER_SIZE_EXPRESSION),
                self.return_pointer_area_size
                    .format(POINTER_SIZE_EXPRESSION),
            );
        }

        if self.needs_string {
            uwriteln!(
                h_str,
                "
                typedef struct {snake}_string_t {{\n\
                  {ty} *ptr;\n\
                  size_t len;\n\
                }} {snake}_string_t;",
                ty = self.char_type(),
            );
        }

        if self.src.h_async.len() > 0 {
            uwriteln!(h_str, "\n// Async Helper Functions");
            h_str.push_str(&self.src.h_async);
            h_str.push_str("\n");
        }

        if self.src.h_defs.len() > 0 {
            h_str.push_str(&self.src.h_defs);
        }

        h_str.push_str(&self.src.h_fns);

        if !self.opts.no_helpers && self.src.h_helpers.len() > 0 {
            uwriteln!(h_str, "\n// Helper Functions");
            h_str.push_str(&self.src.h_helpers);
            h_str.push_str("\n");
        }

        if !self.opts.no_helpers && self.src.c_helpers.len() > 0 {
            uwriteln!(c_str, "\n// Helper Functions");
            c_str.push_str(self.src.c_helpers.as_mut_string());
        }

        if self.src.c_async.len() > 0 {
            uwriteln!(c_str, "\n// Async Helper Functions");
            c_str.push_str(&self.src.c_async);
            c_str.push_str("\n");
        }

        uwriteln!(c_str, "\n// Component Adapters");

        c_str.push_str(&self.src.c_adapters);

        uwriteln!(
            h_str,
            "
            #ifdef __cplusplus
            }}
            #endif
            #endif"
        );

        files.push(&format!("{snake}.h"), h_str.as_bytes());
        files.push(&format!("{snake}.c"), c_str.as_bytes());
        if !self.opts.no_object_file {
            files.push(
                &format!("{snake}_component_type.o",),
                component_type_object::object(
                    resolve,
                    id,
                    &self.world,
                    self.opts.string_encoding,
                    self.opts.type_section_suffix.as_deref(),
                )
                .unwrap()
                .as_slice(),
            );
        }

        Ok(())
    }

    fn pre_export_interface(&mut self, resolve: &Resolve, _files: &mut Files) -> Result<()> {
        self.remove_types_redefined_by_exports(resolve, self.world_id.unwrap());
        Ok(())
    }
}

impl C {
    fn interface<'a>(
        &'a mut self,
        resolve: &'a Resolve,
        in_import: bool,
        wasm_import_module: Option<&'a str>,
    ) -> InterfaceGenerator<'a> {
        InterfaceGenerator {
            src: Source::default(),
            r#gen: self,
            resolve,
            interface: None,
            in_import,
            wasm_import_module,
        }
    }

    fn h_include(&mut self, s: &str) {
        self.h_includes.push(s.to_string());
    }

    fn c_include(&mut self, s: &str) {
        self.c_includes.push(s.to_string());
    }

    fn char_type(&self) -> &'static str {
        match self.opts.string_encoding {
            StringEncoding::UTF8 => "uint8_t",
            StringEncoding::UTF16 => "uint16_t",
            StringEncoding::CompactUTF16 => panic!("Compact UTF16 unsupported"),
        }
    }

    fn type_name(&mut self, ty: &Type) -> String {
        let mut name = String::new();
        self.push_type_name(ty, &mut name);
        name
    }

    fn push_type_name(&mut self, ty: &Type, dst: &mut String) {
        match ty {
            Type::Bool => dst.push_str("bool"),
            Type::Char => dst.push_str("uint32_t"), // TODO: better type?
            Type::U8 => dst.push_str("uint8_t"),
            Type::S8 => dst.push_str("int8_t"),
            Type::U16 => dst.push_str("uint16_t"),
            Type::S16 => dst.push_str("int16_t"),
            Type::U32 => dst.push_str("uint32_t"),
            Type::S32 => dst.push_str("int32_t"),
            Type::U64 => dst.push_str("uint64_t"),
            Type::S64 => dst.push_str("int64_t"),
            Type::F32 => dst.push_str("float"),
            Type::F64 => dst.push_str("double"),
            Type::String => {
                dst.push_str(&self.world.to_snake_case());
                dst.push_str("_");
                dst.push_str("string_t");
                self.needs_string = true;
            }
            Type::ErrorContext => dst.push_str("error_context"),
            Type::Id(id) => {
                if let Some(name) = self.type_names.get(id) {
                    dst.push_str(name);
                    return;
                }

                panic!("failed to find type name for {id:?}");
            }
        }
    }

    /// Removes all types from `self.{dtor_funcs,type_names,resources}` which
    /// are redefined in exports.
    ///
    /// WIT interfaces can be both imported and exported but they're represented
    /// with the same `TypeId` internally within the `wit-parser`
    /// representation. This means that duplicate types need to be generated for
    /// exports, even if the same interface was already imported. If nothing
    /// were done here though then the same type imported and exported wouldn't
    /// generate anything new since preexisting types are skipped in
    /// `define_live_types`.
    ///
    /// This function will trim the sets on `self` to only retain those types
    /// which exports refer to that come from imports.
    fn remove_types_redefined_by_exports(&mut self, resolve: &Resolve, world: WorldId) {
        let live_import_types = imported_types_used_by_exported_interfaces(resolve, world);
        self.dtor_funcs.retain(|k, _| live_import_types.contains(k));
        self.type_names.retain(|k, _| live_import_types.contains(k));
        self.resources.retain(|k, _| live_import_types.contains(k));
    }

    fn perform_cast(&mut self, op: &str, cast: &Bitcast) -> String {
        match cast {
            Bitcast::I32ToF32 | Bitcast::I64ToF32 => {
                self.needs_union_int32_float = true;
                format!("((union int32_float){{ (int32_t) {op} }}).b")
            }
            Bitcast::F32ToI32 | Bitcast::F32ToI64 => {
                self.needs_union_float_int32 = true;
                format!("((union float_int32){{ {op} }}).b")
            }
            Bitcast::I64ToF64 => {
                self.needs_union_int64_double = true;
                format!("((union int64_double){{ (int64_t) {op} }}).b")
            }
            Bitcast::F64ToI64 => {
                self.needs_union_double_int64 = true;
                format!("((union double_int64){{ {op} }}).b")
            }
            Bitcast::I32ToI64 | Bitcast::LToI64 | Bitcast::PToP64 => {
                format!("(int64_t) {op}")
            }
            Bitcast::I64ToI32 | Bitcast::I64ToL => {
                format!("(int32_t) {op}")
            }
            // P64 is currently represented as int64_t, so no conversion is needed.
            Bitcast::I64ToP64 | Bitcast::P64ToI64 => {
                format!("{op}")
            }
            Bitcast::P64ToP | Bitcast::I32ToP | Bitcast::LToP => {
                format!("(uint8_t *) {op}")
            }

            // Cast to uintptr_t to avoid implicit pointer-to-int conversions.
            Bitcast::PToI32 | Bitcast::PToL => format!("(uintptr_t) {op}"),

            Bitcast::I32ToL | Bitcast::LToI32 | Bitcast::None => op.to_string(),

            Bitcast::Sequence(sequence) => {
                let [first, second] = &**sequence;
                let inner = self.perform_cast(op, first);
                self.perform_cast(&inner, second)
            }
        }
    }

    fn generate_threading_helpers(&mut self) {
        let snake = self.world.to_snake_case();
        uwriteln!(
            self.src.h_async,
            r#"
__attribute__((__import_module__("$root"), __import_name__("[context-get-1]")))
extern void* {snake}_context_get_1(void);

__attribute__((__import_module__("$root"), __import_name__("[context-set-1]")))
extern void {snake}_context_set_1(void* value);

__attribute__((__import_module__("$root"), __import_name__("[thread-index]")))
extern uint32_t {snake}_thread_index(void);

__attribute__((__import_module__("$root"), __import_name__("[thread-new-indirect-v0]")))
extern uint32_t {snake}_thread_new_indirect(void (*start_function)(void*), void* arg);
__attribute__((__import_module__("$root"), __import_name__("[thread-resume-later]")))
extern void {snake}_thread_resume_later(uint32_t thread);

__attribute__((__import_module__("$root"), __import_name__("[thread-suspend]")))
extern uint32_t {snake}_thread_suspend(void);
__attribute__((__import_module__("$root"), __import_name__("[cancellable][thread-suspend]")))
extern uint32_t {snake}_thread_suspend_cancellable(void);

__attribute__((__import_module__("$root"), __import_name__("[cancellable][thread-yield]")))
extern uint32_t {snake}_thread_yield_cancellable(void);

__attribute__((__import_module__("$root"), __import_name__("[thread-suspend-then-resume]")))
extern uint32_t {snake}_thread_suspend_then_resume(uint32_t thread);
__attribute__((__import_module__("$root"), __import_name__("[cancellable][thread-suspend-then-resume]")))
extern uint32_t {snake}_thread_suspend_then_resume_cancellable(uint32_t thread);

__attribute__((__import_module__("$root"), __import_name__("[thread-yield-then-resume]")))
extern uint32_t {snake}_thread_yield_then_resume(uint32_t thread);
__attribute__((__import_module__("$root"), __import_name__("[cancellable][thread-yield-then-resume]")))
extern uint32_t {snake}_thread_yield_then_resume_cancellable(uint32_t thread);

__attribute__((__import_module__("$root"), __import_name__("[thread-suspend-then-promote]")))
extern uint32_t {snake}_thread_suspend_then_promote(uint32_t thread);
__attribute__((__import_module__("$root"), __import_name__("[cancellable][thread-suspend-then-promote]")))
extern uint32_t {snake}_thread_suspend_then_promote_cancellable(uint32_t thread);

__attribute__((__import_module__("$root"), __import_name__("[thread-yield-then-promote]")))
extern uint32_t {snake}_thread_yield_then_promote(uint32_t thread);
__attribute__((__import_module__("$root"), __import_name__("[cancellable][thread-yield-then-promote]")))
extern uint32_t {snake}_thread_yield_then_promote_cancellable(uint32_t thread);
            "#
        );
    }

    fn generate_async_helpers(&mut self) {
        let snake = self.world.to_snake_case();
        let shouty = self.world.to_shouty_snake_case();
        uwriteln!(
            self.src.h_async,
            "
typedef uint32_t {snake}_subtask_status_t;
typedef uint32_t {snake}_subtask_t;
#define {shouty}_SUBTASK_STATE(status) (({snake}_subtask_state_t) ((status) & 0xf))
#define {shouty}_SUBTASK_HANDLE(status) (({snake}_subtask_t) ((status) >> 4))

typedef enum {snake}_subtask_state {{
    {shouty}_SUBTASK_STARTING,
    {shouty}_SUBTASK_STARTED,
    {shouty}_SUBTASK_RETURNED,
    {shouty}_SUBTASK_STARTED_CANCELLED,
    {shouty}_SUBTASK_RETURNED_CANCELLED,
}} {snake}_subtask_state_t;

{snake}_subtask_status_t {snake}_subtask_cancel({snake}_subtask_t subtask);
void {snake}_subtask_drop({snake}_subtask_t subtask);

typedef uint32_t {snake}_callback_code_t;
#define {shouty}_CALLBACK_CODE_EXIT 0
#define {shouty}_CALLBACK_CODE_YIELD 1
#define {shouty}_CALLBACK_CODE_WAIT(set) (2 | (set << 4))

typedef enum {snake}_event_code {{
    {shouty}_EVENT_NONE,
    {shouty}_EVENT_SUBTASK,
    {shouty}_EVENT_STREAM_READ,
    {shouty}_EVENT_STREAM_WRITE,
    {shouty}_EVENT_FUTURE_READ,
    {shouty}_EVENT_FUTURE_WRITE,
    {shouty}_EVENT_CANCEL,
}} {snake}_event_code_t;

typedef struct {snake}_event {{
    {snake}_event_code_t event;
    uint32_t waitable;
    uint32_t code;
}} {snake}_event_t;

typedef uint32_t {snake}_waitable_set_t;
{snake}_waitable_set_t {snake}_waitable_set_new(void);
void {snake}_waitable_join(uint32_t waitable, {snake}_waitable_set_t set);
void {snake}_waitable_set_drop({snake}_waitable_set_t set);
void {snake}_waitable_set_wait({snake}_waitable_set_t set, {snake}_event_t *event);
void {snake}_waitable_set_poll({snake}_waitable_set_t set, {snake}_event_t *event);

void {snake}_task_cancel(void);

typedef uint32_t {snake}_waitable_status_t;
#define {shouty}_WAITABLE_STATE(status) (({snake}_waitable_state_t) ((status) & 0xf))
#define {shouty}_WAITABLE_COUNT(status) ((uint32_t) ((status) >> 4))
#define {shouty}_WAITABLE_STATUS_BLOCKED (({snake}_waitable_status_t) -1)

typedef enum {snake}_waitable_state {{
    {shouty}_WAITABLE_COMPLETED,
    {shouty}_WAITABLE_DROPPED,
    {shouty}_WAITABLE_CANCELLED,
}} {snake}_waitable_state_t;

void {snake}_backpressure_inc(void);
void {snake}_backpressure_dec(void);
void* {snake}_context_get_0(void);
void {snake}_context_set_0(void* value);
void {snake}_thread_yield(void);
            "
        );
        uwriteln!(
            self.src.c_async,
            r#"
__attribute__((__import_module__("$root"), __import_name__("[subtask-cancel]")))
extern uint32_t __subtask_cancel(uint32_t handle);

{snake}_subtask_status_t {snake}_subtask_cancel({snake}_subtask_t subtask) {{
    return __subtask_cancel(subtask);
}}

__attribute__((__import_module__("$root"), __import_name__("[subtask-drop]")))
extern void __subtask_drop(uint32_t handle);

void {snake}_subtask_drop({snake}_subtask_t subtask) {{
    __subtask_drop(subtask);
}}

__attribute__((__import_module__("$root"), __import_name__("[waitable-set-new]")))
extern uint32_t __waitable_set_new(void);

{snake}_waitable_set_t {snake}_waitable_set_new(void) {{
    return __waitable_set_new();
}}

__attribute__((__import_module__("$root"), __import_name__("[waitable-join]")))
extern void __waitable_join(uint32_t, uint32_t);

void {snake}_waitable_join(uint32_t waitable, {snake}_waitable_set_t set) {{
    __waitable_join(waitable, set);
}}

__attribute__((__import_module__("$root"), __import_name__("[waitable-set-drop]")))
extern void __waitable_set_drop(uint32_t);

void {snake}_waitable_set_drop({snake}_waitable_set_t set) {{
    __waitable_set_drop(set);
}}

__attribute__((__import_module__("$root"), __import_name__("[waitable-set-wait]")))
extern uint32_t __waitable_set_wait(uint32_t, uint32_t*);
__attribute__((__import_module__("$root"), __import_name__("[waitable-set-poll]")))
extern uint32_t __waitable_set_poll(uint32_t, uint32_t*);

void {snake}_waitable_set_wait({snake}_waitable_set_t set, {snake}_event_t *event) {{
    event->event = ({snake}_event_code_t) __waitable_set_wait(set, &event->waitable);
}}

void {snake}_waitable_set_poll({snake}_waitable_set_t set, {snake}_event_t *event) {{
    event->event = ({snake}_event_code_t) __waitable_set_poll(set, &event->waitable);
}}

__attribute__((__import_module__("[export]$root"), __import_name__("[task-cancel]")))
extern void __task_cancel(void);

void {snake}_task_cancel() {{
    __task_cancel();
}}

__attribute__((__import_module__("$root"), __import_name__("[backpressure-inc]")))
extern void __backpressure_inc(void);

void {snake}_backpressure_inc(void) {{
    __backpressure_inc();
}}

__attribute__((__import_module__("$root"), __import_name__("[backpressure-dec]")))
extern void __backpressure_dec(void);

void {snake}_backpressure_dec(void) {{
    __backpressure_dec();
}}

__attribute__((__import_module__("$root"), __import_name__("[context-get-0]")))
extern void* __context_get_0(void);

void* {snake}_context_get_0(void) {{
    return __context_get_0();
}}

__attribute__((__import_module__("$root"), __import_name__("[context-set-0]")))
extern void __context_set_0(void*);


void {snake}_context_set_0(void *value) {{
    __context_set_0(value);
}}

__attribute__((__import_module__("$root"), __import_name__("[thread-yield]")))
extern uint32_t __thread_yield(void);

void {snake}_thread_yield(void) {{
    __thread_yield();
}}
            "#
        );
    }
}

pub fn imported_types_used_by_exported_interfaces(
    resolve: &Resolve,
    world: WorldId,
) -> HashSet<TypeId> {
    // First build up a set of all types used by exports and all the
    // exported interfaces.
    let mut live_export_types = LiveTypes::default();
    let mut exported_interfaces = HashSet::new();
    for (_, export) in resolve.worlds[world].exports.iter() {
        match export {
            WorldItem::Function(_) => {}
            WorldItem::Interface { id, .. } => {
                exported_interfaces.insert(*id);
                live_export_types.add_interface(resolve, *id)
            }
            WorldItem::Type { .. } => unreachable!(),
        }
    }

    // Using the above sets a set of required import interfaces can be
    // calculated. This is all referred-to-types that are owned by an
    // interface that aren't present in an export. Note that the topological
    // sorting and WIT requirements are what makes this check possible.
    let mut imports_used = HashSet::new();
    for ty in live_export_types.iter() {
        if let TypeOwner::Interface(id) = resolve.types[ty].owner {
            if !exported_interfaces.contains(&id) {
                imports_used.insert(id);
            }
        }
    }

    // With the set of imports used that aren't shadowed by exports the set
    // of types on `self` can now be trimmed. All live types in all the
    // imports are calculated and then everything except these are removed.
    let mut live_import_types = LiveTypes::default();
    for import in imports_used {
        live_import_types.add_interface(resolve, import);
    }
    let live_import_types = live_import_types.iter().collect::<HashSet<_>>();
    live_import_types
}

fn is_prim_type(resolve: &Resolve, ty: &Type) -> bool {
    if let Type::Id(id) = ty {
        is_prim_type_id(resolve, *id)
    } else {
        true
    }
}

fn is_prim_type_id(resolve: &Resolve, id: TypeId) -> bool {
    // A named type contributes its (interface-local) name to the generated
    // type name rather than its structure, so even when its underlying
    // representation is primitive it must not be treated as a world-shareable
    // "primitive" type. Otherwise two interfaces that each define a
    // differently-typed but identically-named type (e.g. `field-value` in two
    // versions of `wasi:http/types`) collapse onto the same anonymous type
    // name, producing structs with incompatible element pointer types.
    // See https://github.com/bytecodealliance/wit-bindgen/issues/1621.
    if resolve.types[id].name.is_some() {
        return false;
    }
    match &resolve.types[id].kind {
        TypeDefKind::List(elem) => is_prim_type(resolve, elem),

        TypeDefKind::Option(ty) => is_prim_type(resolve, ty),

        TypeDefKind::Tuple(tuple) => tuple.types.iter().all(|ty| is_prim_type(resolve, ty)),

        TypeDefKind::Type(ty) => is_prim_type(resolve, ty),

        TypeDefKind::Record(_)
        | TypeDefKind::Resource
        | TypeDefKind::Handle(_)
        | TypeDefKind::Flags(_)
        | TypeDefKind::Variant(_)
        | TypeDefKind::Enum(_)
        | TypeDefKind::Result(_)
        | TypeDefKind::Future(_)
        | TypeDefKind::Stream(_)
        | TypeDefKind::Unknown => false,
        TypeDefKind::FixedLengthList(..) => todo!(),
        TypeDefKind::Map(key, value) => is_prim_type(resolve, key) && is_prim_type(resolve, value),
    }
}

pub fn push_ty_name(resolve: &Resolve, ty: &Type, src: &mut String) {
    match ty {
        Type::Bool => src.push_str("bool"),
        Type::Char => src.push_str("char32"),
        Type::U8 => src.push_str("u8"),
        Type::S8 => src.push_str("s8"),
        Type::U16 => src.push_str("u16"),
        Type::S16 => src.push_str("s16"),
        Type::U32 => src.push_str("u32"),
        Type::S32 => src.push_str("s32"),
        Type::U64 => src.push_str("u64"),
        Type::S64 => src.push_str("s64"),
        Type::F32 => src.push_str("f32"),
        Type::F64 => src.push_str("f64"),
        Type::String => src.push_str("string"),
        Type::ErrorContext => todo!(),
        Type::Id(id) => {
            let ty = &resolve.types[*id];
            if let Some(name) = &ty.name {
                return src.push_str(&name.to_snake_case());
            }
            match &ty.kind {
                TypeDefKind::Type(t) => push_ty_name(resolve, t, src),
                TypeDefKind::Record(_)
                | TypeDefKind::Resource
                | TypeDefKind::Flags(_)
                | TypeDefKind::Enum(_)
                | TypeDefKind::Variant(_) => {
                    unimplemented!()
                }
                TypeDefKind::Tuple(t) => {
                    src.push_str("tuple");
                    src.push_str(&t.types.len().to_string());
                    for ty in t.types.iter() {
                        src.push_str("_");
                        push_ty_name(resolve, ty, src);
                    }
                }
                TypeDefKind::Option(ty) => {
                    src.push_str("option_");
                    push_ty_name(resolve, ty, src);
                }
                TypeDefKind::Result(r) => {
                    src.push_str("result_");
                    match &r.ok {
                        Some(ty) => push_ty_name(resolve, ty, src),
                        None => src.push_str("void"),
                    }
                    src.push_str("_");
                    match &r.err {
                        Some(ty) => push_ty_name(resolve, ty, src),
                        None => src.push_str("void"),
                    }
                }
                TypeDefKind::List(ty) => {
                    src.push_str("list_");
                    push_ty_name(resolve, ty, src);
                }
                TypeDefKind::Future(ty) => {
                    src.push_str("future_");
                    match ty {
                        Some(ty) => push_ty_name(resolve, ty, src),
                        None => src.push_str("void"),
                    }
                }
                TypeDefKind::Stream(ty) => {
                    src.push_str("stream_");
                    match ty {
                        Some(ty) => push_ty_name(resolve, ty, src),
                        None => src.push_str("void"),
                    }
                }
                TypeDefKind::Handle(Handle::Own(resource)) => {
                    src.push_str("own_");
                    push_ty_name(resolve, &Type::Id(*resource), src);
                }
                TypeDefKind::Handle(Handle::Borrow(resource)) => {
                    src.push_str("borrow_");
                    push_ty_name(resolve, &Type::Id(*resource), src);
                }
                TypeDefKind::Unknown => unreachable!(),
                TypeDefKind::FixedLengthList(..) => todo!(),
                TypeDefKind::Map(key, value) => {
                    src.push_str("map_");
                    push_ty_name(resolve, key, src);
                    src.push_str("_");
                    push_ty_name(resolve, value, src);
                }
            }
        }
    }
}

pub fn owner_namespace<'a>(
    interface: Option<(InterfaceId, &'a WorldKey)>,
    in_import: bool,
    world: String,
    resolve: &Resolve,
    id: TypeId,
    renamed_interfaces: &HashMap<WorldKey, String>,
) -> String {
    let ty = &resolve.types[id];
    match (ty.owner, interface) {
        // If this type is owned by an interface, then we must be generating
        // bindings for that interface to proceed.
        (TypeOwner::Interface(a), Some((b, key))) if a == b => {
            interface_identifier(key, resolve, !in_import, renamed_interfaces)
        }
        (TypeOwner::Interface(_), None) => unreachable!(),
        (TypeOwner::Interface(_), Some(_)) => unreachable!(),

        // If this type is owned by a world then we must not be generating
        // bindings for an interface.
        (TypeOwner::World(_), None) => world.to_snake_case(),
        (TypeOwner::World(_), Some(_)) => unreachable!(),

        // If this type has no owner then it's an anonymous type. Here it's
        // assigned to whatever we happen to be generating bindings for.
        (TypeOwner::None, Some((_, key))) => {
            interface_identifier(key, resolve, !in_import, renamed_interfaces)
        }
        (TypeOwner::None, None) => world.to_snake_case(),
    }
}

fn interface_identifier(
    interface_id: &WorldKey,
    resolve: &Resolve,
    in_export: bool,
    renamed_interfaces: &HashMap<WorldKey, String>,
) -> String {
    if let Some(rename) = renamed_interfaces.get(interface_id) {
        let mut ns = String::new();
        if in_export && matches!(interface_id, WorldKey::Interface(_)) {
            ns.push_str("exports_");
        }
        ns.push_str(rename);
        return ns;
    }

    match interface_id {
        WorldKey::Name(name) => name.to_snake_case(),
        WorldKey::Interface(id) => {
            let mut ns = String::new();
            if in_export {
                ns.push_str("exports_");
            }
            let iface = &resolve.interfaces[*id];
            let pkg = &resolve.packages[iface.package.unwrap()];
            ns.push_str(&pkg.name.namespace.to_snake_case());
            ns.push_str("_");
            ns.push_str(&pkg.name.name.to_snake_case());
            ns.push_str("_");
            let pkg_has_multiple_versions = resolve.packages.iter().any(|(_, p)| {
                p.name.namespace == pkg.name.namespace
                    && p.name.name == pkg.name.name
                    && p.name.version != pkg.name.version
            });
            if pkg_has_multiple_versions {
                if let Some(version) = &pkg.name.version {
                    let version = version
                        .to_string()
                        .replace('.', "_")
                        .replace('-', "_")
                        .replace('+', "_");
                    ns.push_str(&version);
                    ns.push_str("_");
                }
            }
            ns.push_str(&iface.name.as_ref().unwrap().to_snake_case());
            ns
        }
    }
}

pub fn c_func_name(
    in_import: bool,
    resolve: &Resolve,
    world: &str,
    interface_id: Option<&WorldKey>,
    func: &Function,
    renamed_interfaces: &HashMap<WorldKey, String>,
) -> String {
    let mut name = String::new();
    match interface_id {
        Some(id) => name.push_str(&interface_identifier(
            id,
            resolve,
            !in_import,
            renamed_interfaces,
        )),
        None => {
            if !in_import {
                name.push_str("exports_");
            }
            name.push_str(&world.to_snake_case());
        }
    }
    name.push_str("_");
    name.push_str(&func.name.to_snake_case().replace('.', "_"));
    name
}

struct InterfaceGenerator<'a> {
    src: Source,
    in_import: bool,
    r#gen: &'a mut C,
    resolve: &'a Resolve,
    interface: Option<(InterfaceId, &'a WorldKey)>,
    wasm_import_module: Option<&'a str>,
}

impl C {
    fn print_intrinsics(&mut self) {
        // Note that these intrinsics are declared as `weak` so they can be
        // overridden from some other symbol.
        self.src.c_fns("\n// Canonical ABI intrinsics");
        self.src.c_fns("\n");
        self.src.c_fns(
            r#"
                __attribute__((__weak__, __export_name__("cabi_realloc")))
                void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) {
                    (void) old_size;
                    if (new_size == 0) return (void*) align;
                    void *ret = realloc(ptr, new_size);
                    if (!ret) abort();
                    return ret;
                }
            "#,
        );
    }
}

impl Return {
    fn return_single(
        &mut self,
        resolve: &Resolve,
        ty: &Type,
        orig_ty: &Type,
        sig_flattening: bool,
    ) {
        let id = match ty {
            Type::Id(id) => *id,
            Type::String => {
                self.retptrs.push(*orig_ty);
                return;
            }
            Type::ErrorContext => todo!("return_single for error-context"),
            _ => {
                self.scalar = Some(Scalar::Type(*orig_ty));
                return;
            }
        };
        match &resolve.types[id].kind {
            TypeDefKind::Type(t) => return self.return_single(resolve, t, orig_ty, sig_flattening),

            // Flags are returned as their bare values, and enums and handles are scalars
            TypeDefKind::Flags(_)
            | TypeDefKind::Enum(_)
            | TypeDefKind::Handle(_)
            | TypeDefKind::Future(_)
            | TypeDefKind::Stream(_) => {
                self.scalar = Some(Scalar::Type(*orig_ty));
                return;
            }

            // Unpack optional returns where a boolean discriminant is
            // returned and then the actual type returned is returned
            // through a return pointer.
            TypeDefKind::Option(ty) => {
                if sig_flattening {
                    self.scalar = Some(Scalar::OptionBool(*ty));
                    self.retptrs.push(*ty);
                    return;
                }
            }

            // Unpack a result as a boolean return type, with two
            // return pointers for ok and err values
            TypeDefKind::Result(r) => {
                if sig_flattening {
                    if let Some(ok) = r.ok {
                        self.retptrs.push(ok);
                    }
                    if let Some(err) = r.err {
                        self.retptrs.push(err);
                    }
                    self.scalar = Some(Scalar::ResultBool(r.ok, r.err));
                    return;
                }
            }

            // These types are always returned indirectly.
            TypeDefKind::Tuple(_)
            | TypeDefKind::Record(_)
            | TypeDefKind::List(_)
            | TypeDefKind::Map(..)
            | TypeDefKind::Variant(_) => {}

            TypeDefKind::Resource => todo!("return_single for resource"),
            TypeDefKind::Unknown => unreachable!(),
            TypeDefKind::FixedLengthList(..) => todo!(),
        }

        self.retptrs.push(*orig_ty);
    }
}

impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> {
    fn resolve(&self) -> &'a Resolve {
        self.resolve
    }

    fn type_record(&mut self, id: TypeId, _name: &str, record: &Record, docs: &Docs) {
        self.src.h_defs("\n");
        self.docs(docs, SourceType::HDefs);
        self.start_typedef_struct(id);
        for field in record.fields.iter() {
            self.docs(&field.docs, SourceType::HDefs);
            self.print_ty(SourceType::HDefs, &field.ty);
            self.src.h_defs(" ");
            self.src.h_defs(&to_c_ident(&field.name));
            self.src.h_defs(";\n");
        }
        self.finish_typedef_struct(id);
    }

    fn type_resource(&mut self, id: TypeId, name: &str, _docs: &Docs) {
        let ns = self.owner_namespace(id);
        let snake = name.to_snake_case();
        let mut own = ns.clone();
        let mut borrow = own.clone();
        own.push_str("_own");
        borrow.push_str("_borrow");
        own.push_str("_");
        borrow.push_str("_");
        own.push_str(&snake);
        borrow.push_str(&snake);
        own.push_str("_t");
        borrow.push_str("_t");

        // All resources, whether or not they're imported or exported, get the
        // ability to drop handles.
        self.src.h_helpers(&format!(
            "
extern void {ns}_{snake}_drop_own({own} handle);
            "
        ));
        let import_module = if self.in_import {
            self.wasm_import_module.unwrap().to_string()
        } else {
            let module = match self.interface {
                Some((_, key)) => self.resolve.name_world_key(key),
                None => unimplemented!("resource exports from worlds"),
            };
            format!("[export]{module}")
        };

        let drop_fn = format!("__wasm_import_{ns}_{snake}_drop");

        self.src.c_helpers(&format!(
            r#"
__attribute__((__import_module__("{import_module}"), __import_name__("[resource-drop]{name}")))
extern void {drop_fn}(int32_t handle);

void {ns}_{snake}_drop_own({own} handle) {{
    {drop_fn}(handle.__handle);
}}
            "#
        ));

        // All resources, whether or not they're imported or exported, have an
        // handle-index-based representation for "own" handles.
        self.src.h_defs(&format!(
            "\ntypedef struct {own} {{\nint32_t __handle;\n}} {own};\n"
        ));

        if self.in_import {
            // For imported resources borrowed handles are represented the same
            // way as owned handles. They're given a unique type, however, to
            // prevent type confusion at runtime in theory.
            self.src.h_defs(&format!(
                "\ntypedef struct {borrow} {{\nint32_t __handle;\n}} {borrow};\n"
            ));

            // Explicit borrow dropping is not required if autodrop is enabled.
            if !self.autodrop_enabled() {
                // As we have two different types for owned vs borrowed resources,
                // but owns and borrows are dropped using the same intrinsic we
                // also generate a version of the drop function for borrows that we
                // possibly acquire through our exports.
                self.src.h_helpers(&format!(
                    "\nextern void {ns}_{snake}_drop_borrow({borrow} handle);\n"
                ));

                self.src.c_helpers(&format!(
                    "
void {ns}_{snake}_drop_borrow({borrow} handle) {{
    __wasm_import_{ns}_{snake}_drop(handle.__handle);
}}
                "
                ));
            }

            // To handle the two types generated for borrow/own this helper
            // function enables converting an own handle to a borrow handle
            // which will have the same index internally.
            self.src.h_helpers(&format!(
                "
extern {borrow} {ns}_borrow_{snake}({own} handle);
                "
            ));

            self.src.c_helpers(&format!(
                r#"
{borrow} {ns}_borrow_{snake}({own} arg) {{
    return ({borrow}) {{ arg.__handle }};
}}
                "#
            ));
        } else {
            // For exported resources first generate a typedef that the user
            // will be required to fill in. This is an empty struct.
            self.src.h_defs("\n");
            self.src.h_defs("typedef struct ");
            let ty_name = self.r#gen.type_names[&id].clone();
            self.src.h_defs(&ty_name);
            self.src.h_defs(" ");
            self.print_typedef_target(id);
            let (_, key) = self.interface.unwrap();
            let module = self.resolve.name_world_key(key);

            // Exported resources use a different representation than imports
            // for borrows which is a raw pointer to the struct declared just
            // above.
            self.src
                .h_defs(&format!("\ntypedef {ty_name}* {borrow};\n"));

            // Exported resources are defined by this module which means they
            // get access to more intrinsics:
            //
            // * construction of a resource (rep to handle)
            // * extraction of the representation of a resource (handle to rep)
            //
            // Additionally users must define a destructor for this resource, so
            // declare its prototype here.
            self.src.h_helpers(&format!(
                "
extern {own} {ns}_{snake}_new({ty_name} *rep);
extern {ty_name}* {ns}_{snake}_rep({own} handle);
void {ns}_{snake}_destructor({ty_name} *rep);
                "
            ));

            self.src.c_helpers(&format!(
                r#"
__attribute__(( __import_module__("[export]{module}"), __import_name__("[resource-new]{name}")))
extern int32_t __wasm_import_{ns}_{snake}_new(int32_t);

__attribute__((__import_module__("[export]{module}"), __import_name__("[resource-rep]{name}")))
extern int32_t __wasm_import_{ns}_{snake}_rep(int32_t);

{own} {ns}_{snake}_new({ty_name} *rep) {{
    return ({own}) {{ __wasm_import_{ns}_{snake}_new((int32_t) rep) }};
}}

{ty_name}* {ns}_{snake}_rep({own} handle) {{
    return ({ns}_{snake}_t*) __wasm_import_{ns}_{snake}_rep(handle.__handle);
}}

__attribute__((__export_name__("{module}#[dtor]{snake}")))
void __wasm_export_{ns}_{snake}_dtor({ns}_{snake}_t* arg) {{
    {ns}_{snake}_destructor(arg);
}}
                "#
            ));
        }

        self.r#gen.resources.insert(
            id,
            ResourceInfo {
                own,
                borrow,
                direction: if self.in_import {
                    Direction::Import
                } else {
                    Direction::Export
                },
                drop_fn,
            },
        );
    }

    fn type_tuple(&mut self, id: TypeId, _name: &str, tuple: &Tuple, docs: &Docs) {
        self.src.h_defs("\n");
        self.docs(docs, SourceType::HDefs);
        self.start_typedef_struct(id);
        for (i, ty) in tuple.types.iter().enumerate() {
            self.print_ty(SourceType::HDefs, ty);
            uwriteln!(self.src.h_defs, " f{i};");
        }
        self.finish_typedef_struct(id);
    }

    fn type_flags(&mut self, id: TypeId, name: &str, flags: &Flags, docs: &Docs) {
        self.src.h_defs("\n");
        self.docs(docs, SourceType::HDefs);
        self.src.h_defs("typedef ");
        let repr = flags_repr(flags);
        self.src.h_defs(int_repr(repr));
        self.src.h_defs(" ");
        self.print_typedef_target(id);

        if flags.flags.len() > 0 {
            self.src.h_defs("\n");
        }
        let ns = self.owner_namespace(id).to_shouty_snake_case();
        for (i, flag) in flags.flags.iter().enumerate() {
            self.docs(&flag.docs, SourceType::HDefs);
            uwriteln!(
                self.src.h_defs,
                "#define {ns}_{}_{} (1 << {i})",
                name.to_shouty_snake_case(),
                flag.name.to_shouty_snake_case(),
            );
        }
    }

    fn type_variant(&mut self, id: TypeId, name: &str, variant: &Variant, docs: &Docs) {
        self.src.h_defs("\n");
        self.docs(docs, SourceType::HDefs);
        self.start_typedef_struct(id);
        self.src.h_defs(int_repr(variant.tag()));
        self.src.h_defs(" tag;\n");

        let cases_with_data = Vec::from_iter(
            variant
                .cases
                .iter()
                .filter_map(|case| case.ty.as_ref().map(|ty| (&case.name, ty))),
        );

        if !cases_with_data.is_empty() {
            self.src.h_defs("union {\n");
            for (name, ty) in cases_with_data {
                self.print_ty(SourceType::HDefs, ty);
                self.src.h_defs(" ");
                self.src.h_defs(&to_c_ident(name));
                self.src.h_defs(";\n");
            }
            self.src.h_defs("} val;\n");
        }
        self.finish_typedef_struct(id);

        if variant.cases.len() > 0 {
            self.src.h_defs("\n");
        }
        let ns = self.owner_namespace(id).to_shouty_snake_case();
        for (i, case) in variant.cases.iter().enumerate() {
            self.docs(&case.docs, SourceType::HDefs);
            uwriteln!(
                self.src.h_defs,
                "#define {ns}_{}_{} {i}",
                name.to_shouty_snake_case(),
                case.name.to_shouty_snake_case(),
            );
        }
    }

    fn type_option(&mut self, id: TypeId, _name: &str, payload: &Type, docs: &Docs) {
        self.src.h_defs("\n");
        self.docs(docs, SourceType::HDefs);
        self.start_typedef_struct(id);
        self.src.h_defs("bool is_some;\n");
        self.print_ty(SourceType::HDefs, payload);
        self.src.h_defs(" val;\n");
        self.finish_typedef_struct(id);
    }

    fn type_result(&mut self, id: TypeId, _name: &str, result: &Result_, docs: &Docs) {
        self.src.h_defs("\n");
        self.docs(docs, SourceType::HDefs);
        self.start_typedef_struct(id);
        self.src.h_defs("bool is_err;\n");
        if result.ok.is_some() || result.err.is_some() {
            self.src.h_defs("union {\n");
            if let Some(ok) = result.ok.as_ref() {
                self.print_ty(SourceType::HDefs, ok);
                self.src.h_defs(" ok;\n");
            }
            if let Some(err) = result.err.as_ref() {
                self.print_ty(SourceType::HDefs, err);
                self.src.h_defs(" err;\n");
            }
            self.src.h_defs("} val;\n");
        }
        self.finish_typedef_struct(id);
    }

    fn type_enum(&mut self, id: TypeId, name: &str, enum_: &Enum, docs: &Docs) {
        uwrite!(self.src.h_defs, "\n");
        self.docs(docs, SourceType::HDefs);
        let int_t = int_repr(enum_.tag());
        uwrite!(self.src.h_defs, "typedef {int_t} ");
        self.print_typedef_target(id);

        if enum_.cases.len() > 0 {
            self.src.h_defs("\n");
        }
        let ns = self.owner_namespace(id).to_shouty_snake_case();
        for (i, case) in enum_.cases.iter().enumerate() {
            self.docs(&case.docs, SourceType::HDefs);
            uwriteln!(
                self.src.h_defs,
                "#define {ns}_{}_{} {i}",
                name.to_shouty_snake_case(),
                case.name.to_shouty_snake_case(),
            );
        }
    }

    fn type_alias(&mut self, id: TypeId, _name: &str, ty: &Type, docs: &Docs) {
        // we should skip generating `typedef` for `Resource` types because they aren't even
        // defined anywhere, not even in `type_resource`. Only its `Handle` types are defined.
        // The aliasing handle types are defined in `define_anonymous_type`.
        let target = dealias(self.resolve, id);
        if matches!(&self.resolve.types[target].kind, TypeDefKind::Resource) {
            return;
        }

        self.src.h_defs("\n");
        self.docs(docs, SourceType::HDefs);
        self.src.h_defs("typedef ");
        self.print_ty(SourceType::HDefs, ty);
        self.src.h_defs(" ");
        self.print_typedef_target(id);
    }

    fn type_list(&mut self, id: TypeId, _name: &str, ty: &Type, docs: &Docs) {
        self.src.h_defs("\n");
        self.docs(docs, SourceType::HDefs);
        self.start_typedef_struct(id);
        self.print_ty(SourceType::HDefs, ty);
        self.src.h_defs(" *ptr;\n");
        self.src.h_defs("size_t len;\n");
        self.finish_typedef_struct(id);
    }

    fn type_fixed_length_list(
        &mut self,
        _id: TypeId,
        _name: &str,
        _ty: &Type,
        _size: u32,
        _docs: &Docs,
    ) {
        todo!("named fixed-length list types are not yet supported in the C backend")
    }

    fn type_map(&mut self, id: TypeId, _name: &str, key: &Type, value: &Type, docs: &Docs) {
        self.src.h_defs("\n");
        self.docs(docs, SourceType::HDefs);
        self.print_map_entry_typedef(id, key, value);
        self.start_typedef_struct(id);
        self.print_map_entry_typename(id);
        self.src.h_defs(" *ptr;\n");
        self.src.h_defs("size_t len;\n");
        self.finish_typedef_struct(id);
    }

    fn type_future(&mut self, id: TypeId, _name: &str, _ty: &Option<Type>, docs: &Docs) {
        self.src.h_defs("\n");
        self.docs(docs, SourceType::HDefs);
        self.src.h_defs("\ntypedef uint32_t ");
        self.print_typedef_target(id);
    }

    fn type_stream(&mut self, id: TypeId, _name: &str, _ty: &Option<Type>, docs: &Docs) {
        self.src.h_defs("\n");
        self.docs(docs, SourceType::HDefs);
        self.src.h_defs("\ntypedef uint32_t ");
        self.print_typedef_target(id);
    }

    fn type_builtin(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs) {
        let _ = (id, name, ty, docs);
    }
}

impl<'a> wit_bindgen_core::AnonymousTypeGenerator<'a> for InterfaceGenerator<'a> {
    fn resolve(&self) -> &'a Resolve {
        self.resolve
    }

    fn anonymous_type_handle(&mut self, id: TypeId, handle: &Handle, _docs: &Docs) {
        self.src.h_defs("\ntypedef ");
        let resource = match handle {
            Handle::Borrow(id) | Handle::Own(id) => id,
        };
        let info = &self.r#gen.resources[&dealias(self.resolve, *resource)];
        match handle {
            Handle::Borrow(_) => self.src.h_defs(&info.borrow),
            Handle::Own(_) => self.src.h_defs(&info.own),
        }
        self.src.h_defs(" ");
        self.print_typedef_target(id);
    }

    fn anonymous_type_tuple(&mut self, id: TypeId, ty: &Tuple, _docs: &Docs) {
        self.src.h_defs("\ntypedef ");
        self.src.h_defs("struct {\n");
        for (i, t) in ty.types.iter().enumerate() {
            let ty = self.r#gen.type_name(t);
            uwriteln!(self.src.h_defs, "{ty} f{i};");
        }
        self.src.h_defs("}");
        self.src.h_defs(" ");
        self.print_typedef_target(id);
    }

    fn anonymous_type_option(&mut self, id: TypeId, ty: &Type, _docs: &Docs) {
        self.src.h_defs("\ntypedef ");
        self.src.h_defs("struct {\n");
        self.src.h_defs("bool is_some;\n");
        let ty = self.r#gen.type_name(ty);
        uwriteln!(self.src.h_defs, "{ty} val;");
        self.src.h_defs("}");
        self.src.h_defs(" ");
        self.print_typedef_target(id);
    }

    fn anonymous_type_result(&mut self, id: TypeId, ty: &Result_, _docs: &Docs) {
        self.src.h_defs("\ntypedef ");
        self.src.h_defs(
            "struct {
                bool is_err;
            ",
        );
        let ok_ty = ty.ok.as_ref();
        let err_ty = ty.err.as_ref();
        if ok_ty.is_some() || err_ty.is_some() {
            self.src.h_defs("union {\n");
            if let Some(ok) = ok_ty {
                let ty = self.r#gen.type_name(ok);
                uwriteln!(self.src.h_defs, "{ty} ok;");
            }
            if let Some(err) = err_ty {
                let ty = self.r#gen.type_name(err);
                uwriteln!(self.src.h_defs, "{ty} err;");
            }
            self.src.h_defs("} val;\n");
        }
        self.src.h_defs("}");
        self.src.h_defs(" ");
        self.print_typedef_target(id);
    }

    fn anonymous_type_list(&mut self, id: TypeId, ty: &Type, _docs: &Docs) {
        self.src.h_defs("\ntypedef ");
        self.src.h_defs("struct {\n");
        let ty = self.r#gen.type_name(ty);
        uwriteln!(self.src.h_defs, "{ty} *ptr;");
        self.src.h_defs("size_t len;\n");
        self.src.h_defs("}");
        self.src.h_defs(" ");
        self.print_typedef_target(id);
    }

    fn anonymous_type_future(&mut self, id: TypeId, _ty: &Option<Type>, _docs: &Docs) {
        self.src.h_defs("\ntypedef uint32_t ");
        self.print_typedef_target(id);
    }

    fn anonymous_type_stream(&mut self, id: TypeId, _ty: &Option<Type>, _docs: &Docs) {
        self.src.h_defs("\ntypedef uint32_t ");
        self.print_typedef_target(id);
    }

    fn anonymous_type_type(&mut self, _id: TypeId, _ty: &Type, _docs: &Docs) {
        todo!("print_anonymous_type for type");
    }

    fn anonymous_type_fixed_length_list(
        &mut self,
        _id: TypeId,
        _ty: &Type,
        _size: u32,
        _docs: &Docs,
    ) {
        todo!("print_anonymous_type for fixed length list");
    }

    fn anonymous_type_map(&mut self, id: TypeId, key: &Type, value: &Type, _docs: &Docs) {
        self.print_map_entry_typedef(id, key, value);
        self.src.h_defs("\ntypedef ");
        self.src.h_defs("struct {\n");
        self.print_map_entry_typename(id);
        self.src.h_defs(" *ptr;\n");
        self.src.h_defs("size_t len;\n");
        self.src.h_defs("}");
        self.src.h_defs(" ");
        self.print_typedef_target(id);
    }
}

pub enum CTypeNameInfo<'a> {
    Named { name: &'a str },
    Anonymous { is_prim: bool },
}

/// Generate the type part of a c identifier, missing the namespace and the `_t` suffix.
/// Additionally return a `CTypeNameInfo` that describes what sort of name has been produced.
pub fn gen_type_name(resolve: &Resolve, ty: TypeId) -> (CTypeNameInfo<'_>, String) {
    let mut encoded = String::new();
    push_ty_name(resolve, &Type::Id(ty), &mut encoded);
    let info = if let Some(name) = &resolve.types[ty].name {
        CTypeNameInfo::Named {
            name: name.as_ref(),
        }
    } else {
        CTypeNameInfo::Anonymous {
            is_prim: is_prim_type_id(resolve, ty),
        }
    };

    (info, encoded)
}

impl InterfaceGenerator<'_> {
    fn define_interface_types(&mut self, id: InterfaceId) {
        let mut live = LiveTypes::default();
        live.add_interface(self.resolve, id);
        self.define_live_types(live);
    }

    fn define_function_types(&mut self, funcs: &[(&str, &Function)]) {
        let mut live = LiveTypes::default();
        for (_, func) in funcs {
            live.add_func(self.resolve, func);
        }
        self.define_live_types(live);
    }

    fn define_live_types(&mut self, live: LiveTypes) {
        for ty in live.iter() {
            if self.r#gen.type_names.contains_key(&ty) {
                continue;
            }

            let (info, encoded) = gen_type_name(&self.resolve, ty);
            match info {
                CTypeNameInfo::Named { name } => {
                    let typedef_name = format!("{}_{encoded}_t", self.owner_namespace(ty));
                    let prev = self.r#gen.type_names.insert(ty, typedef_name.clone());
                    assert!(prev.is_none());

                    self.define_type(name, ty)
                }

                CTypeNameInfo::Anonymous { is_prim } => {
                    let (defined, name) = if is_prim {
                        let namespace = self.r#gen.world.to_snake_case();
                        let name = format!("{namespace}_{encoded}_t");
                        let new_prim = self.r#gen.prim_names.insert(name.clone());
                        (!new_prim, name)
                    } else {
                        let namespace = self.owner_namespace(ty);
                        (false, format!("{namespace}_{encoded}_t"))
                    };

                    let prev = self.r#gen.type_names.insert(ty, name);
                    assert!(prev.is_none());

                    if defined {
                        continue;
                    }

                    let kind = &self.resolve.types[ty].kind;
                    if let TypeDefKind::Handle(handle) = kind {
                        let resource = match handle {
                            Handle::Borrow(id) | Handle::Own(id) => id,
                        };
                        let origin = dealias(self.resolve, *resource);
                        if origin == *resource {
                            continue;
                        }
                    }

                    self.define_anonymous_type(ty)
                }
            }

            self.define_dtor(ty);
        }
    }

    fn define_dtor(&mut self, id: TypeId) {
        let h_helpers_start = self.src.h_helpers.len();
        let c_helpers_start = self.src.c_helpers.len();

        let name = self.r#gen.type_names[&id].clone();
        let prefix = name.strip_suffix("_t").unwrap();

        self.src
            .h_helpers(&format!("\nvoid {prefix}_free({name} *ptr);\n"));
        self.src
            .c_helpers(&format!("\nvoid {prefix}_free({name} *ptr) {{\n"));
        let c_helpers_body_start = self.src.c_helpers.len();
        match &self.resolve.types[id].kind {
            TypeDefKind::Type(t) => self.free(t, "ptr"),

            TypeDefKind::Flags(_) => {}
            TypeDefKind::Enum(_) => {}

            TypeDefKind::Record(r) => {
                for field in r.fields.iter() {
                    self.free(&field.ty, &format!("&ptr->{}", to_c_ident(&field.name)));
                }
            }

            TypeDefKind::Tuple(t) => {
                for (i, ty) in t.types.iter().enumerate() {
                    self.free(ty, &format!("&ptr->f{i}"));
                }
            }

            TypeDefKind::List(t) => {
                self.src.c_helpers("size_t list_len = ptr->len;\n");
                uwriteln!(self.src.c_helpers, "if (list_len > 0) {{");
                let mut t_name = String::new();
                self.r#gen.push_type_name(t, &mut t_name);
                self.src
                    .c_helpers(&format!("{t_name} *list_ptr = ptr->ptr;\n"));
                self.src
                    .c_helpers("for (size_t i = 0; i < list_len; i++) {\n");
                self.free(t, "&list_ptr[i]");
                self.src.c_helpers("}\n");
                uwriteln!(self.src.c_helpers, "free(list_ptr);");
                uwriteln!(self.src.c_helpers, "}}");
            }

            TypeDefKind::Variant(v) => {
                self.src.c_helpers("switch ((int32_t) ptr->tag) {\n");
                for (i, case) in v.cases.iter().enumerate() {
                    if let Some(ty) = &case.ty {
                        uwriteln!(self.src.c_helpers, "case {}: {{", i);
                        let expr = format!("&ptr->val.{}", to_c_ident(&case.name));
                        self.free(ty, &expr);
                        self.src.c_helpers("break;\n");
                        self.src.c_helpers("}\n");
                    }
                }
                self.src.c_helpers("}\n");
            }

            TypeDefKind::Option(t) => {
                self.src.c_helpers("if (ptr->is_some) {\n");
                self.free(t, "&ptr->val");
                self.src.c_helpers("}\n");
            }

            TypeDefKind::Result(r) => {
                self.src.c_helpers("if (!ptr->is_err) {\n");
                if let Some(ok) = &r.ok {
                    self.free(ok, "&ptr->val.ok");
                }
                if let Some(err) = &r.err {
                    self.src.c_helpers("} else {\n");
                    self.free(err, "&ptr->val.err");
                }
                self.src.c_helpers("}\n");
            }
            TypeDefKind::Future(_) | TypeDefKind::Stream(_) => {
                self.free(&Type::Id(id), "*ptr");
            }
            TypeDefKind::Resource => {}
            TypeDefKind::Handle(Handle::Borrow(id) | Handle::Own(id)) => {
                self.free(&Type::Id(*id), "*ptr");
            }
            TypeDefKind::Unknown => unreachable!(),
            TypeDefKind::FixedLengthList(..) => todo!(),
            TypeDefKind::Map(key, value) => {
                let entry_name = self.map_entry_typename(id);
                self.src.c_helpers("size_t map_len = ptr->len;\n");
                uwriteln!(self.src.c_helpers, "if (map_len > 0) {{");
                uwriteln!(self.src.c_helpers, "{entry_name} *map_ptr = ptr->ptr;");
                self.src
                    .c_helpers("for (size_t i = 0; i < map_len; i++) {\n");
                self.free(key, "&map_ptr[i].key");
                self.free(value, "&map_ptr[i].value");
                self.src.c_helpers("}\n");
                uwriteln!(self.src.c_helpers, "free(map_ptr);");
                uwriteln!(self.src.c_helpers, "}}");
            }
        }
        if c_helpers_body_start == self.src.c_helpers.len() {
            self.src.c_helpers.as_mut_string().truncate(c_helpers_start);
            self.src.h_helpers.as_mut_string().truncate(h_helpers_start);
            return;
        }
        self.src.c_helpers("}\n");
        self.r#gen.dtor_funcs.insert(id, format!("{prefix}_free"));
    }

    fn free(&mut self, ty: &Type, expr: &str) {
        match ty {
            Type::Id(id) => {
                if let Some(dtor) = self.r#gen.dtor_funcs.get(&id) {
                    self.src.c_helpers(&format!("{dtor}({expr});\n"));
                }
            }
            Type::String => {
                let snake = self.r#gen.world.to_snake_case();
                self.src
                    .c_helpers(&format!("{snake}_string_free({expr});\n"));
            }
            Type::Bool
            | Type::U8
            | Type::S8
            | Type::U16
            | Type::S16
            | Type::U32
            | Type::S32
            | Type::U64
            | Type::S64
            | Type::F32
            | Type::F64
            | Type::Char => {}
            Type::ErrorContext => todo!("error context free"),
        }
    }

    fn c_func_name(&self, interface_id: Option<&WorldKey>, func: &Function) -> String {
        c_func_name(
            self.in_import,
            self.resolve,
            &self.r#gen.world,
            interface_id,
            func,
            &self.r#gen.renamed_interfaces,
        )
    }

    fn import(&mut self, interface_name: Option<&WorldKey>, func: &Function) {
        let async_ = self
            .r#gen
            .opts
            .async_
            .is_async(self.resolve, interface_name, func, true);
        if async_ {
            self.r#gen.needs_async = true;
        }

        self.docs(&func.docs, SourceType::HFns);
        let (variant, import_prefix) = if async_ {
            (AbiVariant::GuestImportAsync, "[async-lower]")
        } else {
            (AbiVariant::GuestImport, "")
        };
        let sig = self.resolve.wasm_signature(variant, func);

        self.src.c_fns("\n");

        // In the private C file, print a function declaration which is the
        // actual wasm import that we'll be calling, and this has the raw wasm
        // signature.
        uwriteln!(
            self.src.c_fns,
            "__attribute__((__import_module__(\"{}\"), __import_name__(\"{import_prefix}{}\")))",
            match interface_name {
                Some(name) => self.resolve.name_world_key(name),
                None => "$root".to_string(),
            },
            func.name
        );
        let name = self.c_func_name(interface_name, func);
        let import_name = self.r#gen.names.tmp(&format!("__wasm_import_{name}",));
        self.src.c_fns("extern ");
        match sig.results.len() {
            0 => self.src.c_fns("void"),
            1 => self.src.c_fns(wasm_type(sig.results[0])),
            _ => unimplemented!("multi-value return not supported"),
        }
        self.src.c_fns(" ");
        self.src.c_fns(&import_name);
        self.src.c_fns("(");
        for (i, param) in sig.params.iter().enumerate() {
            if i > 0 {
                self.src.c_fns(", ");
            }
            self.src.c_fns(wasm_type(*param));
        }
        if sig.params.len() == 0 {
            self.src.c_fns("void");
        }
        self.src.c_fns(");\n");

        // Print the public facing signature into the header, and since that's
        // what we are defining also print it into the C file.
        self.src.h_fns("extern ");
        let c_sig = self.print_sig(interface_name, func, &sig, async_);
        self.src.c_adapters("\n");
        self.src.c_adapters(&c_sig.sig);
        self.src.c_adapters(" {\n");

        if async_ {
            self.import_body_async(func, c_sig, &sig, &import_name);
        } else {
            self.import_body_sync(func, c_sig, &import_name);
        }

        self.src.c_adapters("}\n");

        self.generate_async_futures_and_streams("", func, interface_name);
    }

    fn import_body_sync(&mut self, func: &Function, c_sig: CSig, import_name: &str) {
        // construct optional adapters from maybe pointers to real optional
        // structs internally
        let mut optional_adapters = String::from("");
        if !self.r#gen.opts.no_sig_flattening {
            for (i, (_, param)) in c_sig.params.iter().enumerate() {
                let ty = &func.params[i].ty;
                if let Type::Id(id) = ty {
                    if let TypeDefKind::Option(_) = &self.resolve.types[*id].kind {
                        let ty = self.r#gen.type_name(ty);
                        uwrite!(
                            optional_adapters,
                            "{ty} {param};
                            {param}.is_some = maybe_{param} != NULL;"
                        );
                        uwriteln!(
                            optional_adapters,
                            "if (maybe_{param}) {{
                                {param}.val = *maybe_{param};
                            }}",
                        );
                    }
                }
            }
        }

        let mut f = FunctionBindgen::new(self, c_sig, &import_name);
        for (pointer, param) in f.sig.params.iter() {
            if *pointer {
                f.params.push(format!("*{param}"));
            } else {
                f.params.push(param.clone());
            }
        }
        for ptr in f.sig.retptrs.iter() {
            f.locals.insert(ptr).unwrap();
        }
        f.src.push_str(&optional_adapters);
        abi::call(
            f.r#gen.resolve,
            AbiVariant::GuestImport,
            LiftLower::LowerArgsLiftResults,
            func,
            &mut f,
            false,
        );

        let FunctionBindgen {
            src,
            import_return_pointer_area_size,
            import_return_pointer_area_align,
            ..
        } = f;

        if !import_return_pointer_area_size.is_empty() {
            self.src.c_adapters(&format!(
                "\
                    __attribute__((__aligned__({})))
                    uint8_t ret_area[{}];
                ",
                import_return_pointer_area_align.format(POINTER_SIZE_EXPRESSION),
                import_return_pointer_area_size.format(POINTER_SIZE_EXPRESSION),
            ));
        }

        self.src.c_adapters(&String::from(src));
    }

    fn import_body_async(
        &mut self,
        func: &Function,
        c_sig: CSig,
        wasm_sig: &WasmSignature,
        import_name: &str,
    ) {
        let mut params = Vec::new();
        if wasm_sig.indirect_params {
            params.push(format!("(uint8_t*) {}", c_sig.params[0].1));
        } else {
            let mut f = FunctionBindgen::new(self, c_sig.clone(), "INVALID");
            for (i, Param { ty, .. }) in func.params.iter().enumerate() {
                let param = &c_sig.params[i].1;
                params.extend(abi::lower_flat(f.r#gen.resolve, &mut f, param.clone(), ty));
            }
            f.r#gen.src.c_adapters.push_str(&f.src);
        }
        if func.result.is_some() {
            params.push(format!("(uint8_t*) {}", c_sig.params.last().unwrap().1));
        }
        uwriteln!(
            self.src.c_adapters,
            "return {import_name}({});",
            params.join(", "),
        );
    }

    fn export(&mut self, func: &Function, interface_name: Option<&WorldKey>) {
        let async_ = self
            .r#gen
            .opts
            .async_
            .is_async(self.resolve, interface_name, func, false);

        let (variant, prefix) = if async_ {
            self.r#gen.needs_async = true;
            (AbiVariant::GuestExportAsync, "[async-lift]")
        } else {
            (AbiVariant::GuestExport, "")
        };

        let sig = self.resolve.wasm_signature(variant, func);

        self.src.c_fns("\n");

        let core_module_name = interface_name.map(|s| self.resolve.name_world_key(s));
        let export_name = func.legacy_core_export_name(core_module_name.as_deref());

        // Print the actual header for this function into the header file, and
        // it's what we'll be calling.
        let h_sig = self.print_sig(interface_name, func, &sig, async_);

        // Generate, in the C source file, the raw wasm signature that has the
        // canonical ABI.
        uwriteln!(
            self.src.c_adapters,
            "\n__attribute__((__export_name__(\"{prefix}{export_name}\")))"
        );
        let name = self.c_func_name(interface_name, func);
        let import_name = self.r#gen.names.tmp(&format!("__wasm_export_{name}"));

        let mut f = FunctionBindgen::new(self, h_sig, &import_name);
        match sig.results.len() {
            0 => f.r#gen.src.c_adapters("void"),
            1 => f.r#gen.src.c_adapters(wasm_type(sig.results[0])),
            _ => unimplemented!("multi-value return not supported"),
        }
        f.r#gen.src.c_adapters(" ");
        f.r#gen.src.c_adapters(&import_name);
        f.r#gen.src.c_adapters("(");
        for (i, param) in sig.params.iter().enumerate() {
            if i > 0 {
                f.r#gen.src.c_adapters(", ");
            }
            let name = f.locals.tmp("arg");
            uwrite!(f.r#gen.src.c_adapters, "{} {}", wasm_type(*param), name);
            f.params.push(name);
        }
        if sig.params.len() == 0 {
            f.r#gen.src.c_adapters("void");
        }
        f.r#gen.src.c_adapters(") {\n");

        // Perform all lifting/lowering and append it to our src.
        abi::call(
            f.r#gen.resolve,
            variant,
            LiftLower::LiftArgsLowerResults,
            func,
            &mut f,
            async_,
        );
        let FunctionBindgen {
            src,
            deferred_task_return,
            ..
        } = f;
        self.src.c_adapters(&src);
        self.src.c_adapters("}\n");

        if async_ {
            let snake = self.r#gen.world.to_snake_case();
            let return_ty = match &func.result {
                Some(ty) => format!("{} ret", self.r#gen.type_name(ty)),
                None => "void".to_string(),
            };
            let DeferredTaskReturn::Emitted {
                body: mut task_return_body,
                name: task_return_name,
                params: task_return_params,
            } = deferred_task_return
            else {
                unreachable!()
            };
            let task_return_param_tys = task_return_params
                .iter()
                .map(|(ty, _expr)| wasm_type(*ty))
                .collect::<Vec<_>>()
                .join(", ");
            let task_return_param_exprs = task_return_params
                .iter()
                .map(|(_ty, expr)| expr.as_str())
                .collect::<Vec<_>>()
                .join(", ");
            let task_return_body = task_return_body.as_mut_string();
            uwriteln!(
                self.src.h_fns,
                "{snake}_callback_code_t {name}_callback({snake}_event_t *event);",
            );
            uwriteln!(self.src.h_helpers, "void {name}_return({return_ty});");
            let import_module = match interface_name {
                Some(name) => self.resolve.name_world_key(name),
                None => "$root".to_string(),
            };
            uwriteln!(
                self.src.c_helpers,
                r#"
__attribute__((__export_name__("[callback]{prefix}{export_name}")))
uint32_t {import_name}_callback(uint32_t event_raw, uint32_t waitable, uint32_t code) {{
    {snake}_event_t event;
    event.event = ({snake}_event_code_t) event_raw;
    event.waitable = waitable;
    event.code = code;
    return {name}_callback(&event);
}}

__attribute__((__import_module__("[export]{import_module}"), __import_name__("{task_return_name}")))
void {import_name}__task_return({task_return_param_tys});

void {name}_return({return_ty}) {{
    {task_return_body}
    {import_name}__task_return({task_return_param_exprs});
}}
                "#
            );
        } else if abi::guest_export_needs_post_return(self.resolve, func) {
            uwriteln!(
                self.src.c_fns,
                "__attribute__((__weak__, __export_name__(\"cabi_post_{export_name}\")))"
            );
            uwrite!(self.src.c_fns, "void {import_name}_post_return(");

            let mut params = Vec::new();
            let mut c_sig = CSig {
                name: String::from("INVALID"),
                sig: String::from("INVALID"),
                params: Vec::new(),
                ret: Return::default(),
                retptrs: Vec::new(),
            };
            for (i, result) in sig.results.iter().enumerate() {
                let name = format!("arg{i}");
                uwrite!(self.src.c_fns, "{} {name}", wasm_type(*result));
                c_sig.params.push((false, name.clone()));
                params.push(name);
            }
            self.src.c_fns.push_str(") {\n");

            let mut f = FunctionBindgen::new(self, c_sig, &import_name);
            f.params = params;
            abi::post_return(f.r#gen.resolve, func, &mut f);
            let FunctionBindgen { src, .. } = f;
            self.src.c_fns(&src);
            self.src.c_fns("}\n");
        }

        self.generate_async_futures_and_streams("[export]", func, interface_name);
    }

    fn print_sig(
        &mut self,
        interface_name: Option<&WorldKey>,
        func: &Function,
        sig: &WasmSignature,
        async_: bool,
    ) -> CSig {
        let name = self.c_func_name(interface_name, func);
        self.r#gen.names.insert(&name).expect("duplicate symbols");

        let start = self.src.h_fns.len();
        let mut result_rets = false;
        let mut result_rets_has_ok_type = false;

        let ret = if async_ && !self.in_import {
            Return {
                scalar: func.result.map(Scalar::Type),
                retptrs: Vec::new(),
            }
        } else {
            self.classify_ret(func)
        };
        if async_ {
            let snake = self.r#gen.world.to_snake_case();
            if self.in_import {
                uwrite!(self.src.h_fns, "{snake}_subtask_status_t");
            } else {
                uwrite!(self.src.h_fns, "{snake}_callback_code_t");
            }
        } else {
            match &ret.scalar {
                None | Some(Scalar::Void) => self.src.h_fns("void"),
                Some(Scalar::OptionBool(_id)) => self.src.h_fns("bool"),
                Some(Scalar::ResultBool(ok, _err)) => {
                    result_rets = true;
                    result_rets_has_ok_type = ok.is_some();
                    self.src.h_fns("bool");
                }
                Some(Scalar::Type(ty)) => self.print_ty(SourceType::HFns, ty),
            }
        }
        self.src.h_fns(" ");
        self.src.h_fns(&name);
        self.src.h_fns("(");
        let params;
        let mut retptrs = Vec::new();
        if async_ && self.in_import {
            params = self.print_sig_async_import_params(&name, func, sig);
        } else if async_ && !self.in_import {
            params = self.print_sig_params(func);
        } else {
            params = self.print_sig_params(func);
            let single_ret = ret.retptrs.len() == 1;
            for (i, ty) in ret.retptrs.iter().enumerate() {
                if i > 0 || func.params.len() > 0 {
                    self.src.h_fns(", ");
                }
                self.print_ty(SourceType::HFns, ty);
                self.src.h_fns(" *");
                let name: String = if result_rets {
                    assert!(i <= 1);
                    if i == 0 && result_rets_has_ok_type {
                        "ret".into()
                    } else {
                        "err".into()
                    }
                } else if single_ret {
                    "ret".into()
                } else {
                    format!("ret{i}")
                };
                self.src.h_fns(&name);
                retptrs.push(name);
            }
            if func.params.len() == 0 && ret.retptrs.len() == 0 {
                self.src.h_fns("void");
            }
        }
        self.src.h_fns(")");

        let sig = self.src.h_fns[start..].to_string();
        self.src.h_fns(";\n");

        CSig {
            sig,
            name,
            params,
            ret,
            retptrs,
        }
    }

    fn print_sig_params(&mut self, func: &Function) -> Vec<(bool, String)> {
        let mut params = Vec::new();
        for (i, Param { name, ty, .. }) in func.params.iter().enumerate() {
            if i > 0 {
                self.src.h_fns(", ");
            }
            let pointer = is_arg_by_pointer(self.resolve, ty);
            // optional param pointer sig_flattening
            let optional_type = if let Type::Id(id) = ty {
                if let TypeDefKind::Option(option_ty) = &self.resolve.types[*id].kind {
                    if !self.r#gen.opts.no_sig_flattening {
                        Some(option_ty)
                    } else {
                        None
                    }
                } else {
                    None
                }
            } else {
                None
            };
            let (print_ty, print_name) = if !self.r#gen.opts.no_sig_flattening {
                if let Some(option_ty) = optional_type {
                    (option_ty, format!("maybe_{}", to_c_ident(name)))
                } else {
                    (ty, to_c_ident(name))
                }
            } else {
                (ty, to_c_ident(name))
            };
            self.print_ty(SourceType::HFns, print_ty);
            self.src.h_fns(" ");
            if pointer {
                self.src.h_fns("*");
            }
            self.src.h_fns(&print_name);
            params.push((optional_type.is_none() && pointer, to_c_ident(name)));
        }
        params
    }

    fn print_sig_async_import_params(
        &mut self,
        c_func_name: &str,
        func: &Function,
        sig: &WasmSignature,
    ) -> Vec<(bool, String)> {
        let mut params = Vec::new();
        let mut printed = false;
        if sig.indirect_params {
            match &func.params[..] {
                [] => {}
                [Param { name: _, ty, .. }] => {
                    printed = true;
                    let name = "arg".to_string();
                    self.print_ty(SourceType::HFns, ty);
                    self.src.h_fns(" *");
                    self.src.h_fns(&name);
                    params.push((true, name));
                }
                multiple => {
                    printed = true;
                    let names = multiple
                        .iter()
                        .map(|Param { name, ty, .. }| (to_c_ident(name), self.r#gen.type_name(ty)))
                        .collect::<Vec<_>>();
                    uwriteln!(self.src.h_defs, "typedef struct {c_func_name}_args {{");
                    for (name, ty) in names {
                        uwriteln!(self.src.h_defs, "{ty} {name};");
                    }
                    uwriteln!(self.src.h_defs, "}} {c_func_name}_args_t;");
                    uwrite!(self.src.h_fns, "{c_func_name}_args_t *args");
                    params.push((true, "args".to_string()));
                }
            }
        } else {
            for Param { name, ty, .. } in func.params.iter() {
                let name = to_c_ident(name);
                if printed {
                    self.src.h_fns(", ");
                } else {
                    printed = true;
                }
                self.print_ty(SourceType::HFns, ty);
                self.src.h_fns(" ");
                self.src.h_fns(&name);
                params.push((false, name));
            }
        }
        if let Some(ty) = &func.result {
            if printed {
                self.src.h_fns(", ");
            } else {
                printed = true;
            }
            let name = "result".to_string();
            self.print_ty(SourceType::HFns, ty);
            self.src.h_fns(" *");
            self.src.h_fns(&name);
            params.push((true, name));
        }
        if !printed {
            self.src.h_fns("void");
        }
        params
    }

    fn classify_ret(&mut self, func: &Function) -> Return {
        let mut ret = Return::default();
        match &func.result {
            None => ret.scalar = Some(Scalar::Void),
            Some(ty) => {
                ret.return_single(self.resolve, ty, ty, !self.r#gen.opts.no_sig_flattening);
            }
        }
        return ret;
    }

    fn print_typedef_target(&mut self, id: TypeId) {
        let name = &self.r#gen.type_names[&id];
        self.src.h_defs(&name);
        self.src.h_defs(";\n");
    }

    fn start_typedef_struct(&mut self, id: TypeId) {
        let name = &self.r#gen.type_names[&id];
        self.src.h_defs("typedef struct ");
        self.src.h_defs(&name);
        self.src.h_defs(" {\n");
    }

    fn finish_typedef_struct(&mut self, id: TypeId) {
        self.src.h_defs("} ");
        self.print_typedef_target(id);
    }

    /// Returns the typedef name of the synthetic entry struct used as the
    /// element type of a `map<K, V>`'s flat-pair representation.
    fn map_entry_typename(&self, id: TypeId) -> String {
        let map_name = &self.r#gen.type_names[&id];
        let prefix = map_name.strip_suffix("_t").unwrap_or(map_name.as_str());
        format!("{prefix}_entry_t")
    }

    fn print_map_entry_typename(&mut self, id: TypeId) {
        let entry_name = self.map_entry_typename(id);
        self.src.h_defs(&entry_name);
    }

    /// Emits the `typedef struct { K key; V value; } <map>_entry_t;` companion
    /// declaration that precedes the map struct itself.
    fn print_map_entry_typedef(&mut self, id: TypeId, key: &Type, value: &Type) {
        self.src.h_defs("\ntypedef struct {\n");
        self.print_ty(SourceType::HDefs, key);
        self.src.h_defs(" key;\n");
        self.print_ty(SourceType::HDefs, value);
        self.src.h_defs(" value;\n} ");
        self.print_map_entry_typename(id);
        self.src.h_defs(";\n");
    }

    fn owner_namespace(&self, id: TypeId) -> String {
        owner_namespace(
            self.interface,
            self.in_import,
            self.r#gen.world.clone(),
            self.resolve,
            id,
            &self.r#gen.renamed_interfaces,
        )
    }

    fn print_ty(&mut self, stype: SourceType, ty: &Type) {
        self.r#gen
            .push_type_name(ty, self.src.src(stype).as_mut_string());
    }

    fn docs(&mut self, docs: &Docs, stype: SourceType) {
        let docs = match &docs.contents {
            Some(docs) => docs,
            None => return,
        };
        let src = self.src.src(stype);
        for line in docs.trim().lines() {
            src.push_str("// ");
            src.push_str(line);
            src.push_str("\n");
        }
    }

    fn autodrop_enabled(&self) -> bool {
        self.r#gen.opts.autodrop_borrows == Enabled::Yes
    }

    fn contains_droppable_borrow(&self, ty: &Type) -> bool {
        if let Type::Id(id) = ty {
            match &self.resolve.types[*id].kind {
                TypeDefKind::Handle(h) => match h {
                    // Handles to imported resources will need to be dropped, if the context
                    // they're used in is an export.
                    Handle::Borrow(id) => {
                        !self.in_import
                            && matches!(
                                self.r#gen.resources[&dealias(self.resolve, *id)].direction,
                                Direction::Import
                            )
                    }

                    Handle::Own(_) => false,
                },

                TypeDefKind::Resource | TypeDefKind::Flags(_) | TypeDefKind::Enum(_) => false,

                TypeDefKind::Record(r) => r
                    .fields
                    .iter()
                    .any(|f| self.contains_droppable_borrow(&f.ty)),

                TypeDefKind::Tuple(t) => {
                    t.types.iter().any(|ty| self.contains_droppable_borrow(ty))
                }

                TypeDefKind::Variant(v) => v.cases.iter().any(|case| {
                    case.ty
                        .as_ref()
                        .map_or(false, |ty| self.contains_droppable_borrow(ty))
                }),

                TypeDefKind::Option(ty) => self.contains_droppable_borrow(ty),

                TypeDefKind::Result(r) => {
                    r.ok.as_ref()
                        .map_or(false, |ty| self.contains_droppable_borrow(ty))
                        || r.err
                            .as_ref()
                            .map_or(false, |ty| self.contains_droppable_borrow(ty))
                }

                TypeDefKind::List(ty) => self.contains_droppable_borrow(ty),

                TypeDefKind::Future(_) | TypeDefKind::Stream(_) => false,

                TypeDefKind::Type(ty) => self.contains_droppable_borrow(ty),

                TypeDefKind::Unknown => false,
                TypeDefKind::FixedLengthList(..) => todo!(),
                TypeDefKind::Map(key, value) => {
                    self.contains_droppable_borrow(key) || self.contains_droppable_borrow(value)
                }
            }
        } else {
            false
        }
    }

    fn generate_async_futures_and_streams(
        &mut self,
        prefix: &str,
        func: &Function,
        interface: Option<&WorldKey>,
    ) {
        let module = format!(
            "{prefix}{}",
            interface
                .map(|name| self.resolve.name_world_key(name))
                .unwrap_or_else(|| "$root".into())
        );
        for (index, ty) in func
            .find_futures_and_streams(self.resolve)
            .into_iter()
            .enumerate()
        {
            let func_name = &func.name;

            match &self.resolve.types[ty].kind {
                TypeDefKind::Future(payload_type) => {
                    self.generate_async_future_or_stream(
                        PayloadFor::Future,
                        &module,
                        index,
                        func_name,
                        ty,
                        payload_type.as_ref(),
                    );
                }
                TypeDefKind::Stream(payload_type) => {
                    self.generate_async_future_or_stream(
                        PayloadFor::Stream,
                        &module,
                        index,
                        func_name,
                        ty,
                        payload_type.as_ref(),
                    );
                }
                _ => unreachable!(),
            }
        }
    }

    fn generate_async_future_or_stream(
        &mut self,
        payload_for: PayloadFor,
        module: &str,
        index: usize,
        func_name: &str,
        ty: TypeId,
        payload_type: Option<&Type>,
    ) {
        if !self.r#gen.futures.insert(ty) {
            return;
        }
        let ty = self.r#gen.type_name(&Type::Id(ty));
        let name = ty.strip_suffix("_t").unwrap();
        let snake = self.r#gen.world.to_snake_case();
        let kind = match payload_for {
            PayloadFor::Future => "future",
            PayloadFor::Stream => "stream",
        };
        let payload_len_arg = match payload_for {
            PayloadFor::Future => "",
            PayloadFor::Stream => ", size_t",
        };
        let (read_arg_ty, read_arg_expr, write_arg_ty, write_arg_expr) =
            match (payload_for, payload_type) {
                (PayloadFor::Future, None) => ("".to_string(), "NULL", "".to_string(), "NULL"),
                (PayloadFor::Future, Some(ty)) => {
                    let ty = self.r#gen.type_name(ty);
                    (
                        format!(", {ty} *buf"),
                        "(uint8_t*) buf",
                        format!(", const {ty} *buf"),
                        "(const uint8_t*) buf",
                    )
                }
                (PayloadFor::Stream, None) => (
                    ", size_t amt".to_string(),
                    "NULL, amt",
                    ", size_t amt".to_string(),
                    "NULL, amt",
                ),
                (PayloadFor::Stream, Some(ty)) => {
                    let ty = self.r#gen.type_name(ty);
                    (
                        format!(", {ty} *buf, size_t amt"),
                        "(uint8_t*) buf, amt",
                        format!(", const {ty} *buf, size_t amt"),
                        "(const uint8_t*) buf, amt",
                    )
                }
            };

        // TODO: this is a hack around space-stripping in `source.rs`, ideally
        // wouldn't be necessary.
        let empty = "";
        uwriteln!(
            self.src.h_helpers,
            r#"
typedef uint32_t {name}_writer_t;

{ty} {name}_new({name}_writer_t *writer);
{snake}_waitable_status_t {name}_read({ty} reader{read_arg_ty});
{snake}_waitable_status_t {name}_write({name}_writer_t writer{write_arg_ty});
{snake}_waitable_status_t {name}_cancel_read({ty} reader);
{snake}_waitable_status_t {name}_cancel_write({name}_writer_t writer);
void {name}_drop_readable({ty} reader);{empty}
void {name}_drop_writable({name}_writer_t writer);
            "#,
        );
        uwriteln!(
            self.src.c_helpers,
            r#"
__attribute__((__import_module__("{module}"), __import_name__("[{kind}-new-{index}]{func_name}")))
extern uint64_t {name}__new(void);
__attribute__((__import_module__("{module}"), __import_name__("[async-lower][{kind}-read-{index}]{func_name}")))
extern uint32_t {name}__read(uint32_t, uint8_t*{payload_len_arg});
__attribute__((__import_module__("{module}"), __import_name__("[async-lower][{kind}-write-{index}]{func_name}")))
extern uint32_t {name}__write(uint32_t, const uint8_t*{payload_len_arg});
__attribute__((__import_module__("{module}"), __import_name__("[{kind}-cancel-read-{index}]{func_name}")))
extern uint32_t {name}__cancel_read(uint32_t);
__attribute__((__import_module__("{module}"), __import_name__("[{kind}-cancel-write-{index}]{func_name}")))
extern uint32_t {name}__cancel_write(uint32_t);
__attribute__((__import_module__("{module}"), __import_name__("[{kind}-drop-readable-{index}]{func_name}")))
extern void {name}__drop_readable(uint32_t);
__attribute__((__import_module__("{module}"), __import_name__("[{kind}-drop-writable-{index}]{func_name}")))
extern void {name}__drop_writable(uint32_t);

{ty} {name}_new({name}_writer_t *writer) {{
    uint64_t packed = {name}__new();
    *writer = (uint32_t) (packed >> 32);
    return (uint32_t) packed;
}}

{snake}_waitable_status_t {name}_read({ty} reader{read_arg_ty}) {{
    return {name}__read(reader, {read_arg_expr});
}}

{snake}_waitable_status_t {name}_write({name}_writer_t writer{write_arg_ty}) {{
    return {name}__write(writer, {write_arg_expr});
}}

{snake}_waitable_status_t {name}_cancel_read({ty} reader){empty} {{
    return {name}__cancel_read(reader);
}}

{snake}_waitable_status_t {name}_cancel_write({name}_writer_t writer) {{
    return {name}__cancel_write(writer);
}}

void {name}_drop_readable({ty} reader){empty} {{
    {name}__drop_readable(reader);
}}

void {name}_drop_writable({name}_writer_t writer) {{
    {name}__drop_writable(writer);
}}
            "#,
        );
    }
}

enum PayloadFor {
    Future,
    Stream,
}

struct DroppableBorrow {
    name: String,
    ty: TypeId,
}

struct FunctionBindgen<'a, 'b> {
    r#gen: &'a mut InterfaceGenerator<'b>,
    locals: Ns,
    src: wit_bindgen_core::Source,
    sig: CSig,
    func_to_call: &'a str,
    block_storage: Vec<wit_bindgen_core::Source>,
    blocks: Vec<(String, Vec<String>)>,
    payloads: Vec<String>,
    params: Vec<String>,
    wasm_return: Option<String>,
    ret_store_cnt: usize,
    import_return_pointer_area_size: ArchitectureSize,
    import_return_pointer_area_align: Alignment,

    /// State of what to generate for the `task.return` intrinsic in the case
    /// that this bindings generator is being used for an async export.
    ///
    /// This typically stays at `DeferredTaskReturn::None` except for the case
    /// of async exports where they'll fill this in after the `CallInterface`
    /// instruction. For some more information see the documentation on
    /// `DeferredTaskReturn`.
    deferred_task_return: DeferredTaskReturn,

    /// Borrows observed during lifting an export, that will need to be dropped when the guest
    /// function exits.
    borrows: Vec<DroppableBorrow>,

    /// Forward declarations for temporary storage of borrow copies.
    borrow_decls: wit_bindgen_core::Source,
}

/// State associated with the generation of the `task.return` intrinsic function
/// with async exports.
enum DeferredTaskReturn {
    /// Default state, meaning that either bindings generation isn't happening
    /// for an async export or the async export is in the bindings generation
    /// mode before `CallInterface`.
    None,

    /// An async export is having bindings generated and `CallInterface` has
    /// been seen. After that instruction the `deferred_task_return` field
    /// transitions to this state.
    ///
    /// This state is then present until the `AsyncTaskReturn` instruction is
    /// met at which point this changes to `Emitted` below.
    Generating {
        /// The previous contents of `self.src` just after the `CallInterface`
        /// had its code generator. This is effectively the bindings-generated
        /// contents of the export and this will get replaced back into
        /// `self.src` once the `AsyncTaskReturn` is generated.
        prev_src: wit_bindgen_core::Source,
    },

    /// An `AsyncTaskReturn` has been seen and all state is now located here to
    /// be used for generating the `task.return` intrinsic.
    ///
    /// This state is only generated during `AsyncTaskReturn` and is used to
    /// record everything necessary to generate `task.return` meaning that the
    /// in-`FunctionBindgen` state is now "back to normal" where it's intended
    /// for the main function having bindings generated.
    Emitted {
        /// The name of the `task.return` intrinsic that should be imported.
        /// Note that this does not include the module.
        name: String,
        /// The wasm type of each parameter provided to the `task.return`
        /// intrinsic as well as the C expression necessary to produce this
        /// parameter. The type is used to declare the intrinsic and the C
        /// expression is used to call the intrinsic from the generated
        /// function.
        params: Vec<(WasmType, String)>,
        /// The body of the `task.return` intrinsic. This contains the bulk of
        /// the lowering code from a function's return value to the canonical
        /// ABI.
        body: wit_bindgen_core::Source,
    },
}

impl<'a, 'b> FunctionBindgen<'a, 'b> {
    fn new(
        r#gen: &'a mut InterfaceGenerator<'b>,
        sig: CSig,
        func_to_call: &'a str,
    ) -> FunctionBindgen<'a, 'b> {
        let mut locals = Ns::default();
        for (_, name) in sig.params.iter() {
            locals.insert(name).unwrap();
        }
        FunctionBindgen {
            r#gen,
            sig,
            locals,
            src: Default::default(),
            func_to_call,
            block_storage: Vec::new(),
            blocks: Vec::new(),
            payloads: Vec::new(),
            params: Vec::new(),
            wasm_return: None,
            ret_store_cnt: 0,
            import_return_pointer_area_size: Default::default(),
            import_return_pointer_area_align: Default::default(),
            borrow_decls: Default::default(),
            borrows: Vec::new(),
            deferred_task_return: DeferredTaskReturn::None,
        }
    }

    fn store_op(&mut self, op: &str, loc: &str) {
        self.src.push_str(loc);
        self.src.push_str(" = ");
        self.src.push_str(op);
        self.src.push_str(";\n");
    }

    fn load(
        &mut self,
        ty: &str,
        offset: ArchitectureSize,
        operands: &[String],
        results: &mut Vec<String>,
    ) {
        results.push(format!(
            "*(({}*) ({} + {}))",
            ty,
            operands[0],
            offset.format(POINTER_SIZE_EXPRESSION)
        ));
    }

    fn load_ext(
        &mut self,
        ty: &str,
        offset: ArchitectureSize,
        operands: &[String],
        results: &mut Vec<String>,
    ) {
        self.load(ty, offset, operands, results);
        let result = results.pop().unwrap();
        results.push(format!("(int32_t) {result}"));
    }

    fn store(&mut self, ty: &str, offset: ArchitectureSize, operands: &[String]) {
        uwriteln!(
            self.src,
            "*(({}*)({} + {})) = {};",
            ty,
            operands[1],
            offset.format(POINTER_SIZE_EXPRESSION),
            operands[0]
        );
    }

    fn store_in_retptr(&mut self, operand: &String) {
        self.store_op(
            operand,
            &format!("*{}", self.sig.retptrs[self.ret_store_cnt]),
        );
        self.ret_store_cnt = self.ret_store_cnt + 1;
    }

    fn empty_return_value(&mut self) {
        // Empty types have no state, so we don't emit stores for them. But we
        // do need to keep track of which return variable we're looking at.
        self.ret_store_cnt = self.ret_store_cnt + 1;
    }

    fn assert_no_droppable_borrows(&self, context: &str, ty: &Type) {
        if !self.r#gen.in_import
            && self.r#gen.autodrop_enabled()
            && self.r#gen.contains_droppable_borrow(ty)
        {
            panic!("Unable to autodrop borrows in `{context}` values, please disable autodrop")
        }
    }
}

impl Bindgen for FunctionBindgen<'_, '_> {
    type Operand = String;

    fn sizes(&self) -> &SizeAlign {
        &self.r#gen.r#gen.sizes
    }

    fn push_block(&mut self) {
        let prev = mem::take(&mut self.src);
        self.block_storage.push(prev);
    }

    fn finish_block(&mut self, operands: &mut Vec<String>) {
        let to_restore = self.block_storage.pop().unwrap();
        let src = mem::replace(&mut self.src, to_restore);
        self.blocks.push((src.into(), mem::take(operands)));
    }

    fn return_pointer(&mut self, size: ArchitectureSize, align: Alignment) -> String {
        let ptr = self.locals.tmp("ptr");

        // Use a stack-based return area for imports, because exports need
        // their return area to be live until the post-return call.
        if self.r#gen.in_import {
            self.import_return_pointer_area_size = self.import_return_pointer_area_size.max(size);
            self.import_return_pointer_area_align =
                self.import_return_pointer_area_align.max(align);
            uwriteln!(self.src, "uint8_t *{} = (uint8_t *) &ret_area;", ptr);
        } else {
            self.r#gen.r#gen.return_pointer_area_size =
                self.r#gen.r#gen.return_pointer_area_size.max(size);
            self.r#gen.r#gen.return_pointer_area_align =
                self.r#gen.r#gen.return_pointer_area_align.max(align);
            // Declare a statically-allocated return area.
            uwriteln!(self.src, "uint8_t *{} = (uint8_t *) &RET_AREA;", ptr);
        }

        ptr
    }

    fn is_list_canonical(&self, resolve: &Resolve, ty: &Type) -> bool {
        resolve.all_bits_valid(ty)
    }

    fn emit(
        &mut self,
        resolve: &Resolve,
        inst: &Instruction<'_>,
        operands: &mut Vec<String>,
        results: &mut Vec<String>,
    ) {
        match inst {
            Instruction::GetArg { nth } => results.push(self.params[*nth].clone()),
            Instruction::I32Const { val } => results.push(val.to_string()),
            Instruction::ConstZero { tys } => {
                for _ in tys.iter() {
                    results.push("0".to_string());
                }
            }

            // TODO: checked?
            Instruction::U8FromI32 => results.push(format!("(uint8_t) ({})", operands[0])),
            Instruction::S8FromI32 => results.push(format!("(int8_t) ({})", operands[0])),
            Instruction::U16FromI32 => results.push(format!("(uint16_t) ({})", operands[0])),
            Instruction::S16FromI32 => results.push(format!("(int16_t) ({})", operands[0])),
            Instruction::U32FromI32 => results.push(format!("(uint32_t) ({})", operands[0])),
            Instruction::S32FromI32 | Instruction::S64FromI64 => results.push(operands[0].clone()),
            Instruction::U64FromI64 => results.push(format!("(uint64_t) ({})", operands[0])),

            Instruction::I32FromU8
            | Instruction::I32FromS8
            | Instruction::I32FromU16
            | Instruction::I32FromS16
            | Instruction::I32FromU32 => {
                results.push(format!("(int32_t) ({})", operands[0]));
            }
            Instruction::I32FromS32 | Instruction::I64FromS64 => results.push(operands[0].clone()),
            Instruction::I64FromU64 => {
                results.push(format!("(int64_t) ({})", operands[0]));
            }

            // f32/f64 have the same representation in the import type and in C,
            // so no conversions necessary.
            Instruction::CoreF32FromF32
            | Instruction::CoreF64FromF64
            | Instruction::F32FromCoreF32
            | Instruction::F64FromCoreF64 => {
                results.push(operands[0].clone());
            }

            // TODO: checked
            Instruction::CharFromI32 => {
                results.push(format!("(uint32_t) ({})", operands[0]));
            }
            Instruction::I32FromChar => {
                results.push(format!("(int32_t) ({})", operands[0]));
            }

            Instruction::Bitcasts { casts } => {
                for (cast, op) in casts.iter().zip(operands) {
                    let op = self.r#gen.r#gen.perform_cast(op, cast);
                    results.push(op);
                }
            }

            Instruction::BoolFromI32 | Instruction::I32FromBool => {
                results.push(operands[0].clone());
            }

            Instruction::RecordLower { record, .. } => {
                let op = &operands[0];
                for f in record.fields.iter() {
                    results.push(format!("({}).{}", op, to_c_ident(&f.name)));
                }
            }
            Instruction::RecordLift { ty, record, .. } => {
                let name = self.r#gen.r#gen.type_name(&Type::Id(*ty));
                let mut result = format!("({name}) {{\n");
                for (field, op) in record.fields.iter().zip(operands.iter()) {
                    let field_ty = self.r#gen.r#gen.type_name(&field.ty);
                    uwriteln!(result, "({}) {},", field_ty, op);
                }
                result.push_str("}");
                results.push(result);
            }

            Instruction::TupleLower { tuple, .. } => {
                let op = &operands[0];
                for i in 0..tuple.types.len() {
                    results.push(format!("({op}).f{i}"));
                }
            }
            Instruction::TupleLift { ty, tuple, .. } => {
                let name = self.r#gen.r#gen.type_name(&Type::Id(*ty));
                let mut result = format!("({name}) {{\n");
                for (ty, op) in tuple.types.iter().zip(operands.iter()) {
                    let ty = self.r#gen.r#gen.type_name(&ty);
                    uwriteln!(result, "({}) {},", ty, op);
                }
                result.push_str("}");
                results.push(result);
            }

            Instruction::HandleLower { .. } => {
                let op = &operands[0];
                results.push(format!("({op}).__handle"))
            }

            Instruction::HandleLift { handle, ty, .. } => match handle {
                Handle::Borrow(resource)
                    if matches!(
                        self.r#gen.r#gen.resources[&dealias(resolve, *resource)].direction,
                        Direction::Export
                    ) =>
                {
                    // Here we've received a borrow of a resource which we've exported ourselves, so we can treat
                    // it as a raw pointer rather than an opaque handle.
                    let op = &operands[0];
                    let name = self
                        .r#gen
                        .r#gen
                        .type_name(&Type::Id(dealias(resolve, *resource)));
                    results.push(format!("(({name}*) {op})"))
                }
                _ => {
                    let op = &operands[0];
                    let name = self.r#gen.r#gen.type_name(&Type::Id(*ty));
                    results.push(format!("({name}) {{ {op} }}"));

                    if let Handle::Borrow(id) = handle {
                        if !self.r#gen.in_import && self.r#gen.autodrop_enabled() {
                            // Here we've received a borrow of an imported resource, which is the
                            // kind we'll need to drop when the exported function is returning.
                            let ty = dealias(self.r#gen.resolve, *id);

                            let name = self.locals.tmp("borrow");
                            uwriteln!(self.borrow_decls, "int32_t {name} = 0;");
                            uwriteln!(self.src, "{name} = {op};");

                            self.borrows.push(DroppableBorrow { name, ty });
                        }
                    }
                }
            },

            // TODO: checked
            Instruction::FlagsLower { flags, ty, .. } => match flags_repr(flags) {
                Int::U8 | Int::U16 | Int::U32 => {
                    results.push(operands.pop().unwrap());
                }
                Int::U64 => {
                    let name = self.r#gen.r#gen.type_name(&Type::Id(*ty));
                    let tmp = self.locals.tmp("flags");
                    uwriteln!(self.src, "{name} {tmp} = {};", operands[0]);
                    results.push(format!("{tmp} & 0xffffffff"));
                    results.push(format!("({tmp} >> 32) & 0xffffffff"));
                }
            },

            Instruction::FlagsLift { flags, ty, .. } => match flags_repr(flags) {
                Int::U8 | Int::U16 | Int::U32 => {
                    results.push(operands.pop().unwrap());
                }
                Int::U64 => {
                    let name = self.r#gen.r#gen.type_name(&Type::Id(*ty));
                    let op0 = &operands[0];
                    let op1 = &operands[1];
                    results.push(format!("(({name}) ({op0})) | ((({name}) ({op1})) << 32)"));
                }
            },

            Instruction::VariantPayloadName => {
                let name = self.locals.tmp("payload");
                results.push(format!("*{name}"));
                self.payloads.push(name);
            }

            Instruction::VariantLower {
                variant,
                results: result_types,
                ..
            } => {
                let blocks = self
                    .blocks
                    .drain(self.blocks.len() - variant.cases.len()..)
                    .collect::<Vec<_>>();
                let payloads = self
                    .payloads
                    .drain(self.payloads.len() - variant.cases.len()..)
                    .collect::<Vec<_>>();

                let mut variant_results = Vec::with_capacity(result_types.len());
                for ty in result_types.iter() {
                    let name = self.locals.tmp("variant");
                    results.push(name.clone());
                    self.src.push_str(wasm_type(*ty));
                    self.src.push_str(" ");
                    self.src.push_str(&name);
                    self.src.push_str(";\n");
                    variant_results.push(name);
                }

                let expr_to_match = format!("({}).tag", operands[0]);

                uwriteln!(self.src, "switch ((int32_t) {}) {{", expr_to_match);
                for (i, ((case, (block, block_results)), payload)) in
                    variant.cases.iter().zip(blocks).zip(payloads).enumerate()
                {
                    uwriteln!(self.src, "case {}: {{", i);
                    if let Some(ty) = case.ty.as_ref() {
                        let ty = self.r#gen.r#gen.type_name(ty);
                        uwrite!(
                            self.src,
                            "const {} *{} = &({}).val",
                            ty,
                            payload,
                            operands[0],
                        );
                        self.src.push_str(".");
                        self.src.push_str(&to_c_ident(&case.name));
                        self.src.push_str(";\n");
                    }
                    self.src.push_str(&block);

                    for (name, result) in variant_results.iter().zip(&block_results) {
                        uwriteln!(self.src, "{} = {};", name, result);
                    }
                    self.src.push_str("break;\n}\n");
                }
                self.src.push_str("}\n");
            }

            Instruction::VariantLift { variant, ty, .. } => {
                let blocks = self
                    .blocks
                    .drain(self.blocks.len() - variant.cases.len()..)
                    .collect::<Vec<_>>();

                let ty = self.r#gen.r#gen.type_name(&Type::Id(*ty));
                let result = self.locals.tmp("variant");
                uwriteln!(self.src, "{} {};", ty, result);
                uwriteln!(self.src, "{}.tag = {};", result, operands[0]);
                uwriteln!(self.src, "switch ((int32_t) {}.tag) {{", result);
                for (i, (case, (block, block_results))) in
                    variant.cases.iter().zip(blocks).enumerate()
                {
                    uwriteln!(self.src, "case {}: {{", i);
                    self.src.push_str(&block);
                    assert!(block_results.len() == (case.ty.is_some() as usize));

                    if let Some(_) = case.ty.as_ref() {
                        let mut dst = format!("{result}.val");
                        dst.push_str(".");
                        dst.push_str(&to_c_ident(&case.name));
                        self.store_op(&block_results[0], &dst);
                    }
                    self.src.push_str("break;\n}\n");
                }
                self.src.push_str("}\n");
                results.push(result);
            }

            Instruction::OptionLower {
                results: result_types,
                payload,
                ..
            } => {
                let (mut some, some_results) = self.blocks.pop().unwrap();
                let (mut none, none_results) = self.blocks.pop().unwrap();
                let some_payload = self.payloads.pop().unwrap();
                let _none_payload = self.payloads.pop().unwrap();

                for (i, ty) in result_types.iter().enumerate() {
                    let name = self.locals.tmp("option");
                    results.push(name.clone());
                    self.src.push_str(wasm_type(*ty));
                    self.src.push_str(" ");
                    self.src.push_str(&name);
                    self.src.push_str(";\n");
                    let some_result = &some_results[i];
                    uwriteln!(some, "{name} = {some_result};");
                    let none_result = &none_results[i];
                    uwriteln!(none, "{name} = {none_result};");
                }

                let op0 = &operands[0];
                let ty = self.r#gen.r#gen.type_name(payload);
                let bind_some = format!("const {ty} *{some_payload} = &({op0}).val;");

                uwrite!(
                    self.src,
                    "\
                    if (({op0}).is_some) {{
                        {bind_some}
                        {some}}} else {{
                        {none}}}
                    "
                );
            }

            Instruction::OptionLift { ty, .. } => {
                let (mut some, some_results) = self.blocks.pop().unwrap();
                let (mut none, none_results) = self.blocks.pop().unwrap();
                assert!(none_results.len() == 0);
                assert!(some_results.len() == 1);
                let some_result = &some_results[0];

                let ty = self.r#gen.r#gen.type_name(&Type::Id(*ty));
                let result = self.locals.tmp("option");
                uwriteln!(self.src, "{ty} {result};");
                let op0 = &operands[0];
                let set_some = format!("{result}.val = {some_result};\n");
                if none.len() > 0 {
                    none.push('\n');
                }
                if some.len() > 0 {
                    some.push('\n');
                }
                uwrite!(
                    self.src,
                    "switch ({op0}) {{
                        case 0: {{
                            {result}.is_some = false;
                            {none}\
                            break;
                        }}
                        case 1: {{
                            {result}.is_some = true;
                            {some}\
                            {set_some}\
                            break;
                        }}
                    }}\n"
                );
                results.push(result);
            }

            Instruction::ResultLower {
                results: result_types,
                result,
                ..
            } => {
                let (mut err, err_results) = self.blocks.pop().unwrap();
                let (mut ok, ok_results) = self.blocks.pop().unwrap();
                let err_payload = self.payloads.pop().unwrap();
                let ok_payload = self.payloads.pop().unwrap();

                for (i, ty) in result_types.iter().enumerate() {
                    let name = self.locals.tmp("result");
                    results.push(name.clone());
                    self.src.push_str(wasm_type(*ty));
                    self.src.push_str(" ");
                    self.src.push_str(&name);
                    self.src.push_str(";\n");
                    let ok_result = &ok_results[i];
                    uwriteln!(ok, "{name} = {ok_result};");
                    let err_result = &err_results[i];
                    uwriteln!(err, "{name} = {err_result};");
                }

                let op0 = &operands[0];
                let bind_ok = if let Some(ok) = result.ok.as_ref() {
                    let ok_ty = self.r#gen.r#gen.type_name(ok);
                    format!("const {ok_ty} *{ok_payload} = &({op0}).val.ok;")
                } else {
                    String::new()
                };
                let bind_err = if let Some(err) = result.err.as_ref() {
                    let err_ty = self.r#gen.r#gen.type_name(err);
                    format!("const {err_ty} *{err_payload} = &({op0}).val.err;")
                } else {
                    String::new()
                };
                uwrite!(
                    self.src,
                    "\
                    if (({op0}).is_err) {{
                        {bind_err}\
                        {err}\
                    }} else {{
                        {bind_ok}\
                        {ok}\
                    }}
                    "
                );
            }

            Instruction::ResultLift { result, ty, .. } => {
                let (mut err, err_results) = self.blocks.pop().unwrap();
                assert!(err_results.len() == (result.err.is_some() as usize));
                let (mut ok, ok_results) = self.blocks.pop().unwrap();
                assert!(ok_results.len() == (result.ok.is_some() as usize));

                if err.len() > 0 {
                    err.push_str("\n");
                }
                if ok.len() > 0 {
                    ok.push_str("\n");
                }

                let result_tmp = self.locals.tmp("result");
                let set_ok = if let Some(_) = result.ok.as_ref() {
                    let ok_result = &ok_results[0];
                    format!("{result_tmp}.val.ok = {ok_result};\n")
                } else {
                    String::new()
                };
                let set_err = if let Some(_) = result.err.as_ref() {
                    let err_result = &err_results[0];
                    format!("{result_tmp}.val.err = {err_result};\n")
                } else {
                    String::new()
                };

                let ty = self.r#gen.r#gen.type_name(&Type::Id(*ty));
                uwriteln!(self.src, "{ty} {result_tmp};");
                let op0 = &operands[0];
                uwriteln!(
                    self.src,
                    "switch ({op0}) {{
                        case 0: {{
                            {result_tmp}.is_err = false;
                            {ok}\
                            {set_ok}\
                            break;
                        }}
                        case 1: {{
                            {result_tmp}.is_err = true;
                            {err}\
                            {set_err}\
                            break;
                        }}
                    }}"
                );
                results.push(result_tmp);
            }

            Instruction::EnumLower { .. } => results.push(format!("(int32_t) {}", operands[0])),
            Instruction::EnumLift { .. } => results.push(operands.pop().unwrap()),

            Instruction::ListCanonLower { .. } | Instruction::StringLower { .. } => {
                results.push(format!("(uint8_t *) ({}).ptr", operands[0]));
                results.push(format!("({}).len", operands[0]));
            }
            Instruction::ListCanonLift { element, ty, .. } => {
                self.assert_no_droppable_borrows("list", &Type::Id(*ty));

                let list_name = self.r#gen.r#gen.type_name(&Type::Id(*ty));
                let elem_name = self.r#gen.r#gen.type_name(element);
                results.push(format!(
                    "({}) {{ ({}*)({}), ({}) }}",
                    list_name, elem_name, operands[0], operands[1]
                ));
            }
            Instruction::StringLift { .. } => {
                let list_name = self.r#gen.r#gen.type_name(&Type::String);
                results.push(format!(
                    "({}) {{ ({}*)({}), ({}) }}",
                    list_name,
                    self.r#gen.r#gen.char_type(),
                    operands[0],
                    operands[1]
                ));
            }

            Instruction::ListLower { .. } => {
                let _body = self.blocks.pop().unwrap();
                results.push(format!("(uint8_t *) ({}).ptr", operands[0]));
                results.push(format!("({}).len", operands[0]));
            }

            Instruction::ListLift { element, ty, .. } => {
                self.assert_no_droppable_borrows("list", &Type::Id(*ty));

                let _body = self.blocks.pop().unwrap();
                let list_name = self.r#gen.r#gen.type_name(&Type::Id(*ty));
                let elem_name = self.r#gen.r#gen.type_name(element);
                results.push(format!(
                    "({}) {{ ({}*)({}), ({}) }}",
                    list_name, elem_name, operands[0], operands[1]
                ));
            }

            // Maps share the canonical "flat array of entries" layout with the
            // C struct emitted by `type_map`/`anonymous_type_map`, so the body
            // block is discarded and we splice `.ptr` / `.len` through directly
            // (mirroring how `ListLower`/`ListLift` work above).
            Instruction::MapLower { .. } => {
                let _body = self.blocks.pop().unwrap();
                results.push(format!("(uint8_t *) ({}).ptr", operands[0]));
                results.push(format!("({}).len", operands[0]));
            }

            Instruction::MapLift { ty, .. } => {
                self.assert_no_droppable_borrows("map", &Type::Id(*ty));

                let _body = self.blocks.pop().unwrap();
                let map_name = self.r#gen.r#gen.type_name(&Type::Id(*ty));
                let entry_name = {
                    let prefix = map_name.strip_suffix("_t").unwrap_or(&map_name);
                    format!("{prefix}_entry_t")
                };
                results.push(format!(
                    "({}) {{ ({}*)({}), ({}) }}",
                    map_name, entry_name, operands[0], operands[1]
                ));
            }

            Instruction::IterElem { .. } => results.push("e".to_string()),
            Instruction::IterMapKey { .. } => results.push("map_key".to_string()),
            Instruction::IterMapValue { .. } => results.push("map_value".to_string()),
            Instruction::IterBasePointer => results.push("base".to_string()),

            Instruction::CallWasm { sig, .. } => {
                match sig.results.len() {
                    0 => {}
                    1 => {
                        self.src.push_str(wasm_type(sig.results[0]));
                        let ret = self.locals.tmp("ret");
                        self.wasm_return = Some(ret.clone());
                        uwrite!(self.src, " {} = ", ret);
                        results.push(ret);
                    }
                    _ => unimplemented!(),
                }
                self.src.push_str(self.func_to_call);
                self.src.push_str("(");
                for (i, op) in operands.iter().enumerate() {
                    if i > 0 {
                        self.src.push_str(", ");
                    }
                    self.src.push_str(op);
                }
                self.src.push_str(");\n");
            }

            Instruction::CallInterface { func, async_ } => {
                let mut args = String::new();
                for (i, (op, (byref, _))) in operands.iter().zip(&self.sig.params).enumerate() {
                    if i > 0 {
                        args.push_str(", ");
                    }
                    let ty = &func.params[i].ty;
                    if *byref {
                        let name = self.locals.tmp("arg");
                        let ty = self.r#gen.r#gen.type_name(ty);
                        uwriteln!(self.src, "{} {} = {};", ty, name, op);
                        args.push_str("&");
                        args.push_str(&name);
                    } else {
                        if !self.r#gen.in_import {
                            if let Type::Id(id) = ty {
                                if let TypeDefKind::Option(_) = &self.r#gen.resolve.types[*id].kind
                                {
                                    uwrite!(args, "{op}.is_some ? &({op}.val) : NULL");
                                    continue;
                                }
                            }
                        }
                        args.push_str(op);
                    }
                }
                if *async_ {
                    let ret = self.locals.tmp("ret");
                    let snake = self.r#gen.r#gen.world.to_snake_case();
                    uwriteln!(
                        self.src,
                        "{snake}_callback_code_t {ret} = {}({args});",
                        self.sig.name,
                    );
                    uwriteln!(self.src, "return {ret};");
                    if func.result.is_some() {
                        results.push("ret".to_string());
                    }
                    assert!(matches!(
                        self.deferred_task_return,
                        DeferredTaskReturn::None
                    ));
                    self.deferred_task_return = DeferredTaskReturn::Generating {
                        prev_src: mem::take(&mut self.src),
                    };
                    return;
                }
                match &self.sig.ret.scalar {
                    None => {
                        let mut retptrs = Vec::new();
                        for ty in self.sig.ret.retptrs.iter() {
                            let name = self.locals.tmp("ret");
                            let ty = self.r#gen.r#gen.type_name(ty);
                            uwriteln!(self.src, "{} {};", ty, name);
                            if args.len() > 0 {
                                args.push_str(", ");
                            }
                            args.push_str("&");
                            args.push_str(&name);
                            retptrs.push(name);
                        }
                        uwriteln!(self.src, "{}({});", self.sig.name, args);
                        results.extend(retptrs);
                    }
                    Some(Scalar::Void) => {
                        uwriteln!(self.src, "{}({});", self.sig.name, args);
                    }
                    Some(Scalar::Type(_)) => {
                        let ret = self.locals.tmp("ret");
                        let ty = func.result.unwrap();
                        let ty = self.r#gen.r#gen.type_name(&ty);
                        uwriteln!(self.src, "{} {} = {}({});", ty, ret, self.sig.name, args);
                        results.push(ret);
                    }
                    Some(Scalar::OptionBool(ty)) => {
                        let ret = self.locals.tmp("ret");
                        let val = self.locals.tmp("val");
                        if args.len() > 0 {
                            args.push_str(", ");
                        }
                        args.push_str("&");
                        args.push_str(&val);
                        let payload_ty = self.r#gen.r#gen.type_name(ty);
                        uwriteln!(self.src, "{} {};", payload_ty, val);
                        uwriteln!(self.src, "bool {} = {}({});", ret, self.sig.name, args);
                        let ty = func.result.unwrap();
                        let option_ty = self.r#gen.r#gen.type_name(&ty);
                        let option_ret = self.locals.tmp("ret");
                        uwrite!(
                            self.src,
                            "
                                {option_ty} {option_ret};
                                {option_ret}.is_some = {ret};
                                {option_ret}.val = {val};
                            ",
                        );
                        results.push(option_ret);
                    }
                    Some(Scalar::ResultBool(ok, err)) => {
                        let ty = &func.result.unwrap();
                        let result_ty = self.r#gen.r#gen.type_name(ty);
                        let ret = self.locals.tmp("ret");
                        let mut ret_iter = self.sig.ret.retptrs.iter();
                        uwriteln!(self.src, "{result_ty} {ret};");
                        let ok_name = if ok.is_some() {
                            if let Some(ty) = ret_iter.next() {
                                let val = self.locals.tmp("ok");
                                if args.len() > 0 {
                                    uwrite!(args, ", ");
                                }
                                uwrite!(args, "&{val}");
                                let ty = self.r#gen.r#gen.type_name(ty);
                                uwriteln!(self.src, "{} {};", ty, val);
                                Some(val)
                            } else {
                                None
                            }
                        } else {
                            None
                        };
                        let err_name = if let Some(ty) = ret_iter.next() {
                            let val = self.locals.tmp("err");
                            if args.len() > 0 {
                                uwrite!(args, ", ")
                            }
                            uwrite!(args, "&{val}");
                            let ty = self.r#gen.r#gen.type_name(ty);
                            uwriteln!(self.src, "{} {};", ty, val);
                            Some(val)
                        } else {
                            None
                        };
                        assert!(ret_iter.next().is_none());
                        uwrite!(self.src, "");
                        uwriteln!(self.src, "{ret}.is_err = !{}({args});", self.sig.name);
                        if err.is_some() {
                            if let Some(err_name) = err_name {
                                uwriteln!(
                                    self.src,
                                    "if ({ret}.is_err) {{
                                        {ret}.val.err = {err_name};
                                    }}",
                                );
                            }
                        }
                        if ok.is_some() {
                            if let Some(ok_name) = ok_name {
                                uwriteln!(
                                    self.src,
                                    "if (!{ret}.is_err) {{
                                        {ret}.val.ok = {ok_name};
                                    }}"
                                );
                            } else {
                                uwrite!(self.src, "\n");
                            }
                        }
                        results.push(ret);
                    }
                }
            }
            Instruction::Return { .. } if self.r#gen.in_import => match self.sig.ret.scalar {
                None => {
                    for op in operands.iter() {
                        self.store_in_retptr(op);
                    }
                }
                Some(Scalar::Void) => {
                    assert!(operands.is_empty());
                }
                Some(Scalar::Type(_)) => {
                    assert_eq!(operands.len(), 1);
                    self.src.push_str("return ");
                    self.src.push_str(&operands[0]);
                    self.src.push_str(";\n");
                }
                Some(Scalar::OptionBool(_)) => {
                    assert_eq!(operands.len(), 1);
                    let variant = &operands[0];
                    self.store_in_retptr(&format!("{variant}.val"));
                    self.src.push_str("return ");
                    self.src.push_str(&variant);
                    self.src.push_str(".is_some;\n");
                }
                Some(Scalar::ResultBool(ok, err)) => {
                    assert_eq!(operands.len(), 1);
                    let variant = &operands[0];
                    assert!(self.sig.retptrs.len() <= 2);
                    uwriteln!(self.src, "if (!{}.is_err) {{", variant);
                    if ok.is_some() {
                        if ok.is_some() {
                            self.store_in_retptr(&format!("{variant}.val.ok"));
                        } else {
                            self.empty_return_value();
                        }
                    }
                    uwriteln!(
                        self.src,
                        "   return 1;
                            }} else {{"
                    );
                    if err.is_some() {
                        if err.is_some() {
                            self.store_in_retptr(&format!("{variant}.val.err"));
                        } else {
                            self.empty_return_value();
                        }
                    }
                    uwriteln!(
                        self.src,
                        "   return 0;
                            }}"
                    );
                    assert_eq!(self.ret_store_cnt, self.sig.retptrs.len());
                }
            },
            Instruction::Return { amt, .. } => {
                // Emit all temporary borrow decls
                let src = std::mem::replace(&mut self.src, std::mem::take(&mut self.borrow_decls));
                self.src.append_src(&src);

                for DroppableBorrow { name, ty } in self.borrows.iter() {
                    let drop_fn = self.r#gen.r#gen.resources[ty].drop_fn.as_str();
                    uwriteln!(self.src, "if ({name} != 0) {{");
                    uwriteln!(self.src, "  {drop_fn}({name});");
                    uwriteln!(self.src, "}}");
                }

                assert!(*amt <= 1);
                if *amt == 1 {
                    uwriteln!(self.src, "return {};", operands[0]);
                }
            }

            Instruction::I32Load { offset } => self.load("int32_t", *offset, operands, results),
            Instruction::I64Load { offset } => self.load("int64_t", *offset, operands, results),
            Instruction::F32Load { offset } => self.load("float", *offset, operands, results),
            Instruction::F64Load { offset } => self.load("double", *offset, operands, results),
            Instruction::PointerLoad { offset } => {
                self.load("uint8_t *", *offset, operands, results)
            }
            Instruction::LengthLoad { offset } => self.load("size_t", *offset, operands, results),
            Instruction::I32Store { offset } => self.store("int32_t", *offset, operands),
            Instruction::I64Store { offset } => self.store("int64_t", *offset, operands),
            Instruction::F32Store { offset } => self.store("float", *offset, operands),
            Instruction::F64Store { offset } => self.store("double", *offset, operands),
            Instruction::I32Store8 { offset } => self.store("int8_t", *offset, operands),
            Instruction::I32Store16 { offset } => self.store("int16_t", *offset, operands),
            Instruction::PointerStore { offset } => self.store("uint8_t *", *offset, operands),
            Instruction::LengthStore { offset } => self.store("size_t", *offset, operands),

            Instruction::I32Load8U { offset } => {
                self.load_ext("uint8_t", *offset, operands, results)
            }
            Instruction::I32Load8S { offset } => {
                self.load_ext("int8_t", *offset, operands, results)
            }
            Instruction::I32Load16U { offset } => {
                self.load_ext("uint16_t", *offset, operands, results)
            }
            Instruction::I32Load16S { offset } => {
                self.load_ext("int16_t", *offset, operands, results)
            }

            Instruction::GuestDeallocate { .. } => {
                uwriteln!(self.src, "free({});", operands[0]);
            }
            Instruction::GuestDeallocateString => {
                uwriteln!(self.src, "if (({}) > 0) {{", operands[1]);
                uwriteln!(self.src, "free({});", operands[0]);
                uwriteln!(self.src, "}}");
            }
            Instruction::GuestDeallocateVariant { blocks } => {
                let blocks = self
                    .blocks
                    .drain(self.blocks.len() - blocks..)
                    .collect::<Vec<_>>();

                uwriteln!(self.src, "switch ((int32_t) {}) {{", operands[0]);
                for (i, (block, results)) in blocks.into_iter().enumerate() {
                    assert!(results.is_empty());
                    uwriteln!(self.src, "case {}: {{", i);
                    self.src.push_str(&block);
                    self.src.push_str("break;\n}\n");
                }
                self.src.push_str("}\n");
            }
            Instruction::GuestDeallocateList { element } => {
                let (body, results) = self.blocks.pop().unwrap();
                assert!(results.is_empty());
                let len = self.locals.tmp("len");
                uwriteln!(self.src, "size_t {len} = {};", operands[1]);
                uwriteln!(self.src, "if ({len} > 0) {{");
                let ptr = self.locals.tmp("ptr");
                uwriteln!(self.src, "uint8_t *{ptr} = {};", operands[0]);
                let i = self.locals.tmp("i");
                uwriteln!(self.src, "for (size_t {i} = 0; {i} < {len}; {i}++) {{");
                let size = self.r#gen.r#gen.sizes.size(element);
                uwriteln!(
                    self.src,
                    "uint8_t *base = {ptr} + {i} * {};",
                    size.format(POINTER_SIZE_EXPRESSION)
                );
                uwriteln!(self.src, "(void) base;");
                uwrite!(self.src, "{body}");
                uwriteln!(self.src, "}}");
                uwriteln!(self.src, "free({ptr});");
                uwriteln!(self.src, "}}");
            }

            Instruction::GuestDeallocateMap { key, value } => {
                let (body, results) = self.blocks.pop().unwrap();
                assert!(results.is_empty());
                let len = self.locals.tmp("len");
                uwriteln!(self.src, "size_t {len} = {};", operands[1]);
                uwriteln!(self.src, "if ({len} > 0) {{");
                let ptr = self.locals.tmp("ptr");
                uwriteln!(self.src, "uint8_t *{ptr} = {};", operands[0]);
                let i = self.locals.tmp("i");
                uwriteln!(self.src, "for (size_t {i} = 0; {i} < {len}; {i}++) {{");
                let entry = self.r#gen.r#gen.sizes.record([*key, *value]);
                uwriteln!(
                    self.src,
                    "uint8_t *base = {ptr} + {i} * {};",
                    entry.size.format(POINTER_SIZE_EXPRESSION)
                );
                uwriteln!(self.src, "(void) base;");
                uwrite!(self.src, "{body}");
                uwriteln!(self.src, "}}");
                uwriteln!(self.src, "free({ptr});");
                uwriteln!(self.src, "}}");
            }

            Instruction::Flush { amt } => {
                results.extend(operands.iter().take(*amt).cloned());
            }

            Instruction::AsyncTaskReturn { name, params } => {
                let body = match &mut self.deferred_task_return {
                    DeferredTaskReturn::Generating { prev_src } => {
                        mem::swap(&mut self.src, prev_src);
                        mem::take(prev_src)
                    }
                    _ => unreachable!(),
                };
                assert_eq!(params.len(), operands.len());
                self.deferred_task_return = DeferredTaskReturn::Emitted {
                    name: name.to_string(),
                    body,
                    params: params
                        .iter()
                        .zip(operands)
                        .map(|(a, b)| (*a, b.clone()))
                        .collect(),
                };
            }

            Instruction::FutureLift { .. } => {
                results.push(format!("((uint32_t) {})", operands[0]));
            }
            Instruction::FutureLower { .. } => {
                results.push(format!("((int32_t) {})", operands[0]));
            }
            Instruction::StreamLift { .. } => {
                results.push(format!("((uint32_t) {})", operands[0]));
            }
            Instruction::StreamLower { .. } => {
                results.push(format!("((int32_t) {})", operands[0]));
            }

            i => unimplemented!("{:?}", i),
        }
    }
}

#[derive(Default, Clone, Copy)]
enum SourceType {
    #[default]
    HDefs,
    HFns,
    // HHelpers,
    // CDefs,
    // CFns,
    // CHelpers,
    // CAdapters,
}

#[derive(Default)]
struct Source {
    h_defs: wit_bindgen_core::Source,
    h_fns: wit_bindgen_core::Source,
    h_helpers: wit_bindgen_core::Source,
    h_async: wit_bindgen_core::Source,
    c_defs: wit_bindgen_core::Source,
    c_fns: wit_bindgen_core::Source,
    c_helpers: wit_bindgen_core::Source,
    c_adapters: wit_bindgen_core::Source,
    c_async: wit_bindgen_core::Source,
}

impl Source {
    fn src(&mut self, stype: SourceType) -> &mut wit_bindgen_core::Source {
        match stype {
            SourceType::HDefs => &mut self.h_defs,
            SourceType::HFns => &mut self.h_fns,
        }
    }
    fn append(&mut self, append_src: &Source) {
        self.h_defs.push_str(&append_src.h_defs);
        self.h_fns.push_str(&append_src.h_fns);
        self.h_helpers.push_str(&append_src.h_helpers);
        self.h_async.push_str(&append_src.h_async);
        self.c_defs.push_str(&append_src.c_defs);
        self.c_fns.push_str(&append_src.c_fns);
        self.c_helpers.push_str(&append_src.c_helpers);
        self.c_adapters.push_str(&append_src.c_adapters);
        self.c_async.push_str(&append_src.c_async);
    }
    fn h_defs(&mut self, s: &str) {
        self.h_defs.push_str(s);
    }
    fn h_fns(&mut self, s: &str) {
        self.h_fns.push_str(s);
    }
    fn h_helpers(&mut self, s: &str) {
        self.h_helpers.push_str(s);
    }
    fn c_fns(&mut self, s: &str) {
        self.c_fns.push_str(s);
    }
    fn c_helpers(&mut self, s: &str) {
        self.c_helpers.push_str(s);
    }
    fn c_adapters(&mut self, s: &str) {
        self.c_adapters.push_str(s);
    }
}

pub fn wasm_type(ty: WasmType) -> &'static str {
    match ty {
        WasmType::I32 => "int32_t",
        WasmType::I64 => "int64_t",
        WasmType::F32 => "float",
        WasmType::F64 => "double",
        WasmType::Pointer => "uint8_t *",
        WasmType::PointerOrI64 => "int64_t",
        WasmType::Length => "size_t",
    }
}

pub fn int_repr(ty: Int) -> &'static str {
    match ty {
        Int::U8 => "uint8_t",
        Int::U16 => "uint16_t",
        Int::U32 => "uint32_t",
        Int::U64 => "uint64_t",
    }
}

pub fn flags_repr(f: &Flags) -> Int {
    match f.repr() {
        FlagsRepr::U8 => Int::U8,
        FlagsRepr::U16 => Int::U16,
        FlagsRepr::U32(1) => Int::U32,
        FlagsRepr::U32(2) => Int::U64,
        repr => panic!("unimplemented flags {repr:?}"),
    }
}

pub fn is_arg_by_pointer(resolve: &Resolve, ty: &Type) -> bool {
    match ty {
        Type::Id(id) => match resolve.types[*id].kind {
            TypeDefKind::Type(t) => is_arg_by_pointer(resolve, &t),
            TypeDefKind::Variant(_) => true,
            TypeDefKind::Option(_) => true,
            TypeDefKind::Result(_) => true,
            TypeDefKind::Enum(_) => false,
            TypeDefKind::Flags(_) => false,
            TypeDefKind::Handle(_) => false,
            TypeDefKind::Tuple(_)
            | TypeDefKind::Record(_)
            | TypeDefKind::List(_)
            | TypeDefKind::Map(..) => true,
            TypeDefKind::Future(_) => false,
            TypeDefKind::Stream(_) => false,
            TypeDefKind::Resource => todo!("is_arg_by_pointer for resource"),
            TypeDefKind::Unknown => unreachable!(),
            TypeDefKind::FixedLengthList(..) => todo!(),
        },
        Type::String => true,
        _ => false,
    }
}

pub fn to_c_ident(name: &str) -> String {
    match name {
        // Escape C and C++ keywords.
        // Source: https://en.cppreference.com/w/cpp/keyword
        "alignas" => "alignas_".into(),
        "alignof" => "alignof_".into(),
        "and" => "and_".into(),
        "and_eq" => "and_eq_".into(),
        "asm" => "asm_".into(),
        "atomic_cancel" => "atomic_cancel_".into(),
        "atomic_commit" => "atomic_commit_".into(),
        "atomic_noexcept" => "atomic_noexcept_".into(),
        "auto" => "auto_".into(),
        "bitand" => "bitand_".into(),
        "bitor" => "bitor_".into(),
        "bool" => "bool_".into(),
        "break" => "break_".into(),
        "case" => "case_".into(),
        "catch" => "catch_".into(),
        "char" => "char_".into(),
        "char8_t" => "char8_t_".into(),
        "char16_t" => "char16_t_".into(),
        "char32_t" => "char32_t_".into(),
        "class" => "class_".into(),
        "compl" => "compl_".into(),
        "concept" => "concept_".into(),
        "const" => "const_".into(),
        "consteval" => "consteval_".into(),
        "constexpr" => "constexpr_".into(),
        "constinit" => "constinit_".into(),
        "const_cast" => "const_cast_".into(),
        "continue" => "continue_".into(),
        "co_await" => "co_await_".into(),
        "co_return" => "co_return_".into(),
        "co_yield" => "co_yield_".into(),
        "decltype" => "decltype_".into(),
        "default" => "default_".into(),
        "delete" => "delete_".into(),
        "do" => "do_".into(),
        "double" => "double_".into(),
        "dynamic_cast" => "dynamic_cast_".into(),
        "else" => "else_".into(),
        "enum" => "enum_".into(),
        "explicit" => "explicit_".into(),
        "export" => "export_".into(),
        "extern" => "extern_".into(),
        "false" => "false_".into(),
        "float" => "float_".into(),
        "for" => "for_".into(),
        "friend" => "friend_".into(),
        "goto" => "goto_".into(),
        "if" => "if_".into(),
        "inline" => "inline_".into(),
        "int" => "int_".into(),
        "long" => "long_".into(),
        "mutable" => "mutable_".into(),
        "namespace" => "namespace_".into(),
        "new" => "new_".into(),
        "noexcept" => "noexcept_".into(),
        "not" => "not_".into(),
        "not_eq" => "not_eq_".into(),
        "nullptr" => "nullptr_".into(),
        "operator" => "operator_".into(),
        "or" => "or_".into(),
        "or_eq" => "or_eq_".into(),
        "private" => "private_".into(),
        "protected" => "protected_".into(),
        "public" => "public_".into(),
        "reflexpr" => "reflexpr_".into(),
        "register" => "register_".into(),
        "reinterpret_cast" => "reinterpret_cast_".into(),
        "requires" => "requires_".into(),
        "return" => "return_".into(),
        "short" => "short_".into(),
        "signed" => "signed_".into(),
        "sizeof" => "sizeof_".into(),
        "static" => "static_".into(),
        "static_assert" => "static_assert_".into(),
        "static_cast" => "static_cast_".into(),
        "struct" => "struct_".into(),
        "switch" => "switch_".into(),
        "synchronized" => "synchronized_".into(),
        "template" => "template_".into(),
        "this" => "this_".into(),
        "thread_local" => "thread_local_".into(),
        "throw" => "throw_".into(),
        "true" => "true_".into(),
        "try" => "try_".into(),
        "typedef" => "typedef_".into(),
        "typeid" => "typeid_".into(),
        "typename" => "typename_".into(),
        "union" => "union_".into(),
        "unsigned" => "unsigned_".into(),
        "using" => "using_".into(),
        "virtual" => "virtual_".into(),
        "void" => "void_".into(),
        "volatile" => "volatile_".into(),
        "wchar_t" => "wchar_t_".into(),
        "while" => "while_".into(),
        "xor" => "xor_".into(),
        "xor_eq" => "xor_eq_".into(),
        "_Packed" => "_Packed_".into(),
        // ret and err needs to be escaped because they are used as
        //  variable names for option and result flattening.
        "ret" => "ret_".into(),
        "err" => "err_".into(),
        // C standard library macros that conflict when used as identifiers
        "stdin" => "stdin_".into(),
        "stdout" => "stdout_".into(),
        "stderr" => "stderr_".into(),
        s => s.to_snake_case(),
    }
}

const POINTER_SIZE_EXPRESSION: &str = "sizeof(void*)";
