use heck::*;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::io::{Read, Write};
use std::mem;
use std::process::{Command, Stdio};
use std::str::FromStr;
use wit_bindgen_gen_core::wit_parser::abi::{
    AbiVariant, Bindgen, Instruction, LiftLower, WasmType,
};
use wit_bindgen_gen_core::{wit_parser::*, Direction, Files, Generator, Source, TypeInfo, Types};
use wit_bindgen_gen_rust::{
    to_rust_ident, wasm_type, FnSig, RustFlagsRepr, RustFunctionGenerator, RustGenerator, TypeMode,
};

#[derive(Default)]
pub struct Wasmtime {
    src: Source,
    opts: Opts,
    needs_get_memory: bool,
    needs_get_func: bool,
    needs_char_from_i32: bool,
    needs_invalid_variant: bool,
    needs_validate_flags: bool,
    needs_raw_mem: bool,
    needs_bad_int: bool,
    needs_copy_slice: bool,
    needs_buffer_glue: bool,
    needs_le: bool,
    needs_custom_error_to_trap: bool,
    needs_custom_error_to_types: BTreeSet<String>,
    all_needed_handles: BTreeSet<String>,
    exported_resources: BTreeSet<ResourceId>,
    types: Types,
    guest_imports: HashMap<String, Vec<Import>>,
    guest_exports: HashMap<String, Exports>,
    in_import: bool,
    in_trait: bool,
    trait_name: String,
    sizes: SizeAlign,
}

enum NeededFunction {
    Realloc,
    Free,
}

struct Import {
    is_async: bool,
    name: String,
    trait_signature: String,
    num_wasm_params: usize,
    closure: String,
}

#[derive(Default)]
struct Exports {
    fields: BTreeMap<String, (String, String)>,
    funcs: Vec<String>,
}

#[derive(Default, Debug, Clone)]
#[cfg_attr(feature = "structopt", derive(structopt::StructOpt))]
pub struct Opts {
    /// Whether or not `rustfmt` is executed to format generated code.
    #[cfg_attr(feature = "structopt", structopt(long))]
    pub rustfmt: bool,

    /// Whether or not to emit `tracing` macro calls on function entry/exit.
    #[cfg_attr(feature = "structopt", structopt(long))]
    pub tracing: bool,

    /// Indicates which functions should be `async`: `all`, `none`, or a
    /// comma-separated list.
    #[cfg_attr(
        feature = "structopt",
        structopt(long = "async", default_value = "none")
    )]
    pub async_: Async,

    /// A flag to indicate that all trait methods in imports should return a
    /// custom trait-defined error. Applicable for import bindings.
    #[cfg_attr(feature = "structopt", structopt(long))]
    pub custom_error: bool,
}

#[derive(Debug, Clone)]
pub enum Async {
    None,
    All,
    Only(HashSet<String>),
}

impl Async {
    fn includes(&self, name: &str) -> bool {
        match self {
            Async::None => false,
            Async::All => true,
            Async::Only(list) => list.contains(name),
        }
    }

    fn is_none(&self) -> bool {
        match self {
            Async::None => true,
            _ => false,
        }
    }
}

impl Default for Async {
    fn default() -> Async {
        Async::None
    }
}

impl FromStr for Async {
    type Err = String;
    fn from_str(s: &str) -> Result<Async, String> {
        Ok(if s == "all" {
            Async::All
        } else if s == "none" {
            Async::None
        } else {
            Async::Only(s.split(',').map(|s| s.trim().to_string()).collect())
        })
    }
}

impl Opts {
    pub fn build(self) -> Wasmtime {
        let mut r = Wasmtime::new();
        r.opts = self;
        r
    }
}

enum FunctionRet {
    /// The function return is normal and needs to extra handling.
    Normal,
    /// The function return was wrapped in a `Result` in Rust. The `Ok` variant
    /// is the actual value that will be lowered, and the `Err`, if present,
    /// means that a trap has occurred.
    CustomToTrap,
    /// The function returns a `Result` in both wasm and in Rust, but the
    /// Rust error type is a custom error and must be converted to `err`. The
    /// `ok` variant payload is provided here too.
    CustomToError { ok: Type, err: String },
}

impl Wasmtime {
    pub fn new() -> Wasmtime {
        Wasmtime::default()
    }

    fn abi_variant(dir: Direction) -> AbiVariant {
        // This generator uses a reversed mapping! In the Wasmtime host-side
        // bindings, we don't use any extra adapter layer between guest wasm
        // modules and the host. When the guest imports functions using the
        // `GuestImport` ABI, the host directly implements the `GuestImport`
        // ABI, even though the host is *exporting* functions. Similarly, when
        // the guest exports functions using the `GuestExport` ABI, the host
        // directly imports them with the `GuestExport` ABI, even though the
        // host is *importing* functions.
        match dir {
            Direction::Import => AbiVariant::GuestExport,
            Direction::Export => AbiVariant::GuestImport,
        }
    }

    fn print_intrinsics(&mut self) {
        if self.needs_raw_mem {
            self.push_str("use wit_bindgen_wasmtime::rt::RawMem;\n");
        }
        if self.needs_char_from_i32 {
            self.push_str("use wit_bindgen_wasmtime::rt::char_from_i32;\n");
        }
        if self.needs_invalid_variant {
            self.push_str("use wit_bindgen_wasmtime::rt::invalid_variant;\n");
        }
        if self.needs_bad_int {
            self.push_str("use core::convert::TryFrom;\n");
            self.push_str("use wit_bindgen_wasmtime::rt::bad_int;\n");
        }
        if self.needs_validate_flags {
            self.push_str("use wit_bindgen_wasmtime::rt::validate_flags;\n");
        }
        if self.needs_le {
            self.push_str("use wit_bindgen_wasmtime::Le;\n");
        }
        if self.needs_copy_slice {
            self.push_str("use wit_bindgen_wasmtime::rt::copy_slice;\n");
        }
    }

    /// Classifies the return value of a function to see if it needs handling
    /// with respect to the `custom_error` configuration option.
    fn classify_fn_ret(&mut self, iface: &Interface, f: &Function) -> FunctionRet {
        if !self.opts.custom_error {
            return FunctionRet::Normal;
        }

        if let Type::Id(id) = &f.result {
            if let TypeDefKind::Expected(e) = &iface.types[*id].kind {
                if let Type::Id(err) = e.err {
                    if let Some(name) = &iface.types[err].name {
                        self.needs_custom_error_to_types.insert(name.clone());
                        return FunctionRet::CustomToError {
                            ok: e.ok,
                            err: name.to_string(),
                        };
                    }
                }
            }
        }

        self.needs_custom_error_to_trap = true;
        FunctionRet::CustomToTrap
    }
}

impl RustGenerator for Wasmtime {
    fn default_param_mode(&self) -> TypeMode {
        if self.in_import {
            // The default here is that only leaf values can be borrowed because
            // otherwise lists and such need to be copied into our own memory.
            TypeMode::LeafBorrowed("'a")
        } else {
            // When we're calling wasm exports, however, there's no need to take
            // any ownership of anything from the host so everything is borrowed
            // in the parameter position.
            TypeMode::AllBorrowed("'a")
        }
    }

    fn handle_projection(&self) -> Option<(&'static str, String)> {
        if self.in_import {
            if self.in_trait {
                Some(("Self", self.trait_name.clone()))
            } else {
                Some(("T", self.trait_name.clone()))
            }
        } else {
            None
        }
    }

    fn handle_wrapper(&self) -> Option<&'static str> {
        None
    }

    fn push_str(&mut self, s: &str) {
        self.src.push_str(s);
    }

    fn info(&self, ty: TypeId) -> TypeInfo {
        self.types.get(ty)
    }

    fn types_mut(&mut self) -> &mut Types {
        &mut self.types
    }

    fn print_borrowed_slice(
        &mut self,
        iface: &Interface,
        mutbl: bool,
        ty: &Type,
        lifetime: &'static str,
    ) {
        if self.sizes.align(ty) > 1 && self.in_import {
            // If we're generating bindings for an import we ideally want to
            // hand out raw pointers into memory. We can't guarantee anything
            // about alignment in memory, though, so if the alignment
            // requirement is bigger than one then we have to use slices where
            // the type has a `Le<...>` wrapper.
            //
            // For exports we're generating functions that take values from
            // Rust, so we can assume alignment and use raw slices. For types
            // with an align of 1, then raw pointers are fine since Rust will
            // have the same alignment requirement.
            self.needs_le = true;
            self.push_str("&");
            if lifetime != "'_" {
                self.push_str(lifetime);
                self.push_str(" ");
            }
            if mutbl {
                self.push_str(" mut ");
            }
            self.push_str("[Le<");
            self.print_ty(iface, ty, TypeMode::AllBorrowed(lifetime));
            self.push_str(">]");
        } else {
            self.print_rust_slice(iface, mutbl, ty, lifetime);
        }
    }

    fn print_borrowed_str(&mut self, lifetime: &'static str) {
        self.push_str("&");
        if lifetime != "'_" {
            self.push_str(lifetime);
            self.push_str(" ");
        }
        self.push_str(" str");
    }
}

impl Generator for Wasmtime {
    fn preprocess_one(&mut self, iface: &Interface, dir: Direction) {
        let variant = Self::abi_variant(dir);
        self.types.analyze(iface);
        self.in_import = variant == AbiVariant::GuestImport;
        self.trait_name = iface.name.to_camel_case();
        self.src.push_str(&format!(
            "#[allow(clippy::all)]\npub mod {} {{\n",
            iface.name.to_snake_case(),
        ));
        self.src
            .push_str("#[allow(unused_imports)]\nuse wit_bindgen_wasmtime::{wasmtime, anyhow};\n");
        self.sizes.fill(iface);
    }

    fn type_record(
        &mut self,
        iface: &Interface,
        id: TypeId,
        name: &str,
        record: &Record,
        docs: &Docs,
    ) {
        self.print_typedef_record(iface, id, record, docs);

        // If this record might be used as a slice type in various places then
        // we synthesize an `Endian` implementation for it so `&[Le<ThisType>]`
        // is usable.
        if self.modes_of(iface, id).len() > 0
            && record.fields.iter().all(|f| iface.all_bits_valid(&f.ty))
        {
            self.src.push_str("impl wit_bindgen_wasmtime::Endian for ");
            self.src.push_str(&name.to_camel_case());
            self.src.push_str(" {\n");

            self.src.push_str("fn into_le(self) -> Self {\n");
            self.src.push_str("Self {\n");
            for field in record.fields.iter() {
                self.src.push_str(&field.name.to_snake_case());
                self.src.push_str(": self.");
                self.src.push_str(&field.name.to_snake_case());
                self.src.push_str(".into_le(),\n");
            }
            self.src.push_str("}\n");
            self.src.push_str("}\n");

            self.src.push_str("fn from_le(self) -> Self {\n");
            self.src.push_str("Self {\n");
            for field in record.fields.iter() {
                self.src.push_str(&field.name.to_snake_case());
                self.src.push_str(": self.");
                self.src.push_str(&field.name.to_snake_case());
                self.src.push_str(".from_le(),\n");
            }
            self.src.push_str("}\n");
            self.src.push_str("}\n");

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

            // Also add an `AllBytesValid` valid impl since this structure's
            // byte representations are valid (guarded by the `all_bits_valid`
            // predicate).
            self.src
                .push_str("unsafe impl wit_bindgen_wasmtime::AllBytesValid for ");
            self.src.push_str(&name.to_camel_case());
            self.src.push_str(" {}\n");
        }
    }

    fn type_tuple(
        &mut self,
        iface: &Interface,
        id: TypeId,
        _name: &str,
        tuple: &Tuple,
        docs: &Docs,
    ) {
        self.print_typedef_tuple(iface, id, tuple, docs);
    }

    fn type_flags(
        &mut self,
        _iface: &Interface,
        _id: TypeId,
        name: &str,
        flags: &Flags,
        docs: &Docs,
    ) {
        self.src
            .push_str("wit_bindgen_wasmtime::bitflags::bitflags! {\n");
        self.rustdoc(docs);
        let repr = RustFlagsRepr::new(flags);
        self.src
            .push_str(&format!("pub struct {}: {repr} {{\n", name.to_camel_case()));
        for (i, flag) in flags.flags.iter().enumerate() {
            self.rustdoc(&flag.docs);
            self.src.push_str(&format!(
                "const {} = 1 << {};\n",
                flag.name.to_shouty_snake_case(),
                i,
            ));
        }
        self.src.push_str("}\n");
        self.src.push_str("}\n\n");

        self.src.push_str("impl core::fmt::Display for ");
        self.src.push_str(&name.to_camel_case());
        self.src.push_str(
            "{\nfn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n",
        );

        self.src.push_str("f.write_str(\"");
        self.src.push_str(&name.to_camel_case());
        self.src.push_str("(\")?;\n");
        self.src.push_str("core::fmt::Debug::fmt(self, f)?;\n");
        self.src.push_str("f.write_str(\" (0x\")?;\n");
        self.src
            .push_str("core::fmt::LowerHex::fmt(&self.bits, f)?;\n");
        self.src.push_str("f.write_str(\"))\")?;\n");
        self.src.push_str("Ok(())");

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

    fn type_variant(
        &mut self,
        iface: &Interface,
        id: TypeId,
        _name: &str,
        variant: &Variant,
        docs: &Docs,
    ) {
        self.print_typedef_variant(iface, id, variant, docs);
    }

    fn type_union(
        &mut self,
        iface: &Interface,
        id: TypeId,
        _name: &str,
        union: &Union,
        docs: &Docs,
    ) {
        self.print_typedef_union(iface, id, union, docs);
    }

    fn type_option(
        &mut self,
        iface: &Interface,
        id: TypeId,
        _name: &str,
        payload: &Type,
        docs: &Docs,
    ) {
        self.print_typedef_option(iface, id, payload, docs);
    }

    fn type_expected(
        &mut self,
        iface: &Interface,
        id: TypeId,
        _name: &str,
        expected: &Expected,
        docs: &Docs,
    ) {
        self.print_typedef_expected(iface, id, expected, docs);
    }

    fn type_enum(&mut self, _iface: &Interface, id: TypeId, name: &str, enum_: &Enum, docs: &Docs) {
        self.print_typedef_enum(id, name, enum_, docs);
    }

    fn type_resource(&mut self, iface: &Interface, ty: ResourceId) {
        let name = &iface.resources[ty].name;
        self.all_needed_handles.insert(name.to_string());

        // If we're binding imports then all handles are associated types so
        // there's nothing that we need to do about that.
        if self.in_import {
            return;
        }

        self.exported_resources.insert(ty);

        // ... otherwise for exports we generate a newtype wrapper around an
        // `i32` to manage the resultt.
        let tyname = name.to_camel_case();
        self.rustdoc(&iface.resources[ty].docs);
        self.src.push_str("#[derive(Debug)]\n");
        self.src.push_str(&format!(
            "pub struct {}(wit_bindgen_wasmtime::rt::ResourceIndex);\n",
            tyname
        ));
    }

    fn type_alias(&mut self, iface: &Interface, id: TypeId, _name: &str, ty: &Type, docs: &Docs) {
        self.print_typedef_alias(iface, id, ty, docs);
    }

    fn type_list(&mut self, iface: &Interface, id: TypeId, _name: &str, ty: &Type, docs: &Docs) {
        self.print_type_list(iface, id, ty, docs);
    }

    fn type_builtin(&mut self, iface: &Interface, _id: TypeId, name: &str, ty: &Type, docs: &Docs) {
        self.rustdoc(docs);
        self.src
            .push_str(&format!("pub type {}", name.to_camel_case()));
        self.src.push_str(" = ");
        self.print_ty(iface, ty, TypeMode::Owned);
        self.src.push_str(";\n");
    }

    // As with `abi_variant` above, we're generating host-side bindings here
    // so a user "export" uses the "guest import" ABI variant on the inside of
    // this `Generator` implementation.
    fn export(&mut self, iface: &Interface, func: &Function) {
        assert!(!func.is_async, "async not supported yet");
        let prev = mem::take(&mut self.src);

        // Generate the closure that's passed to a `Linker`, the final piece of
        // codegen here.
        let sig = iface.wasm_signature(AbiVariant::GuestImport, func);
        let params = (0..sig.params.len())
            .map(|i| format!("arg{}", i))
            .collect::<Vec<_>>();
        let mut f = FunctionBindgen::new(self, params);
        iface.call(
            AbiVariant::GuestImport,
            LiftLower::LiftArgsLowerResults,
            func,
            &mut f,
        );
        let FunctionBindgen {
            src,
            cleanup,
            needs_borrow_checker,
            needs_memory,
            needs_buffer_transaction,
            needs_functions,
            closures,
            async_intrinsic_called,
            ..
        } = f;
        assert!(cleanup.is_none());
        assert!(!needs_buffer_transaction);

        // Generate the signature this function will have in the final trait
        let self_arg = "&mut self".to_string();
        self.in_trait = true;

        let mut fnsig = FnSig::default();
        fnsig.private = true;
        fnsig.async_ = self.opts.async_.includes(&func.name);
        fnsig.self_arg = Some(self_arg);
        self.print_docs_and_params(iface, func, TypeMode::LeafBorrowed("'_"), &fnsig);
        // The Rust return type may differ from the wasm return type based on
        // the `custom_error` configuration of this code generator.
        match self.classify_fn_ret(iface, func) {
            FunctionRet::Normal => {
                self.push_str(" -> ");
                self.print_ty(iface, &func.result, TypeMode::Owned);
            }
            FunctionRet::CustomToTrap => {
                self.push_str(" -> Result<");
                self.print_ty(iface, &func.result, TypeMode::Owned);
                self.push_str(", Self::Error>");
            }
            FunctionRet::CustomToError { ok, .. } => {
                self.push_str(" -> Result<");
                self.print_ty(iface, &ok, TypeMode::Owned);
                self.push_str(", Self::Error>");
            }
        }
        self.in_trait = false;
        let trait_signature = mem::take(&mut self.src).into();

        // Generate the closure that's passed to a `Linker`, the final piece of
        // codegen here.
        self.src
            .push_str("move |mut caller: wasmtime::Caller<'_, T>");
        for (i, param) in sig.params.iter().enumerate() {
            let arg = format!("arg{}", i);
            self.src.push_str(",");
            self.src.push_str(&arg);
            self.src.push_str(":");
            self.wasm_type(*param);
        }
        self.src.push_str("| {\n");

        // If an intrinsic was called asynchronously, which happens if anything
        // in the module could be asynchronous, then we must wrap this host
        // import with an async block. Otherwise if the function is itself
        // explicitly async then we must also wrap it in an async block.
        //
        // If none of that happens, then this is fine to be sync because
        // everything is sync.
        let is_async = if async_intrinsic_called || self.opts.async_.includes(&func.name) {
            self.src.push_str("Box::new(async move {\n");
            true
        } else {
            false
        };

        if self.opts.tracing {
            self.src.push_str(&format!(
                "
                    let span = wit_bindgen_wasmtime::tracing::span!(
                        wit_bindgen_wasmtime::tracing::Level::TRACE,
                        \"wit-bindgen abi\",
                        module = \"{}\",
                        function = \"{}\",
                    );
                    let _enter = span.enter();
                ",
                iface.name, func.name,
            ));
        }
        self.src.push_str(&closures);

        for (name, func) in needs_functions {
            self.src.push_str(&format!(
                "
                    let func = get_func(&mut caller, \"{name}\")?;
                    let func_{name} = func.typed::<{cvt}, _>(&caller)?;
                ",
                name = name,
                cvt = func.cvt(),
            ));
            self.needs_get_func = true;
        }

        if needs_memory || needs_borrow_checker {
            self.src
                .push_str("let memory = &get_memory(&mut caller, \"memory\")?;\n");
            self.needs_get_memory = true;
        }

        if needs_borrow_checker {
            self.src.push_str(
                "let (mem, data) = memory.data_and_store_mut(&mut caller);
                let mut _bc = wit_bindgen_wasmtime::BorrowChecker::new(mem);
                let host = get(data);\n",
            );
        } else {
            self.src.push_str("let host = get(caller.data_mut());\n");
        }

        if self.all_needed_handles.len() > 0 {
            self.src.push_str("let (host, _tables) = host;\n");
        }

        self.src.push_str(&String::from(src));

        if is_async {
            self.src.push_str("})\n");
        }
        self.src.push_str("}");
        let closure = mem::replace(&mut self.src, prev).into();

        self.guest_imports
            .entry(iface.name.to_string())
            .or_insert(Vec::new())
            .push(Import {
                is_async,
                num_wasm_params: sig.params.len(),
                name: func.name.to_string(),
                closure,
                trait_signature,
            });
    }

    // As with `abi_variant` above, we're generating host-side bindings here
    // so a user "import" uses the "export" ABI variant on the inside of
    // this `Generator` implementation.
    fn import(&mut self, iface: &Interface, func: &Function) {
        assert!(!func.is_async, "async not supported yet");
        let prev = mem::take(&mut self.src);

        // If anything is asynchronous on exports then everything must be
        // asynchronous, Wasmtime can't intermix async and sync calls because
        // it's unknown whether the wasm module will make an async host call.
        let is_async = !self.opts.async_.is_none();
        let mut sig = FnSig::default();
        sig.async_ = is_async;
        sig.self_arg = Some("&self, mut caller: impl wasmtime::AsContextMut<Data = T>".to_string());
        self.print_docs_and_params(iface, func, TypeMode::AllBorrowed("'_"), &sig);
        self.push_str("-> Result<");
        self.print_ty(iface, &func.result, TypeMode::Owned);
        self.push_str(", wasmtime::Trap> {\n");

        let params = func
            .params
            .iter()
            .map(|(name, _)| to_rust_ident(name).to_string())
            .collect();
        let mut f = FunctionBindgen::new(self, params);
        iface.call(
            AbiVariant::GuestExport,
            LiftLower::LowerArgsLiftResults,
            func,
            &mut f,
        );
        let FunctionBindgen {
            needs_memory,
            src,
            needs_borrow_checker,
            needs_buffer_transaction,
            closures,
            needs_functions,
            ..
        } = f;

        let exports = self
            .guest_exports
            .entry(iface.name.to_string())
            .or_insert_with(Exports::default);
        for (name, func) in needs_functions {
            self.src
                .push_str(&format!("let func_{0} = &self.{0};\n", name));
            let get = format!(
                "instance.get_typed_func::<{}, _>(&mut store, \"{}\")?",
                func.cvt(),
                name
            );
            exports.fields.insert(name, (func.ty(), get));
        }

        self.src.push_str(&closures);

        assert!(!needs_borrow_checker);
        if needs_memory {
            self.src.push_str("let memory = &self.memory;\n");
            exports.fields.insert(
                "memory".to_string(),
                (
                    "wasmtime::Memory".to_string(),
                    "instance
                        .get_memory(&mut store, \"memory\")
                         .ok_or_else(|| {
                             anyhow::anyhow!(\"`memory` export not a memory\")
                         })?
                    "
                    .to_string(),
                ),
            );
        }

        if needs_buffer_transaction {
            self.needs_buffer_glue = true;
            self.src
                .push_str("let mut buffer_transaction = self.buffer_glue.transaction();\n");
        }

        self.src.push_str(&String::from(src));
        self.src.push_str("}\n");
        let func_body = mem::replace(&mut self.src, prev);
        exports.funcs.push(func_body.into());

        // Create the code snippet which will define the type of this field in
        // the struct that we're exporting and additionally extracts the
        // function from an instantiated instance.
        let sig = iface.wasm_signature(AbiVariant::GuestExport, func);
        let mut cvt = "(".to_string();
        for param in sig.params.iter() {
            cvt.push_str(wasm_type(*param));
            cvt.push_str(",");
        }
        cvt.push_str("), (");
        for result in sig.results.iter() {
            cvt.push_str(wasm_type(*result));
            cvt.push_str(",");
        }
        cvt.push_str(")");
        exports.fields.insert(
            to_rust_ident(&func.name),
            (
                format!("wasmtime::TypedFunc<{}>", cvt),
                format!(
                    "instance.get_typed_func::<{}, _>(&mut store, \"{}\")?",
                    cvt, func.name,
                ),
            ),
        );
    }

    fn finish_one(&mut self, iface: &Interface, files: &mut Files) {
        for (module, funcs) in sorted_iter(&self.guest_imports) {
            let module_camel = module.to_camel_case();
            let is_async = !self.opts.async_.is_none();
            if is_async {
                self.src.push_str("#[wit_bindgen_wasmtime::async_trait]\n");
            }
            self.src.push_str("pub trait ");
            self.src.push_str(&module_camel);
            self.src.push_str(": Sized ");
            if is_async {
                self.src.push_str(" + Send");
            }
            self.src.push_str("{\n");
            if self.all_needed_handles.len() > 0 {
                for handle in self.all_needed_handles.iter() {
                    self.src.push_str("type ");
                    self.src.push_str(&handle.to_camel_case());
                    self.src.push_str(": std::fmt::Debug");
                    if is_async {
                        self.src.push_str(" + Send + Sync");
                    }
                    self.src.push_str(";\n");
                }
            }
            if self.opts.custom_error {
                self.src.push_str("type Error;\n");
                if self.needs_custom_error_to_trap {
                    self.src.push_str(
                        "fn error_to_trap(&mut self, err: Self::Error) -> wasmtime::Trap;\n",
                    );
                }
                for ty in self.needs_custom_error_to_types.iter() {
                    self.src.push_str(&format!(
                        "fn error_to_{}(&mut self, err: Self::Error) -> Result<{}, wasmtime::Trap>;\n",
                        ty.to_snake_case(),
                        ty.to_camel_case(),
                    ));
                }
            }
            for f in funcs {
                self.src.push_str(&f.trait_signature);
                self.src.push_str(";\n\n");
            }
            for handle in self.all_needed_handles.iter() {
                self.src.push_str(&format!(
                    "fn drop_{}(&mut self, state: Self::{}) {{
                        drop(state);
                    }}\n",
                    handle.to_snake_case(),
                    handle.to_camel_case(),
                ));
            }
            self.src.push_str("}\n");

            if self.all_needed_handles.len() > 0 {
                self.src.push_str("\npub struct ");
                self.src.push_str(&module_camel);
                self.src.push_str("Tables<T: ");
                self.src.push_str(&module_camel);
                self.src.push_str("> {\n");
                for handle in self.all_needed_handles.iter() {
                    self.src.push_str("pub(crate) ");
                    self.src.push_str(&handle.to_snake_case());
                    self.src.push_str("_table: wit_bindgen_wasmtime::Table<T::");
                    self.src.push_str(&handle.to_camel_case());
                    self.src.push_str(">,\n");
                }
                self.src.push_str("}\n");
                self.src.push_str("impl<T: ");
                self.src.push_str(&module_camel);
                self.src.push_str("> Default for ");
                self.src.push_str(&module_camel);
                self.src.push_str("Tables<T> {\n");
                self.src.push_str("fn default() -> Self { Self {");
                for handle in self.all_needed_handles.iter() {
                    self.src.push_str(&handle.to_snake_case());
                    self.src.push_str("_table: Default::default(),");
                }
                self.src.push_str("}}}");
            }
        }

        for (module, funcs) in mem::take(&mut self.guest_imports) {
            let module_camel = module.to_camel_case();
            let is_async = !self.opts.async_.is_none();
            self.push_str("\npub fn add_to_linker<T, U>(linker: &mut wasmtime::Linker<T>");
            self.push_str(", get: impl Fn(&mut T) -> ");
            if self.all_needed_handles.is_empty() {
                self.push_str("&mut U");
            } else {
                self.push_str(&format!("(&mut U, &mut {}Tables<U>)", module_camel));
            }
            self.push_str("+ Send + Sync + Copy + 'static) -> anyhow::Result<()> \n");
            self.push_str("where U: ");
            self.push_str(&module_camel);
            if is_async {
                self.push_str(", T: Send,");
            }
            self.push_str("\n{\n");
            if self.needs_get_memory {
                self.push_str("use wit_bindgen_wasmtime::rt::get_memory;\n");
            }
            if self.needs_get_func {
                self.push_str("use wit_bindgen_wasmtime::rt::get_func;\n");
            }
            for f in funcs {
                let method = if f.is_async {
                    format!("func_wrap{}_async", f.num_wasm_params)
                } else {
                    String::from("func_wrap")
                };
                self.push_str(&format!(
                    "linker.{}(\"{}\", \"{}\", {})?;\n",
                    method, module, f.name, f.closure,
                ));
            }
            for handle in self.all_needed_handles.iter() {
                self.src.push_str(&format!(
                    "linker.func_wrap(
                        \"canonical_abi\",
                        \"resource_drop_{name}\",
                        move |mut caller: wasmtime::Caller<'_, T>, handle: u32| {{
                            let (host, tables) = get(caller.data_mut());
                            let handle = tables
                                .{snake}_table
                                .remove(handle)
                                .map_err(|e| {{
                                    wasmtime::Trap::new(format!(\"failed to remove handle: {{}}\", e))
                                }})?;
                            host.drop_{snake}(handle);
                            Ok(())
                        }}
                    )?;\n",
                    name = handle,
                    snake = handle.to_snake_case(),
                ));
            }
            self.push_str("Ok(())\n}\n");
        }

        for (module, exports) in sorted_iter(&mem::take(&mut self.guest_exports)) {
            let name = module.to_camel_case();

            // Generate a struct that is the "state" of this exported module
            // which is required to be included in the host state `T` of the
            // store.
            self.push_str(
                "
                /// Auxiliary data associated with the wasm exports.
                ///
                /// This is required to be stored within the data of a
                /// `Store<T>` itself so lifting/lowering state can be managed
                /// when translating between the host and wasm.
                ",
            );
            self.push_str("#[derive(Default)]\n");
            self.push_str("pub struct ");
            self.push_str(&name);
            self.push_str("Data {\n");
            for r in self.exported_resources.iter() {
                self.src.push_str(&format!(
                    "
                        index_slab{}: wit_bindgen_wasmtime::rt::IndexSlab,
                        resource_slab{0}: wit_bindgen_wasmtime::rt::ResourceSlab,
                        dtor{0}: Option<wasmtime::TypedFunc<i32, ()>>,
                    ",
                    r.index()
                ));
            }
            self.push_str("}\n");

            self.push_str("pub struct ");
            self.push_str(&name);
            self.push_str("<T> {\n");
            self.push_str(&format!(
                "get_state: Box<dyn Fn(&mut T) -> &mut {}Data + Send + Sync>,\n",
                name
            ));
            for (name, (ty, _)) in exports.fields.iter() {
                self.push_str(name);
                self.push_str(": ");
                self.push_str(ty);
                self.push_str(",\n");
            }
            self.push_str("}\n");
            let bound = if self.opts.async_.is_none() {
                ""
            } else {
                ": Send"
            };
            self.push_str(&format!("impl<T{}> {}<T> {{\n", bound, name));

            if self.exported_resources.len() == 0 {
                self.push_str("#[allow(unused_variables)]\n");
            }
            self.push_str(&format!(
                "
                    /// Adds any intrinsics, if necessary for this exported wasm
                    /// functionality to the `linker` provided.
                    ///
                    /// The `get_state` closure is required to access the
                    /// auxiliary data necessary for these wasm exports from
                    /// the general store's state.
                    pub fn add_to_linker(
                        linker: &mut wasmtime::Linker<T>,
                        get_state: impl Fn(&mut T) -> &mut {}Data + Send + Sync + Copy + 'static,
                    ) -> anyhow::Result<()> {{
                ",
                name,
            ));
            for r in self.exported_resources.iter() {
                let (func_wrap, call, wait, prefix, suffix) = if self.opts.async_.is_none() {
                    ("func_wrap", "call", "", "", "")
                } else {
                    (
                        "func_wrap1_async",
                        "call_async",
                        ".await",
                        "Box::new(async move {",
                        "})",
                    )
                };
                self.src.push_str(&format!(
                    "
                        linker.{func_wrap}(
                            \"canonical_abi\",
                            \"resource_drop_{name}\",
                            move |mut caller: wasmtime::Caller<'_, T>, idx: u32| {prefix}{{
                                let state = get_state(caller.data_mut());
                                let resource_idx = state.index_slab{idx}.remove(idx)?;
                                let wasm = match state.resource_slab{idx}.drop(resource_idx) {{
                                    Some(wasm) => wasm,
                                    None => return Ok(()),
                                }};
                                let dtor = state.dtor{idx}.expect(\"destructor not set yet\");
                                dtor.{call}(&mut caller, wasm){wait}?;
                                Ok(())
                            }}{suffix},
                        )?;
                        linker.func_wrap(
                            \"canonical_abi\",
                            \"resource_clone_{name}\",
                            move |mut caller: wasmtime::Caller<'_, T>, idx: u32| {{
                                let state = get_state(caller.data_mut());
                                let resource_idx = state.index_slab{idx}.get(idx)?;
                                state.resource_slab{idx}.clone(resource_idx)?;
                                Ok(state.index_slab{idx}.insert(resource_idx))
                            }},
                        )?;
                        linker.func_wrap(
                            \"canonical_abi\",
                            \"resource_get_{name}\",
                            move |mut caller: wasmtime::Caller<'_, T>, idx: u32| {{
                                let state = get_state(caller.data_mut());
                                let resource_idx = state.index_slab{idx}.get(idx)?;
                                Ok(state.resource_slab{idx}.get(resource_idx))
                            }},
                        )?;
                        linker.func_wrap(
                            \"canonical_abi\",
                            \"resource_new_{name}\",
                            move |mut caller: wasmtime::Caller<'_, T>, val: i32| {{
                                let state = get_state(caller.data_mut());
                                let resource_idx = state.resource_slab{idx}.insert(val);
                                Ok(state.index_slab{idx}.insert(resource_idx))
                            }},
                        )?;
                    ",
                    name = iface.resources[*r].name,
                    idx = r.index(),
                    func_wrap = func_wrap,
                    call = call,
                    wait = wait,
                    prefix = prefix,
                    suffix = suffix,
                ));
            }
            self.push_str("Ok(())\n");
            self.push_str("}\n");

            let (async_fn, instantiate, wait) = if self.opts.async_.is_none() {
                ("", "", "")
            } else {
                ("async ", "_async", ".await")
            };
            self.push_str(&format!(
                "
                    /// Instantiates the provided `module` using the specified
                    /// parameters, wrapping up the result in a structure that
                    /// translates between wasm and the host.
                    ///
                    /// The `linker` provided will have intrinsics added to it
                    /// automatically, so it's not necessary to call
                    /// `add_to_linker` beforehand. This function will
                    /// instantiate the `module` otherwise using `linker`, and
                    /// both an instance of this structure and the underlying
                    /// `wasmtime::Instance` will be returned.
                    ///
                    /// The `get_state` parameter is used to access the
                    /// auxiliary state necessary for these wasm exports from
                    /// the general store state `T`.
                    pub {}fn instantiate(
                        mut store: impl wasmtime::AsContextMut<Data = T>,
                        module: &wasmtime::Module,
                        linker: &mut wasmtime::Linker<T>,
                        get_state: impl Fn(&mut T) -> &mut {}Data + Send + Sync + Copy + 'static,
                    ) -> anyhow::Result<(Self, wasmtime::Instance)> {{
                        Self::add_to_linker(linker, get_state)?;
                        let instance = linker.instantiate{}(&mut store, module){}?;
                        Ok((Self::new(store, &instance,get_state)?, instance))
                    }}
                ",
                async_fn, name, instantiate, wait,
            ));

            self.push_str(&format!(
                "
                    /// Low-level creation wrapper for wrapping up the exports
                    /// of the `instance` provided in this structure of wasm
                    /// exports.
                    ///
                    /// This function will extract exports from the `instance`
                    /// defined within `store` and wrap them all up in the
                    /// returned structure which can be used to interact with
                    /// the wasm module.
                    pub fn new(
                        mut store: impl wasmtime::AsContextMut<Data = T>,
                        instance: &wasmtime::Instance,
                        get_state: impl Fn(&mut T) -> &mut {}Data + Send + Sync + Copy + 'static,
                    ) -> anyhow::Result<Self> {{
                ",
                name,
            ));
            self.push_str("let mut store = store.as_context_mut();\n");
            assert!(!self.needs_get_func);
            for (name, (_, get)) in exports.fields.iter() {
                self.push_str("let ");
                self.push_str(&name);
                self.push_str("= ");
                self.push_str(&get);
                self.push_str(";\n");
            }
            for r in self.exported_resources.iter() {
                self.src.push_str(&format!(
                    "
                        get_state(store.data_mut()).dtor{} = \
                            Some(instance.get_typed_func::<i32, (), _>(\
                                &mut store, \
                                \"canonical_abi_drop_{}\", \
                            )?);\n
                    ",
                    r.index(),
                    iface.resources[*r].name,
                ));
            }
            self.push_str("Ok(");
            self.push_str(&name);
            self.push_str("{\n");
            for (name, _) in exports.fields.iter() {
                self.push_str(name);
                self.push_str(",\n");
            }
            self.push_str("get_state: Box::new(get_state),\n");
            self.push_str("\n})\n");
            self.push_str("}\n");

            for func in exports.funcs.iter() {
                self.push_str(func);
            }

            for r in self.exported_resources.iter() {
                let (async_fn, call, wait) = if self.opts.async_.is_none() {
                    ("", "call", "")
                } else {
                    ("async ", "call_async", ".await")
                };
                self.src.push_str(&format!(
                    "
                        /// Drops the host-owned handle to the resource
                        /// specified.
                        ///
                        /// Note that this may execute the WebAssembly-defined
                        /// destructor for this type. This also may not run
                        /// the destructor if there are still other references
                        /// to this type.
                        pub {async}fn drop_{name_snake}(
                            &self,
                            mut store: impl wasmtime::AsContextMut<Data = T>,
                            val: {name_camel},
                        ) -> Result<(), wasmtime::Trap> {{
                            let mut store = store.as_context_mut();
                            let data = (self.get_state)(store.data_mut());
                            let wasm = match data.resource_slab{idx}.drop(val.0) {{
                                Some(val) => val,
                                None => return Ok(()),
                            }};
                            data.dtor{idx}.unwrap().{call}(&mut store, wasm){wait}?;
                            Ok(())
                        }}
                    ",
                    name_snake = iface.resources[*r].name.to_snake_case(),
                    name_camel = iface.resources[*r].name.to_camel_case(),
                    idx = r.index(),
                    async = async_fn,
                    call = call,
                    wait = wait,
                ));
            }

            self.push_str("}\n");
        }
        self.print_intrinsics();

        // Close the opening `mod`.
        self.push_str("}\n");

        let mut src = mem::take(&mut self.src);
        if self.opts.rustfmt {
            let mut child = Command::new("rustfmt")
                .arg("--edition=2018")
                .stdin(Stdio::piped())
                .stdout(Stdio::piped())
                .spawn()
                .expect("failed to spawn `rustfmt`");
            child
                .stdin
                .take()
                .unwrap()
                .write_all(src.as_bytes())
                .unwrap();
            src.as_mut_string().truncate(0);
            child
                .stdout
                .take()
                .unwrap()
                .read_to_string(src.as_mut_string())
                .unwrap();
            let status = child.wait().unwrap();
            assert!(status.success());
        }

        files.push("bindings.rs", src.as_bytes());
    }
}

struct FunctionBindgen<'a> {
    gen: &'a mut Wasmtime,

    // Number used to assign unique names to temporary variables.
    tmp: usize,

    // Destination where source code is pushed onto for this function
    src: Source,

    // The named parameters that are available to this function
    params: Vec<String>,

    // Management of block scopes used by `Bindgen`.
    block_storage: Vec<Source>,
    blocks: Vec<String>,

    // Whether or not the code generator is after the invocation of wasm or the
    // host, used for knowing where to acquire memory from.
    after_call: bool,
    // Whether or not the `caller_memory` variable has been defined and is
    // available for use.
    caller_memory_available: bool,
    // Whether or not a helper function was called in an async fashion. If so
    // and this is an import, then the import must be defined asynchronously as
    // well.
    async_intrinsic_called: bool,
    // Code that must be executed before a return, generated during instruction
    // lowering.
    cleanup: Option<String>,

    // Rust clousures for buffers that must be placed at the front of the
    // function.
    closures: Source,

    // Various intrinsic properties this function's codegen required, must be
    // satisfied in the function header if any are set.
    needs_buffer_transaction: bool,
    needs_borrow_checker: bool,
    needs_memory: bool,
    needs_functions: HashMap<String, NeededFunction>,
}

impl FunctionBindgen<'_> {
    fn new(gen: &mut Wasmtime, params: Vec<String>) -> FunctionBindgen<'_> {
        FunctionBindgen {
            gen,
            block_storage: Vec::new(),
            blocks: Vec::new(),
            src: Source::default(),
            after_call: false,
            caller_memory_available: false,
            async_intrinsic_called: false,
            tmp: 0,
            cleanup: None,
            closures: Source::default(),
            needs_buffer_transaction: false,
            needs_borrow_checker: false,
            needs_memory: false,
            needs_functions: HashMap::new(),
            params,
        }
    }

    fn memory_src(&mut self) -> String {
        if self.gen.in_import {
            if !self.after_call {
                // Before calls we use `_bc` which is a borrow checker used for
                // getting long-lasting borrows into memory.
                self.needs_borrow_checker = true;
                return format!("_bc");
            }

            if !self.caller_memory_available {
                self.needs_memory = true;
                self.caller_memory_available = true;
                // get separate borrows of `caller_memory` and `_tables` if we
                // might need handle tables later. If we don't end up using
                // `_tables` that's ok, it'll almost always be optimized away.
                if self.gen.all_needed_handles.len() > 0 {
                    self.push_str(
                        "let (caller_memory, data) = memory.data_and_store_mut(&mut caller);\n",
                    );
                    self.push_str("let (_, _tables) = get(data);\n");
                } else {
                    self.push_str("let caller_memory = memory.data_mut(&mut caller);\n");
                }
            }
            format!("caller_memory")
        } else {
            self.needs_memory = true;
            format!("memory.data_mut(&mut caller)")
        }
    }

    fn call_intrinsic(&mut self, name: &str, args: String) {
        let (method, suffix) = if self.gen.opts.async_.is_none() {
            ("call", "")
        } else {
            self.async_intrinsic_called = true;
            ("call_async", ".await")
        };
        self.push_str(&format!(
            "func_{}.{}(&mut caller, {}){}?;\n",
            name, method, args, suffix
        ));
        self.caller_memory_available = false; // invalidated by call
    }

    fn load(&mut self, offset: i32, ty: &str, operands: &[String]) -> String {
        let mem = self.memory_src();
        self.gen.needs_raw_mem = true;
        let tmp = self.tmp();
        self.push_str(&format!(
            "let load{} = {}.load::<{}>({} + {})?;\n",
            tmp, mem, ty, operands[0], offset
        ));
        format!("load{}", tmp)
    }

    fn store(&mut self, offset: i32, method: &str, extra: &str, operands: &[String]) {
        let mem = self.memory_src();
        self.gen.needs_raw_mem = true;
        self.push_str(&format!(
            "{}.store({} + {}, wit_bindgen_wasmtime::rt::{}({}){})?;\n",
            mem, operands[1], offset, method, operands[0], extra
        ));
    }
}

impl RustFunctionGenerator for FunctionBindgen<'_> {
    fn push_str(&mut self, s: &str) {
        self.src.push_str(s);
    }

    fn tmp(&mut self) -> usize {
        let ret = self.tmp;
        self.tmp += 1;
        ret
    }

    fn rust_gen(&self) -> &dyn RustGenerator {
        self.gen
    }

    fn lift_lower(&self) -> LiftLower {
        if self.gen.in_import {
            LiftLower::LiftArgsLowerResults
        } else {
            LiftLower::LowerArgsLiftResults
        }
    }
}

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

    fn sizes(&self) -> &SizeAlign {
        &self.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);
        let expr = match operands.len() {
            0 => "()".to_string(),
            1 => operands[0].clone(),
            _ => format!("({})", operands.join(", ")),
        };
        if src.is_empty() {
            self.blocks.push(expr);
        } else if operands.is_empty() {
            self.blocks.push(format!("{{\n{}}}", &src[..]));
        } else {
            self.blocks.push(format!("{{\n{}{}\n}}", &src[..], expr));
        }
        self.caller_memory_available = false;
    }

    fn return_pointer(&mut self, _iface: &Interface, _size: usize, _align: usize) -> String {
        unimplemented!()
    }

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

    fn emit(
        &mut self,
        iface: &Interface,
        inst: &Instruction<'_>,
        operands: &mut Vec<String>,
        results: &mut Vec<String>,
    ) {
        let mut top_as = |cvt: &str| {
            let mut s = operands.pop().unwrap();
            s.push_str(" as ");
            s.push_str(cvt);
            results.push(s);
        };

        let mut try_from = |cvt: &str, operands: &[String], results: &mut Vec<String>| {
            self.gen.needs_bad_int = true;
            let result = format!("{}::try_from({}).map_err(bad_int)?", cvt, operands[0]);
            results.push(result);
        };

        match inst {
            Instruction::GetArg { nth } => results.push(self.params[*nth].clone()),
            Instruction::I32Const { val } => results.push(format!("{}i32", val)),
            Instruction::ConstZero { tys } => {
                for ty in tys.iter() {
                    match ty {
                        WasmType::I32 => results.push("0i32".to_string()),
                        WasmType::I64 => results.push("0i64".to_string()),
                        WasmType::F32 => results.push("0.0f32".to_string()),
                        WasmType::F64 => results.push("0.0f64".to_string()),
                    }
                }
            }

            Instruction::I64FromU64 | Instruction::I64FromS64 => {
                let s = operands.pop().unwrap();
                results.push(format!("wit_bindgen_wasmtime::rt::as_i64({})", s));
            }
            Instruction::I32FromChar
            | Instruction::I32FromU8
            | Instruction::I32FromS8
            | Instruction::I32FromU16
            | Instruction::I32FromS16
            | Instruction::I32FromU32
            | Instruction::I32FromS32 => {
                let s = operands.pop().unwrap();
                results.push(format!("wit_bindgen_wasmtime::rt::as_i32({})", s));
            }

            Instruction::F32FromFloat32
            | Instruction::F64FromFloat64
            | Instruction::Float32FromF32
            | Instruction::Float64FromF64
            | Instruction::S32FromI32
            | Instruction::S64FromI64 => {
                results.push(operands.pop().unwrap());
            }

            // Downcasts from `i32` into smaller integers are checked to ensure
            // that they fit within the valid range. While not strictly
            // necessary since we could chop bits off this should be more
            // forward-compatible with any future changes.
            Instruction::S8FromI32 => try_from("i8", operands, results),
            Instruction::U8FromI32 => try_from("u8", operands, results),
            Instruction::S16FromI32 => try_from("i16", operands, results),
            Instruction::U16FromI32 => try_from("u16", operands, results),

            // Casts of the same bit width simply use `as` since we're just
            // reinterpreting the bits already there.
            Instruction::U32FromI32 => top_as("u32"),
            Instruction::U64FromI64 => top_as("u64"),

            Instruction::CharFromI32 => {
                self.gen.needs_char_from_i32 = true;
                results.push(format!("char_from_i32({})?", operands[0]));
            }

            Instruction::Bitcasts { casts } => {
                wit_bindgen_gen_rust::bitcast(casts, operands, results)
            }

            Instruction::UnitLower => {
                self.push_str(&format!("let () = {};\n", operands[0]));
            }
            Instruction::UnitLift => {
                results.push("()".to_string());
            }

            Instruction::I32FromBool => {
                results.push(format!("match {} {{ true => 1, false => 0 }}", operands[0]));
            }
            Instruction::BoolFromI32 => {
                self.gen.needs_invalid_variant = true;
                results.push(format!(
                    "match {} {{
                        0 => false,
                        1 => true,
                        _ => return Err(invalid_variant(\"bool\")),
                    }}",
                    operands[0],
                ));
            }

            Instruction::I32FromOwnedHandle { ty } => {
                let name = &iface.resources[*ty].name;
                results.push(format!(
                    "_tables.{}_table.insert({}) as i32",
                    name.to_snake_case(),
                    operands[0]
                ));
            }
            Instruction::HandleBorrowedFromI32 { ty } => {
                let name = &iface.resources[*ty].name;
                results.push(format!(
                    "_tables.{}_table.get(({}) as u32).ok_or_else(|| {{
                            wasmtime::Trap::new(\"invalid handle index\")
                        }})?",
                    name.to_snake_case(),
                    operands[0]
                ));
            }
            Instruction::I32FromBorrowedHandle { ty } => {
                let tmp = self.tmp();
                self.push_str(&format!(
                    "
                        let obj{tmp} = {op};
                        (self.get_state)(caller.as_context_mut().data_mut()).resource_slab{idx}.clone(obj{tmp}.0)?;
                        let handle{tmp} = (self.get_state)(caller.as_context_mut().data_mut()).index_slab{idx}.insert(obj{tmp}.0);
                    ",
                    tmp = tmp,
                    idx = ty.index(),
                    op = operands[0],
                ));

                results.push(format!("handle{} as i32", tmp,));
            }
            Instruction::HandleOwnedFromI32 { ty } => {
                let tmp = self.tmp();
                self.push_str(&format!(
                    "let handle{} = (self.get_state)(caller.as_context_mut().data_mut()).index_slab{}.remove({} as u32)?;\n",
                    tmp,
                    ty.index(),
                    operands[0],
                ));

                let name = iface.resources[*ty].name.to_camel_case();
                results.push(format!("{}(handle{})", name, tmp));
            }

            Instruction::RecordLower { ty, record, .. } => {
                self.record_lower(iface, *ty, record, &operands[0], results);
            }
            Instruction::RecordLift { ty, record, .. } => {
                self.record_lift(iface, *ty, record, operands, results);
            }

            Instruction::TupleLower { tuple, .. } => {
                self.tuple_lower(tuple, &operands[0], results);
            }
            Instruction::TupleLift { .. } => {
                self.tuple_lift(operands, results);
            }

            Instruction::FlagsLower { flags, .. } => {
                let tmp = self.tmp();
                self.push_str(&format!("let flags{} = {};\n", tmp, operands[0]));
                for i in 0..flags.repr().count() {
                    results.push(format!("(flags{}.bits >> {}) as i32", tmp, i * 32));
                }
            }
            Instruction::FlagsLift { flags, name, .. } => {
                self.gen.needs_validate_flags = true;
                let repr = RustFlagsRepr::new(flags);
                let mut flags = String::from("0");
                for (i, op) in operands.iter().enumerate() {
                    flags.push_str(&format!("| (({} as {repr}) << {})", op, i * 32));
                }
                results.push(format!(
                    "validate_flags(
                        {},
                        {name}::all().bits(),
                        \"{name}\",
                        |bits| {name} {{ bits }}
                    )?",
                    flags,
                    name = name.to_camel_case(),
                ));
            }

            Instruction::VariantPayloadName => results.push("e".to_string()),

            Instruction::VariantLower {
                variant,
                results: result_types,
                ty,
                ..
            } => {
                let blocks = self
                    .blocks
                    .drain(self.blocks.len() - variant.cases.len()..)
                    .collect::<Vec<_>>();
                self.let_results(result_types.len(), results);
                let op0 = &operands[0];
                self.push_str(&format!("match {op0} {{\n"));
                let name = self.typename_lower(iface, *ty);
                for (case, block) in variant.cases.iter().zip(blocks) {
                    let case_name = case.name.to_camel_case();
                    self.push_str(&format!("{name}::{case_name}"));
                    if case.ty == Type::Unit {
                        self.push_str(&format!(" => {{\nlet e = ();\n{block}\n}}\n"));
                    } else {
                        self.push_str(&format!("(e) => {block},\n"));
                    }
                }
                self.push_str("};\n");
            }

            Instruction::VariantLift { variant, ty, .. } => {
                let blocks = self
                    .blocks
                    .drain(self.blocks.len() - variant.cases.len()..)
                    .collect::<Vec<_>>();
                let op0 = &operands[0];
                let mut result = format!("match {op0} {{\n");
                let name = self.typename_lift(iface, *ty);
                for (i, (case, block)) in variant.cases.iter().zip(blocks).enumerate() {
                    let block = if case.ty != Type::Unit {
                        format!("({block})")
                    } else {
                        String::new()
                    };
                    let case = case.name.to_camel_case();
                    result.push_str(&format!("{i} => {name}::{case}{block},\n"));
                }
                result.push_str(&format!("_ => return Err(invalid_variant(\"{name}\")),\n"));
                result.push_str("}");
                results.push(result);
                self.gen.needs_invalid_variant = true;
            }

            Instruction::UnionLower {
                union,
                results: result_types,
                ty,
                ..
            } => {
                let blocks = self
                    .blocks
                    .drain(self.blocks.len() - union.cases.len()..)
                    .collect::<Vec<_>>();
                self.let_results(result_types.len(), results);
                let op0 = &operands[0];
                self.push_str(&format!("match {op0} {{\n"));
                let name = self.typename_lower(iface, *ty);
                for (case_name, block) in self
                    .gen
                    .union_case_names(iface, union)
                    .into_iter()
                    .zip(blocks)
                {
                    self.push_str(&format!("{name}::{case_name}(e) => {block},\n"));
                }
                self.push_str("};\n");
            }

            Instruction::UnionLift { union, ty, .. } => {
                let blocks = self
                    .blocks
                    .drain(self.blocks.len() - union.cases.len()..)
                    .collect::<Vec<_>>();
                let op0 = &operands[0];
                let mut result = format!("match {op0} {{\n");
                let name = self.typename_lift(iface, *ty);
                for (i, (case_name, block)) in self
                    .gen
                    .union_case_names(iface, union)
                    .into_iter()
                    .zip(blocks)
                    .enumerate()
                {
                    result.push_str(&format!("{i} => {name}::{case_name}({block}),\n"));
                }
                result.push_str(&format!("_ => return Err(invalid_variant(\"{name}\")),\n"));
                result.push_str("}");
                results.push(result);
            }

            Instruction::OptionLower {
                results: result_types,
                ..
            } => {
                let some = self.blocks.pop().unwrap();
                let none = self.blocks.pop().unwrap();
                self.let_results(result_types.len(), results);
                let operand = &operands[0];
                self.push_str(&format!(
                    "match {operand} {{
                        Some(e) => {some},
                        None => {{\nlet e = ();\n{none}\n}},
                    }};"
                ));
            }

            Instruction::OptionLift { .. } => {
                let some = self.blocks.pop().unwrap();
                let none = self.blocks.pop().unwrap();
                assert_eq!(none, "()");
                let operand = &operands[0];
                results.push(format!(
                    "match {operand} {{
                        0 => None,
                        1 => Some({some}),
                        _ => return Err(invalid_variant(\"option\")),
                    }}"
                ));
                self.gen.needs_invalid_variant = true;
            }

            Instruction::ExpectedLower {
                results: result_types,
                ..
            } => {
                let err = self.blocks.pop().unwrap();
                let ok = self.blocks.pop().unwrap();
                self.let_results(result_types.len(), results);
                let operand = &operands[0];
                self.push_str(&format!(
                    "match {operand} {{
                        Ok(e) => {{ {ok} }},
                        Err(e) => {{ {err} }},
                    }};"
                ));
            }

            Instruction::ExpectedLift { .. } => {
                let err = self.blocks.pop().unwrap();
                let ok = self.blocks.pop().unwrap();
                let operand = &operands[0];
                results.push(format!(
                    "match {operand} {{
                        0 => Ok({ok}),
                        1 => Err({err}),
                        _ => return Err(invalid_variant(\"expected\")),
                    }}"
                ));
                self.gen.needs_invalid_variant = true;
            }

            Instruction::EnumLower { .. } => {
                results.push(format!("{} as i32", operands[0]));
            }

            Instruction::EnumLift { name, enum_, .. } => {
                let op0 = &operands[0];
                let mut result = format!("match {op0} {{\n");
                let name = name.to_camel_case();
                for (i, case) in enum_.cases.iter().enumerate() {
                    let case = case.name.to_camel_case();
                    result.push_str(&format!("{i} => {name}::{case},\n"));
                }
                result.push_str(&format!("_ => return Err(invalid_variant(\"{name}\")),\n"));
                result.push_str("}");
                results.push(result);
                self.gen.needs_invalid_variant = true;
            }

            Instruction::ListCanonLower { element, realloc } => {
                // Lowering only happens when we're passing lists into wasm,
                // which forces us to always allocate, so this should always be
                // `Some`.
                let realloc = realloc.unwrap();
                self.needs_functions
                    .insert(realloc.to_string(), NeededFunction::Realloc);
                let (size, align) = (self.gen.sizes.size(element), self.gen.sizes.align(element));

                // Store the operand into a temporary...
                let tmp = self.tmp();
                let val = format!("vec{}", tmp);
                self.push_str(&format!("let {} = {};\n", val, operands[0]));

                // ... and then realloc space for the result in the guest module
                let ptr = format!("ptr{}", tmp);
                self.push_str(&format!("let {} = ", ptr));
                self.call_intrinsic(
                    realloc,
                    format!("(0, 0, {}, ({}.len() as i32) * {})", align, val, size),
                );

                // ... and then copy over the result.
                let mem = self.memory_src();
                self.push_str(&format!("{}.store_many({}, &{})?;\n", mem, ptr, val));
                self.gen.needs_raw_mem = true;
                self.needs_memory = true;
                results.push(ptr);
                results.push(format!("{}.len() as i32", val));
            }

            Instruction::ListCanonLift { element, free, .. } => match free {
                Some(free) => {
                    self.needs_memory = true;
                    self.gen.needs_copy_slice = true;
                    self.needs_functions
                        .insert(free.to_string(), NeededFunction::Free);
                    let (align, el_size) =
                        (self.sizes().align(element), self.sizes().size(element));
                    let tmp = self.tmp();
                    self.push_str(&format!("let ptr{} = {};\n", tmp, operands[0]));
                    self.push_str(&format!("let len{} = {};\n", tmp, operands[1]));
                    self.push_str(&format!(
                        "
                            let data{tmp} = copy_slice(
                                &mut caller,
                                memory,
                                ptr{tmp}, len{tmp}, {}
                            )?;
                        ",
                        align,
                        tmp = tmp,
                    ));
                    self.call_intrinsic(
                        free,
                        // we use normal multiplication here as copy_slice has
                        // already verified that multiplied size fits i32
                        format!("(ptr{tmp}, len{tmp} * {}, {})", el_size, align, tmp = tmp),
                    );
                    results.push(format!("data{}", tmp));
                }
                None => {
                    self.needs_borrow_checker = true;
                    let tmp = self.tmp();
                    self.push_str(&format!("let ptr{} = {};\n", tmp, operands[0]));
                    self.push_str(&format!("let len{} = {};\n", tmp, operands[1]));
                    let slice = format!("_bc.slice(ptr{0}, len{0})?", tmp);
                    results.push(slice);
                }
            },

            Instruction::StringLower { realloc } => {
                // see above for this unwrap
                let realloc = realloc.unwrap();
                self.needs_functions
                    .insert(realloc.to_string(), NeededFunction::Realloc);

                // Store the operand into a temporary...
                let tmp = self.tmp();
                let val = format!("vec{}", tmp);
                self.push_str(&format!("let {} = {};\n", val, operands[0]));

                // ... and then realloc space for the result in the guest module
                let ptr = format!("ptr{}", tmp);
                self.push_str(&format!("let {} = ", ptr));
                self.call_intrinsic(realloc, format!("(0, 0, 1, {}.len() as i32)", val));

                // ... and then copy over the result.
                let mem = self.memory_src();
                self.push_str(&format!(
                    "{}.store_many({}, {}.as_bytes())?;\n",
                    mem, ptr, val
                ));
                self.gen.needs_raw_mem = true;
                self.needs_memory = true;
                results.push(ptr);
                results.push(format!("{}.len() as i32", val));
            }

            Instruction::StringLift { free } => match free {
                Some(free) => {
                    self.needs_memory = true;
                    self.gen.needs_copy_slice = true;
                    self.needs_functions
                        .insert(free.to_string(), NeededFunction::Free);
                    let tmp = self.tmp();
                    self.push_str(&format!("let ptr{} = {};\n", tmp, operands[0]));
                    self.push_str(&format!("let len{} = {};\n", tmp, operands[1]));
                    self.push_str(&format!(
                        "
                            let data{tmp} = copy_slice(
                                &mut caller,
                                memory,
                                ptr{tmp}, len{tmp}, 1,
                            )?;
                        ",
                        tmp = tmp,
                    ));
                    self.call_intrinsic(
                        free,
                        // we use normal multiplication here as copy_slice has
                        // already verified that multiplied size fits i32
                        format!("(ptr{tmp}, len{tmp}, 1)", tmp = tmp),
                    );
                    results.push(format!(
                        "String::from_utf8(data{})
                            .map_err(|_| wasmtime::Trap::new(\"invalid utf-8\"))?",
                        tmp,
                    ));
                }
                None => {
                    self.needs_borrow_checker = true;
                    let tmp = self.tmp();
                    self.push_str(&format!("let ptr{} = {};\n", tmp, operands[0]));
                    self.push_str(&format!("let len{} = {};\n", tmp, operands[1]));
                    let slice = format!("_bc.slice_str(ptr{0}, len{0})?", tmp);
                    results.push(slice);
                }
            },

            Instruction::ListLower { element, realloc } => {
                let realloc = realloc.unwrap();
                let body = self.blocks.pop().unwrap();
                let tmp = self.tmp();
                let vec = format!("vec{}", tmp);
                let result = format!("result{}", tmp);
                let len = format!("len{}", tmp);
                self.needs_functions
                    .insert(realloc.to_string(), NeededFunction::Realloc);
                let size = self.gen.sizes.size(element);
                let align = self.gen.sizes.align(element);

                // first store our vec-to-lower in a temporary since we'll
                // reference it multiple times.
                self.push_str(&format!("let {} = {};\n", vec, operands[0]));
                self.push_str(&format!("let {} = {}.len() as i32;\n", len, vec));

                // ... then realloc space for the result in the guest module
                self.push_str(&format!("let {} = ", result));
                self.call_intrinsic(realloc, format!("(0, 0, {}, {} * {})", align, len, size));

                // ... then consume the vector and use the block to lower the
                // result.
                self.push_str(&format!(
                    "for (i, e) in {}.into_iter().enumerate() {{\n",
                    vec
                ));
                self.push_str(&format!("let base = {} + (i as i32) * {};\n", result, size));
                self.push_str(&body);
                self.push_str("}");

                results.push(result);
                results.push(len);
            }

            Instruction::ListLift { element, free, .. } => {
                let body = self.blocks.pop().unwrap();
                let tmp = self.tmp();
                let size = self.gen.sizes.size(element);
                let align = self.gen.sizes.align(element);
                let len = format!("len{}", tmp);
                self.push_str(&format!("let {} = {};\n", len, operands[1]));
                let base = format!("base{}", tmp);
                self.push_str(&format!("let {} = {};\n", base, operands[0]));
                let result = format!("result{}", tmp);
                self.push_str(&format!(
                    "let mut {} = Vec::with_capacity({} as usize);\n",
                    result, len,
                ));

                self.push_str("for i in 0..");
                self.push_str(&len);
                self.push_str(" {\n");
                self.push_str("let base = ");
                self.push_str(&base);
                self.push_str(" + i *");
                self.push_str(&size.to_string());
                self.push_str(";\n");
                self.push_str(&result);
                self.push_str(".push(");
                self.push_str(&body);
                self.push_str(");\n");
                self.push_str("}\n");
                results.push(result);

                if let Some(free) = free {
                    self.call_intrinsic(free, format!("({}, {} * {}, {})", base, len, size, align));
                    self.needs_functions
                        .insert(free.to_string(), NeededFunction::Free);
                }
            }

            Instruction::IterElem { .. } => {
                self.caller_memory_available = false; // invalidated by for loop
                results.push("e".to_string())
            }

            Instruction::IterBasePointer => results.push("base".to_string()),

            Instruction::CallWasm {
                iface: _,
                name,
                sig,
            } => {
                if sig.results.len() > 0 {
                    let tmp = self.tmp();
                    self.push_str("let (");
                    for i in 0..sig.results.len() {
                        let arg = format!("result{}_{}", tmp, i);
                        self.push_str(&arg);
                        self.push_str(",");
                        results.push(arg);
                    }
                    self.push_str(") = ");
                }
                self.push_str("self.");
                self.push_str(&to_rust_ident(name));
                if self.gen.opts.async_.includes(name) {
                    self.push_str(".call_async(");
                } else {
                    self.push_str(".call(");
                }
                self.push_str("&mut caller, (");
                for operand in operands {
                    self.push_str(operand);
                    self.push_str(", ");
                }
                self.push_str("))");
                if self.gen.opts.async_.includes(name) {
                    self.push_str(".await");
                }
                self.push_str("?;\n");
                self.after_call = true;
                self.caller_memory_available = false; // invalidated by call
            }

            Instruction::CallWasmAsyncImport { .. } => unimplemented!(),
            Instruction::CallWasmAsyncExport { .. } => unimplemented!(),

            Instruction::CallInterface { module: _, func } => {
                for (i, operand) in operands.iter().enumerate() {
                    self.push_str(&format!("let param{} = {};\n", i, operand));
                }
                if self.gen.opts.tracing && func.params.len() > 0 {
                    self.push_str("wit_bindgen_wasmtime::tracing::event!(\n");
                    self.push_str("wit_bindgen_wasmtime::tracing::Level::TRACE,\n");
                    for (i, (name, _ty)) in func.params.iter().enumerate() {
                        self.push_str(&format!(
                            "{} = wit_bindgen_wasmtime::tracing::field::debug(&param{}),\n",
                            to_rust_ident(name),
                            i
                        ));
                    }
                    self.push_str(");\n");
                }

                let mut call = format!("host.{}(", func.name.to_snake_case());
                for i in 0..operands.len() {
                    call.push_str(&format!("param{}, ", i));
                }
                call.push_str(")");
                if self.gen.opts.async_.includes(&func.name) {
                    call.push_str(".await");
                }

                self.push_str("let result = ");
                results.push("result".to_string());
                match self.gen.classify_fn_ret(iface, func) {
                    FunctionRet::Normal => self.push_str(&call),
                    // Unwrap the result, translating errors to unconditional
                    // traps
                    FunctionRet::CustomToTrap => {
                        self.push_str("match ");
                        self.push_str(&call);
                        self.push_str("{\n");
                        self.push_str("Ok(val) => val,\n");
                        self.push_str("Err(e) => return Err(host.error_to_trap(e)),\n");
                        self.push_str("}");
                    }
                    // Keep the `Result` as a `Result`, but convert the error
                    // to either the expected destination value or a trap,
                    // propagating a trap outwards.
                    FunctionRet::CustomToError { err, .. } => {
                        self.push_str("match ");
                        self.push_str(&call);
                        self.push_str("{\n");
                        self.push_str("Ok(val) => Ok(val),\n");
                        self.push_str(&format!(
                            "Err(e) => Err(host.error_to_{}(e)?),\n",
                            err.to_snake_case()
                        ));
                        self.push_str("}");
                    }
                }
                self.push_str(";\n");
                self.after_call = true;
                match &func.result {
                    Type::Unit => {}
                    _ if self.gen.opts.tracing => {
                        self.push_str("wit_bindgen_wasmtime::tracing::event!(\n");
                        self.push_str("wit_bindgen_wasmtime::tracing::Level::TRACE,\n");
                        self.push_str(&format!(
                            "{} = wit_bindgen_wasmtime::tracing::field::debug(&{0}),\n",
                            results[0],
                        ));
                        self.push_str(");\n");
                    }
                    _ => {}
                }
            }

            Instruction::Return { amt, .. } => {
                let result = match amt {
                    0 => format!("Ok(())\n"),
                    1 => format!("Ok({})\n", operands[0]),
                    _ => format!("Ok(({}))\n", operands.join(", ")),
                };
                match self.cleanup.take() {
                    Some(cleanup) => {
                        self.push_str("let ret = ");
                        self.push_str(&result);
                        self.push_str(";\n");
                        self.push_str(&cleanup);
                        self.push_str("ret");
                    }
                    None => self.push_str(&result),
                }
            }

            Instruction::ReturnAsyncExport { .. } => unimplemented!(),
            Instruction::ReturnAsyncImport { .. } => unimplemented!(),

            Instruction::I32Load { offset } => results.push(self.load(*offset, "i32", operands)),
            Instruction::I32Load8U { offset } => {
                results.push(format!("i32::from({})", self.load(*offset, "u8", operands)));
            }
            Instruction::I32Load8S { offset } => {
                results.push(format!("i32::from({})", self.load(*offset, "i8", operands)));
            }
            Instruction::I32Load16U { offset } => {
                results.push(format!(
                    "i32::from({})",
                    self.load(*offset, "u16", operands)
                ));
            }
            Instruction::I32Load16S { offset } => {
                results.push(format!(
                    "i32::from({})",
                    self.load(*offset, "i16", operands)
                ));
            }
            Instruction::I64Load { offset } => results.push(self.load(*offset, "i64", operands)),
            Instruction::F32Load { offset } => results.push(self.load(*offset, "f32", operands)),
            Instruction::F64Load { offset } => results.push(self.load(*offset, "f64", operands)),

            Instruction::I32Store { offset } => self.store(*offset, "as_i32", "", operands),
            Instruction::I64Store { offset } => self.store(*offset, "as_i64", "", operands),
            Instruction::F32Store { offset } => self.store(*offset, "as_f32", "", operands),
            Instruction::F64Store { offset } => self.store(*offset, "as_f64", "", operands),
            Instruction::I32Store8 { offset } => self.store(*offset, "as_i32", " as u8", operands),
            Instruction::I32Store16 { offset } => {
                self.store(*offset, "as_i32", " as u16", operands)
            }

            Instruction::Malloc {
                realloc,
                size,
                align,
            } => {
                self.needs_functions
                    .insert(realloc.to_string(), NeededFunction::Realloc);
                let tmp = self.tmp();
                let ptr = format!("ptr{}", tmp);
                self.push_str(&format!("let {} = ", ptr));
                self.call_intrinsic(realloc, format!("(0, 0, {}, {})", align, size));
                results.push(ptr);
            }

            Instruction::Free { .. } => unimplemented!(),
        }
    }
}

impl NeededFunction {
    fn cvt(&self) -> &'static str {
        match self {
            NeededFunction::Realloc => "(i32, i32, i32, i32), i32",
            NeededFunction::Free => "(i32, i32, i32), ()",
        }
    }

    fn ty(&self) -> String {
        format!("wasmtime::TypedFunc<{}>", self.cvt())
    }
}

fn sorted_iter<K: Ord, V>(map: &HashMap<K, V>) -> impl Iterator<Item = (&K, &V)> {
    let mut list = map.into_iter().collect::<Vec<_>>();
    list.sort_by_key(|p| p.0);
    list.into_iter()
}
