use heck::*;
use std::collections::BTreeMap;
use std::io::{Read, Write};
use std::mem;
use std::process::{Command, Stdio};
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::{
    int_repr, wasm_type, FnSig, RustFlagsRepr, RustFunctionGenerator, RustGenerator, TypeMode,
};

#[derive(Default)]
pub struct RustWasm {
    src: Source,
    opts: Opts,
    types: Types,
    in_import: bool,
    traits: BTreeMap<String, Trait>,
    in_trait: bool,
    trait_name: String,
    return_pointer_area_size: usize,
    return_pointer_area_align: usize,
    sizes: SizeAlign,
}

#[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,

    /// Adds the wit module name into import binding names when enabled.
    #[cfg_attr(feature = "structopt", structopt(long))]
    pub multi_module: bool,

    /// Whether or not the bindings assume interface values are always
    /// well-formed or whether checks are performed.
    #[cfg_attr(feature = "structopt", structopt(long))]
    pub unchecked: bool,

    /// A prefix to prepend to all exported symbols. Note that this is only
    /// intended for testing because it breaks the general form of the ABI.
    #[cfg_attr(feature = "structopt", structopt(skip))]
    pub symbol_namespace: String,

    /// If true, the code generation is intended for standalone crates.
    ///
    /// Standalone mode generates bindings without a wrapping module.
    ///
    /// For exported interfaces, an `export!` macro is also generated
    /// that can be used to export an implementation from a different
    /// crate.
    #[cfg_attr(feature = "structopt", structopt(skip))]
    pub standalone: bool,
}

#[derive(Default)]
struct Trait {
    methods: Vec<String>,
    resource_methods: BTreeMap<ResourceId, Vec<String>>,
}

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

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

    fn abi_variant(dir: Direction) -> AbiVariant {
        // This generator uses the obvious direction to ABI variant mapping.
        match dir {
            Direction::Export => AbiVariant::GuestExport,
            Direction::Import => AbiVariant::GuestImport,
        }
    }

    fn ret_area_name(iface: &Interface) -> String {
        format!("{}_RET_AREA", iface.name.to_shouty_snake_case())
    }
}

impl RustGenerator for RustWasm {
    fn default_param_mode(&self) -> TypeMode {
        if self.in_import {
            // We default to borrowing as much as possible to maximize the ability
            // for host to take views into our memory without forcing wasm modules
            // to allocate anything.
            TypeMode::AllBorrowed("'a")
        } else {
            // In exports everythig is always owned, slices and handles and all.
            // Nothing is borrowed.
            TypeMode::Owned
        }
    }

    fn handle_projection(&self) -> Option<(&'static str, String)> {
        None
    }

    fn handle_in_super(&self) -> bool {
        !self.in_import
    }

    fn handle_wrapper(&self) -> Option<&'static str> {
        if self.in_import {
            None
        } else {
            Some("wit_bindgen_rust::Handle")
        }
    }

    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,
    ) {
        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 RustWasm {
    fn preprocess_one(&mut self, iface: &Interface, dir: Direction) {
        let variant = Self::abi_variant(dir);
        self.in_import = variant == AbiVariant::GuestImport;
        self.types.analyze(iface);
        self.trait_name = iface.name.to_camel_case();

        if !self.opts.standalone {
            self.src.push_str(&format!(
                "#[allow(clippy::all)]\nmod {} {{\n",
                iface.name.to_snake_case(),
            ));
        }

        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);
    }

    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_rust::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");

        // Add a `from_bits_preserve` method.
        self.src
            .push_str(&format!("impl {} {{\n", name.to_camel_case()));
        self.src.push_str(&format!(
            "    /// Convert from a raw integer, preserving any unknown bits. See\n"
        ));
        self.src.push_str(&format!(
            "    /// <https://github.com/bitflags/bitflags/issues/263#issuecomment-957088321>\n"
        ));
        self.src.push_str(&format!(
            "    pub fn from_bits_preserve(bits: {repr}) -> Self {{\n",
        ));
        self.src.push_str(&format!("        Self {{ bits }}\n"));
        self.src.push_str(&format!("    }}\n"));
        self.src.push_str(&format!("}}\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) {
        // For exported handles we synthesize some trait implementations
        // automatically for runtime-required traits.
        if !self.in_import {
            let panic = "
                #[cfg(not(target_arch = \"wasm32\"))]
                {
                    panic!(\"handles can only be used on wasm32\");
                }
                #[cfg(target_arch = \"wasm32\")]
            ";
            self.src.push_str(&format!(
                "
                    unsafe impl wit_bindgen_rust::HandleType for super::{ty} {{
                        #[inline]
                        fn clone(_val: i32) -> i32 {{
                            {panic_not_wasm}
                            {{
                                #[link(wasm_import_module = \"canonical_abi\")]
                                extern \"C\" {{
                                    #[link_name = \"resource_clone_{name}\"]
                                    fn clone(val: i32) -> i32;
                                }}
                                unsafe {{ clone(_val) }}
                            }}
                        }}

                        #[inline]
                        fn drop(_val: i32) {{
                            {panic_not_wasm}
                            {{
                                #[link(wasm_import_module = \"canonical_abi\")]
                                extern \"C\" {{
                                    #[link_name = \"resource_drop_{name}\"]
                                    fn drop(val: i32);
                                }}
                                unsafe {{ drop(_val) }}
                            }}
                        }}
                    }}

                    unsafe impl wit_bindgen_rust::LocalHandle for super::{ty} {{
                        #[inline]
                        fn new(_val: i32) -> i32 {{
                            {panic_not_wasm}
                            {{
                                #[link(wasm_import_module = \"canonical_abi\")]
                                extern \"C\" {{
                                    #[link_name = \"resource_new_{name}\"]
                                    fn new(val: i32) -> i32;
                                }}
                                unsafe {{ new(_val) }}
                            }}
                        }}

                        #[inline]
                        fn get(_val: i32) -> i32 {{
                            {panic_not_wasm}
                            {{
                                #[link(wasm_import_module = \"canonical_abi\")]
                                extern \"C\" {{
                                    #[link_name = \"resource_get_{name}\"]
                                    fn get(val: i32) -> i32;
                                }}
                                unsafe {{ get(_val) }}
                            }}
                        }}
                    }}

                    const _: () = {{
                        #[export_name = \"{ns}canonical_abi_drop_{name}\"]
                        extern \"C\" fn drop(ty: Box<super::{ty}>) {{
                            <super::{iface} as {iface}>::drop_{name_snake}(*ty)
                        }}
                    }};
                ",
                ty = iface.resources[ty].name.to_camel_case(),
                name = iface.resources[ty].name,
                name_snake = iface.resources[ty].name.to_snake_case(),
                iface = iface.name.to_camel_case(),
                ns = self.opts.symbol_namespace,
                panic_not_wasm = panic,
            ));
            let trait_ = self
                .traits
                .entry(iface.name.to_camel_case())
                .or_insert(Trait::default());
            trait_.methods.push(format!(
                "
                    /// An optional callback invoked when a handle is finalized
                    /// and destroyed.
                    fn drop_{}(val: super::{}) {{
                        drop(val);
                    }}
                ",
                iface.resources[ty].name.to_snake_case(),
                iface.resources[ty].name.to_camel_case(),
            ));
            return;
        }

        let resource = &iface.resources[ty];
        let name = &resource.name;

        self.rustdoc(&resource.docs);
        self.src.push_str("#[derive(Debug)]\n");
        self.src.push_str("#[repr(transparent)]\n");
        self.src
            .push_str(&format!("pub struct {}(i32);\n", name.to_camel_case()));
        self.src.push_str("impl ");
        self.src.push_str(&name.to_camel_case());
        self.src.push_str(
            " {
                pub unsafe fn from_raw(raw: i32) -> Self {
                    Self(raw)
                }

                pub fn into_raw(self) -> i32 {
                    let ret = self.0;
                    core::mem::forget(self);
                    return ret;
                }

                pub fn as_raw(&self) -> i32 {
                    self.0
                }
            }\n",
        );

        self.src.push_str("impl Drop for ");
        self.src.push_str(&name.to_camel_case());
        self.src.push_str(&format!(
            "{{
                fn drop(&mut self) {{
                    #[link(wasm_import_module = \"canonical_abi\")]
                    extern \"C\" {{
                        #[link_name = \"resource_drop_{}\"]
                        fn close(fd: i32);
                    }}
                    unsafe {{
                        close(self.0);
                    }}
                }}
            }}\n",
            name,
        ));

        self.src.push_str("impl Clone for ");
        self.src.push_str(&name.to_camel_case());
        self.src.push_str(&format!(
            "{{
                fn clone(&self) -> Self {{
                    #[link(wasm_import_module = \"canonical_abi\")]
                    extern \"C\" {{
                        #[link_name = \"resource_clone_{}\"]
                        fn clone(val: i32) -> i32;
                    }}
                    unsafe {{
                        Self(clone(self.0))
                    }}
                }}
            }}\n",
            name,
        ));
    }

    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");
    }

    fn preprocess_functions(&mut self, _iface: &Interface, dir: Direction) {
        if self.opts.standalone && dir == Direction::Export {
            self.src.push_str(
                "/// Declares the export of the interface for the given type.\n\
                 #[macro_export]\n\
                 macro_rules! export(($t:ident) => {\n",
            );
        }
    }

    fn import(&mut self, iface: &Interface, func: &Function) {
        let mut sig = FnSig::default();
        let param_mode = TypeMode::AllBorrowed("'_");
        sig.async_ = func.is_async;
        match &func.kind {
            FunctionKind::Freestanding => {}
            FunctionKind::Static { resource, .. } | FunctionKind::Method { resource, .. } => {
                sig.use_item_name = true;
                self.src.push_str(&format!(
                    "impl {} {{\n",
                    iface.resources[*resource].name.to_camel_case()
                ));
            }
        }
        if let FunctionKind::Method { .. } = func.kind {
            sig.self_arg = Some("&self".to_string());
            sig.self_is_first_param = true;
        }
        let params = self.print_signature(iface, func, param_mode, &sig);
        self.src.push_str("{\n");
        self.src.push_str("unsafe {\n");

        let mut f = FunctionBindgen::new(self, params);
        iface.call(
            AbiVariant::GuestImport,
            LiftLower::LowerArgsLiftResults,
            func,
            &mut f,
        );
        let FunctionBindgen {
            needs_cleanup_list,
            src,
            ..
        } = f;

        if needs_cleanup_list {
            self.src.push_str("let mut cleanup_list = Vec::new();\n");
        }
        self.src.push_str(&String::from(src));

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

        match &func.kind {
            FunctionKind::Freestanding => {}
            FunctionKind::Static { .. } | FunctionKind::Method { .. } => {
                self.src.push_str("}\n");
            }
        }
    }

    fn export(&mut self, iface: &Interface, func: &Function) {
        let iface_name = iface.name.to_snake_case();

        self.src.push_str("#[export_name = \"");
        match &iface.module {
            Some(module) => {
                self.src.push_str(module);
                self.src.push_str("#");
                self.src.push_str(&func.name);
            }
            None => {
                self.src.push_str(&self.opts.symbol_namespace);
                self.src.push_str(&func.name);
            }
        }
        self.src.push_str("\"]\n");
        self.src.push_str("unsafe extern \"C\" fn __wit_bindgen_");
        self.src.push_str(&iface_name);
        self.src.push_str("_");
        self.src.push_str(&func.name.to_snake_case());
        self.src.push_str("(");
        let sig = iface.wasm_signature(AbiVariant::GuestExport, func);
        let mut params = Vec::new();
        for (i, param) in sig.params.iter().enumerate() {
            let name = format!("arg{}", i);
            self.src.push_str(&name);
            self.src.push_str(": ");
            self.wasm_type(*param);
            self.src.push_str(", ");
            params.push(name);
        }
        self.src.push_str(")");

        match sig.results.len() {
            0 => {}
            1 => {
                self.src.push_str(" -> ");
                self.wasm_type(sig.results[0]);
            }
            _ => unimplemented!(),
        }

        self.push_str("{\n");

        if self.opts.standalone {
            // Force the macro code to reference wit_bindgen_rust for standalone crates.
            // Also ensure any referenced types are also used from the external crate.
            self.src
                .push_str("#[allow(unused_imports)]\nuse wit_bindgen_rust;\nuse ");
            self.src.push_str(&iface_name);
            self.src.push_str("::*;\n");
        }

        if func.is_async {
            self.src.push_str("let future = async move {\n");
        }

        let mut f = FunctionBindgen::new(self, params);
        iface.call(
            AbiVariant::GuestExport,
            LiftLower::LiftArgsLowerResults,
            func,
            &mut f,
        );
        let FunctionBindgen {
            needs_cleanup_list,
            src,
            ..
        } = f;
        assert!(!needs_cleanup_list);
        self.src.push_str(&String::from(src));
        if func.is_async {
            self.src.push_str("};\n");
            self.src
                .push_str("wit_bindgen_rust::rt::execute(Box::pin(future));\n");
        }
        self.src.push_str("}\n");

        let prev = mem::take(&mut self.src);
        self.in_trait = true;
        let mut sig = FnSig::default();
        sig.private = true;
        sig.async_ = func.is_async;
        match &func.kind {
            FunctionKind::Freestanding => {}
            FunctionKind::Static { .. } => sig.use_item_name = true,
            FunctionKind::Method { .. } => {
                sig.use_item_name = true;
                sig.self_is_first_param = true;
                sig.self_arg = Some("&self".to_string());
            }
        }
        self.print_signature(iface, func, TypeMode::Owned, &sig);
        self.src.push_str(";");
        self.in_trait = false;
        let trait_ = self
            .traits
            .entry(iface.name.to_camel_case())
            .or_insert(Trait::default());
        let dst = match &func.kind {
            FunctionKind::Freestanding => &mut trait_.methods,
            FunctionKind::Static { resource, .. } | FunctionKind::Method { resource, .. } => trait_
                .resource_methods
                .entry(*resource)
                .or_insert(Vec::new()),
        };
        dst.push(mem::replace(&mut self.src, prev).into());
    }

    fn finish_functions(&mut self, iface: &Interface, dir: Direction) {
        if self.return_pointer_area_align > 0 {
            self.src.push_str(&format!(
                "
                    #[repr(align({align}))]
                    struct RetArea([u8; {size}]);
                    static mut {name}: RetArea = RetArea([0; {size}]);
                ",
                name = Self::ret_area_name(iface),
                align = self.return_pointer_area_align,
                size = self.return_pointer_area_size,
            ));
        }

        // For standalone generation, close the export! macro
        if self.opts.standalone && dir == Direction::Export {
            self.src.push_str("});\n");
        }
    }

    fn finish_one(&mut self, iface: &Interface, files: &mut Files) {
        let mut src = mem::take(&mut self.src);

        let any_async = iface.functions.iter().any(|f| f.is_async);
        for (name, trait_) in self.traits.iter() {
            if any_async {
                src.push_str("#[wit_bindgen_rust::async_trait(?Send)]\n");
            }
            src.push_str("pub trait ");
            src.push_str(&name);
            src.push_str(" {\n");
            for f in trait_.methods.iter() {
                src.push_str(&f);
                src.push_str("\n");
            }
            src.push_str("}\n");

            for (id, methods) in trait_.resource_methods.iter() {
                if any_async {
                    src.push_str("#[wit_bindgen_rust::async_trait(?Send)]\n");
                }
                src.push_str(&format!(
                    "pub trait {} {{\n",
                    iface.resources[*id].name.to_camel_case()
                ));
                for f in methods {
                    src.push_str(&f);
                    src.push_str("\n");
                }
                src.push_str("}\n");
            }
        }

        // Close the opening `mod`.
        if !self.opts.standalone {
            src.push_str("}\n");
        }

        if self.opts.rustfmt {
            let mut child = Command::new("rustfmt")
                .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 RustWasm,
    params: Vec<String>,
    src: Source,
    blocks: Vec<String>,
    block_storage: Vec<(Source, Vec<(String, String)>)>,
    tmp: usize,
    needs_cleanup_list: bool,
    cleanup: Vec<(String, String)>,
}

impl FunctionBindgen<'_> {
    fn new(gen: &mut RustWasm, params: Vec<String>) -> FunctionBindgen<'_> {
        FunctionBindgen {
            gen,
            params,
            src: Default::default(),
            blocks: Vec::new(),
            block_storage: Vec::new(),
            tmp: 0,
            needs_cleanup_list: false,
            cleanup: Vec::new(),
        }
    }

    fn emit_cleanup(&mut self) {
        for (ptr, layout) in mem::take(&mut self.cleanup) {
            self.push_str(&format!("std::alloc::dealloc({}, {});\n", ptr, layout));
        }
        if self.needs_cleanup_list {
            self.push_str(
                "for (ptr, layout) in cleanup_list {
                    std::alloc::dealloc(ptr, layout);
                }\n",
            );
        }
    }

    fn declare_import(
        &mut self,
        iface: &Interface,
        name: &str,
        params: &[WasmType],
        results: &[WasmType],
    ) -> String {
        let module = iface.module.as_deref().unwrap_or(&iface.name);

        // Define the actual function we're calling inline
        self.push_str("#[link(wasm_import_module = \"");
        self.push_str(module);
        self.push_str("\")]\n");
        self.push_str("extern \"C\" {\n");
        self.push_str("#[cfg_attr(target_arch = \"wasm32\", link_name = \"");
        self.push_str(name);
        self.push_str("\")]\n");
        self.push_str("#[cfg_attr(not(target_arch = \"wasm32\"), link_name = \"");
        self.push_str(module);
        self.push_str("_");
        self.push_str(name);
        self.push_str("\")]\n");
        self.push_str("fn wit_import(");
        for param in params.iter() {
            self.push_str("_: ");
            self.push_str(wasm_type(*param));
            self.push_str(", ");
        }
        self.push_str(")");
        assert!(results.len() < 2);
        for result in results.iter() {
            self.push_str(" -> ");
            self.push_str(wasm_type(*result));
        }
        self.push_str(";\n}\n");
        "wit_import".to_string()
    }
}

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::LowerArgsLiftResults
        } else {
            LiftLower::LiftArgsLowerResults
        }
    }
}

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

    fn push_block(&mut self) {
        let prev_src = mem::take(&mut self.src);
        let prev_cleanup = mem::take(&mut self.cleanup);
        self.block_storage.push((prev_src, prev_cleanup));
    }

    fn finish_block(&mut self, operands: &mut Vec<String>) {
        if self.cleanup.len() > 0 {
            self.needs_cleanup_list = true;
            self.push_str("cleanup_list.extend_from_slice(&[");
            for (ptr, layout) in mem::take(&mut self.cleanup) {
                self.push_str("(");
                self.push_str(&ptr);
                self.push_str(", ");
                self.push_str(&layout);
                self.push_str("),");
            }
            self.push_str("]);\n");
        }
        let (prev_src, prev_cleanup) = self.block_storage.pop().unwrap();
        let src = mem::replace(&mut self.src, prev_src);
        self.cleanup = prev_cleanup;
        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{}\n}}", &src[..]));
        } else {
            self.blocks.push(format!("{{\n{}\n{}\n}}", &src[..], expr));
        }
    }

    fn return_pointer(&mut self, iface: &Interface, size: usize, align: usize) -> String {
        self.gen.return_pointer_area_size = self.gen.return_pointer_area_size.max(size);
        self.gen.return_pointer_area_align = self.gen.return_pointer_area_align.max(align);
        let tmp = self.tmp();

        self.push_str(&format!(
            "let ptr{} = {}.0.as_mut_ptr() as i32;\n",
            tmp,
            RustWasm::ret_area_name(iface),
        ));
        format!("ptr{}", tmp)
    }

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

    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 unchecked = self.gen.opts.unchecked;
        let mut top_as = |cvt: &str| {
            let mut s = operands.pop().unwrap();
            s.push_str(" as ");
            s.push_str(cvt);
            results.push(s);
        };

        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_rust::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_rust::rt::as_i32({})", s));
            }

            Instruction::F32FromFloat32 => {
                let s = operands.pop().unwrap();
                results.push(format!("wit_bindgen_rust::rt::as_f32({})", s));
            }
            Instruction::F64FromFloat64 => {
                let s = operands.pop().unwrap();
                results.push(format!("wit_bindgen_rust::rt::as_f64({})", s));
            }
            Instruction::Float32FromF32
            | Instruction::Float64FromF64
            | Instruction::S32FromI32
            | Instruction::S64FromI64 => {
                results.push(operands.pop().unwrap());
            }
            Instruction::S8FromI32 => top_as("i8"),
            Instruction::U8FromI32 => top_as("u8"),
            Instruction::S16FromI32 => top_as("i16"),
            Instruction::U16FromI32 => top_as("u16"),
            Instruction::U32FromI32 => top_as("u32"),
            Instruction::U64FromI64 => top_as("u64"),
            Instruction::CharFromI32 => {
                if unchecked {
                    results.push(format!(
                        "core::char::from_u32_unchecked({} as u32)",
                        operands[0]
                    ));
                } else {
                    results.push(format!(
                        "core::char::from_u32({} as u32).unwrap()",
                        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 => {
                if unchecked {
                    results.push(format!(
                        "core::mem::transmute::<u8, bool>({} as u8)",
                        operands[0],
                    ));
                } else {
                    results.push(format!(
                        "match {} {{
                            0 => false,
                            1 => true,
                            _ => panic!(\"invalid bool discriminant\"),
                        }}",
                        operands[0],
                    ));
                }
            }

            // handles in exports
            Instruction::I32FromOwnedHandle { .. } => {
                results.push(format!(
                    "wit_bindgen_rust::Handle::into_raw({})",
                    operands[0]
                ));
            }
            Instruction::HandleBorrowedFromI32 { .. } => {
                results.push(format!(
                    "wit_bindgen_rust::Handle::from_raw({})",
                    operands[0],
                ));
            }

            // handles in imports
            Instruction::I32FromBorrowedHandle { .. } => {
                results.push(format!("{}.0", operands[0]));
            }
            Instruction::HandleOwnedFromI32 { ty } => {
                results.push(format!(
                    "{}({})",
                    iface.resources[*ty].name.to_camel_case(),
                    operands[0]
                ));
            }

            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 { name, flags, .. } => {
                let repr = RustFlagsRepr::new(flags);
                let name = name.to_camel_case();
                let mut result = format!("{}::empty()", name);
                for (i, op) in operands.iter().enumerate() {
                    result.push_str(&format!(
                        " | {}::from_bits_preserve((({} as {repr}) << {}) as _)",
                        name,
                        op,
                        i * 32
                    ));
                }
                results.push(result);
            }

            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::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");
            }

            // In unchecked mode when this type is a named enum then we know we
            // defined the type so we can transmute directly into it.
            Instruction::VariantLift { name, variant, .. }
                if variant.cases.iter().all(|c| c.ty == Type::Unit) && unchecked =>
            {
                self.blocks.drain(self.blocks.len() - variant.cases.len()..);
                let mut result = format!("core::mem::transmute::<_, ");
                result.push_str(&name.to_camel_case());
                result.push_str(">(");
                result.push_str(&operands[0]);
                result.push_str(" as ");
                result.push_str(int_repr(variant.tag()));
                result.push_str(")");
                results.push(result);
            }

            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 pat = if i == variant.cases.len() - 1 && unchecked {
                        String::from("_")
                    } else {
                        i.to_string()
                    };
                    let block = if case.ty != Type::Unit {
                        format!("({block})")
                    } else {
                        String::new()
                    };
                    let case = case.name.to_camel_case();
                    result.push_str(&format!("{pat} => {name}::{case}{block},\n"));
                }
                if !unchecked {
                    result.push_str("_ => panic!(\"invalid enum discriminant\"),\n");
                }
                result.push_str("}");
                results.push(result);
            }

            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");
                for (i, (case_name, block)) in self
                    .gen
                    .union_case_names(iface, union)
                    .into_iter()
                    .zip(blocks)
                    .enumerate()
                {
                    let pat = if i == union.cases.len() - 1 && unchecked {
                        String::from("_")
                    } else {
                        i.to_string()
                    };
                    let name = self.typename_lift(iface, *ty);
                    result.push_str(&format!("{pat} => {name}::{case_name}({block}),\n"));
                }
                if !unchecked {
                    result.push_str("_ => panic!(\"invalid union discriminant\"),\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];
                let invalid = if unchecked {
                    "std::hint::unreachable_unchecked()"
                } else {
                    "panic!(\"invalid enum discriminant\")"
                };
                results.push(format!(
                    "match {operand} {{
                        0 => None,
                        1 => Some({some}),
                        _ => {invalid},
                    }}"
                ));
            }

            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];
                let invalid = if unchecked {
                    "std::hint::unreachable_unchecked()"
                } else {
                    "panic!(\"invalid enum discriminant\")"
                };
                results.push(format!(
                    "match {operand} {{
                        0 => Ok({ok}),
                        1 => Err({err}),
                        _ => {invalid},
                    }}"
                ));
            }

            Instruction::EnumLower { enum_, name, .. } => {
                let mut result = format!("match {} {{\n", operands[0]);
                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!("{name}::{case} => {i},\n"));
                }
                result.push_str("}");
                results.push(result);
            }

            // In unchecked mode when this type is a named enum then we know we
            // defined the type so we can transmute directly into it.
            Instruction::EnumLift { enum_, name, .. } if unchecked => {
                let mut result = format!("core::mem::transmute::<_, ");
                result.push_str(&name.to_camel_case());
                result.push_str(">(");
                result.push_str(&operands[0]);
                result.push_str(" as ");
                result.push_str(int_repr(enum_.tag()));
                result.push_str(")");
                results.push(result);
            }

            Instruction::EnumLift { enum_, name, .. } => {
                let mut result = format!("match ");
                result.push_str(&operands[0]);
                result.push_str(" {\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("_ => panic!(\"invalid enum discriminant\"),\n");
                result.push_str("}");
                results.push(result);
            }

            Instruction::ListCanonLower { realloc, .. } => {
                let tmp = self.tmp();
                let val = format!("vec{}", tmp);
                let ptr = format!("ptr{}", tmp);
                let len = format!("len{}", tmp);
                if realloc.is_none() {
                    self.push_str(&format!("let {} = {};\n", val, operands[0]));
                } else {
                    let op0 = operands.pop().unwrap();
                    self.push_str(&format!("let {} = ({}).into_boxed_slice();\n", val, op0));
                }
                self.push_str(&format!("let {} = {}.as_ptr() as i32;\n", ptr, val));
                self.push_str(&format!("let {} = {}.len() as i32;\n", len, val));
                if realloc.is_some() {
                    self.push_str(&format!("core::mem::forget({});\n", val));
                }
                results.push(ptr);
                results.push(len);
            }

            Instruction::ListCanonLift { free, .. } => {
                // This only happens when we're receiving a list from the
                // outside world, so `free` should always be `Some`.
                assert!(free.is_some());
                let tmp = self.tmp();
                let len = format!("len{}", tmp);
                self.push_str(&format!("let {} = {} as usize;\n", len, operands[1]));
                let result = format!(
                    "Vec::from_raw_parts({} as *mut _, {1}, {1})",
                    operands[0], len
                );
                results.push(result);
            }

            Instruction::StringLower { realloc } => {
                let tmp = self.tmp();
                let val = format!("vec{}", tmp);
                let ptr = format!("ptr{}", tmp);
                let len = format!("len{}", tmp);
                if realloc.is_none() {
                    self.push_str(&format!("let {} = {};\n", val, operands[0]));
                } else {
                    let op0 = format!("{}.into_bytes()", operands[0]);
                    self.push_str(&format!("let {} = ({}).into_boxed_slice();\n", val, op0));
                }
                self.push_str(&format!("let {} = {}.as_ptr() as i32;\n", ptr, val));
                self.push_str(&format!("let {} = {}.len() as i32;\n", len, val));
                if realloc.is_some() {
                    self.push_str(&format!("core::mem::forget({});\n", val));
                }
                results.push(ptr);
                results.push(len);
            }

            Instruction::StringLift { free, .. } => {
                // This only happens when we're receiving a string from the
                // outside world, so `free` should always be `Some`.
                assert!(free.is_some());
                let tmp = self.tmp();
                let len = format!("len{}", tmp);
                self.push_str(&format!("let {} = {} as usize;\n", len, operands[1]));
                let result = format!(
                    "Vec::from_raw_parts({} as *mut _, {1}, {1})",
                    operands[0], len
                );
                if unchecked {
                    results.push(format!("String::from_utf8_unchecked({})", result));
                } else {
                    results.push(format!("String::from_utf8({}).unwrap()", result));
                }
            }

            Instruction::ListLower { element, realloc } => {
                let body = self.blocks.pop().unwrap();
                let tmp = self.tmp();
                let vec = format!("vec{}", tmp);
                let result = format!("result{}", tmp);
                let layout = format!("layout{}", tmp);
                let len = format!("len{}", tmp);
                self.push_str(&format!("let {} = {};\n", vec, operands[0]));
                self.push_str(&format!("let {} = {}.len() as i32;\n", len, vec));
                let size = self.gen.sizes.size(element);
                let align = self.gen.sizes.align(element);
                self.push_str(&format!(
                    "let {} = core::alloc::Layout::from_size_align_unchecked({}.len() * {}, {});\n",
                    layout, vec, size, align,
                ));
                self.push_str(&format!(
                    "let {} = std::alloc::alloc({});\n",
                    result, layout,
                ));
                self.push_str(&format!(
                    "if {}.is_null() {{ std::alloc::handle_alloc_error({}); }}\n",
                    result, layout,
                ));
                self.push_str(&format!(
                    "for (i, e) in {}.into_iter().enumerate() {{\n",
                    vec
                ));
                self.push_str(&format!(
                    "let base = {} as i32 + (i as i32) * {};\n",
                    result, size,
                ));
                self.push_str(&body);
                self.push_str("}\n");
                results.push(format!("{} as i32", result));
                results.push(len);

                if realloc.is_none() {
                    // If an allocator isn't requested then we must clean up the
                    // allocation ourselves since our callee isn't taking
                    // ownership.
                    self.cleanup.push((result, layout));
                }
            }

            Instruction::ListLift { element, free, .. } => {
                // This only happens when we're receiving a list from the
                // outside world, so `free` should always be `Some`.
                assert!(free.is_some());
                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);
                let base = format!("base{}", tmp);
                let result = format!("result{}", tmp);
                self.push_str(&format!("let {} = {};\n", base, operands[0]));
                self.push_str(&format!("let {} = {};\n", len, operands[1],));
                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);
                self.push_str(&format!(
                    "std::alloc::dealloc(
                        {} as *mut _,
                        std::alloc::Layout::from_size_align_unchecked(
                            ({} as usize) * {},
                            {},
                        ),
                    );\n",
                    base, len, size, align
                ));
            }

            Instruction::IterElem { .. } => results.push("e".to_string()),

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

            Instruction::CallWasm { iface, name, sig } => {
                let func = self.declare_import(iface, name, &sig.params, &sig.results);

                // ... then call the function with all our operands
                if sig.results.len() > 0 {
                    self.push_str("let ret = ");
                    results.push("ret".to_string());
                }
                self.push_str(&func);
                self.push_str("(");
                self.push_str(&operands.join(", "));
                self.push_str(");\n");
            }

            Instruction::CallWasmAsyncImport {
                iface,
                name,
                params: wasm_params,
                results: wasm_results,
            } => {
                // The first thing we do here is define the completion callback
                // which the host will invoke when the asynchronous call
                // actually finishes. This receives our own custom state
                // parameter as the first parameter which is the `Sender`
                // converted to a `usize`. Afterwards it receives all the
                // results which we'll transfer ove the `sender`, the canonical
                // ABI of the results.
                self.push_str("unsafe extern \"C\" fn completion_callback(sender: usize");
                for (i, result) in wasm_results.iter().enumerate() {
                    self.push_str(", ");
                    self.push_str(&format!("ret{}: ", i));
                    self.push_str(wasm_type(*result));
                }
                self.push_str(") {\n");
                self.push_str("wit_bindgen_rust::rt::Sender::from_usize(sender).send((");
                for i in 0..wasm_results.len() {
                    self.push_str(&format!("ret{},", i));
                }
                self.push_str("));\n");
                self.push_str("}\n");

                // Next we create the future channel which will be used to track
                // the state of this import. The "oneshot" here means that the
                // sender (`tx`) will send something once over `rx`. The type of
                // the `Oneshot` is the type of the `wasm_results` which is the
                // canonical ABI of the results that this function produces.
                self.push_str("let (rx, tx) = wit_bindgen_rust::rt::Oneshot::<(");
                for ty in *wasm_results {
                    self.push_str(wasm_type(*ty));
                    self.push_str(", ");
                }
                self.push_str(")>::new();\n");

                // Then we can actually call the function now that we have
                // all the parameters. The first parameters to the import are
                // the canonical ABI `operands` we were provided, and the last
                // two arguments are our completion callback and the context for
                // the callback, our `tx` sender.
                let func = self.declare_import(iface, name, wasm_params, &[]);
                self.push_str(&func);
                self.push_str("(");
                for op in operands {
                    self.push_str(op);
                    self.push_str(", ");
                }
                self.push_str("completion_callback as i32, ");
                self.push_str("tx.into_usize() as i32");
                self.push_str(");\n");

                // And finally we want to "appear synchronous" with an async
                // function, so we immediately `.await` the results of the
                // oneshot. This binds all the canonical ABI results to then get
                // translated in further instructions to the result of this
                // function call.
                let tmp = self.tmp();
                self.push_str("let (");
                for i in 0..wasm_results.len() {
                    let name = format!("ret{}_{}", tmp, i);
                    self.push_str(&name);
                    self.push_str(",");
                    results.push(name);
                }
                self.push_str(") = rx.await;\n");
            }

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

            Instruction::CallInterface { module, func } => {
                self.push_str("let result = ");
                results.push("result".to_string());
                match &func.kind {
                    FunctionKind::Freestanding => {
                        if self.gen.opts.standalone {
                            // For standalone mode, use the macro identifier
                            self.push_str(&format!(
                                "<$t as {t}>::{}",
                                func.name.to_snake_case(),
                                t = module.to_camel_case(),
                            ));
                        } else {
                            self.push_str(&format!(
                                "<super::{m} as {m}>::{}",
                                func.name.to_snake_case(),
                                m = module.to_camel_case()
                            ));
                        }
                    }
                    FunctionKind::Static { resource, name }
                    | FunctionKind::Method { resource, name } => {
                        self.push_str(&format!(
                            "<super::{r} as {r}>::{}",
                            name.to_snake_case(),
                            r = iface.resources[*resource].name.to_camel_case(),
                        ));
                    }
                }
                self.push_str("(");
                if let FunctionKind::Method { .. } = func.kind {
                    self.push_str("&");
                }
                self.push_str(&operands.join(", "));
                self.push_str(")");
                if func.is_async {
                    self.push_str(".await");
                }
                self.push_str(";\n");
            }

            Instruction::Return { amt, .. } => {
                self.emit_cleanup();
                match amt {
                    0 => {}
                    1 => {
                        self.push_str(&operands[0]);
                        self.push_str("\n");
                    }
                    _ => {
                        self.push_str("(");
                        self.push_str(&operands.join(", "));
                        self.push_str(")\n");
                    }
                }
            }

            Instruction::ReturnAsyncExport { .. } => {
                self.emit_cleanup();
                self.push_str(&format!(
                    "wit_bindgen_rust::rt::async_export_done({}, {});\n",
                    operands[0], operands[1]
                ));
            }
            Instruction::ReturnAsyncImport { .. } => unreachable!(),

            Instruction::I32Load { offset } => {
                results.push(format!("*(({} + {}) as *const i32)", operands[0], offset));
            }
            Instruction::I32Load8U { offset } => {
                results.push(format!(
                    "i32::from(*(({} + {}) as *const u8))",
                    operands[0], offset
                ));
            }
            Instruction::I32Load8S { offset } => {
                results.push(format!(
                    "i32::from(*(({} + {}) as *const i8))",
                    operands[0], offset
                ));
            }
            Instruction::I32Load16U { offset } => {
                results.push(format!(
                    "i32::from(*(({} + {}) as *const u16))",
                    operands[0], offset
                ));
            }
            Instruction::I32Load16S { offset } => {
                results.push(format!(
                    "i32::from(*(({} + {}) as *const i16))",
                    operands[0], offset
                ));
            }
            Instruction::I64Load { offset } => {
                results.push(format!("*(({} + {}) as *const i64)", operands[0], offset));
            }
            Instruction::F32Load { offset } => {
                results.push(format!("*(({} + {}) as *const f32)", operands[0], offset));
            }
            Instruction::F64Load { offset } => {
                results.push(format!("*(({} + {}) as *const f64)", operands[0], offset));
            }
            Instruction::I32Store { offset } => {
                self.push_str(&format!(
                    "*(({} + {}) as *mut i32) = {};\n",
                    operands[1], offset, operands[0]
                ));
            }
            Instruction::I32Store8 { offset } => {
                self.push_str(&format!(
                    "*(({} + {}) as *mut u8) = ({}) as u8;\n",
                    operands[1], offset, operands[0]
                ));
            }
            Instruction::I32Store16 { offset } => {
                self.push_str(&format!(
                    "*(({} + {}) as *mut u16) = ({}) as u16;\n",
                    operands[1], offset, operands[0]
                ));
            }
            Instruction::I64Store { offset } => {
                self.push_str(&format!(
                    "*(({} + {}) as *mut i64) = {};\n",
                    operands[1], offset, operands[0]
                ));
            }
            Instruction::F32Store { offset } => {
                self.push_str(&format!(
                    "*(({} + {}) as *mut f32) = {};\n",
                    operands[1], offset, operands[0]
                ));
            }
            Instruction::F64Store { offset } => {
                self.push_str(&format!(
                    "*(({} + {}) as *mut f64) = {};\n",
                    operands[1], offset, operands[0]
                ));
            }

            Instruction::Malloc { .. } => unimplemented!(),
            Instruction::Free {
                free: _,
                size,
                align,
            } => {
                self.push_str(&format!(
                    "wit_bindgen_rust::rt::canonical_abi_free({} as *mut u8, {}, {});\n",
                    operands[0], size, align
                ));
            }
        }
    }
}
