Skip to main content

js_component_bindgen/
transpile_bindgen.rs

1use std::cell::RefCell;
2use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
3use std::fmt::Write;
4use std::mem;
5use std::ops::Index;
6
7use base64::Engine as _;
8use base64::engine::general_purpose;
9use heck::{ToKebabCase, ToLowerCamelCase, ToUpperCamelCase};
10use semver::Version;
11use wasmtime_environ::component::{
12    CanonicalOptions, CanonicalOptionsDataModel, Component, ComponentTranslation, ComponentTypes,
13    CoreDef, CoreExport, Export, ExportItem, FixedEncoding, GlobalInitializer, InstantiateModule,
14    InterfaceType, LinearMemoryOptions, LoweredIndex, ResourceIndex, RuntimeComponentInstanceIndex,
15    RuntimeImportIndex, RuntimeInstanceIndex, StaticModuleIndex, Trampoline, TrampolineIndex,
16    TypeDef, TypeFuncIndex, TypeResourceTableIndex, TypeStreamTableIndex,
17};
18use wasmtime_environ::component::{
19    ExportIndex, ExtractCallback, NameMap, NameMapNoIntern, Transcode,
20    TypeComponentLocalErrorContextTableIndex,
21};
22use wasmtime_environ::{EntityIndex, PrimaryMap};
23use wit_bindgen_core::abi::{self, LiftLower};
24use wit_component::StringEncoding;
25use wit_parser::abi::AbiVariant;
26use wit_parser::{
27    Function, FunctionKind, Handle, Resolve, Result_, SizeAlign, Type, TypeDefKind, TypeId,
28    WorldId, WorldItem, WorldKey,
29};
30
31use crate::esm_bindgen::EsmBindgen;
32use crate::files::Files;
33use crate::function_bindgen::{
34    ErrHandling, FunctionBindgen, ResourceData, ResourceExtraData, ResourceMap, ResourceTable,
35};
36use crate::intrinsics::component::ComponentIntrinsic;
37use crate::intrinsics::js_helper::JsHelperIntrinsic;
38use crate::intrinsics::lift::LiftIntrinsic;
39use crate::intrinsics::lower::LowerIntrinsic;
40use crate::intrinsics::p3::async_future::AsyncFutureIntrinsic;
41use crate::intrinsics::p3::async_stream::AsyncStreamIntrinsic;
42use crate::intrinsics::p3::async_task::AsyncTaskIntrinsic;
43use crate::intrinsics::p3::error_context::ErrCtxIntrinsic;
44use crate::intrinsics::p3::host::HostIntrinsic;
45use crate::intrinsics::p3::waitable::WaitableIntrinsic;
46use crate::intrinsics::resource::ResourceIntrinsic;
47use crate::intrinsics::string::StringIntrinsic;
48use crate::intrinsics::webidl::WebIdlIntrinsic;
49use crate::intrinsics::{
50    AsyncDeterminismProfile, Intrinsic, RenderIntrinsicsArgs, render_intrinsics,
51};
52use crate::names::{LocalNames, is_js_reserved_word, maybe_quote_id, maybe_quote_member};
53use crate::{
54    FunctionIdentifier, ManagesIntrinsics, core, get_thrown_type, is_async_fn,
55    requires_async_porcelain, source, uwrite, uwriteln,
56};
57
58/// Number of flat parameters allowed before spilling over to memory
59/// for an async function
60///
61/// See [`wit-bindgen-core`] and the Component Model spec
62const MAX_ASYNC_FLAT_PARAMS: usize = 4;
63
64#[derive(Debug, Default, Clone)]
65pub struct TranspileOpts {
66    pub name: String,
67    /// Disables generation of `*.d.ts` files and instead only generates `*.js`
68    /// source files.
69    pub no_typescript: bool,
70    /// Provide a custom JS instantiation API for the component instead
71    /// of the direct importable native ESM output.
72    pub instantiation: Option<InstantiationMode>,
73    /// Configure how import bindings are provided, as high-level JS bindings,
74    /// or as hybrid optimized bindings.
75    pub import_bindings: Option<BindingsMode>,
76    /// Comma-separated list of "from-specifier=./to-specifier.js" mappings of
77    /// component import specifiers to JS import specifiers.
78    pub map: Option<HashMap<String, String>>,
79    /// Disables compatibility in Node.js without a fetch global.
80    pub no_nodejs_compat: bool,
81    /// Set the cutoff byte size for base64 inlining core Wasm in instantiation mode
82    /// (set to 0 to disable all base64 inlining)
83    pub base64_cutoff: usize,
84    /// Enables compatibility for JS environments without top-level await support
85    /// via an async $init promise export to wait for instead.
86    pub tla_compat: bool,
87    /// Disable verification of component Wasm data structures when
88    /// lifting as a production optimization
89    pub valid_lifting_optimization: bool,
90    /// Whether or not to emit `tracing` calls on function entry/exit.
91    pub tracing: bool,
92    /// Whether to generate namespaced exports like `foo as "local:package/foo"`.
93    /// These exports can break typescript builds.
94    pub no_namespaced_exports: bool,
95    /// Whether to output core Wasm utilizing multi-memory or to polyfill
96    /// this handling.
97    pub multi_memory: bool,
98    /// Whether to generate types for a guest module using module declarations.
99    pub guest: bool,
100    /// Configure whether to use `async` imports or exports with
101    /// JavaScript Promise Integration (JSPI).
102    pub async_mode: Option<AsyncMode>,
103}
104
105#[derive(Default, Clone, Debug)]
106pub enum AsyncMode {
107    #[default]
108    Sync,
109    JavaScriptPromiseIntegration {
110        imports: Vec<String>,
111        exports: Vec<String>,
112    },
113}
114
115#[derive(Default, Clone, Debug)]
116pub enum InstantiationMode {
117    #[default]
118    Async,
119    Sync,
120}
121
122/// Internal Bindgen calling convention
123enum CallType {
124    /// Standard calls - inner function is called directly with parameters
125    Standard,
126    /// Standard calls that are async (p3)
127    AsyncStandard,
128    /// Exported resource method calls - this is passed as the first argument
129    FirstArgIsThis,
130    /// Exported resource method calls that are async (p3)
131    AsyncFirstArgIsThis,
132    /// Imported resource method calls - callee is a member of the parameter
133    CalleeResourceDispatch,
134    /// Imported resource method calls that are async (p3)
135    AsyncCalleeResourceDispatch,
136}
137
138#[derive(Default, Clone, Debug)]
139pub enum BindingsMode {
140    Hybrid,
141    #[default]
142    Js,
143    Optimized,
144    DirectOptimized,
145}
146
147struct JsBindgen<'a> {
148    local_names: LocalNames,
149
150    esm_bindgen: EsmBindgen,
151
152    /// The source code for the "main" file that's going to be created for the
153    /// component we're generating bindings for. This is incrementally added to
154    /// over time and primarily contains the main `instantiate` function as well
155    /// as a type-description of the input/output interfaces.
156    src: Source,
157
158    /// Core module count
159    core_module_cnt: usize,
160
161    /// Various options for code generation.
162    opts: &'a TranspileOpts,
163
164    /// List of all intrinsics emitted to `src` so far.
165    all_intrinsics: BTreeSet<Intrinsic>,
166
167    /// List of all core Wasm exported functions (and if is async) referenced in
168    /// `src` so far.
169    ///
170    /// The second boolean is true when async procelain is required *or* if the
171    /// export itself is async.
172    all_core_exported_funcs: Vec<(String, bool)>,
173}
174
175/// Arguments provided to `JSBindgen::bindgen`, normally called to perform bindgen on a given function
176struct JsFunctionBindgenArgs<'a> {
177    /// Number of params that the function expects
178    nparams: usize,
179    /// Internal convention for function calls (ex. whether the first argument is known to be 'this')
180    call_type: CallType,
181    /// Interface name (if inside an interface)
182    iface_name: Option<&'a str>,
183    /// Callee of the function
184    callee: &'a str,
185    /// Canon opts provided for the functions
186    opts: &'a CanonicalOptions,
187    /// Parsed function metadata
188    func: &'a Function,
189    resource_map: &'a ResourceMap,
190    /// ABI variant of the function
191    abi: AbiVariant,
192    /// Whether the function in question is a host async function (i.e. JSPI)
193    requires_async_porcelain: bool,
194    /// Whether the function in question is a guest async function (i.e. WASI P3)
195    is_async: bool,
196}
197
198impl<'a> ManagesIntrinsics for JsBindgen<'a> {
199    fn add_intrinsic(&mut self, intrinsic: Intrinsic) {
200        self.intrinsic(intrinsic);
201    }
202}
203
204#[allow(clippy::too_many_arguments)]
205pub fn transpile_bindgen(
206    name: &str,
207    component: &ComponentTranslation,
208    modules: &PrimaryMap<StaticModuleIndex, core::Translation<'_>>,
209    types: &ComponentTypes,
210    resolve: &Resolve,
211    id: WorldId,
212    opts: TranspileOpts,
213    files: &mut Files,
214) -> (Vec<String>, Vec<(String, Export)>) {
215    let (async_imports, async_exports) = match opts.async_mode.clone() {
216        None | Some(AsyncMode::Sync) => (Default::default(), Default::default()),
217        Some(AsyncMode::JavaScriptPromiseIntegration { imports, exports }) => {
218            (imports.into_iter().collect(), exports.into_iter().collect())
219        }
220    };
221
222    let mut bindgen = JsBindgen {
223        local_names: LocalNames::default(),
224        src: Source::default(),
225        esm_bindgen: EsmBindgen::default(),
226        core_module_cnt: 0,
227        opts: &opts,
228        all_intrinsics: BTreeSet::new(),
229        all_core_exported_funcs: Vec::new(),
230    };
231    bindgen.local_names.exclude_globals(
232        &Intrinsic::get_global_names()
233            .into_iter()
234            .collect::<Vec<_>>(),
235    );
236    bindgen.core_module_cnt = modules.len();
237
238    // Generate mapping of stream tables to components that are related
239    let mut stream_tables = BTreeMap::new();
240    for idx in 0..component.component.num_stream_tables {
241        let stream_table_idx = TypeStreamTableIndex::from_u32(idx as u32);
242        let stream_table_ty = &types[stream_table_idx];
243        stream_tables.insert(stream_table_idx, stream_table_ty.instance);
244    }
245
246    // Generate mapping of err_ctx tables to components that are related
247    let mut err_ctx_tables = BTreeMap::new();
248    for idx in 0..component.component.num_error_context_tables {
249        let err_ctx_table_idx = TypeComponentLocalErrorContextTableIndex::from_u32(idx as u32);
250        let err_ctx_table_ty = &types[err_ctx_table_idx];
251        err_ctx_tables.insert(err_ctx_table_idx, err_ctx_table_ty.instance);
252    }
253
254    // Bindings are generated when the `instantiate` method is called on the
255    // Instantiator structure created below
256    let mut instantiator = Instantiator {
257        src: Source::default(),
258        sizes: SizeAlign::default(),
259        bindgen: &mut bindgen,
260        modules,
261        instances: Default::default(),
262        error_context_component_initialized: (0..component
263            .component
264            .num_runtime_component_instances)
265            .map(|_| false)
266            .collect(),
267        error_context_component_table_initialized: (0..component
268            .component
269            .num_error_context_tables)
270            .map(|_| false)
271            .collect(),
272        resolve,
273        world: id,
274        translation: component,
275        component: &component.component,
276        types,
277        async_imports,
278        async_exports,
279        imports: Default::default(),
280        exports: Default::default(),
281        lowering_options: Default::default(),
282        used_instance_flags: Default::default(),
283        defined_resource_classes: Default::default(),
284        imports_resource_types: Default::default(),
285        imports_resource_index_types: Default::default(),
286        exports_resource_types: Default::default(),
287        exports_resource_index_types: Default::default(),
288        resource_exports: Default::default(),
289        resource_imports: Default::default(),
290        resources_initialized: BTreeMap::new(),
291        resource_tables_initialized: BTreeMap::new(),
292        stream_tables,
293        err_ctx_tables,
294    };
295    instantiator.sizes.fill(resolve);
296    instantiator.initialize();
297    instantiator.instantiate();
298
299    let mut intrinsic_definitions = source::Source::default();
300
301    instantiator.resource_definitions(&mut intrinsic_definitions);
302    instantiator.instance_flags();
303
304    instantiator.bindgen.src.js(&instantiator.src.js);
305    instantiator.bindgen.src.js_init(&instantiator.src.js_init);
306
307    instantiator
308        .bindgen
309        .finish_component(name, files, &opts, intrinsic_definitions);
310
311    let exports = instantiator
312        .bindgen
313        .esm_bindgen
314        .exports()
315        .iter()
316        .map(|(export_name, canon_export_name)| {
317            let expected_export_name =
318                if canon_export_name.contains(':') || canon_export_name.starts_with("[async]") {
319                    canon_export_name.to_string()
320                } else {
321                    canon_export_name.to_kebab_case()
322                };
323            let export = instantiator
324                .component
325                .exports
326                .get(&expected_export_name, &NameMapNoIntern)
327                .unwrap_or_else(|| panic!("failed to find component export [{expected_export_name}] (original '{canon_export_name}')"));
328            (
329                export_name.to_string(),
330                instantiator.component.export_items[*export].clone(),
331            )
332        })
333        .collect();
334
335    (bindgen.esm_bindgen.import_specifiers(), exports)
336}
337
338impl JsBindgen<'_> {
339    fn finish_component(
340        &mut self,
341        name: &str,
342        files: &mut Files,
343        opts: &TranspileOpts,
344        intrinsic_definitions: source::Source,
345    ) {
346        let mut output = source::Source::default();
347        let mut compilation_promises = source::Source::default();
348        let mut core_exported_funcs = source::Source::default();
349
350        for (core_export_fn, is_async) in self.all_core_exported_funcs.iter() {
351            let local_name = self.local_names.get(core_export_fn);
352            if *is_async {
353                uwriteln!(
354                    core_exported_funcs,
355                    "{local_name} = WebAssembly.promising({core_export_fn});",
356                );
357            } else {
358                // TODO: run may be sync lifted, but it COULD call an async lowered function!
359
360                uwriteln!(core_exported_funcs, "{local_name} = {core_export_fn};",);
361            }
362        }
363
364        // adds a default implementation of `getCoreModule`
365        if matches!(self.opts.instantiation, Some(InstantiationMode::Async)) {
366            uwriteln!(
367                compilation_promises,
368                "if (!getCoreModule) getCoreModule = (name) => {}(new URL(`./${{name}}`, import.meta.url));",
369                self.intrinsic(Intrinsic::FetchCompile)
370            );
371        }
372
373        // Setup the compilation data and compilation promises
374        let mut removed = BTreeSet::new();
375        for i in 0..self.core_module_cnt {
376            let local_name = format!("module{i}");
377            let mut name_idx = core_file_name(name, i as u32);
378            if self.opts.instantiation.is_some() {
379                uwriteln!(
380                    compilation_promises,
381                    "const {local_name} = getCoreModule('{name_idx}');"
382                );
383            } else if files.get_size(&name_idx).unwrap() < self.opts.base64_cutoff {
384                assert!(removed.insert(i));
385                let data = files.remove(&name_idx).unwrap();
386                uwriteln!(
387                    compilation_promises,
388                    "const {local_name} = {}('{}');",
389                    self.intrinsic(Intrinsic::Base64Compile),
390                    general_purpose::STANDARD_NO_PAD.encode(&data),
391                );
392            } else {
393                // Maintain numerical file orderings when a previous file was
394                // inlined
395                if let Some(&replacement) = removed.iter().next() {
396                    assert!(removed.remove(&replacement) && removed.insert(i));
397                    let data = files.remove(&name_idx).unwrap();
398                    name_idx = core_file_name(name, replacement as u32);
399                    files.push(&name_idx, &data);
400                }
401                uwriteln!(
402                    compilation_promises,
403                    "const {local_name} = {}(new URL('./{name_idx}', import.meta.url));",
404                    self.intrinsic(Intrinsic::FetchCompile)
405                );
406            }
407        }
408
409        // Render the telemery directive
410        uwriteln!(output, r#""use jco";"#);
411
412        let js_intrinsics = render_intrinsics(RenderIntrinsicsArgs {
413            intrinsics: &mut self.all_intrinsics,
414            no_nodejs_compat: self.opts.no_nodejs_compat,
415            instantiation: self.opts.instantiation.is_some(),
416            determinism: AsyncDeterminismProfile::default(),
417        });
418
419        if let Some(instantiation) = &self.opts.instantiation {
420            uwrite!(
421                output,
422                "\
423                    export function instantiate(getCoreModule, imports, instantiateCore = {}) {{
424                        {}
425                        {}
426                        {}
427                ",
428                match instantiation {
429                    InstantiationMode::Async => "WebAssembly.instantiate",
430                    InstantiationMode::Sync =>
431                        "(module, importObject) => new WebAssembly.Instance(module, importObject)",
432                },
433                &js_intrinsics as &str,
434                &intrinsic_definitions as &str,
435                &compilation_promises as &str,
436            );
437        }
438
439        // Render all imports
440        let imports_object = if self.opts.instantiation.is_some() {
441            Some("imports")
442        } else {
443            None
444        };
445        self.esm_bindgen
446            .render_imports(&mut output, imports_object, &mut self.local_names);
447
448        // Create instantiation code
449        if self.opts.instantiation.is_some() {
450            uwrite!(&mut self.src.js, "{}", &core_exported_funcs as &str);
451            self.esm_bindgen.render_exports(
452                &mut self.src.js,
453                self.opts.instantiation.is_some(),
454                &mut self.local_names,
455                opts,
456            );
457            uwrite!(
458                output,
459                "\
460                        let gen = (function* _initGenerator () {{
461                            {}\
462                            {};
463                        }})();
464                        let promise, resolve, reject;
465                        function runNext (value) {{
466                            try {{
467                                let done;
468                                do {{
469                                    ({{ value, done }} = gen.next(value));
470                                }} while (!(value instanceof Promise) && !done);
471                                if (done) {{
472                                    if (resolve) return resolve(value);
473                                    else return value;
474                                }}
475                                if (!promise) promise = new Promise((_resolve, _reject) => (resolve = _resolve, reject = _reject));
476                                value.then(nextVal => done ? resolve() : runNext(nextVal), reject);
477                            }}
478                            catch (e) {{
479                                if (reject) reject(e);
480                                else throw e;
481                            }}
482                        }}
483                        const maybeSyncReturn = runNext(null);
484                        return promise || maybeSyncReturn;
485                    }};
486                ",
487                &self.src.js_init as &str,
488                &self.src.js as &str,
489            );
490        } else {
491            let (maybe_init_export, maybe_init) =
492                if self.opts.tla_compat && opts.instantiation.is_none() {
493                    uwriteln!(self.src.js_init, "_initialized = true;");
494                    (
495                        "\
496                        let _initialized = false;
497                        export ",
498                        "",
499                    )
500                } else {
501                    (
502                        "",
503                        "
504                        await $init;
505                    ",
506                    )
507                };
508
509            uwrite!(
510                output,
511                "\
512                    {}
513                    {}
514                    {}
515                    {maybe_init_export}const $init = (() => {{
516                        let gen = (function* _initGenerator () {{
517                            {}\
518                            {}\
519                            {}\
520                        }})();
521                        let promise, resolve, reject;
522                        function runNext (value) {{
523                            try {{
524                                let done;
525                                do {{
526                                    ({{ value, done }} = gen.next(value));
527                                }} while (!(value instanceof Promise) && !done);
528                                if (done) {{
529                                    if (resolve) resolve(value);
530                                    else return value;
531                                }}
532                                if (!promise) promise = new Promise((_resolve, _reject) => (resolve = _resolve, reject = _reject));
533                                value.then(runNext, reject);
534                            }}
535                            catch (e) {{
536                                if (reject) reject(e);
537                                else throw e;
538                            }}
539                        }}
540                        const maybeSyncReturn = runNext(null);
541                        return promise || maybeSyncReturn;
542                    }})();
543                    {maybe_init}\
544                ",
545                &js_intrinsics as &str,
546                &intrinsic_definitions as &str,
547                &self.src.js as &str,
548                &compilation_promises as &str,
549                &self.src.js_init as &str,
550                &core_exported_funcs as &str,
551            );
552
553            self.esm_bindgen.render_exports(
554                &mut output,
555                self.opts.instantiation.is_some(),
556                &mut self.local_names,
557                opts,
558            );
559        }
560
561        let mut bytes = output.as_bytes();
562        // strip leading newline
563        if bytes[0] == b'\n' {
564            bytes = &bytes[1..];
565        }
566        files.push(&format!("{name}.js"), bytes);
567    }
568
569    fn intrinsic(&mut self, intrinsic: Intrinsic) -> String {
570        self.all_intrinsics.insert(intrinsic);
571        intrinsic.name().to_string()
572    }
573}
574
575/// Helper structure used to generate the `instantiate` method of a component.
576///
577/// This is the main structure for parsing the output of Wasmtime.
578pub(crate) struct Instantiator<'a, 'b> {
579    src: Source,
580    bindgen: &'a mut JsBindgen<'b>,
581    modules: &'a PrimaryMap<StaticModuleIndex, core::Translation<'a>>,
582    instances: PrimaryMap<RuntimeInstanceIndex, StaticModuleIndex>,
583    types: &'a ComponentTypes,
584    resolve: &'a Resolve,
585    world: WorldId,
586    sizes: SizeAlign,
587    component: &'a Component,
588
589    /// Map of error contexts tables for a given component & error context index pair
590    /// that have been initialized
591    error_context_component_initialized: PrimaryMap<RuntimeComponentInstanceIndex, bool>,
592    error_context_component_table_initialized:
593        PrimaryMap<TypeComponentLocalErrorContextTableIndex, bool>,
594
595    /// Component-level translation information, including trampolines
596    translation: &'a ComponentTranslation,
597
598    /// Lookup of exported types to resource indices
599    exports_resource_types: BTreeMap<TypeId, ResourceIndex>,
600    /// Lookup of resource indices to exported types
601    exports_resource_index_types: BTreeMap<ResourceIndex, TypeId>,
602
603    /// Lookup of imported types to resource indices
604    imports_resource_types: BTreeMap<TypeId, ResourceIndex>,
605    /// Lookup of resource indices to imported types
606    #[allow(unused)]
607    imports_resource_index_types: BTreeMap<ResourceIndex, TypeId>,
608
609    resources_initialized: BTreeMap<ResourceIndex, bool>,
610    resource_tables_initialized: BTreeMap<TypeResourceTableIndex, bool>,
611
612    exports: BTreeMap<String, WorldKey>,
613    imports: BTreeMap<String, WorldKey>,
614    /// Instance flags which references have been emitted externally at least once.
615    used_instance_flags: RefCell<BTreeSet<RuntimeComponentInstanceIndex>>,
616    defined_resource_classes: BTreeSet<String>,
617    async_imports: HashSet<String>,
618    async_exports: HashSet<String>,
619    lowering_options:
620        PrimaryMap<LoweredIndex, (&'a CanonicalOptions, TrampolineIndex, TypeFuncIndex)>,
621
622    /// Mapping of stream table indices to component indices
623    stream_tables: BTreeMap<TypeStreamTableIndex, RuntimeComponentInstanceIndex>,
624
625    /// Mapping of err ctx indices to component indices
626    err_ctx_tables:
627        BTreeMap<TypeComponentLocalErrorContextTableIndex, RuntimeComponentInstanceIndex>,
628
629    /// Map of exported resources built during export bindgen
630    resource_exports: ResourceMap,
631    /// Map of imported resources built during export bindgen
632    resource_imports: ResourceMap,
633}
634
635impl<'a> ManagesIntrinsics for Instantiator<'a, '_> {
636    fn add_intrinsic(&mut self, intrinsic: Intrinsic) {
637        self.bindgen.intrinsic(intrinsic);
638    }
639}
640
641impl<'a> Instantiator<'a, '_> {
642    fn initialize(&mut self) {
643        // Populate reverse map from import and export names to world items
644        for (key, _) in &self.resolve.worlds[self.world].imports {
645            let name = &self.resolve.name_world_key(key);
646            self.imports.insert(name.to_string(), key.clone());
647        }
648        for (key, _) in &self.resolve.worlds[self.world].exports {
649            let name = &self.resolve.name_world_key(key);
650            self.exports.insert(name.to_string(), key.clone());
651        }
652
653        // Populate reverse map from TypeId to ResourceIndex
654        // Populate the resource type to resource index map
655        for (key, item) in &self.resolve.worlds[self.world].imports {
656            let name = &self.resolve.name_world_key(key);
657            let Some((_, (_, import))) = self
658                .component
659                .import_types
660                .iter()
661                .find(|(_, (impt_name, _))| impt_name == name)
662            else {
663                match item {
664                    WorldItem::Interface { .. } => {
665                        unreachable!("unexpected interface in import types during initialization")
666                    }
667                    WorldItem::Function(_) => {
668                        unreachable!("unexpected function in import types during initialization")
669                    }
670                    WorldItem::Type { id, .. } => {
671                        assert!(!matches!(
672                            self.resolve.types[*id].kind,
673                            TypeDefKind::Resource
674                        ))
675                    }
676                }
677                continue;
678            };
679            match item {
680                WorldItem::Interface { id, .. } => {
681                    let TypeDef::ComponentInstance(instance) = import else {
682                        unreachable!("unexpectedly non-component instance import in interface")
683                    };
684                    let import_ty = &self.types[*instance];
685                    let iface = &self.resolve.interfaces[*id];
686                    for (ty_name, ty) in &iface.types {
687                        match &import_ty.exports.get(ty_name) {
688                            Some(TypeDef::Resource(resource_table_idx)) => {
689                                let ty = crate::dealias(self.resolve, *ty);
690                                let resource_table_ty = &self.types[*resource_table_idx];
691                                self.imports_resource_types
692                                    .insert(ty, resource_table_ty.unwrap_concrete_ty());
693                            }
694                            Some(TypeDef::Interface(_)) | None => {}
695                            Some(_) => unreachable!("unexpected type in interface"),
696                        }
697                    }
698                }
699                WorldItem::Function(_) => {}
700                WorldItem::Type { id, .. } => match import {
701                    TypeDef::Resource(resource) => {
702                        let ty = crate::dealias(self.resolve, *id);
703                        let resource_table_ty = &self.types[*resource];
704                        self.imports_resource_types
705                            .insert(ty, resource_table_ty.unwrap_concrete_ty());
706                    }
707                    TypeDef::Interface(_) => {}
708                    _ => unreachable!("unexpected type in import world item"),
709                },
710            }
711        }
712        self.exports_resource_types = self.imports_resource_types.clone();
713
714        for (key, item) in &self.resolve.worlds[self.world].exports {
715            let name = &self.resolve.name_world_key(key);
716            let (_, export_idx) = self
717                .component
718                .exports
719                .raw_iter()
720                .find(|(expt_name, _)| *expt_name == name)
721                .unwrap();
722            let export = &self.component.export_items[*export_idx];
723            match item {
724                WorldItem::Interface { id, .. } => {
725                    let iface = &self.resolve.interfaces[*id];
726                    let Export::Instance { exports, .. } = &export else {
727                        unreachable!("unexpectedly non export instance item")
728                    };
729                    for (ty_name, ty) in &iface.types {
730                        match self.component.export_items
731                            [*exports.get(ty_name, &NameMapNoIntern).unwrap()]
732                        {
733                            Export::Type(TypeDef::Resource(resource)) => {
734                                let ty = crate::dealias(self.resolve, *ty);
735                                let resource_table_ty = &self.types[resource];
736                                let concrete_ty = resource_table_ty.unwrap_concrete_ty();
737                                self.exports_resource_types.insert(ty, concrete_ty);
738                                self.exports_resource_index_types.insert(concrete_ty, ty);
739                            }
740                            Export::Type(_) => {}
741                            _ => unreachable!(
742                                "unexpected type in component export items on iface [{iface_name}]",
743                                iface_name = iface.name.as_deref().unwrap_or("<unknown>"),
744                            ),
745                        }
746                    }
747                }
748                WorldItem::Function(_) => {}
749                WorldItem::Type { .. } => unreachable!("unexpected exported world item type"),
750            }
751        }
752    }
753
754    fn instantiate(&mut self) {
755        // Handle all built in trampolines
756        for (i, trampoline) in self.translation.trampolines.iter() {
757            let Trampoline::LowerImport {
758                index,
759                lower_ty,
760                options,
761            } = trampoline
762            else {
763                continue;
764            };
765
766            let options = self
767                .component
768                .options
769                .get(*options)
770                .expect("failed to find canon options");
771
772            let i = self.lowering_options.push((options, i, *lower_ty));
773            assert_eq!(i, *index);
774        }
775
776        if let Some(InstantiationMode::Async) = self.bindgen.opts.instantiation {
777            // To avoid uncaught promise rejection errors, we attach an intermediate
778            // Promise.all with a rejection handler, if there are multiple promises.
779            if self.modules.len() > 1 {
780                self.src.js_init.push_str("Promise.all([");
781                for i in 0..self.modules.len() {
782                    if i > 0 {
783                        self.src.js_init.push_str(", ");
784                    }
785                    self.src.js_init.push_str(&format!("module{i}"));
786                }
787                uwriteln!(self.src.js_init, "]).catch(() => {{}});");
788            }
789        }
790
791        // Set up global stream map, which is used by intrinsics like stream.transfer
792        let global_stream_table_map =
793            Intrinsic::AsyncStream(AsyncStreamIntrinsic::GlobalStreamTableMap).name();
794        let rep_table_class = Intrinsic::RepTableClass.name();
795        for (table_idx, component_idx) in self.stream_tables.iter() {
796            self.src.js.push_str(&format!(
797                "{global_stream_table_map}[{}] = {{ componentIdx: {}, table: new {rep_table_class}() }};\n",
798                table_idx.as_u32(),
799                component_idx.as_u32(),
800            ));
801        }
802
803        // Set up global error context map, which is used by intrinsics like err_ctx.transfer
804        let global_err_ctx_table_map =
805            Intrinsic::ErrCtx(ErrCtxIntrinsic::GlobalErrCtxTableMap).name();
806        let rep_table_class = Intrinsic::RepTableClass.name();
807        for (table_idx, component_idx) in self.err_ctx_tables.iter() {
808            self.src.js.push_str(&format!(
809                "{global_err_ctx_table_map}[{}] = {{ componentIdx: {}, table: new {rep_table_class}() }};\n",
810                table_idx.as_u32(),
811                component_idx.as_u32(),
812            ));
813        }
814
815        // Process global initializers
816        //
817        // The order of initialization is unfortunately quite fragile.
818        //
819        // We take care in processing module instantiations because we must ensure that
820        // $wit-component.fixups must be instantiated directly after $wit-component.shim
821        //
822        let mut lower_import_initializers = Vec::new();
823
824        // Process first n lower import initializers until the first instantiate module initializer
825        for init in self.component.initializers.iter() {
826            match init {
827                GlobalInitializer::InstantiateModule(_m, _maybe_idx) => {
828                    // Ensure lower import initializers are processed before the first module instantiation
829                    for lower_import_init in lower_import_initializers.drain(..) {
830                        self.instantiation_global_initializer(lower_import_init);
831                    }
832                }
833
834                // We push lower import initializers down to right before instantiate, so that the
835                // memory, realloc and postReturn functions are available to the import lowerings
836                // for optimized bindgen
837                GlobalInitializer::LowerImport { .. } => {
838                    lower_import_initializers.push(init);
839                    continue;
840                }
841                _ => {}
842            }
843
844            self.instantiation_global_initializer(init);
845        }
846
847        // Process lower import initializers that were discovered after the last module instantiation
848        for init in lower_import_initializers.drain(..) {
849            self.instantiation_global_initializer(init);
850        }
851
852        // Process exports and build mappings
853        //
854        // NOTE: generating exports fills out export resource map information
855        // that is needed for some trampolines.
856        self.exports(&self.component.exports);
857
858        // Some trampolines that correspond to host-provided imports need to be defined before the
859        // instantiation bits since they are referred to.
860        for (i, trampoline) in self
861            .translation
862            .trampolines
863            .iter()
864            .filter(|(_, t)| Instantiator::is_early_trampoline(t))
865        {
866            self.trampoline(i, trampoline);
867        }
868
869        if self.bindgen.opts.instantiation.is_some() {
870            let js_init = mem::take(&mut self.src.js_init);
871            self.src.js.push_str(&js_init);
872        }
873
874        // Trampolines here so we have static module indices, and resource maps populated
875        // (both imports and exports may still be populting resource map)
876        for (i, trampoline) in self
877            .translation
878            .trampolines
879            .iter()
880            .filter(|(_, t)| !Instantiator::is_early_trampoline(t))
881        {
882            self.trampoline(i, trampoline);
883        }
884    }
885
886    fn ensure_local_resource_class(&mut self, local_name: String) {
887        if !self.defined_resource_classes.contains(&local_name) {
888            uwriteln!(
889                self.src.js,
890                "\nclass {local_name} {{
891                constructor () {{
892                    throw new Error('\"{local_name}\" resource does not define a constructor');
893                }}
894            }}"
895            );
896            self.defined_resource_classes.insert(local_name.to_string());
897        }
898    }
899
900    fn resource_definitions(&mut self, definitions: &mut source::Source) {
901        // It is theoretically possible for locally defined resources used in no functions
902        // to still be exported
903        for resource in 0..self.component.num_resources {
904            let resource = ResourceIndex::from_u32(resource);
905            let is_imported = self.component.defined_resource_index(resource).is_none();
906            if is_imported {
907                continue;
908            }
909            if let Some(local_name) = self.bindgen.local_names.try_get(resource) {
910                self.ensure_local_resource_class(local_name.to_string());
911            }
912        }
913
914        // Write out the defined resource table indices for the runtime
915        if self.bindgen.all_intrinsics.contains(&Intrinsic::Resource(
916            ResourceIntrinsic::ResourceTransferBorrow,
917        )) || self.bindgen.all_intrinsics.contains(&Intrinsic::Resource(
918            ResourceIntrinsic::ResourceTransferBorrowValidLifting,
919        )) {
920            let defined_resource_tables = Intrinsic::DefinedResourceTables.name();
921            uwrite!(definitions, "const {defined_resource_tables} = [");
922            // Table per-resource
923            for tidx in 0..self.component.num_resources {
924                let tid = TypeResourceTableIndex::from_u32(tidx);
925                let resource_table_ty = &self.types[tid];
926                let rid = resource_table_ty.unwrap_concrete_ty();
927                if let Some(defined_index) = self.component.defined_resource_index(rid) {
928                    let instance_idx = resource_table_ty.unwrap_concrete_instance();
929                    if instance_idx == self.component.defined_resource_instances[defined_index] {
930                        uwrite!(definitions, "true,");
931                    }
932                } else {
933                    uwrite!(definitions, ",");
934                };
935            }
936            uwrite!(definitions, "];\n");
937        }
938    }
939
940    /// Ensure a component-local `error-context` table has been created
941    ///
942    /// # Arguments
943    ///
944    /// * `component_idx` - component index
945    /// * `err_ctx_tbl_idx` - The component-local error-context table index
946    ///
947    fn ensure_error_context_local_table(
948        &mut self,
949        component_idx: RuntimeComponentInstanceIndex,
950        err_ctx_tbl_idx: TypeComponentLocalErrorContextTableIndex,
951    ) {
952        if self.error_context_component_initialized[component_idx]
953            && self.error_context_component_table_initialized[err_ctx_tbl_idx]
954        {
955            return;
956        }
957        let err_ctx_local_tables = self
958            .bindgen
959            .intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::ComponentLocalTable));
960        let rep_table_class = Intrinsic::RepTableClass.name();
961        let c = component_idx.as_u32();
962        if !self.error_context_component_initialized[component_idx] {
963            uwriteln!(self.src.js, "{err_ctx_local_tables}.set({c}, new Map());");
964            self.error_context_component_initialized[component_idx] = true;
965        }
966        if !self.error_context_component_table_initialized[err_ctx_tbl_idx] {
967            let t = err_ctx_tbl_idx.as_u32();
968            uwriteln!(
969                self.src.js,
970                "{err_ctx_local_tables}.get({c}).set({t}, new {rep_table_class}({{ target: `component [{c}] local error ctx table [{t}]` }}));"
971            );
972            self.error_context_component_table_initialized[err_ctx_tbl_idx] = true;
973        }
974    }
975
976    /// Ensure that a resource table has been initialized
977    ///
978    /// For the relevant resource table, this function will generate initialization
979    /// blocks, exactly once.
980    ///
981    /// This is not done for *all* resources, but instead for those that are explicitly used.
982    fn ensure_resource_table(&mut self, resource_table_idx: TypeResourceTableIndex) {
983        if self
984            .resource_tables_initialized
985            .contains_key(&resource_table_idx)
986        {
987            return;
988        }
989
990        let resource_table_ty = &self.types[resource_table_idx];
991        let resource_idx = resource_table_ty.unwrap_concrete_ty();
992
993        let (is_imported, maybe_dtor) =
994            if let Some(resource_idx) = self.component.defined_resource_index(resource_idx) {
995                let resource_def = self
996                    .component
997                    .initializers
998                    .iter()
999                    .find_map(|i| match i {
1000                        GlobalInitializer::Resource(r) if r.index == resource_idx => Some(r),
1001                        _ => None,
1002                    })
1003                    .unwrap();
1004
1005                if let Some(dtor) = &resource_def.dtor {
1006                    (false, format!("\n{}(rep);", self.core_def(dtor)))
1007                } else {
1008                    (false, "".into())
1009                }
1010            } else {
1011                (true, "".into())
1012            };
1013
1014        let handle_tables = self.bindgen.intrinsic(Intrinsic::HandleTables);
1015        let rsc_table_flag = self
1016            .bindgen
1017            .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableFlag));
1018        let rsc_table_remove = self
1019            .bindgen
1020            .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableRemove));
1021
1022        let rtid = resource_table_idx.as_u32();
1023        if is_imported {
1024            uwriteln!(
1025                self.src.js,
1026                "const handleTable{rtid} = [{rsc_table_flag}, 0];",
1027            );
1028            if !self.resources_initialized.contains_key(&resource_idx) {
1029                let ridx = resource_idx.as_u32();
1030                uwriteln!(
1031                    self.src.js,
1032                    "const captureTable{ridx} = new Map();
1033                    let captureCnt{ridx} = 0;"
1034                );
1035                self.resources_initialized.insert(resource_idx, true);
1036            }
1037        } else {
1038            let finalization_registry_create = self
1039                .bindgen
1040                .intrinsic(Intrinsic::FinalizationRegistryCreate);
1041            uwriteln!(
1042                self.src.js,
1043                "const handleTable{rtid} = [{rsc_table_flag}, 0];
1044                const finalizationRegistry{rtid} = {finalization_registry_create}((handle) => {{
1045                    const {{ rep }} = {rsc_table_remove}(handleTable{rtid}, handle);{maybe_dtor}
1046                }});
1047                ",
1048            );
1049        }
1050        uwriteln!(self.src.js, "{handle_tables}[{rtid}] = handleTable{rtid};");
1051        self.resource_tables_initialized
1052            .insert(resource_table_idx, true);
1053    }
1054
1055    fn instance_flags(&mut self) {
1056        // SAFETY: short-lived borrow, and the refcell isn't mutably borrowed in the loop's body.
1057        let mut instance_flag_defs = String::new();
1058        for used in self.used_instance_flags.borrow().iter() {
1059            let i = used.as_u32();
1060            uwriteln!(
1061                &mut instance_flag_defs,
1062                "const instanceFlags{i} = new WebAssembly.Global({{ value: \"i32\", mutable: true }}, {});",
1063                wasmtime_environ::component::FLAG_MAY_LEAVE
1064            );
1065        }
1066        self.src.js_init.prepend_str(&instance_flag_defs);
1067    }
1068
1069    // Trampolines defined in is_early_trampoline() below that use:
1070    //   const trampoline{} = ...
1071    // require early initialization since their bindings aren't auto-hoisted
1072    // like JS functions are in the JS runtime.
1073    fn is_early_trampoline(trampoline: &Trampoline) -> bool {
1074        matches!(
1075            trampoline,
1076            Trampoline::AsyncStartCall { .. }
1077                | Trampoline::BackpressureDec { .. }
1078                | Trampoline::BackpressureInc { .. }
1079                | Trampoline::ContextGet { .. }
1080                | Trampoline::ContextSet { .. }
1081                | Trampoline::EnterSyncCall
1082                | Trampoline::ErrorContextDebugMessage { .. }
1083                | Trampoline::ErrorContextDrop { .. }
1084                | Trampoline::ErrorContextNew { .. }
1085                | Trampoline::ErrorContextTransfer
1086                | Trampoline::ExitSyncCall
1087                | Trampoline::FutureCancelRead { .. }
1088                | Trampoline::FutureCancelWrite { .. }
1089                | Trampoline::FutureDropReadable { .. }
1090                | Trampoline::FutureDropWritable { .. }
1091                | Trampoline::FutureRead { .. }
1092                | Trampoline::FutureWrite { .. }
1093                | Trampoline::LowerImport { .. }
1094                | Trampoline::PrepareCall { .. }
1095                | Trampoline::ResourceDrop { .. }
1096                | Trampoline::ResourceNew { .. }
1097                | Trampoline::ResourceRep { .. }
1098                | Trampoline::ResourceTransferBorrow
1099                | Trampoline::ResourceTransferOwn
1100                | Trampoline::StreamCancelRead { .. }
1101                | Trampoline::StreamCancelWrite { .. }
1102                | Trampoline::StreamDropReadable { .. }
1103                | Trampoline::StreamDropWritable { .. }
1104                | Trampoline::StreamNew { .. }
1105                | Trampoline::StreamRead { .. }
1106                | Trampoline::StreamTransfer
1107                | Trampoline::StreamWrite { .. }
1108                | Trampoline::SubtaskCancel { .. }
1109                | Trampoline::SubtaskDrop { .. }
1110                | Trampoline::SyncStartCall { .. }
1111                | Trampoline::TaskCancel { .. }
1112                | Trampoline::TaskReturn { .. }
1113                | Trampoline::WaitableJoin { .. }
1114                | Trampoline::WaitableSetDrop { .. }
1115                | Trampoline::WaitableSetNew { .. }
1116                | Trampoline::WaitableSetPoll { .. }
1117                | Trampoline::WaitableSetWait { .. }
1118        )
1119    }
1120
1121    fn trampoline(&mut self, i: TrampolineIndex, trampoline: &'a Trampoline) {
1122        let i = i.as_u32();
1123        match trampoline {
1124            Trampoline::TaskCancel { instance } => {
1125                let task_cancel_fn = self
1126                    .bindgen
1127                    .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::TaskCancel));
1128                uwriteln!(
1129                    self.src.js,
1130                    "const trampoline{i} = {task_cancel_fn}.bind(null, {instance_idx});\n",
1131                    instance_idx = instance.as_u32(),
1132                );
1133            }
1134
1135            Trampoline::SubtaskCancel { instance, async_ } => {
1136                let task_cancel_fn = self
1137                    .bindgen
1138                    .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::SubtaskCancel));
1139                uwriteln!(
1140                    self.src.js,
1141                    "const trampoline{i} = {task_cancel_fn}.bind(null, {instance_idx}, {async_});\n",
1142                    instance_idx = instance.as_u32(),
1143                );
1144            }
1145
1146            Trampoline::SubtaskDrop { instance } => {
1147                let component_idx = instance.as_u32();
1148                let subtask_drop_fn = self
1149                    .bindgen
1150                    .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::SubtaskDrop));
1151                uwriteln!(
1152                    self.src.js,
1153                    "const trampoline{i} = {subtask_drop_fn}.bind(
1154                         null,
1155                         {component_idx},
1156                     );"
1157                );
1158            }
1159
1160            Trampoline::WaitableSetNew { instance } => {
1161                let waitable_set_new_fn = self
1162                    .bindgen
1163                    .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableSetNew));
1164                uwriteln!(
1165                    self.src.js,
1166                    "const trampoline{i} = {waitable_set_new_fn}.bind(null, {});\n",
1167                    instance.as_u32(),
1168                );
1169            }
1170
1171            Trampoline::WaitableSetWait { instance, options } => {
1172                let options = self
1173                    .component
1174                    .options
1175                    .get(*options)
1176                    .expect("failed to find options");
1177                assert_eq!(
1178                    instance.as_u32(),
1179                    options.instance.as_u32(),
1180                    "options index instance must match trampoline"
1181                );
1182
1183                let CanonicalOptions {
1184                    instance,
1185                    async_,
1186                    data_model:
1187                        CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, .. }),
1188                    ..
1189                } = options
1190                else {
1191                    panic!("unexpected/missing memory data model during waitable-set.wait");
1192                };
1193
1194                let instance_idx = instance.as_u32();
1195                let memory_idx = memory
1196                    .expect("missing memory idx for waitable-set.wait")
1197                    .as_u32();
1198                let waitable_set_wait_fn = self
1199                    .bindgen
1200                    .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableSetWait));
1201
1202                uwriteln!(
1203                    self.src.js,
1204                    r#"
1205                    const trampoline{i} = new WebAssembly.Suspending({waitable_set_wait_fn}.bind(null, {{
1206                        componentIdx: {instance_idx},
1207                        isAsync: {async_},
1208                        memoryIdx: {memory_idx},
1209                        getMemoryFn: () => memory{memory_idx},
1210                    }}));
1211                    "#,
1212                );
1213            }
1214
1215            Trampoline::WaitableSetPoll { options, .. } => {
1216                let CanonicalOptions {
1217                    instance,
1218                    async_,
1219                    data_model:
1220                        CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, .. }),
1221                    cancellable,
1222                    ..
1223                } = self
1224                    .component
1225                    .options
1226                    .get(*options)
1227                    .expect("failed to find options")
1228                else {
1229                    panic!("unexpected memory data model during waitable-set.poll");
1230                };
1231
1232                let instance_idx = instance.as_u32();
1233                let memory_idx = memory
1234                    .expect("missing memory idx for waitable-set.poll")
1235                    .as_u32();
1236                let waitable_set_poll_fn = self
1237                    .bindgen
1238                    .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableSetPoll));
1239
1240                uwriteln!(
1241                    self.src.js,
1242                    r#"
1243                    const trampoline{i} = {waitable_set_poll_fn}.bind(
1244                        null,
1245                        {{
1246                            componentIdx: {instance_idx},
1247                            isAsync: {async_},
1248                            isCancellable: {cancellable},
1249                            memoryIdx: {memory_idx},
1250                            getMemoryFn: () => memory{memory_idx},
1251                        }}
1252                    );
1253                    "#,
1254                );
1255            }
1256
1257            Trampoline::WaitableSetDrop { instance } => {
1258                let waitable_set_drop_fn = self
1259                    .bindgen
1260                    .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableSetDrop));
1261                uwriteln!(
1262                    self.src.js,
1263                    "const trampoline{i} = {waitable_set_drop_fn}.bind(null, {instance_idx});\n",
1264                    instance_idx = instance.as_u32(),
1265                );
1266            }
1267
1268            Trampoline::WaitableJoin { instance } => {
1269                let waitable_join_fn = self
1270                    .bindgen
1271                    .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableJoin));
1272                uwriteln!(
1273                    self.src.js,
1274                    "const trampoline{i} = {waitable_join_fn}.bind(null, {instance_idx});\n",
1275                    instance_idx = instance.as_u32(),
1276                );
1277            }
1278
1279            Trampoline::StreamNew { ty, instance } => {
1280                let stream_new_fn = self
1281                    .bindgen
1282                    .intrinsic(Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamNew));
1283                let instance_idx = instance.as_u32();
1284                let stream_table_idx = ty.as_u32();
1285
1286                // Get to the payload type for the given stream table idx
1287                let table_ty = &self.types[*ty];
1288                let stream_ty_idx = table_ty.ty;
1289                let stream_ty_idx_js = stream_ty_idx.as_u32();
1290                let stream_ty = &self.types[stream_ty_idx];
1291
1292                // Gather type metadata
1293                let (
1294                    align_32_js,
1295                    size_32_js,
1296                    flat_count_js,
1297                    lift_fn_js,
1298                    lower_fn_js,
1299                    is_none_js,
1300                    is_numeric_type_js,
1301                    is_borrow_js,
1302                    is_async_value_js,
1303                ) = match stream_ty.payload {
1304                    // If there is no payload for the stream, we know the values
1305                    None => (
1306                        "0".into(),
1307                        "0".into(),
1308                        "0".into(),
1309                        "null".into(),
1310                        "null".into(),
1311                        "true",
1312                        "false".into(),
1313                        "false".into(),
1314                        "false".into(),
1315                    ),
1316                    // If there is a payload, generate relevant lift/lower and other metadata
1317                    Some(ty) => (
1318                        self.types.canonical_abi(&ty).align32.to_string(),
1319                        self.types.canonical_abi(&ty).size32.to_string(),
1320                        self.types
1321                            .canonical_abi(&ty)
1322                            .flat_count
1323                            .map(|v| v.to_string())
1324                            .unwrap_or_else(|| "null".into()),
1325                        gen_flat_lift_fn_js_expr(
1326                            self,
1327                            &ty,
1328                            &wasmtime_environ::component::StringEncoding::Utf8,
1329                        ),
1330                        gen_flat_lower_fn_js_expr(
1331                            self,
1332                            self.types,
1333                            &ty,
1334                            &wasmtime_environ::component::StringEncoding::Utf8,
1335                        ),
1336                        "false",
1337                        format!(
1338                            "{}",
1339                            matches!(
1340                                ty,
1341                                InterfaceType::U8
1342                                    | InterfaceType::U16
1343                                    | InterfaceType::U32
1344                                    | InterfaceType::U64
1345                                    | InterfaceType::S8
1346                                    | InterfaceType::S16
1347                                    | InterfaceType::S32
1348                                    | InterfaceType::S64
1349                                    | InterfaceType::Float32
1350                                    | InterfaceType::Float64
1351                            )
1352                        ),
1353                        format!("{}", matches!(ty, InterfaceType::Borrow(_))),
1354                        format!(
1355                            "{}",
1356                            matches!(ty, InterfaceType::Stream(_) | InterfaceType::Future(_))
1357                        ),
1358                    ),
1359                };
1360
1361                uwriteln!(
1362                    self.src.js,
1363                    "const trampoline{i} = {stream_new_fn}.bind(null, {{
1364                        streamTableIdx: {stream_table_idx},
1365                        callerComponentIdx: {instance_idx},
1366                        elemMeta: {{
1367                            liftFn: {lift_fn_js},
1368                            lowerFn: {lower_fn_js},
1369                            typeIdx: {stream_ty_idx_js},
1370                            isNone: {is_none_js},
1371                            isNumeric: {is_numeric_type_js},
1372                            isBorrowed: {is_borrow_js},
1373                            isAsyncValue: {is_async_value_js},
1374                            flatCount: {flat_count_js},
1375                            align32: {align_32_js},
1376                            size32: {size_32_js},
1377                        }},
1378                    }});\n",
1379                );
1380            }
1381
1382            Trampoline::StreamRead {
1383                instance,
1384                ty,
1385                options,
1386            } => {
1387                let options = self
1388                    .component
1389                    .options
1390                    .get(*options)
1391                    .expect("failed to find options");
1392                assert_eq!(
1393                    instance.as_u32(),
1394                    options.instance.as_u32(),
1395                    "options index instance must match trampoline"
1396                );
1397
1398                let CanonicalOptions {
1399                    instance,
1400                    string_encoding,
1401                    async_,
1402                    data_model:
1403                        CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1404                    ..
1405                } = options
1406                else {
1407                    unreachable!("missing/invalid data model for options during stream.read")
1408                };
1409                let memory_idx = memory.expect("missing memory idx for stream.read").as_u32();
1410                let (realloc_idx, get_realloc_fn_js) = match realloc {
1411                    Some(v) => {
1412                        let v = v.as_u32().to_string();
1413                        (v.to_string(), format!("() => realloc{v}"))
1414                    }
1415                    None => ("null".into(), "null".into()),
1416                };
1417
1418                let component_instance_id = instance.as_u32();
1419                let string_encoding = string_encoding_js_literal(string_encoding);
1420                let stream_table_idx = ty.as_u32();
1421                let stream_read_fn = self
1422                    .bindgen
1423                    .intrinsic(Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamRead));
1424
1425                // PrepareCall for an async call is sometimes missing memories,
1426                // so we augment and save here, knowing that any stream.write/read operation
1427                // that uses a memory is indicative of that component's memory
1428                //
1429                let register_global_memory_for_component_fn =
1430                    Intrinsic::RegisterGlobalMemoryForComponent.name();
1431                uwriteln!(
1432                    self.src.js_init,
1433                    r#"{register_global_memory_for_component_fn}({{
1434                         componentIdx: {component_instance_id},
1435                         memoryIdx: {memory_idx},
1436                         memory: memory{memory_idx},
1437                     }});"#
1438                );
1439
1440                uwriteln!(
1441                    self.src.js,
1442                    r#"const trampoline{i} = new WebAssembly.Suspending({stream_read_fn}.bind(
1443                         null,
1444                         {{
1445                             componentIdx: {component_instance_id},
1446                             memoryIdx: {memory_idx},
1447                             getMemoryFn: () => memory{memory_idx},
1448                             reallocIdx: {realloc_idx},
1449                             getReallocFn: {get_realloc_fn_js},
1450                             stringEncoding: {string_encoding},
1451                             isAsync: {async_},
1452                             streamTableIdx: {stream_table_idx},
1453                         }}
1454                     ));
1455                    "#,
1456                );
1457            }
1458
1459            Trampoline::StreamWrite {
1460                instance,
1461                ty,
1462                options,
1463            } => {
1464                let options = self
1465                    .component
1466                    .options
1467                    .get(*options)
1468                    .expect("failed to find options");
1469                assert_eq!(
1470                    instance.as_u32(),
1471                    options.instance.as_u32(),
1472                    "options index instance must match trampoline"
1473                );
1474
1475                let CanonicalOptions {
1476                    instance,
1477                    string_encoding,
1478                    async_,
1479                    data_model:
1480                        CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1481                    ..
1482                } = options
1483                else {
1484                    unreachable!("unexpected memory data model during stream.write");
1485                };
1486                let component_instance_id = instance.as_u32();
1487                let memory_idx = memory
1488                    .expect("missing memory idx for stream.write")
1489                    .as_u32();
1490                let (realloc_idx, get_realloc_fn_js) = match realloc {
1491                    Some(v) => {
1492                        let v = v.as_u32().to_string();
1493                        (v.to_string(), format!("() => realloc{v}"))
1494                    }
1495                    None => ("null".into(), "() => null".into()),
1496                };
1497
1498                let string_encoding = string_encoding_js_literal(string_encoding);
1499                let stream_table_idx = ty.as_u32();
1500                let stream_write_fn = self
1501                    .bindgen
1502                    .intrinsic(Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamWrite));
1503
1504                // PrepareCall for an async call is sometimes missing memories,
1505                // so we augment and save here, knowing that any stream.write/read operation
1506                // that uses a memory is indicative of that component's memory
1507                let register_global_memory_for_component_fn =
1508                    Intrinsic::RegisterGlobalMemoryForComponent.name();
1509                uwriteln!(
1510                    self.src.js_init,
1511                    r#"{register_global_memory_for_component_fn}({{
1512                         componentIdx: {component_instance_id},
1513                         memoryIdx: {memory_idx},
1514                         memory: memory{memory_idx},
1515                     }});"#
1516                );
1517
1518                uwriteln!(
1519                    self.src.js,
1520                    r#"
1521                     const trampoline{i} = new WebAssembly.Suspending({stream_write_fn}.bind(
1522                         null,
1523                         {{
1524                             componentIdx: {component_instance_id},
1525                             memoryIdx: {memory_idx},
1526                             getMemoryFn: () => memory{memory_idx},
1527                             reallocIdx: {realloc_idx},
1528                             getReallocFn: {get_realloc_fn_js},
1529                             stringEncoding: {string_encoding},
1530                             isAsync: {async_},
1531                             streamTableIdx: {stream_table_idx},
1532                         }}
1533                     ));
1534                    "#,
1535                );
1536            }
1537
1538            Trampoline::StreamCancelRead {
1539                instance,
1540                ty,
1541                async_,
1542            }
1543            | Trampoline::StreamCancelWrite {
1544                instance,
1545                ty,
1546                async_,
1547            } => {
1548                let stream_cancel_fn = match trampoline {
1549                    Trampoline::StreamCancelRead { .. } => self.bindgen.intrinsic(
1550                        Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamCancelRead),
1551                    ),
1552                    Trampoline::StreamCancelWrite { .. } => self.bindgen.intrinsic(
1553                        Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamCancelWrite),
1554                    ),
1555                    _ => unreachable!("unexpected trampoline"),
1556                };
1557
1558                let stream_table_idx = ty.as_u32();
1559                let component_idx = instance.as_u32();
1560                uwriteln!(
1561                    self.src.js,
1562                    r#"
1563                      const trampoline{i} = new WebAssembly.Suspending({stream_cancel_fn}.bind(null, {{
1564                          streamTableIdx: {stream_table_idx},
1565                          isAsync: {async_},
1566                          componentIdx: {component_idx},
1567                      }}));
1568                    "#,
1569                );
1570            }
1571
1572            Trampoline::StreamDropReadable { ty, instance }
1573            | Trampoline::StreamDropWritable { ty, instance } => {
1574                let intrinsic_fn = match trampoline {
1575                    Trampoline::StreamDropReadable { .. } => self.bindgen.intrinsic(
1576                        Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamDropReadable),
1577                    ),
1578                    Trampoline::StreamDropWritable { .. } => self.bindgen.intrinsic(
1579                        Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamDropWritable),
1580                    ),
1581                    _ => unreachable!("unexpected trampoline"),
1582                };
1583                let stream_idx = ty.as_u32();
1584                let instance_idx = instance.as_u32();
1585                uwriteln!(
1586                    self.src.js,
1587                    "const trampoline{i} = {intrinsic_fn}.bind(null, {{
1588                        streamTableIdx: {stream_idx},
1589                        componentIdx: {instance_idx},
1590                    }});\n",
1591                );
1592            }
1593
1594            Trampoline::StreamTransfer => {
1595                let stream_transfer_fn = self
1596                    .bindgen
1597                    .intrinsic(Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamTransfer));
1598                uwriteln!(self.src.js, "const trampoline{i} = {stream_transfer_fn};\n",);
1599            }
1600
1601            Trampoline::FutureNew { ty, .. } => {
1602                let future_new_fn = self
1603                    .bindgen
1604                    .intrinsic(Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureNew));
1605                uwriteln!(
1606                    self.src.js,
1607                    "const trampoline{i} = {future_new_fn}.bind(null, {});\n",
1608                    ty.as_u32(),
1609                );
1610            }
1611
1612            Trampoline::FutureRead { ty, options, .. } => {
1613                let options = self
1614                    .component
1615                    .options
1616                    .get(*options)
1617                    .expect("failed to find options");
1618
1619                let future_idx = ty.as_u32();
1620
1621                let CanonicalOptions {
1622                    instance,
1623                    string_encoding,
1624                    callback,
1625                    post_return,
1626                    async_,
1627                    data_model:
1628                        CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1629                    ..
1630                } = options
1631                else {
1632                    unreachable!("unexpected memory data model during future.read");
1633                };
1634                let component_instance_id = instance.as_u32();
1635                let memory_idx = memory.expect("missing memory idx for future.read").as_u32();
1636                let realloc_idx = realloc
1637                    .map(|v| v.as_u32().to_string())
1638                    .unwrap_or_else(|| "null".into());
1639                let string_encoding = string_encoding_js_literal(string_encoding);
1640
1641                assert!(
1642                    callback.is_none(),
1643                    "callback should not be present for future read"
1644                );
1645                assert!(
1646                    post_return.is_none(),
1647                    "post_return should not be present for future read"
1648                );
1649
1650                let future_read_fn = self
1651                    .bindgen
1652                    .intrinsic(Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureRead));
1653                uwriteln!(
1654                    self.src.js,
1655                    r#"const trampoline{i} = {future_read_fn}.bind(
1656                         null,
1657                         {component_instance_id},
1658                         {memory_idx},
1659                         {realloc_idx},
1660                         {string_encoding},
1661                         {async_},
1662                         {future_idx},
1663                     );
1664                    "#,
1665                );
1666            }
1667
1668            Trampoline::FutureWrite { ty, options, .. } => {
1669                let options = self
1670                    .component
1671                    .options
1672                    .get(*options)
1673                    .expect("failed to find options");
1674
1675                let future_idx = ty.as_u32();
1676                let CanonicalOptions {
1677                    instance,
1678                    string_encoding,
1679                    async_,
1680                    data_model:
1681                        CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1682                    ..
1683                } = options
1684                else {
1685                    unreachable!("unexpected memory data model during future.write");
1686                };
1687                let component_instance_id = instance.as_u32();
1688                let memory_idx = memory
1689                    .expect("missing memory idx for future.write")
1690                    .as_u32();
1691                let realloc_idx = realloc
1692                    .map(|v| v.as_u32().to_string())
1693                    .unwrap_or_else(|| "null".into());
1694                let string_encoding = string_encoding_js_literal(string_encoding);
1695
1696                let future_write_fn = self
1697                    .bindgen
1698                    .intrinsic(Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureWrite));
1699                uwriteln!(
1700                    self.src.js,
1701                    r#"const trampoline{i} = {future_write_fn}.bind(
1702                         null,
1703                         {component_instance_id},
1704                         {memory_idx},
1705                         {realloc_idx},
1706                         {string_encoding},
1707                         {async_},
1708                         {future_idx},
1709                     );
1710                    "#,
1711                );
1712            }
1713
1714            Trampoline::FutureCancelRead { ty, async_, .. } => {
1715                let future_cancel_read_fn = self.bindgen.intrinsic(Intrinsic::AsyncFuture(
1716                    AsyncFutureIntrinsic::FutureCancelRead,
1717                ));
1718                uwriteln!(
1719                    self.src.js,
1720                    "const trampoline{i} = {future_cancel_read_fn}.bind(null, {future_idx}, {async_});\n",
1721                    future_idx = ty.as_u32(),
1722                );
1723            }
1724
1725            Trampoline::FutureCancelWrite { ty, async_, .. } => {
1726                let future_cancel_write_fn = self.bindgen.intrinsic(Intrinsic::AsyncFuture(
1727                    AsyncFutureIntrinsic::FutureCancelWrite,
1728                ));
1729                uwriteln!(
1730                    self.src.js,
1731                    "const trampoline{i} = {future_cancel_write_fn}.bind(null, {future_idx}, {async_});\n",
1732                    future_idx = ty.as_u32(),
1733                );
1734            }
1735
1736            Trampoline::FutureDropReadable { ty, .. } => {
1737                let future_drop_readable_fn = self.bindgen.intrinsic(Intrinsic::AsyncFuture(
1738                    AsyncFutureIntrinsic::FutureDropReadable,
1739                ));
1740                uwriteln!(
1741                    self.src.js,
1742                    "const trampoline{i} = {future_drop_readable_fn}.bind(null, {future_idx});\n",
1743                    future_idx = ty.as_u32(),
1744                );
1745            }
1746
1747            Trampoline::FutureDropWritable { ty, .. } => {
1748                let future_drop_writable_fn = self.bindgen.intrinsic(Intrinsic::AsyncFuture(
1749                    AsyncFutureIntrinsic::FutureDropWritable,
1750                ));
1751                uwriteln!(
1752                    self.src.js,
1753                    "const trampoline{i} = {future_drop_writable_fn}.bind(null, {future_idx});\n",
1754                    future_idx = ty.as_u32(),
1755                );
1756            }
1757
1758            Trampoline::FutureTransfer => todo!("Trampoline::FutureTransfer"),
1759
1760            Trampoline::ErrorContextNew { ty, options, .. } => {
1761                let CanonicalOptions {
1762                    instance,
1763                    string_encoding,
1764                    data_model:
1765                        CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, .. }),
1766                    ..
1767                } = self
1768                    .component
1769                    .options
1770                    .get(*options)
1771                    .expect("failed to find options")
1772                else {
1773                    panic!("unexpected memory data model during error-context.new");
1774                };
1775
1776                self.ensure_error_context_local_table(*instance, *ty);
1777
1778                let local_err_tbl_idx = ty.as_u32();
1779                let component_idx = instance.as_u32();
1780
1781                let memory_idx = memory
1782                    .expect("missing realloc fn idx for error-context.debug-message")
1783                    .as_u32();
1784
1785                // Generate a string decoding function to match this trampoline that does appropriate encoding
1786                let decoder = match string_encoding {
1787                    wasmtime_environ::component::StringEncoding::Utf8 => self
1788                        .bindgen
1789                        .intrinsic(Intrinsic::String(StringIntrinsic::GlobalTextDecoderUtf8)),
1790                    wasmtime_environ::component::StringEncoding::Utf16 => self
1791                        .bindgen
1792                        .intrinsic(Intrinsic::String(StringIntrinsic::Utf16Decoder)),
1793                    enc => panic!(
1794                        "unsupported string encoding [{enc:?}] for error-context.debug-message"
1795                    ),
1796                };
1797                uwriteln!(
1798                    self.src.js,
1799                    "function trampoline{i}InputStr(ptr, len) {{
1800                         return {decoder}.decode(new DataView(memory{memory_idx}.buffer, ptr, len));
1801                    }}"
1802                );
1803
1804                let err_ctx_new_fn = self
1805                    .bindgen
1806                    .intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::ErrorContextNew));
1807                // Store the options associated with this new error context for later use in the global array
1808                uwriteln!(
1809                    self.src.js,
1810                    "const trampoline{i} = {err_ctx_new_fn}.bind(
1811                         null,
1812                         {{
1813                             componentIdx: {component_idx},
1814                             localTableIdx: {local_err_tbl_idx},
1815                             readStrFn: trampoline{i}InputStr,
1816                         }}
1817                     );
1818                    "
1819                );
1820            }
1821
1822            Trampoline::ErrorContextDebugMessage {
1823                instance, options, ..
1824            } => {
1825                let CanonicalOptions {
1826                    async_,
1827                    callback,
1828                    post_return,
1829                    string_encoding,
1830                    data_model:
1831                        CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1832                    ..
1833                } = self
1834                    .component
1835                    .options
1836                    .get(*options)
1837                    .expect("failed to find options")
1838                else {
1839                    panic!("unexpected memory data model during error-context.debug-message");
1840                };
1841
1842                let debug_message_fn = self
1843                    .bindgen
1844                    .intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::ErrorContextDebugMessage));
1845
1846                let realloc_fn_idx = realloc
1847                    .expect("missing realloc fn idx for error-context.debug-message")
1848                    .as_u32();
1849                let memory_idx = memory
1850                    .expect("missing realloc fn idx for error-context.debug-message")
1851                    .as_u32();
1852
1853                // Generate a string encoding function to match this trampoline that does appropriate encoding
1854                match string_encoding {
1855                    wasmtime_environ::component::StringEncoding::Utf8 => {
1856                        let encode_fn = self
1857                            .bindgen
1858                            .intrinsic(Intrinsic::String(StringIntrinsic::Utf8Encode));
1859                        uwriteln!(
1860                            self.src.js,
1861                            "function trampoline{i}OutputStr(s, outputPtr) {{
1862                                 const memory = memory{memory_idx};
1863                                 const reallocFn = realloc{realloc_fn_idx};
1864                                 let {{ ptr, len }} = {encode_fn}(s, reallocFn, memory);
1865                                 new DataView(memory.buffer).setUint32(outputPtr, ptr, true)
1866                                 new DataView(memory.buffer).setUint32(outputPtr + 4, len, true)
1867                             }}"
1868                        );
1869                    }
1870                    wasmtime_environ::component::StringEncoding::Utf16 => {
1871                        let encode_fn = self
1872                            .bindgen
1873                            .intrinsic(Intrinsic::String(StringIntrinsic::Utf16Encode));
1874                        uwriteln!(
1875                            self.src.js,
1876                            "function trampoline{i}OutputStr(s, outputPtr) {{
1877                                 const memory = memory{memory_idx};
1878                                 const reallocFn = realloc{realloc_fn_idx};
1879                                 let ptr = {encode_fn}(s, reallocFn, memory);
1880                                 let len = s.length;
1881                                 new DataView(memory.buffer).setUint32(outputPtr, ptr, true)
1882                                 new DataView(memory.buffer).setUint32(outputPtr + 4, len, true)
1883                             }}"
1884                        );
1885                    }
1886                    enc => panic!(
1887                        "unsupported string encoding [{enc:?}] for error-context.debug-message"
1888                    ),
1889                };
1890
1891                let options_obj = format!(
1892                    "{{callback:{callback}, postReturn: {post_return}, async: {async_}}}",
1893                    callback = callback
1894                        .map(|v| v.as_u32().to_string())
1895                        .unwrap_or_else(|| "null".into()),
1896                    post_return = post_return
1897                        .map(|v| v.as_u32().to_string())
1898                        .unwrap_or_else(|| "null".into()),
1899                );
1900
1901                let component_idx = instance.as_u32();
1902                uwriteln!(
1903                    self.src.js,
1904                    "const trampoline{i} = {debug_message_fn}.bind(
1905                         null,
1906                         {{
1907                             componentIdx: {component_idx},
1908                             options: {options_obj},
1909                             writeStrFn: trampoline{i}OutputStr,
1910                         }}
1911                     );"
1912                );
1913            }
1914
1915            Trampoline::ErrorContextDrop { instance, ty } => {
1916                let drop_fn = self
1917                    .bindgen
1918                    .intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::ErrorContextDrop));
1919                let local_err_tbl_idx = ty.as_u32();
1920                let component_idx = instance.as_u32();
1921                uwriteln!(
1922                    self.src.js,
1923                    r#"
1924                      const trampoline{i} = {drop_fn}.bind(
1925                          null,
1926                          {{ componentIdx: {component_idx}, localTableIdx: {local_err_tbl_idx} }},
1927                      );
1928                    "#
1929                );
1930            }
1931
1932            Trampoline::ErrorContextTransfer => {
1933                let transfer_fn = self
1934                    .bindgen
1935                    .intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::ErrorContextTransfer));
1936                uwriteln!(self.src.js, "const trampoline{i} = {transfer_fn};");
1937            }
1938
1939            // This sets up a subtask (sets parent, etc) for guest -> guest calls
1940            Trampoline::PrepareCall { memory } => {
1941                let prepare_call_fn = self
1942                    .bindgen
1943                    .intrinsic(Intrinsic::Host(HostIntrinsic::PrepareCall));
1944                let (memory_idx_js, memory_fn_js) = memory
1945                    .map(|v| {
1946                        (
1947                            v.as_u32().to_string(),
1948                            format!("() => memory{}", v.as_u32()),
1949                        )
1950                    })
1951                    .unwrap_or_else(|| ("null".into(), "() => null".into()));
1952                uwriteln!(
1953                    self.src.js,
1954                    "const trampoline{i} = {prepare_call_fn}.bind(null, {memory_idx_js}, {memory_fn_js});",
1955                )
1956            }
1957
1958            Trampoline::SyncStartCall { callback } => {
1959                let sync_start_call_fn = self
1960                    .bindgen
1961                    .intrinsic(Intrinsic::Host(HostIntrinsic::SyncStartCall));
1962                uwriteln!(
1963                    self.src.js,
1964                    "const trampoline{i} = {sync_start_call_fn}.bind(null, {});",
1965                    callback
1966                        .map(|v| v.as_u32().to_string())
1967                        .unwrap_or_else(|| "null".into()),
1968                );
1969            }
1970
1971            // This actually starts a Task (whose parent is a subtask generated during PrepareCall)
1972            // for a from-component async import call
1973            Trampoline::AsyncStartCall {
1974                callback,
1975                post_return,
1976            } => {
1977                let async_start_call_fn = self
1978                    .bindgen
1979                    .intrinsic(Intrinsic::Host(HostIntrinsic::AsyncStartCall));
1980                let (callback_idx, callback_fn) = callback
1981                    .map(|v| (v.as_u32().to_string(), format!("callback_{}", v.as_u32())))
1982                    .unwrap_or_else(|| ("null".into(), "null".into()));
1983                let (post_return_idx, post_return_fn) = post_return
1984                    .map(|v| (v.as_u32().to_string(), format!("postReturn{}", v.as_u32())))
1985                    .unwrap_or_else(|| ("null".into(), "null".into()));
1986
1987                uwriteln!(
1988                    self.src.js,
1989                    "const trampoline{i} = {async_start_call_fn}.bind(
1990                         null,
1991                         {{
1992                             postReturnIdx: {post_return_idx},
1993                             getPostReturnFn: () => {post_return_fn},
1994                             callbackIdx: {callback_idx},
1995                             getCallbackFn: () => {callback_fn},
1996                         }},
1997                     );",
1998                );
1999            }
2000
2001            Trampoline::LowerImport {
2002                index: _,
2003                lower_ty,
2004                options,
2005            } => {
2006                let canon_opts = self
2007                    .component
2008                    .options
2009                    .get(*options)
2010                    .expect("failed to find options");
2011
2012                // TODO(fix): remove Global lowers, should enable using just exports[x] to export[y] call
2013                // TODO(fix): promising for the run (*as well as exports*)
2014                // TODO(fix): delete all asyncImports/exports
2015                // TODO(opt): opt-in sync import
2016
2017                let component_idx = canon_opts.instance.as_u32();
2018                let is_async = canon_opts.async_;
2019
2020                let cancellable = canon_opts.cancellable;
2021
2022                let func_ty = self.types.index(*lower_ty);
2023
2024                // Build list of lift functions for the params of the lowered import
2025                let param_types = &self.types.index(func_ty.params).types;
2026                let param_lift_fns_js =
2027                    gen_flat_lift_fn_list_js_expr(self, param_types.iter().as_slice(), canon_opts);
2028
2029                // Build list of lower functions for the results of the lowered import
2030                let result_types = &self.types.index(func_ty.results).types;
2031                let result_lower_fns_js = gen_flat_lower_fn_list_js_expr(
2032                    self,
2033                    self.types,
2034                    result_types.iter().as_slice(),
2035                    &canon_opts.string_encoding,
2036                );
2037
2038                let get_callback_fn_js = canon_opts
2039                    .callback
2040                    .map(|idx| format!("() => callback_{}", idx.as_u32()))
2041                    .unwrap_or_else(|| "() => null".into());
2042                let get_post_return_fn_js = canon_opts
2043                    .post_return
2044                    .map(|idx| format!("() => postReturn{}", idx.as_u32()))
2045                    .unwrap_or_else(|| "() => null".into());
2046
2047                // Build the memory and realloc js expressions, retrieving the memory index and getter functions
2048                let (memory_exprs, realloc_expr_js) =
2049                    if let CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions {
2050                        memory,
2051                        realloc,
2052                    }) = canon_opts.data_model
2053                    {
2054                        (
2055                            memory.map(|idx| {
2056                                (
2057                                    idx.as_u32().to_string(),
2058                                    format!("() => memory{}", idx.as_u32()),
2059                                )
2060                            }),
2061                            realloc.map(|idx| format!("() => realloc{}", idx.as_u32())),
2062                        )
2063                    } else {
2064                        (None, None)
2065                    };
2066                let (memory_idx_js, memory_expr_js) =
2067                    memory_exprs.unwrap_or_else(|| ("null".into(), "() => null".into()));
2068                let realloc_expr_js = realloc_expr_js.unwrap_or_else(|| "() => null".into());
2069
2070                // Build the lower import call that will wrap the actual trampoline
2071                let func_ty_async = func_ty.async_;
2072                let call = format!(
2073                    r#"{lower_import_intrinsic}.bind(
2074                        null,
2075                        {{
2076                            trampolineIdx: {i},
2077                            componentIdx: {component_idx},
2078                            isAsync: {is_async},
2079                            isManualAsync: _trampoline{i}.manuallyAsync,
2080                            paramLiftFns: {param_lift_fns_js},
2081                            resultLowerFns: {result_lower_fns_js},
2082                            funcTypeIsAsync: {func_ty_async},
2083                            getCallbackFn: {get_callback_fn_js},
2084                            getPostReturnFn: {get_post_return_fn_js},
2085                            isCancellable: {cancellable},
2086                            memoryIdx: {memory_idx_js},
2087                            getMemoryFn: {memory_expr_js},
2088                            getReallocFn: {realloc_expr_js},
2089                            importFn: _trampoline{i},
2090                        }},
2091                    )"#,
2092                    lower_import_intrinsic = if is_async || func_ty_async {
2093                        self.bindgen
2094                            .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::LowerImport))
2095                    } else {
2096                        self.bindgen.intrinsic(Intrinsic::AsyncTask(
2097                            AsyncTaskIntrinsic::LowerImportBackwardsCompat,
2098                        ))
2099                    }
2100                );
2101
2102                // NOTE: For Trampoline::LowerImport, the trampoline index is actually already defined,
2103                // but we *redefine* it to call the lower import function first.
2104                if is_async || func_ty_async {
2105                    uwriteln!(
2106                        self.src.js,
2107                        "let trampoline{i} = new WebAssembly.Suspending({call});"
2108                    );
2109                } else {
2110                    // TODO(breaking): once manually specifying async imports is removed,
2111                    // we can avoid the second check below.
2112                    uwriteln!(
2113                        self.src.js,
2114                        "let trampoline{i} = _trampoline{i}.manuallyAsync ? new WebAssembly.Suspending({call}) : {call};"
2115                    );
2116                }
2117            }
2118
2119            Trampoline::Transcoder {
2120                op,
2121                from,
2122                from64,
2123                to,
2124                to64,
2125            } => {
2126                if *from64 || *to64 {
2127                    unimplemented!("memory 64 transcoder");
2128                }
2129                let from = from.as_u32();
2130                let to = to.as_u32();
2131                match op {
2132                    Transcode::Copy(FixedEncoding::Utf8) => {
2133                        uwriteln!(
2134                            self.src.js,
2135                            r#"
2136                              function trampoline{i} (from_ptr, len, to_ptr) {{
2137                                  new Uint8Array(memory{to}.buffer, to_ptr, len).set(new Uint8Array(memory{from}.buffer, from_ptr, len));
2138                              }}
2139                            "#
2140                        );
2141                    }
2142                    Transcode::Copy(FixedEncoding::Utf16) => unimplemented!("utf16 copier"),
2143                    Transcode::Copy(FixedEncoding::Latin1) => unimplemented!("latin1 copier"),
2144                    Transcode::Latin1ToUtf16 => unimplemented!("latin to utf16 transcoder"),
2145                    Transcode::Latin1ToUtf8 => unimplemented!("latin to utf8 transcoder"),
2146                    Transcode::Utf16ToCompactProbablyUtf16 => {
2147                        unimplemented!("utf16 to compact wtf16 transcoder")
2148                    }
2149                    Transcode::Utf16ToCompactUtf16 => {
2150                        unimplemented!("utf16 to compact utf16 transcoder")
2151                    }
2152                    Transcode::Utf16ToLatin1 => unimplemented!("utf16 to latin1 transcoder"),
2153                    Transcode::Utf16ToUtf8 => {
2154                        uwriteln!(
2155                            self.src.js,
2156                            r#"
2157                              function trampoline{i} (src, src_len, dst, dst_len) {{
2158                                  const encoder = new TextEncoder();
2159                                  const {{ read, written }} = encoder.encodeInto(String.fromCharCode.apply(null, new Uint16Array(memory{from}.buffer, src, src_len)), new Uint8Array(memory{to}.buffer, dst, dst_len));
2160                                  return [read, written];
2161                              }}
2162                            "#,
2163                        );
2164                    }
2165                    Transcode::Utf8ToCompactUtf16 => {
2166                        unimplemented!("utf8 to compact utf16 transcoder")
2167                    }
2168                    Transcode::Utf8ToLatin1 => unimplemented!("utf8 to latin1 transcoder"),
2169                    Transcode::Utf8ToUtf16 => {
2170                        uwriteln!(
2171                            self.src.js,
2172                            r#"
2173                              function trampoline{i} (from_ptr, len, to_ptr) {{
2174                                  const decoder = new TextDecoder();
2175                                  const content = decoder.decode(new Uint8Array(memory{from}.buffer, from_ptr, len));
2176                                  const strlen = content.length
2177                                  const view = new Uint16Array(memory{to}.buffer, to_ptr, strlen * 2)
2178                                  for (var i = 0; i < strlen; i++) {{
2179                                      view[i] = content.charCodeAt(i);
2180                                  }}
2181                                  return strlen;
2182                              }}
2183                            "#,
2184                        );
2185                    }
2186                };
2187            }
2188
2189            Trampoline::ResourceNew {
2190                ty: resource_ty_idx,
2191                ..
2192            } => {
2193                self.ensure_resource_table(*resource_ty_idx);
2194                let rid = resource_ty_idx.as_u32();
2195                let rsc_table_create_own = self.bindgen.intrinsic(Intrinsic::Resource(
2196                    ResourceIntrinsic::ResourceTableCreateOwn,
2197                ));
2198                uwriteln!(
2199                    self.src.js,
2200                    "const trampoline{i} = {rsc_table_create_own}.bind(null, handleTable{rid});"
2201                );
2202            }
2203
2204            Trampoline::ResourceRep {
2205                ty: resource_ty_idx,
2206                ..
2207            } => {
2208                self.ensure_resource_table(*resource_ty_idx);
2209                let rid = resource_ty_idx.as_u32();
2210                let rsc_flag = self
2211                    .bindgen
2212                    .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableFlag));
2213                uwriteln!(
2214                    self.src.js,
2215                    "function trampoline{i} (handle) {{
2216                        return handleTable{rid}[(handle << 1) + 1] & ~{rsc_flag};
2217                    }}"
2218                );
2219            }
2220
2221            Trampoline::ResourceDrop {
2222                ty: resource_table_ty_idx,
2223                ..
2224            } => {
2225                self.ensure_resource_table(*resource_table_ty_idx);
2226                let tid = resource_table_ty_idx.as_u32();
2227                let resource_table_ty = &self.types[*resource_table_ty_idx];
2228                let resource_ty = resource_table_ty.unwrap_concrete_ty();
2229                let rid = resource_ty.as_u32();
2230
2231                // Build the code fragment that encapsulates calling the destructor
2232                let dtor = if let Some(resource_idx) =
2233                    self.component.defined_resource_index(resource_ty)
2234                {
2235                    let resource_def = self
2236                        .component
2237                        .initializers
2238                        .iter()
2239                        .find_map(|i| match i {
2240                            GlobalInitializer::Resource(r) if r.index == resource_idx => Some(r),
2241                            _ => None,
2242                        })
2243                        .unwrap();
2244
2245                    // If a destructor index is defined for the resource, call it
2246                    if let Some(dtor) = &resource_def.dtor {
2247                        format!(
2248                            "
2249                            {}(handleEntry.rep);",
2250                            self.core_def(dtor)
2251                        )
2252                    } else {
2253                        "".into()
2254                    }
2255                } else {
2256                    // Imported resource is one without a defined resource index.
2257                    // If it is a captured instance (class instance was created externally so had to
2258                    // be assigned a rep), and there is a Symbol.dispose handler, call it explicitly
2259                    // for imported resources when the resource is dropped.
2260                    // Otherwise if it is an instance without a captured class definition, then
2261                    // call the low-level bindgen destructor.
2262                    let symbol_dispose = self.bindgen.intrinsic(Intrinsic::SymbolDispose);
2263                    let symbol_cabi_dispose = self.bindgen.intrinsic(Intrinsic::SymbolCabiDispose);
2264
2265                    // previous imports walk should define all imported resources which are accessible
2266                    if let Some(imported_resource_local_name) =
2267                        self.bindgen.local_names.try_get(resource_ty)
2268                    {
2269                        format!(
2270                                            "
2271                            const rsc = captureTable{rid}.get(handleEntry.rep);
2272                            if (rsc) {{
2273                                if (rsc[{symbol_dispose}]) rsc[{symbol_dispose}]();
2274                                captureTable{rid}.delete(handleEntry.rep);
2275                            }} else if ({imported_resource_local_name}[{symbol_cabi_dispose}]) {{
2276                                {imported_resource_local_name}[{symbol_cabi_dispose}](handleEntry.rep);
2277                            }}"
2278                                        )
2279                    } else {
2280                        // If not, then capture / disposal paths are never called
2281                        format!(
2282                            "throw new TypeError('unreachable trampoline for resource [{:?}]')",
2283                            resource_ty
2284                        )
2285                    }
2286                };
2287
2288                let rsc_table_remove = self
2289                    .bindgen
2290                    .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableRemove));
2291                uwrite!(
2292                    self.src.js,
2293                    "function trampoline{i}(handle) {{
2294                        const handleEntry = {rsc_table_remove}(handleTable{tid}, handle);
2295                        if (handleEntry.own) {{
2296                            {dtor}
2297                        }}
2298                    }}
2299                    ",
2300                );
2301            }
2302
2303            Trampoline::ResourceTransferOwn => {
2304                let resource_transfer = self
2305                    .bindgen
2306                    .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTransferOwn));
2307                uwriteln!(self.src.js, "const trampoline{i} = {resource_transfer};");
2308            }
2309
2310            Trampoline::ResourceTransferBorrow => {
2311                let resource_transfer =
2312                    self.bindgen
2313                        .intrinsic(if self.bindgen.opts.valid_lifting_optimization {
2314                            Intrinsic::Resource(
2315                                ResourceIntrinsic::ResourceTransferBorrowValidLifting,
2316                            )
2317                        } else {
2318                            Intrinsic::Resource(ResourceIntrinsic::ResourceTransferBorrow)
2319                        });
2320                uwriteln!(self.src.js, "const trampoline{i} = {resource_transfer};");
2321            }
2322
2323            Trampoline::ResourceEnterCall => {
2324                let scope_id = self.bindgen.intrinsic(Intrinsic::ScopeId);
2325                uwrite!(self.src.js, "function trampoline{i}() {{ {scope_id}++; }}");
2326            }
2327
2328            Trampoline::ResourceExitCall => {
2329                let scope_id = self.bindgen.intrinsic(Intrinsic::ScopeId);
2330                let resource_borrows = self
2331                    .bindgen
2332                    .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceCallBorrows));
2333                let handle_tables = self.bindgen.intrinsic(Intrinsic::HandleTables);
2334                // To verify that borrows are dropped, it is enough to verify that the handle
2335                // either no longer exists (part of free list) or belongs to another scope, since
2336                // the enter call closed off the ability to create new handles in the parent scope
2337                uwrite!(
2338                    self.src.js,
2339                    r#"function trampoline{i}() {{
2340                        {scope_id}--;
2341                        for (const {{ rid, handle }} of {resource_borrows}) {{
2342                            const storedScopeId = {handle_tables}[rid][handle << 1]
2343                            if (storedScopeId === {scope_id}) {{
2344                                throw new TypeError('borrows not dropped for resource call');
2345                            }}
2346                        }}
2347                        {resource_borrows} = [];
2348                    }}
2349                    "#,
2350                );
2351            }
2352
2353            Trampoline::ContextSet { instance, slot, .. } => {
2354                let context_set_fn = self
2355                    .bindgen
2356                    .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::ContextSet));
2357                let component_idx = instance.as_u32();
2358                uwriteln!(
2359                    self.src.js,
2360                    r#"
2361                      const trampoline{i} = {context_set_fn}.bind(null, {{
2362                          componentIdx: {component_idx},
2363                          slot: {slot},
2364                      }});
2365                    "#
2366                );
2367            }
2368
2369            Trampoline::ContextGet { instance, slot } => {
2370                let context_get_fn = self
2371                    .bindgen
2372                    .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::ContextGet));
2373                let component_idx = instance.as_u32();
2374                uwriteln!(
2375                    self.src.js,
2376                    r#"
2377                      const trampoline{i} = {context_get_fn}.bind(null, {{
2378                          componentIdx: {component_idx},
2379                          slot: {slot},
2380                      }});
2381                    "#
2382                );
2383            }
2384
2385            Trampoline::TaskReturn {
2386                results, options, ..
2387            } => {
2388                let canon_opts = self
2389                    .component
2390                    .options
2391                    .get(*options)
2392                    .expect("failed to find options");
2393                let CanonicalOptions {
2394                    instance,
2395                    async_,
2396                    data_model:
2397                        CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
2398                    callback,
2399                    post_return,
2400                    ..
2401                } = canon_opts
2402                else {
2403                    unreachable!("unexpected memory data model during task.return");
2404                };
2405
2406                // Validate canonopts
2407                if realloc.is_some() && memory.is_none() {
2408                    panic!("memory must be present if realloc is");
2409                }
2410                if *async_ && post_return.is_some() {
2411                    panic!("async and post return must not be specified together");
2412                }
2413                if *async_ && callback.is_none() {
2414                    panic!("callback must be specified for async");
2415                }
2416                if let Some(cb_idx) = callback {
2417                    let cb_fn = &self.types[TypeFuncIndex::from_u32(cb_idx.as_u32())];
2418                    match self.types[cb_fn.params].types[..] {
2419                        [InterfaceType::S32, InterfaceType::S32, InterfaceType::S32] => {}
2420                        _ => panic!("unexpected params for async callback fn"),
2421                    }
2422                    match self.types[cb_fn.results].types[..] {
2423                        [InterfaceType::S32] => {}
2424                        _ => panic!("unexpected results for async callback fn"),
2425                    }
2426                }
2427
2428                let result_types = &self.types[*results].types;
2429
2430                // Calculate the number of parameters required to represent the results,
2431                // and whether they'll be stored in memory
2432                let result_flat_param_total: usize = result_types
2433                    .iter()
2434                    .map(|t| {
2435                        self.types
2436                            .canonical_abi(t)
2437                            .flat_count
2438                            .map(usize::from)
2439                            .unwrap_or(0)
2440                    })
2441                    .sum();
2442                let use_direct_params = result_flat_param_total < MAX_ASYNC_FLAT_PARAMS;
2443
2444                // Build up a list of all the lifting functions that will be needed for the types
2445                // that are actually being passed through task.return
2446                let mut lift_fns: Vec<String> = Vec::with_capacity(result_types.len());
2447                for result_ty in result_types {
2448                    lift_fns.push(gen_flat_lift_fn_js_expr(
2449                        self,
2450                        result_ty,
2451                        &canon_opts.string_encoding,
2452                    ));
2453                }
2454                let lift_fns_js = format!("[{}]", lift_fns.join(","));
2455
2456                // Build up a list of all the lowering functions that will be needed for the types
2457                // that are actually being passed through task.return
2458                //
2459                // This is usually only necessary if this task is part of a guest->guest async call
2460                // (i.e. via prepare & async start call)
2461                let mut lower_fns: Vec<String> = Vec::with_capacity(result_types.len());
2462                for result_ty in result_types {
2463                    lower_fns.push(gen_flat_lower_fn_js_expr(
2464                        self.bindgen,
2465                        self.types,
2466                        result_ty,
2467                        &canon_opts.string_encoding,
2468                    ));
2469                }
2470                let lower_fns_js = format!("[{}]", lower_fns.join(","));
2471
2472                let get_memory_fn_js = memory
2473                    .map(|idx| format!("() => memory{}", idx.as_u32()))
2474                    .unwrap_or_else(|| "() => null".into());
2475                let memory_idx_js = memory
2476                    .map(|idx| idx.as_u32().to_string())
2477                    .unwrap_or_else(|| "null".into());
2478                let component_idx = instance.as_u32();
2479                let task_return_fn = self
2480                    .bindgen
2481                    .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::TaskReturn));
2482                let callback_fn_idx = callback
2483                    .map(|v| v.as_u32().to_string())
2484                    .unwrap_or_else(|| "null".into());
2485
2486                uwriteln!(
2487                    self.src.js,
2488                    "const trampoline{i} = {task_return_fn}.bind(
2489                         null,
2490                         {{
2491                             componentIdx: {component_idx},
2492                             useDirectParams: {use_direct_params},
2493                             getMemoryFn: {get_memory_fn_js},
2494                             memoryIdx: {memory_idx_js},
2495                             callbackFnIdx: {callback_fn_idx},
2496                             liftFns: {lift_fns_js},
2497                             lowerFns: {lower_fns_js},
2498                         }},
2499                     );",
2500                );
2501            }
2502
2503            Trampoline::BackpressureInc { instance } => {
2504                let backpressure_inc_fn = self
2505                    .bindgen
2506                    .intrinsic(Intrinsic::Component(ComponentIntrinsic::BackpressureInc));
2507                uwriteln!(
2508                    self.src.js,
2509                    "const trampoline{i} = {backpressure_inc_fn}.bind(null, {instance});\n",
2510                    instance = instance.as_u32(),
2511                );
2512            }
2513
2514            Trampoline::BackpressureDec { instance } => {
2515                let backpressure_dec_fn = self
2516                    .bindgen
2517                    .intrinsic(Intrinsic::Component(ComponentIntrinsic::BackpressureDec));
2518                uwriteln!(
2519                    self.src.js,
2520                    "const trampoline{i} = {backpressure_dec_fn}.bind(null, {instance});\n",
2521                    instance = instance.as_u32(),
2522                );
2523            }
2524
2525            Trampoline::ThreadYield {
2526                cancellable,
2527                instance,
2528            } => {
2529                let yield_fn = self
2530                    .bindgen
2531                    .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::Yield));
2532                let component_instance_idx = instance.as_u32();
2533                uwriteln!(
2534                    self.src.js,
2535                    r#"
2536                      const trampoline{i} = {yield_fn}.bind(null, {{
2537                          isCancellable: {cancellable},
2538                          componentIdx: {component_instance_idx},
2539                      }});
2540                    "#,
2541                );
2542            }
2543            Trampoline::ThreadIndex => todo!("Trampoline::ThreadIndex"),
2544            Trampoline::ThreadNewIndirect { .. } => todo!("Trampoline::ThreadNewIndirect"),
2545            Trampoline::ThreadSwitchTo { .. } => todo!("Trampoline::ThreadSwitchTo"),
2546            Trampoline::ThreadSuspend { .. } => todo!("Trampoline::ThreadSuspend"),
2547            Trampoline::ThreadResumeLater { .. } => todo!("Trampoline::ThreadResumeLater"),
2548            Trampoline::ThreadYieldTo { .. } => todo!("Trampoline::ThreadYieldTo"),
2549
2550            Trampoline::Trap => {
2551                uwriteln!(
2552                    self.src.js,
2553                    "function trampoline{i}(rep) {{ throw new TypeError('Trap'); }}"
2554                );
2555            }
2556
2557            Trampoline::EnterSyncCall => {
2558                let enter_symmetric_sync_guest_call_fn = self.bindgen.intrinsic(
2559                    Intrinsic::AsyncTask(AsyncTaskIntrinsic::EnterSymmetricSyncGuestCall),
2560                );
2561                uwriteln!(
2562                    self.src.js,
2563                    r#"
2564                      const trampoline{i} = {enter_symmetric_sync_guest_call_fn};
2565                    "#,
2566                );
2567            }
2568
2569            Trampoline::ExitSyncCall => {
2570                let exit_symmetric_sync_guest_call_fn = self.bindgen.intrinsic(
2571                    Intrinsic::AsyncTask(AsyncTaskIntrinsic::ExitSymmetricSyncGuestCall),
2572                );
2573                uwriteln!(
2574                    self.src.js,
2575                    "const trampoline{i} = {exit_symmetric_sync_guest_call_fn};\n",
2576                );
2577            }
2578        }
2579    }
2580
2581    fn instantiation_global_initializer(&mut self, init: &GlobalInitializer) {
2582        match init {
2583            // Extracting callbacks is a part of the async support for hosts -- it ensures that
2584            // a given core export can be turned into a callback function that will be used
2585            // later.
2586            //
2587            // Generally what we have to do here is to create a callback that can be called upon re-entrance
2588            // into the component after a related suspension.
2589            GlobalInitializer::ExtractCallback(ExtractCallback { index, def }) => {
2590                let callback_idx = index.as_u32();
2591                let core_def = self.core_def(def);
2592
2593                uwriteln!(self.src.js, "let callback_{callback_idx};",);
2594
2595                // If the function returns an async value like a stream or future,
2596                // the callback that is executed in the the event loop (`AsyncTaskIntrinsic::DriverLoop`)
2597                // may attempt to wait due to calling necessarily async host imports like {stream, future}.{write, read}.
2598                //
2599                // Here, we mark the task with an indicator that denotes whether the callback should be run this way.
2600                //
2601                // TODO: can we be more selective here rather than wrapping every callback in WebAssembly.promising?
2602                // every callback *could* do stream.write, but many may not.
2603                uwriteln!(
2604                    self.src.js_init,
2605                    r#"
2606                      callback_{callback_idx} = WebAssembly.promising({core_def});
2607                      callback_{callback_idx}.fnName = "{core_def}";
2608                    "#
2609                );
2610            }
2611
2612            GlobalInitializer::InstantiateModule(m, _) => match m {
2613                InstantiateModule::Static(idx, args) => {
2614                    self.instantiate_static_module(*idx, args);
2615                }
2616                // This is only needed when instantiating an imported core wasm
2617                // module which while easy to implement here is not possible to
2618                // test at this time so it's left unimplemented.
2619                InstantiateModule::Import(..) => unimplemented!(),
2620            },
2621
2622            GlobalInitializer::LowerImport { index, import } => {
2623                self.lower_import(*index, *import);
2624            }
2625
2626            GlobalInitializer::ExtractMemory(m) => {
2627                let def = self.core_export_var_name(&m.export);
2628                let idx = m.index.as_u32();
2629                uwriteln!(self.src.js, "let memory{idx};");
2630                uwriteln!(self.src.js_init, "memory{idx} = {def};");
2631            }
2632
2633            GlobalInitializer::ExtractRealloc(r) => {
2634                let def = self.core_def(&r.def);
2635                let idx = r.index.as_u32();
2636                uwriteln!(self.src.js, "let realloc{idx};");
2637                uwriteln!(self.src.js, "let realloc{idx}Async;");
2638                uwriteln!(self.src.js_init, "realloc{idx} = {def};",);
2639                // NOTE: sometimes we may be fed a realloc that isn't a webassembly function at all
2640                // but has instead been converted to JS (see 'flavorful' test in test/runtime.js')
2641                uwriteln!(
2642                    self.src.js_init,
2643                    r#"
2644                      try {{
2645                          realloc{idx}Async = WebAssembly.promising({def});
2646                      }} catch(err) {{
2647                          realloc{idx}Async = {def};
2648                      }}
2649                    "#
2650                );
2651            }
2652
2653            GlobalInitializer::ExtractPostReturn(p) => {
2654                let def = self.core_def(&p.def);
2655                let idx = p.index.as_u32();
2656                uwriteln!(self.src.js, "let postReturn{idx};");
2657                uwriteln!(self.src.js, "let postReturn{idx}Async;");
2658                uwriteln!(self.src.js_init, "postReturn{idx} = {def};");
2659                // NOTE: sometimes we may be fed a post return fn that isn't a webassembly function
2660                // at all but has instead been converted to JS (see 'flavorful' test in test/runtime.js)
2661                uwriteln!(
2662                    self.src.js_init,
2663                    r#"
2664                      try {{
2665                          postReturn{idx}Async = WebAssembly.promising({def});
2666                      }} catch(err) {{
2667                          postReturn{idx}Async = {def};
2668                      }}
2669                    "#
2670                );
2671            }
2672
2673            GlobalInitializer::Resource(_) => {}
2674
2675            GlobalInitializer::ExtractTable(_) => {}
2676        }
2677    }
2678
2679    fn instantiate_static_module(&mut self, module_idx: StaticModuleIndex, args: &[CoreDef]) {
2680        // Build a JS "import object" which represents `args`. The `args` is a
2681        // flat representation which needs to be zip'd with the list of names to
2682        // correspond to the JS wasm embedding API. This is one of the major
2683        // differences between Wasmtime's and JS's embedding API.
2684        let mut import_obj = BTreeMap::new();
2685        for (module, name, arg) in self.modules[module_idx].imports(args) {
2686            let def = self.augmented_import_def(&arg);
2687            let dst = import_obj.entry(module).or_insert(BTreeMap::new());
2688            let prev = dst.insert(name, def);
2689            assert!(
2690                prev.is_none(),
2691                "unsupported duplicate import of `{module}::{name}`"
2692            );
2693            assert!(prev.is_none());
2694        }
2695
2696        // Build list of imports
2697        let mut imports = String::new();
2698        if !import_obj.is_empty() {
2699            imports.push_str(", {\n");
2700            for (module, names) in import_obj {
2701                imports.push_str(&maybe_quote_id(module));
2702                imports.push_str(": {\n");
2703                for (name, val) in names {
2704                    imports.push_str(&maybe_quote_id(name));
2705                    uwriteln!(imports, ": {val},");
2706                }
2707                imports.push_str("},\n");
2708            }
2709            imports.push('}');
2710        }
2711
2712        let i = self.instances.push(module_idx);
2713        let iu32 = i.as_u32();
2714        let instantiate = self.bindgen.intrinsic(Intrinsic::InstantiateCore);
2715        uwriteln!(self.src.js, "let exports{iu32};");
2716
2717        match self.bindgen.opts.instantiation {
2718            Some(InstantiationMode::Async) | None => {
2719                uwriteln!(
2720                    self.src.js_init,
2721                    "({{ exports: exports{iu32} }} = yield {instantiate}(yield module{}{imports}));",
2722                    module_idx.as_u32(),
2723                )
2724            }
2725
2726            Some(InstantiationMode::Sync) => {
2727                uwriteln!(
2728                    self.src.js_init,
2729                    "({{ exports: exports{iu32} }} = {instantiate}(module{}{imports}));",
2730                    module_idx.as_u32(),
2731                );
2732            }
2733        }
2734    }
2735
2736    /// Map all types in parameters and results to local resource types
2737    ///
2738    /// # Arguments
2739    ///
2740    /// * `func` - The function in question
2741    /// * `ty_func_idx` - Type index of the function
2742    /// * `resource_map` - resource map of locally resolved types
2743    fn create_resource_fn_map(
2744        &mut self,
2745        func: &Function,
2746        ty_func_idx: TypeFuncIndex,
2747        resource_map: &mut ResourceMap,
2748    ) {
2749        // Connect resources used in parameters
2750        let params_ty = &self.types[self.types[ty_func_idx].params];
2751        for (p, iface_ty) in func.params.iter().zip(params_ty.types.iter()) {
2752            if let Type::Id(id) = p.ty {
2753                self.connect_resource_types(id, iface_ty, resource_map);
2754            }
2755        }
2756        // Connect resources used in results
2757        let results_ty = &self.types[self.types[ty_func_idx].results];
2758        if let (Some(Type::Id(id)), Some(iface_ty)) = (func.result, results_ty.types.first()) {
2759            self.connect_resource_types(id, iface_ty, resource_map);
2760        }
2761    }
2762
2763    fn resource_name(
2764        resolve: &Resolve,
2765        local_names: &'a mut LocalNames,
2766        resource: TypeId,
2767        resource_map: &BTreeMap<TypeId, ResourceIndex>,
2768    ) -> &'a str {
2769        let resource = crate::dealias(resolve, resource);
2770        local_names
2771            .get_or_create(
2772                resource_map[&resource],
2773                &resolve.types[resource]
2774                    .name
2775                    .as_ref()
2776                    .unwrap()
2777                    .to_upper_camel_case(),
2778            )
2779            .0
2780    }
2781
2782    fn lower_import(&mut self, index: LoweredIndex, import: RuntimeImportIndex) {
2783        let (options, trampoline, func_ty) = self.lowering_options[index];
2784
2785        // Get the world key for the CM import
2786        let (import_index, path) = &self.component.imports[import];
2787        let (import_name, _) = &self.component.import_types[*import_index];
2788        let world_key = &self.imports[import_name];
2789
2790        // Determine the name of the function
2791        let (func, func_name, iface_name) =
2792            match &self.resolve.worlds[self.world].imports[world_key] {
2793                WorldItem::Function(func) => {
2794                    assert_eq!(path.len(), 0);
2795                    (func, import_name, None)
2796                }
2797                WorldItem::Interface { id, .. } => {
2798                    assert_eq!(path.len(), 1);
2799                    let iface = &self.resolve.interfaces[*id];
2800                    let func = &iface.functions[&path[0]];
2801                    (
2802                        func,
2803                        &path[0],
2804                        Some(iface.name.as_deref().unwrap_or_else(|| import_name)),
2805                    )
2806                }
2807                WorldItem::Type { .. } => unreachable!("unexpected imported world item type"),
2808            };
2809
2810        let is_async = is_async_fn(func, options);
2811
2812        if options.async_ {
2813            assert!(
2814                options.post_return.is_none(),
2815                "async function {func_name} (import {import_name}) can't have post return",
2816            );
2817        }
2818
2819        // Host lifted async import (i.e. JSPI)
2820        let requires_async_porcelain = requires_async_porcelain(
2821            FunctionIdentifier::Fn(func),
2822            import_name,
2823            &self.async_imports,
2824        );
2825
2826        // Nested interfaces only currently possible through mapping
2827        let (import_specifier, maybe_iface_member) = map_import(
2828            &self.bindgen.opts.map,
2829            if iface_name.is_some() {
2830                import_name
2831            } else {
2832                match func.kind {
2833                    FunctionKind::Method(_) => {
2834                        let stripped = import_name.strip_prefix("[method]").unwrap();
2835                        &stripped[0..stripped.find(".").unwrap()]
2836                    }
2837                    FunctionKind::AsyncMethod(_) => {
2838                        let stripped = import_name.strip_prefix("[async method]").unwrap();
2839                        &stripped[0..stripped.find(".").unwrap()]
2840                    }
2841                    FunctionKind::Static(_) => {
2842                        let stripped = import_name.strip_prefix("[static]").unwrap();
2843                        &stripped[0..stripped.find(".").unwrap()]
2844                    }
2845                    FunctionKind::AsyncStatic(_) => {
2846                        let stripped = import_name.strip_prefix("[async static]").unwrap();
2847                        &stripped[0..stripped.find(".").unwrap()]
2848                    }
2849                    FunctionKind::Constructor(_) => {
2850                        import_name.strip_prefix("[constructor]").unwrap()
2851                    }
2852                    FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => import_name,
2853                }
2854            },
2855        );
2856
2857        // Create mappings for resources
2858        let mut import_resource_map = ResourceMap::new();
2859
2860        self.create_resource_fn_map(func, func_ty, &mut import_resource_map);
2861
2862        let (callee_name, call_type) = match func.kind {
2863            FunctionKind::Freestanding => (
2864                self.bindgen
2865                    .local_names
2866                    .get_or_create(
2867                        format!(
2868                            "import:{import}-{maybe_iface_member}-{func_name}",
2869                            import = import_specifier,
2870                            maybe_iface_member = maybe_iface_member.as_deref().unwrap_or(""),
2871                            func_name = &func.name
2872                        ),
2873                        &func.name,
2874                    )
2875                    .0
2876                    .to_string(),
2877                CallType::Standard,
2878            ),
2879
2880            FunctionKind::AsyncFreestanding => (
2881                self.bindgen
2882                    .local_names
2883                    .get_or_create(
2884                        format!(
2885                            "import:async-{import}-{maybe_iface_member}-{func_name}",
2886                            import = import_specifier,
2887                            maybe_iface_member = maybe_iface_member.as_deref().unwrap_or(""),
2888                            func_name = &func.name
2889                        ),
2890                        &func.name,
2891                    )
2892                    .0
2893                    .to_string(),
2894                CallType::AsyncStandard,
2895            ),
2896
2897            FunctionKind::Method(_) => (
2898                func.item_name().to_lower_camel_case(),
2899                CallType::CalleeResourceDispatch,
2900            ),
2901
2902            FunctionKind::AsyncMethod(_) => (
2903                func.item_name().to_lower_camel_case(),
2904                CallType::AsyncCalleeResourceDispatch,
2905            ),
2906
2907            FunctionKind::Static(resource_id) => (
2908                format!(
2909                    "{}.{}",
2910                    Instantiator::resource_name(
2911                        self.resolve,
2912                        &mut self.bindgen.local_names,
2913                        resource_id,
2914                        &self.imports_resource_types
2915                    ),
2916                    func.item_name().to_lower_camel_case()
2917                ),
2918                CallType::Standard,
2919            ),
2920
2921            FunctionKind::AsyncStatic(resource_id) => (
2922                format!(
2923                    "{}.{}",
2924                    Instantiator::resource_name(
2925                        self.resolve,
2926                        &mut self.bindgen.local_names,
2927                        resource_id,
2928                        &self.imports_resource_types
2929                    ),
2930                    func.item_name().to_lower_camel_case()
2931                ),
2932                CallType::AsyncStandard,
2933            ),
2934
2935            FunctionKind::Constructor(resource_id) => (
2936                format!(
2937                    "new {}",
2938                    Instantiator::resource_name(
2939                        self.resolve,
2940                        &mut self.bindgen.local_names,
2941                        resource_id,
2942                        &self.imports_resource_types
2943                    )
2944                ),
2945                CallType::Standard,
2946            ),
2947        };
2948
2949        let nparams = self
2950            .resolve
2951            .wasm_signature(AbiVariant::GuestImport, func)
2952            .params
2953            .len();
2954
2955        // Generate the JS trampoline function for a bound import
2956        let trampoline_idx = trampoline.as_u32();
2957        match self.bindgen.opts.import_bindings {
2958            None | Some(BindingsMode::Js) | Some(BindingsMode::Hybrid) => {
2959                // TODO(breaking): remove as we do not not need to manually specify async imports anymore in P3 w/ native coloring
2960                if is_async | requires_async_porcelain {
2961                    // NOTE: for async imports that will go through Trampoline::LowerImport,
2962                    // we prefix the raw import with '_' as it will later be used in the
2963                    // definition of trampoline{i} which will actually be fed into
2964                    // unbundled modules
2965                    uwrite!(
2966                        self.src.js,
2967                        "\nconst _trampoline{trampoline_idx} = async function"
2968                    );
2969                } else {
2970                    uwrite!(
2971                        self.src.js,
2972                        "\nconst _trampoline{trampoline_idx} = function"
2973                    );
2974                }
2975
2976                let iface_name = if import_name.is_empty() {
2977                    None
2978                } else {
2979                    Some(import_name.to_string())
2980                };
2981
2982                // Write out the function (brace + body + brace)
2983                self.bindgen(JsFunctionBindgenArgs {
2984                    nparams,
2985                    call_type,
2986                    iface_name: iface_name.as_deref(),
2987                    callee: &callee_name,
2988                    opts: options,
2989                    func,
2990                    resource_map: &import_resource_map,
2991                    abi: AbiVariant::GuestImport,
2992                    requires_async_porcelain,
2993                    is_async,
2994                });
2995                uwriteln!(self.src.js, "");
2996
2997                uwriteln!(
2998                    self.src.js,
2999                    "_trampoline{trampoline_idx}.fnName = '{}#{callee_name}';",
3000                    iface_name.unwrap_or_default(),
3001                );
3002
3003                // TODO(breaking): remove once support for manually specified async imports is removed
3004                if requires_async_porcelain {
3005                    uwriteln!(
3006                        self.src.js,
3007                        "_trampoline{trampoline_idx}.manuallyAsync = true;"
3008                    );
3009                }
3010            }
3011
3012            Some(BindingsMode::Optimized) | Some(BindingsMode::DirectOptimized) => {
3013                uwriteln!(self.src.js, "let trampoline{trampoline_idx};");
3014            }
3015        };
3016
3017        // Build import bindings & trampolines for the import
3018        //
3019        // This is only necessary if an import binding mode is specified and not JS (the default),
3020        // (e.g. Optimized, Direct, Hybrid).
3021        if !matches!(
3022            self.bindgen.opts.import_bindings,
3023            None | Some(BindingsMode::Js)
3024        ) {
3025            let (memory, realloc) =
3026                if let CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions {
3027                    memory,
3028                    realloc,
3029                }) = options.data_model
3030                {
3031                    (
3032                        memory.map(|idx| format!(" memory: memory{},", idx.as_u32())),
3033                        realloc.map(|idx| format!(" realloc: realloc{},", idx.as_u32())),
3034                    )
3035                } else {
3036                    (None, None)
3037                };
3038            let memory = memory.unwrap_or_default();
3039            let realloc = realloc.unwrap_or_default();
3040
3041            let post_return = options
3042                .post_return
3043                .map(|idx| format!(" postReturn: postReturn{},", idx.as_u32()))
3044                .unwrap_or("".into());
3045            let string_encoding = match options.string_encoding {
3046                wasmtime_environ::component::StringEncoding::Utf8 => "",
3047                wasmtime_environ::component::StringEncoding::Utf16 => " stringEncoding: 'utf16',",
3048                wasmtime_environ::component::StringEncoding::CompactUtf16 => {
3049                    " stringEncoding: 'compact-utf16',"
3050                }
3051            };
3052
3053            let callee_name = match func.kind {
3054                FunctionKind::Constructor(_) => callee_name[4..].to_string(),
3055
3056                FunctionKind::Static(_)
3057                | FunctionKind::AsyncStatic(_)
3058                | FunctionKind::Freestanding
3059                | FunctionKind::AsyncFreestanding => callee_name.to_string(),
3060
3061                FunctionKind::Method(resource_id) | FunctionKind::AsyncMethod(resource_id) => {
3062                    format!(
3063                        "{}.prototype.{callee_name}",
3064                        Instantiator::resource_name(
3065                            self.resolve,
3066                            &mut self.bindgen.local_names,
3067                            resource_id,
3068                            &self.imports_resource_types
3069                        )
3070                    )
3071                }
3072            };
3073
3074            // Save information about imported resources for later
3075            self.resource_imports.extend(import_resource_map.clone());
3076
3077            let resource_tables = {
3078                let mut resource_tables: Vec<TypeResourceTableIndex> = Vec::new();
3079
3080                for (_, data) in import_resource_map {
3081                    let ResourceTable {
3082                        data: ResourceData::Host { tid, .. },
3083                        ..
3084                    } = &data
3085                    else {
3086                        unreachable!("unexpected non-host resource table");
3087                    };
3088                    resource_tables.push(*tid);
3089                }
3090
3091                if resource_tables.is_empty() {
3092                    "".to_string()
3093                } else {
3094                    format!(
3095                        " resourceTables: [{}],",
3096                        resource_tables
3097                            .iter()
3098                            .map(|x| format!("handleTable{}", x.as_u32()))
3099                            .collect::<Vec<String>>()
3100                            .join(", ")
3101                    )
3102                }
3103            };
3104
3105            // Build trampolines for the import
3106            match self.bindgen.opts.import_bindings {
3107                Some(BindingsMode::Hybrid) => {
3108                    let symbol_cabi_lower = self.bindgen.intrinsic(Intrinsic::SymbolCabiLower);
3109                    uwriteln!(self.src.js_init, "if ({callee_name}[{symbol_cabi_lower}]) {{
3110                        trampoline{} = {callee_name}[{symbol_cabi_lower}]({{{memory}{realloc}{post_return}{string_encoding}{resource_tables}}});
3111                    }}", trampoline.as_u32());
3112                }
3113                Some(BindingsMode::Optimized) => {
3114                    let symbol_cabi_lower = self.bindgen.intrinsic(Intrinsic::SymbolCabiLower);
3115                    if !self.bindgen.opts.valid_lifting_optimization {
3116                        uwriteln!(self.src.js_init, "if (!{callee_name}[{symbol_cabi_lower}]) {{
3117                            throw new TypeError('import for \"{import_name}\" does not define a Symbol.for(\"cabiLower\") optimized binding');
3118                        }}");
3119                    }
3120                    uwriteln!(
3121                        self.src.js_init,
3122                        "trampoline{} = {callee_name}[{symbol_cabi_lower}]({{{memory}{realloc}{post_return}{string_encoding}{resource_tables}}});",
3123                        trampoline.as_u32()
3124                    );
3125                }
3126                Some(BindingsMode::DirectOptimized) => {
3127                    uwriteln!(
3128                        self.src.js_init,
3129                        "trampoline{} = {callee_name}({{{memory}{realloc}{post_return}{string_encoding}}});",
3130                        trampoline.as_u32()
3131                    );
3132                }
3133                None | Some(BindingsMode::Js) => unreachable!("invalid bindings mode"),
3134            };
3135        }
3136
3137        // Figure out the function name and callee (e.g. class for a given resource) to use
3138        let (import_name, binding_name) = match func.kind {
3139            FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
3140                (func_name.to_lower_camel_case(), callee_name)
3141            }
3142
3143            FunctionKind::Method(tid)
3144            | FunctionKind::AsyncMethod(tid)
3145            | FunctionKind::Static(tid)
3146            | FunctionKind::AsyncStatic(tid)
3147            | FunctionKind::Constructor(tid) => {
3148                let ty = &self.resolve.types[tid];
3149                let class_name = ty.name.as_ref().unwrap().to_upper_camel_case();
3150                let resource_name = Instantiator::resource_name(
3151                    self.resolve,
3152                    &mut self.bindgen.local_names,
3153                    tid,
3154                    &self.imports_resource_types,
3155                )
3156                .to_string();
3157                (class_name, resource_name)
3158            }
3159        };
3160
3161        self.ensure_import(
3162            import_specifier,
3163            iface_name,
3164            maybe_iface_member.as_deref(),
3165            if iface_name.is_some() {
3166                Some(import_name.to_string())
3167            } else {
3168                None
3169            },
3170            binding_name,
3171        );
3172    }
3173
3174    /// Process an import if it has not already been processed
3175    ///
3176    /// # Arguments
3177    ///
3178    /// * `import_specifier` - The specifier of the import as used in JS (ex. `"@bytecodealliance/preview2-shim/random"`)
3179    /// * `iface_name` - The name of the WIT interface related to this binding, if present (ex. `"random"`)
3180    /// * `iface_member` - The name of the interface member, if present (ex. `"random"`)
3181    /// * `import_binding` - The name of binding, if present (ex. `"getRandomBytes"`)
3182    /// * `local_name` - Local name of the import (ex. `"getRandomBytes"`)
3183    ///
3184    fn ensure_import(
3185        &mut self,
3186        import_specifier: String,
3187        iface_name: Option<&str>,
3188        iface_member: Option<&str>,
3189        import_binding: Option<String>,
3190        local_name: String,
3191    ) {
3192        if import_specifier.starts_with("webidl:") {
3193            self.bindgen
3194                .intrinsic(Intrinsic::WebIdl(WebIdlIntrinsic::GlobalThisIdlProxy));
3195        }
3196
3197        // Build the import path depending on the kind of interface
3198        let mut import_path = Vec::with_capacity(2);
3199        import_path.push(import_specifier);
3200        if let Some(_iface_name) = iface_name {
3201            // Mapping can be used to construct virtual nested namespaces
3202            // which is used eg to support WASI interface groupings
3203            if let Some(iface_member) = iface_member {
3204                import_path.push(iface_member.to_lower_camel_case());
3205            }
3206            import_path.push(import_binding.clone().unwrap());
3207        } else if let Some(iface_member) = iface_member {
3208            import_path.push(iface_member.into());
3209        } else if let Some(import_binding) = &import_binding {
3210            import_path.push(import_binding.into());
3211        }
3212
3213        // Add the import binding that represents this import
3214        self.bindgen
3215            .esm_bindgen
3216            .add_import_binding(&import_path, local_name);
3217    }
3218
3219    /// Connect resources that have no types
3220    ///
3221    /// Commonly this is used for resources that have a type on on the import side
3222    /// but no relevant type on the receiving side, for which local types must be generated locally:
3223    /// - `error-context`
3224    /// - `future<_>`
3225    /// - `stream<_>`
3226    ///
3227    fn connect_p3_resources(
3228        &mut self,
3229        id: &TypeId,
3230        maybe_elem_ty: &Option<Type>,
3231        iface_ty: &InterfaceType,
3232        resource_map: &mut ResourceMap,
3233    ) {
3234        let remote_resource = match iface_ty {
3235            InterfaceType::Future(table_idx) => ResourceTable {
3236                imported: true,
3237                data: ResourceData::Guest {
3238                    resource_name: "Future".into(),
3239                    prefix: Some(format!("${}", table_idx.as_u32())),
3240                    extra: Some(ResourceExtraData::Future {
3241                        table_idx: *table_idx,
3242                        elem_ty: *maybe_elem_ty,
3243                    }),
3244                },
3245            },
3246            InterfaceType::Stream(table_idx) => ResourceTable {
3247                imported: true,
3248                data: ResourceData::Guest {
3249                    resource_name: "Stream".into(),
3250                    prefix: Some(format!("${}", table_idx.as_u32())),
3251                    extra: Some(ResourceExtraData::Stream {
3252                        table_idx: *table_idx,
3253                        elem_ty: *maybe_elem_ty,
3254                    }),
3255                },
3256            },
3257            InterfaceType::ErrorContext(table_idx) => ResourceTable {
3258                imported: true,
3259                data: ResourceData::Guest {
3260                    resource_name: "ErrorContext".into(),
3261                    prefix: Some(format!("${}", table_idx.as_u32())),
3262                    extra: Some(ResourceExtraData::ErrorContext {
3263                        table_idx: *table_idx,
3264                    }),
3265                },
3266            },
3267            _ => unreachable!("unexpected interface type [{iface_ty:?}] with no type"),
3268        };
3269
3270        resource_map.insert(*id, remote_resource);
3271    }
3272
3273    /// Connect two types as host resources
3274    ///
3275    /// # Arguments
3276    ///
3277    /// * `t` - the TypeId
3278    /// * `tid` - Index into the type resource table of the interface (foreign side)
3279    /// * `resource_map` - Resource map that holds resource pairings
3280    ///
3281    fn connect_host_resource(
3282        &mut self,
3283        t: TypeId,
3284        resource_table_ty_idx: TypeResourceTableIndex,
3285        resource_map: &mut ResourceMap,
3286    ) {
3287        self.ensure_resource_table(resource_table_ty_idx);
3288
3289        // Figure out whether the resource index we're dealing with is for an imported type
3290        let resource_table_ty = &self.types[resource_table_ty_idx];
3291        let resource_idx = resource_table_ty.unwrap_concrete_ty();
3292        let imported = self
3293            .component
3294            .defined_resource_index(resource_idx)
3295            .is_none();
3296
3297        // Retrieve the resource id for the type definition
3298        let resource_id = crate::dealias(self.resolve, t);
3299        let ty = &self.resolve.types[resource_id];
3300
3301        // If the resource is defined by this component (i.e. exported/used internally, *not* imported),
3302        // then determine the destructor that should be run based on the relevant resource
3303        let mut dtor_str = None;
3304        if let Some(resource_idx) = self.component.defined_resource_index(resource_idx) {
3305            assert!(!imported);
3306            let resource_def = self
3307                .component
3308                .initializers
3309                .iter()
3310                .find_map(|i| match i {
3311                    GlobalInitializer::Resource(r) if r.index == resource_idx => Some(r),
3312                    _ => None,
3313                })
3314                .unwrap();
3315
3316            if let Some(dtor) = &resource_def.dtor {
3317                dtor_str = Some(self.core_def(dtor));
3318            }
3319        }
3320
3321        // Look up the local import name
3322        let resource_name = ty.name.as_ref().unwrap().to_upper_camel_case();
3323
3324        let local_name = if imported {
3325            let (world_key, iface_name) = match ty.owner {
3326                wit_parser::TypeOwner::World(world) => (
3327                    self.resolve.worlds[world]
3328                        .imports
3329                        .iter()
3330                        .find(|&(_, item)| matches!(*item, WorldItem::Type { id, .. } if id == t))
3331                        .unwrap()
3332                        .0
3333                        .clone(),
3334                    None,
3335                ),
3336                wit_parser::TypeOwner::Interface(iface) => {
3337                    match &self.resolve.interfaces[iface].name {
3338                        Some(name) => (WorldKey::Interface(iface), Some(name.as_str())),
3339                        None => {
3340                            let key = self.resolve.worlds[self.world]
3341                                .imports
3342                                .iter()
3343                                .find(|&(_, item)| match item {
3344                                    WorldItem::Interface { id, .. } => *id == iface,
3345                                    _ => false,
3346                                })
3347                                .unwrap()
3348                                .0;
3349                            (
3350                                key.clone(),
3351                                match key {
3352                                    WorldKey::Name(name) => Some(name.as_str()),
3353                                    WorldKey::Interface(_) => None,
3354                                },
3355                            )
3356                        }
3357                    }
3358                }
3359                wit_parser::TypeOwner::None => unimplemented!(),
3360            };
3361
3362            let import_name = self.resolve.name_world_key(&world_key);
3363            let (local_name, _) = self
3364                .bindgen
3365                .local_names
3366                .get_or_create(resource_idx, &resource_name);
3367
3368            let local_name_str = local_name.to_string();
3369
3370            // Nested interfaces only currently possible through mapping
3371            let (import_specifier, maybe_iface_member) =
3372                map_import(&self.bindgen.opts.map, &import_name);
3373
3374            // Ensure that the import exists
3375            self.ensure_import(
3376                import_specifier,
3377                iface_name,
3378                maybe_iface_member.as_deref(),
3379                iface_name.map(|_| resource_name),
3380                local_name_str.to_string(),
3381            );
3382            local_name_str
3383        } else {
3384            let (local_name, _) = self
3385                .bindgen
3386                .local_names
3387                .get_or_create(resource_idx, &resource_name);
3388            local_name.to_string()
3389        };
3390
3391        // Add a resource table to track the host resource
3392        let entry = ResourceTable {
3393            imported,
3394            data: ResourceData::Host {
3395                tid: resource_table_ty_idx,
3396                rid: resource_idx,
3397                local_name,
3398                dtor_name: dtor_str,
3399            },
3400        };
3401
3402        // If the the resource already exists, then  ensure that it is exactly the same as the
3403        // value we're attempting to insert
3404        if let Some(existing) = resource_map.get(&resource_id) {
3405            assert_eq!(*existing, entry);
3406            return;
3407        }
3408
3409        // Insert the resource into the map,
3410        resource_map.insert(resource_id, entry);
3411    }
3412
3413    /// Connect resources that are defined at the type levels in `wit-parser`
3414    /// to their types as defined in `wasmtime-environ`
3415    ///
3416    /// The types that are connected here are stored in the `resource_map` for
3417    /// use later.
3418    ///
3419    /// # Arguments
3420    ///
3421    /// * `id` - The ID of the type if present (can be missing when dealing with `error-context`s, `future<_>`, etc)
3422    /// * `iface_ty` - The relevant interface type
3423    /// * `resource_map` - Resource map that we will update with pairings
3424    ///
3425    fn connect_resource_types(
3426        &mut self,
3427        id: TypeId,
3428        iface_ty: &InterfaceType,
3429        resource_map: &mut ResourceMap,
3430    ) {
3431        let kind = &self.resolve.types[id].kind;
3432        match (kind, iface_ty) {
3433            // For flags and enums we can do nothing -- they're simple values (string/number)
3434            (TypeDefKind::Flags(_), InterfaceType::Flags(_))
3435            | (TypeDefKind::Enum(_), InterfaceType::Enum(_)) => {}
3436
3437            // Connect records to records
3438            (TypeDefKind::Record(t1), InterfaceType::Record(t2)) => {
3439                let t2 = &self.types[*t2];
3440                for (f1, f2) in t1.fields.iter().zip(t2.fields.iter()) {
3441                    if let Type::Id(id) = f1.ty {
3442                        self.connect_resource_types(id, &f2.ty, resource_map);
3443                    }
3444                }
3445            }
3446
3447            // Handle connecting owned/borrowed handles to owned/borrowed handles
3448            (
3449                TypeDefKind::Handle(Handle::Own(t1) | Handle::Borrow(t1)),
3450                InterfaceType::Own(t2) | InterfaceType::Borrow(t2),
3451            ) => {
3452                self.connect_host_resource(*t1, *t2, resource_map);
3453            }
3454
3455            // Connect tuples to interface tuples
3456            (TypeDefKind::Tuple(t1), InterfaceType::Tuple(t2)) => {
3457                let t2 = &self.types[*t2];
3458                for (f1, f2) in t1.types.iter().zip(t2.types.iter()) {
3459                    if let Type::Id(id) = f1 {
3460                        self.connect_resource_types(*id, f2, resource_map);
3461                    }
3462                }
3463            }
3464
3465            // Connect inner types of variants to their interface types
3466            (TypeDefKind::Variant(t1), InterfaceType::Variant(t2)) => {
3467                let t2 = &self.types[*t2];
3468                for (f1, f2) in t1.cases.iter().zip(t2.cases.iter()) {
3469                    if let Some(Type::Id(id)) = &f1.ty {
3470                        self.connect_resource_types(*id, f2.1.as_ref().unwrap(), resource_map);
3471                    }
3472                }
3473            }
3474
3475            // Connect option<t> to option<t>
3476            (TypeDefKind::Option(t1), InterfaceType::Option(t2)) => {
3477                let t2 = &self.types[*t2];
3478                if let Type::Id(id) = t1 {
3479                    self.connect_resource_types(*id, &t2.ty, resource_map);
3480                }
3481            }
3482
3483            // Connect result<t> to result<t>
3484            (TypeDefKind::Result(t1), InterfaceType::Result(t2)) => {
3485                let t2 = &self.types[*t2];
3486                if let Some(Type::Id(id)) = &t1.ok {
3487                    self.connect_resource_types(*id, &t2.ok.unwrap(), resource_map);
3488                }
3489                if let Some(Type::Id(id)) = &t1.err {
3490                    self.connect_resource_types(*id, &t2.err.unwrap(), resource_map);
3491                }
3492            }
3493
3494            // Connect list<t> to list types
3495            (TypeDefKind::List(t1), InterfaceType::List(t2)) => {
3496                let t2 = &self.types[*t2];
3497                if let Type::Id(id) = t1 {
3498                    self.connect_resource_types(*id, &t2.element, resource_map);
3499                }
3500            }
3501
3502            // Connect list<t, size> to list types
3503            (TypeDefKind::FixedLengthList(t1, _len), InterfaceType::FixedLengthList(t2)) => {
3504                let t2 = &self.types[*t2];
3505                if let Type::Id(id) = t1 {
3506                    self.connect_resource_types(*id, &t2.element, resource_map);
3507                }
3508            }
3509
3510            // Connect named types
3511            (TypeDefKind::Type(ty), _) => {
3512                if let Type::Id(id) = ty {
3513                    self.connect_resource_types(*id, iface_ty, resource_map);
3514                }
3515            }
3516
3517            // Connect futures & stream types
3518            (TypeDefKind::Future(maybe_elem_ty), _iface_ty)
3519            | (TypeDefKind::Stream(maybe_elem_ty), _iface_ty) => {
3520                match maybe_elem_ty {
3521                    // The case of an empty future is the propagation of a `null`-like value, usually a simple signal
3522                    // which we'll connect with the *normally invalid* type value 0 as an indicator
3523                    None => {
3524                        self.connect_p3_resources(&id, maybe_elem_ty, iface_ty, resource_map);
3525                    }
3526                    // For custom types we must recur to properly connect the inner type
3527                    Some(elem_ty @ Type::Id(_t)) => {
3528                        self.connect_p3_resources(&id, &Some(*elem_ty), iface_ty, resource_map);
3529                    }
3530                    // For basic types that are connected (non inner types) we can do a generic connect
3531                    Some(_) => {
3532                        self.connect_p3_resources(&id, maybe_elem_ty, iface_ty, resource_map);
3533                    }
3534                }
3535            }
3536
3537            // Connect the types in an ok/error variant of a Result to the future that they're being sent in
3538            (
3539                TypeDefKind::Result(Result_ { ok, err }),
3540                tk2 @ (InterfaceType::Future(_) | InterfaceType::Stream(_)),
3541            ) => {
3542                if let Some(Type::Id(ok_t)) = ok {
3543                    self.connect_resource_types(*ok_t, tk2, resource_map)
3544                }
3545                if let Some(Type::Id(err_t)) = err {
3546                    self.connect_resource_types(*err_t, tk2, resource_map)
3547                }
3548            }
3549
3550            // Connect the types in an option to the future that they're being sent in
3551            (
3552                TypeDefKind::Option(ty),
3553                tk2 @ (InterfaceType::Future(_) | InterfaceType::Stream(_)),
3554            ) => {
3555                if let Type::Id(some_t) = ty {
3556                    self.connect_resource_types(*some_t, tk2, resource_map)
3557                }
3558            }
3559
3560            // Connect resources to the future/stream that they're being sent in
3561            (
3562                TypeDefKind::Handle(Handle::Own(t1) | Handle::Borrow(t1)),
3563                tk2 @ (InterfaceType::Future(_) | InterfaceType::Stream(_)),
3564            ) => self.connect_resource_types(*t1, tk2, resource_map),
3565
3566            (TypeDefKind::Resource, InterfaceType::Future(_) | InterfaceType::Stream(_)) => {}
3567
3568            // Connect the inner types of variants to the future they're being sent in
3569            (
3570                TypeDefKind::Variant(variant),
3571                tk2 @ (InterfaceType::Future(_) | InterfaceType::Stream(_)),
3572            ) => {
3573                for f1 in variant.cases.iter() {
3574                    if let Some(Type::Id(id)) = &f1.ty {
3575                        self.connect_resource_types(*id, tk2, resource_map);
3576                    }
3577                }
3578            }
3579
3580            // Connect the inner types of variants to the future they're being sent in
3581            (
3582                TypeDefKind::Record(record),
3583                tk2 @ (InterfaceType::Future(_) | InterfaceType::Stream(_)),
3584            ) => {
3585                for f1 in record.fields.iter() {
3586                    if let Type::Id(id) = f1.ty {
3587                        self.connect_resource_types(id, tk2, resource_map);
3588                    }
3589                }
3590            }
3591
3592            // Simliar to the non-stream/future case, we don't have to do anything for
3593            // flags and plain enums as they are read directly
3594            (
3595                TypeDefKind::Enum(_) | TypeDefKind::Flags(_),
3596                InterfaceType::Future(_) | InterfaceType::Stream(_),
3597            ) => {}
3598
3599            (TypeDefKind::Resource, tk2) => {
3600                unreachable!(
3601                    "resource types do not need to be connected (in this case, to [{tk2:?}])"
3602                )
3603            }
3604
3605            (TypeDefKind::Unknown, tk2) => {
3606                unreachable!("unknown types cannot be connected (in this case to [{tk2:?}])")
3607            }
3608
3609            (tk1, tk2) => unreachable!("invalid typedef kind combination [{tk1:?}] [{tk2:?}]",),
3610        }
3611    }
3612
3613    fn bindgen(&mut self, args: JsFunctionBindgenArgs) {
3614        let JsFunctionBindgenArgs {
3615            nparams,
3616            call_type,
3617            iface_name,
3618            callee,
3619            opts,
3620            func,
3621            resource_map,
3622            abi,
3623            requires_async_porcelain,
3624            is_async,
3625        } = args;
3626
3627        let (memory, realloc) =
3628            if let CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions {
3629                memory,
3630                realloc,
3631            }) = opts.data_model
3632            {
3633                (
3634                    memory.map(|idx| format!("memory{}", idx.as_u32())),
3635                    realloc.map(|idx| {
3636                        format!(
3637                            "realloc{}{}",
3638                            idx.as_u32(),
3639                            if is_async {
3640                                "Async"
3641                            } else {
3642                                Default::default()
3643                            }
3644                        )
3645                    }),
3646                )
3647            } else {
3648                (None, None)
3649            };
3650
3651        let post_return = opts.post_return.map(|idx| {
3652            format!(
3653                "postReturn{}{}",
3654                idx.as_u32(),
3655                if is_async {
3656                    "Async"
3657                } else {
3658                    Default::default()
3659                }
3660            )
3661        });
3662
3663        let tracing_prefix = format!(
3664            "[iface=\"{}\", function=\"{}\"]",
3665            iface_name.unwrap_or("<no iface>"),
3666            func.name
3667        );
3668
3669        // Write the function argument list
3670        //
3671        // At this point, only the function preamble (e.g. 'function nameOfFunc()') has been written
3672        self.src.js("(");
3673        let mut params = Vec::new();
3674        let mut first = true;
3675        for i in 0..nparams {
3676            if i == 0
3677                && matches!(
3678                    call_type,
3679                    CallType::FirstArgIsThis | CallType::AsyncFirstArgIsThis
3680                )
3681            {
3682                params.push("this".into());
3683                continue;
3684            }
3685            if !first {
3686                self.src.js(", ");
3687            } else {
3688                first = false;
3689            }
3690            let param = format!("arg{i}");
3691            self.src.js(&param);
3692            params.push(param);
3693        }
3694        uwriteln!(self.src.js, ") {{");
3695
3696        // If tracing is enabled, output a function entry tracing message
3697        if self.bindgen.opts.tracing {
3698            let event_fields = func
3699                .params
3700                .iter()
3701                .enumerate()
3702                .map(|(i, p)| format!("{}=${{arguments[{i}]}}", p.name))
3703                .collect::<Vec<String>>();
3704            uwriteln!(
3705                self.src.js,
3706                "console.error(`{tracing_prefix} call {}`);",
3707                event_fields.join(", ")
3708            );
3709        }
3710
3711        // If TLA compat was enabled, ensure that it was initialized
3712        if self.bindgen.opts.tla_compat
3713            && matches!(abi, AbiVariant::GuestExport)
3714            && self.bindgen.opts.instantiation.is_none()
3715        {
3716            let throw_uninitialized = self.bindgen.intrinsic(Intrinsic::ThrowUninitialized);
3717            uwrite!(
3718                self.src.js,
3719                "\
3720                if (!_initialized) {throw_uninitialized}();
3721            "
3722            );
3723        }
3724
3725        // Generate function body
3726        let mut f = FunctionBindgen {
3727            resource_map,
3728            clear_resource_borrows: false,
3729            intrinsics: &mut self.bindgen.all_intrinsics,
3730            valid_lifting_optimization: self.bindgen.opts.valid_lifting_optimization,
3731            sizes: &self.sizes,
3732            err: if get_thrown_type(self.resolve, func.result).is_some() {
3733                match abi {
3734                    AbiVariant::GuestExport
3735                    | AbiVariant::GuestExportAsync
3736                    | AbiVariant::GuestExportAsyncStackful => ErrHandling::ThrowResultErr,
3737                    AbiVariant::GuestImport | AbiVariant::GuestImportAsync => {
3738                        ErrHandling::ResultCatchHandler
3739                    }
3740                }
3741            } else {
3742                ErrHandling::None
3743            },
3744            block_storage: Vec::new(),
3745            blocks: Vec::new(),
3746            callee,
3747            callee_resource_dynamic: matches!(call_type, CallType::CalleeResourceDispatch),
3748            memory: memory.as_ref(),
3749            realloc: realloc.as_ref(),
3750            tmp: 0,
3751            params,
3752            post_return: post_return.as_ref(),
3753            tracing_prefix: &tracing_prefix,
3754            tracing_enabled: self.bindgen.opts.tracing,
3755            encoding: match opts.string_encoding {
3756                wasmtime_environ::component::StringEncoding::Utf8 => StringEncoding::UTF8,
3757                wasmtime_environ::component::StringEncoding::Utf16 => StringEncoding::UTF16,
3758                wasmtime_environ::component::StringEncoding::CompactUtf16 => {
3759                    StringEncoding::CompactUTF16
3760                }
3761            },
3762            src: source::Source::default(),
3763            resolve: self.resolve,
3764            requires_async_porcelain,
3765            is_async,
3766            canon_opts: opts,
3767            iface_name,
3768        };
3769
3770        // Emit (and visit, via the `FunctionBindgen` object) an abstract sequence of
3771        // instructions which represents the function being generated.
3772        abi::call(
3773            self.resolve,
3774            abi,
3775            match abi {
3776                AbiVariant::GuestImport | AbiVariant::GuestImportAsync => {
3777                    LiftLower::LiftArgsLowerResults
3778                }
3779                AbiVariant::GuestExport
3780                | AbiVariant::GuestExportAsync
3781                | AbiVariant::GuestExportAsyncStackful => LiftLower::LowerArgsLiftResults,
3782            },
3783            func,
3784            &mut f,
3785            is_async,
3786        );
3787
3788        // Once visiting has completed, write the contents the `FunctionBindgen` generated to output
3789        self.src.js(&f.src);
3790
3791        // Close function body
3792        self.src.js("}");
3793    }
3794
3795    fn augmented_import_def(&self, def: &core::AugmentedImport<'_>) -> String {
3796        match def {
3797            core::AugmentedImport::CoreDef(def) => self.core_def(def),
3798            core::AugmentedImport::Memory { mem, op } => {
3799                let mem = self.core_def(mem);
3800                match op {
3801                    core::AugmentedOp::I32Load => {
3802                        format!(
3803                            "(ptr, off) => new DataView({mem}.buffer).getInt32(ptr + off, true)"
3804                        )
3805                    }
3806                    core::AugmentedOp::I32Load8U => {
3807                        format!(
3808                            "(ptr, off) => new DataView({mem}.buffer).getUint8(ptr + off, true)"
3809                        )
3810                    }
3811                    core::AugmentedOp::I32Load8S => {
3812                        format!("(ptr, off) => new DataView({mem}.buffer).getInt8(ptr + off, true)")
3813                    }
3814                    core::AugmentedOp::I32Load16U => {
3815                        format!(
3816                            "(ptr, off) => new DataView({mem}.buffer).getUint16(ptr + off, true)"
3817                        )
3818                    }
3819                    core::AugmentedOp::I32Load16S => {
3820                        format!(
3821                            "(ptr, off) => new DataView({mem}.buffer).getInt16(ptr + off, true)"
3822                        )
3823                    }
3824                    core::AugmentedOp::I64Load => {
3825                        format!(
3826                            "(ptr, off) => new DataView({mem}.buffer).getBigInt64(ptr + off, true)"
3827                        )
3828                    }
3829                    core::AugmentedOp::F32Load => {
3830                        format!(
3831                            "(ptr, off) => new DataView({mem}.buffer).getFloat32(ptr + off, true)"
3832                        )
3833                    }
3834                    core::AugmentedOp::F64Load => {
3835                        format!(
3836                            "(ptr, off) => new DataView({mem}.buffer).getFloat64(ptr + off, true)"
3837                        )
3838                    }
3839                    core::AugmentedOp::I32Store8 => {
3840                        format!(
3841                            "(ptr, val, offset) => {{
3842                                new DataView({mem}.buffer).setInt8(ptr + offset, val, true);
3843                            }}"
3844                        )
3845                    }
3846                    core::AugmentedOp::I32Store16 => {
3847                        format!(
3848                            "(ptr, val, offset) => {{
3849                                new DataView({mem}.buffer).setInt16(ptr + offset, val, true);
3850                            }}"
3851                        )
3852                    }
3853                    core::AugmentedOp::I32Store => {
3854                        format!(
3855                            "(ptr, val, offset) => {{
3856                                new DataView({mem}.buffer).setInt32(ptr + offset, val, true);
3857                            }}"
3858                        )
3859                    }
3860                    core::AugmentedOp::I64Store => {
3861                        format!(
3862                            "(ptr, val, offset) => {{
3863                                new DataView({mem}.buffer).setBigInt64(ptr + offset, val, true);
3864                            }}"
3865                        )
3866                    }
3867                    core::AugmentedOp::F32Store => {
3868                        format!(
3869                            "(ptr, val, offset) => {{
3870                                new DataView({mem}.buffer).setFloat32(ptr + offset, val, true);
3871                            }}"
3872                        )
3873                    }
3874                    core::AugmentedOp::F64Store => {
3875                        format!(
3876                            "(ptr, val, offset) => {{
3877                                new DataView({mem}.buffer).setFloat64(ptr + offset, val, true);
3878                            }}"
3879                        )
3880                    }
3881                    core::AugmentedOp::MemorySize => {
3882                        format!("ptr => {mem}.buffer.byteLength / 65536")
3883                    }
3884                }
3885            }
3886        }
3887    }
3888
3889    fn core_def(&self, def: &CoreDef) -> String {
3890        match def {
3891            CoreDef::Export(e) => self.core_export_var_name(e),
3892            CoreDef::TaskMayBlock => AsyncTaskIntrinsic::CurrentTaskMayBlock.name().into(),
3893            CoreDef::Trampoline(i) => format!("trampoline{}", i.as_u32()),
3894            CoreDef::InstanceFlags(i) => {
3895                // SAFETY: short-lived borrow-mut.
3896                self.used_instance_flags.borrow_mut().insert(*i);
3897                format!("instanceFlags{}", i.as_u32())
3898            }
3899            CoreDef::UnsafeIntrinsic(ui) => {
3900                let idx = ui.index();
3901                format!("unsafeIntrinsic{idx}")
3902            }
3903        }
3904    }
3905
3906    fn core_export_var_name<T>(&self, export: &CoreExport<T>) -> String
3907    where
3908        T: Into<EntityIndex> + Copy,
3909    {
3910        let name = match &export.item {
3911            ExportItem::Index(idx) => {
3912                let module_idx = self
3913                    .instances
3914                    .get(export.instance)
3915                    .expect("unexpectedly missing export instance");
3916                let module = &self
3917                    .modules
3918                    .get(*module_idx)
3919                    .expect("unexpectedly missing module by idx");
3920                let idx = (*idx).into();
3921                module
3922                    .exports()
3923                    .iter()
3924                    .find_map(|(name, i)| if *i == idx { Some(name) } else { None })
3925                    .unwrap()
3926            }
3927            ExportItem::Name(s) => s,
3928        };
3929        let i = export.instance.as_u32() as usize;
3930        let quoted = maybe_quote_member(name);
3931        format!("exports{i}{quoted}")
3932    }
3933
3934    fn exports(&mut self, exports: &NameMap<String, ExportIndex>) {
3935        for (export_name, export_idx) in exports.raw_iter() {
3936            let export = &self.component.export_items[*export_idx];
3937            let world_key = &self.exports[export_name];
3938            let item = &self.resolve.worlds[self.world].exports[world_key];
3939            let mut export_resource_map = ResourceMap::new();
3940
3941            match export {
3942                Export::LiftedFunction {
3943                    func: def,
3944                    options,
3945                    ty: func_ty,
3946                } => {
3947                    let func = match item {
3948                        WorldItem::Function(f) => f,
3949                        WorldItem::Interface { .. } | WorldItem::Type { .. } => {
3950                            unreachable!("unexpectedly non-function lifted function export")
3951                        }
3952                    };
3953                    self.create_resource_fn_map(func, *func_ty, &mut export_resource_map);
3954
3955                    let local_name = String::from(match func.kind {
3956                        // For resources, we must take the type name (adding `.prototype.<fn name>` later)
3957                        FunctionKind::Constructor(resource_id)
3958                        | FunctionKind::Method(resource_id)
3959                        | FunctionKind::AsyncMethod(resource_id)
3960                        | FunctionKind::Static(resource_id)
3961                        | FunctionKind::AsyncStatic(resource_id) => Instantiator::resource_name(
3962                            self.resolve,
3963                            &mut self.bindgen.local_names,
3964                            resource_id,
3965                            &self.exports_resource_types,
3966                        ),
3967                        // Fore free standing functions we can use the exoprt name directly as a local name
3968                        FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
3969                            self.bindgen.local_names.create_once(export_name)
3970                        }
3971                    });
3972
3973                    let options = self
3974                        .component
3975                        .options
3976                        .get(*options)
3977                        .expect("failed to find options");
3978
3979                    self.export_bindgen(
3980                        &local_name,
3981                        def,
3982                        options,
3983                        func,
3984                        func_ty,
3985                        export_name,
3986                        &export_resource_map,
3987                    );
3988
3989                    let js_binding_name = match func.kind {
3990                        // For resources, we must take the type name (adding `.prototype.<fn name>` later)
3991                        FunctionKind::Constructor(ty)
3992                        | FunctionKind::Method(ty)
3993                        | FunctionKind::AsyncMethod(ty)
3994                        | FunctionKind::Static(ty)
3995                        | FunctionKind::AsyncStatic(ty) => self.resolve.types[ty]
3996                            .name
3997                            .as_ref()
3998                            .unwrap()
3999                            .to_upper_camel_case(),
4000                        // For free standing functions we can use the export name directly
4001                        FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
4002                            export_name.to_lower_camel_case()
4003                        }
4004                    };
4005
4006                    // Add the export binding
4007                    self.bindgen.esm_bindgen.add_export_binding(
4008                        None,
4009                        local_name,
4010                        js_binding_name,
4011                        func,
4012                    );
4013                }
4014
4015                Export::Instance { exports, .. } => {
4016                    let iface_id = match item {
4017                        WorldItem::Interface { id, .. } => *id,
4018                        WorldItem::Function(_) | WorldItem::Type { .. } => {
4019                            unreachable!("unexpectedly non-interface export instance")
4020                        }
4021                    };
4022
4023                    // Process exported instances
4024                    for (func_name, export_idx) in exports.raw_iter() {
4025                        let export = &self.component.export_items[*export_idx];
4026
4027                        // Gather function information for all lifted functions in the isntance export
4028                        let (def, options, func_ty) = match export {
4029                            Export::LiftedFunction { func, options, ty } => (func, options, ty),
4030                            Export::Type(_) => continue, // ignored
4031                            _ => unreachable!("unexpected non-lifted function export"),
4032                        };
4033
4034                        let func = &self.resolve.interfaces[iface_id].functions[func_name];
4035
4036                        self.create_resource_fn_map(func, *func_ty, &mut export_resource_map);
4037
4038                        let local_name = String::from(match func.kind {
4039                            // For resources, we must use the name of the type
4040                            FunctionKind::Constructor(resource_id)
4041                            | FunctionKind::Method(resource_id)
4042                            | FunctionKind::AsyncMethod(resource_id)
4043                            | FunctionKind::Static(resource_id)
4044                            | FunctionKind::AsyncStatic(resource_id) => {
4045                                Instantiator::resource_name(
4046                                    self.resolve,
4047                                    &mut self.bindgen.local_names,
4048                                    resource_id,
4049                                    &self.exports_resource_types,
4050                                )
4051                            }
4052                            // For free standing functions we can use the bare func name
4053                            FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
4054                                self.bindgen.local_names.create_once(func_name)
4055                            }
4056                        });
4057
4058                        let options = self
4059                            .component
4060                            .options
4061                            .get(*options)
4062                            .expect("failed to find options");
4063
4064                        self.export_bindgen(
4065                            &local_name,
4066                            def,
4067                            options,
4068                            func,
4069                            func_ty,
4070                            export_name,
4071                            &export_resource_map,
4072                        );
4073
4074                        // Determine the export func name (this can also be a class name)
4075                        let export_binding_name = match func.kind {
4076                            // For resources, we must use the type name (later adding `.prototype.<actual fn>`)
4077                            FunctionKind::Constructor(ty)
4078                            | FunctionKind::Method(ty)
4079                            | FunctionKind::AsyncMethod(ty)
4080                            | FunctionKind::Static(ty)
4081                            | FunctionKind::AsyncStatic(ty) => self.resolve.types[ty]
4082                                .name
4083                                .as_ref()
4084                                .unwrap()
4085                                .to_upper_camel_case(),
4086                            // Free standing functions we can use the function name directly
4087                            FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
4088                                func_name.to_lower_camel_case()
4089                            }
4090                        };
4091
4092                        // Add the export binding
4093                        self.bindgen.esm_bindgen.add_export_binding(
4094                            Some(export_name),
4095                            local_name,
4096                            export_binding_name,
4097                            func,
4098                        );
4099                    }
4100                }
4101
4102                // ignore type exports for now
4103                Export::Type(_) => {}
4104
4105                // This can't be tested at this time so leave it unimplemented
4106                Export::ModuleStatic { .. } | Export::ModuleImport { .. } => unimplemented!(),
4107            }
4108
4109            // Save information about exported resources for later
4110            self.resource_exports.extend(export_resource_map);
4111        }
4112
4113        self.bindgen.esm_bindgen.populate_export_aliases();
4114    }
4115
4116    #[allow(clippy::too_many_arguments)]
4117    fn export_bindgen(
4118        &mut self,
4119        local_name: &str,
4120        def: &CoreDef,
4121        options: &CanonicalOptions,
4122        func: &Function,
4123        _func_ty_idx: &TypeFuncIndex,
4124        export_name: &String,
4125        export_resource_map: &ResourceMap,
4126    ) {
4127        // Determine whether the function should be generated as async
4128        let requires_async_porcelain = requires_async_porcelain(
4129            FunctionIdentifier::Fn(func),
4130            export_name,
4131            &self.async_exports,
4132        );
4133        // If the function is *also* async lifted, it
4134        if options.async_ {
4135            assert!(
4136                options.post_return.is_none(),
4137                "async function {local_name} (export {export_name}) can't have post return"
4138            );
4139        }
4140
4141        let is_async = is_async_fn(func, options);
4142
4143        let maybe_async = if requires_async_porcelain || is_async {
4144            "async "
4145        } else {
4146            ""
4147        };
4148
4149        // Start building early variable declarations
4150        let core_export_fn = self.core_def(def);
4151        let callee = match self
4152            .bindgen
4153            .local_names
4154            .get_or_create(&core_export_fn, &core_export_fn)
4155        {
4156            (local_name, true) => local_name.to_string(),
4157            (local_name, false) => {
4158                let local_name = local_name.to_string();
4159                uwriteln!(self.src.js, "let {local_name};");
4160                self.bindgen
4161                    .all_core_exported_funcs
4162                    // TODO(breaking): remove requires_async_porcelain  once support
4163                    // for manual async import specification is removed, as p3 has
4164                    // built-in function async coloring
4165                    .push((core_export_fn.clone(), is_async | requires_async_porcelain));
4166                local_name
4167            }
4168        };
4169
4170        let iface_name = if export_name.is_empty() {
4171            None
4172        } else {
4173            Some(export_name)
4174        };
4175
4176        // Write function preamble (everything up to the `(` in `function (...`)
4177        match func.kind {
4178            FunctionKind::Freestanding => {
4179                uwrite!(self.src.js, "\n{maybe_async}function {local_name}")
4180            }
4181            FunctionKind::Method(_) => {
4182                self.ensure_local_resource_class(local_name.to_string());
4183                let method_name = func.item_name().to_lower_camel_case();
4184
4185                uwrite!(
4186                    self.src.js,
4187                    "\n{local_name}.prototype.{method_name} = {maybe_async}function {}",
4188                    if !is_js_reserved_word(&method_name) {
4189                        method_name.to_string()
4190                    } else {
4191                        format!("${method_name}")
4192                    }
4193                );
4194            }
4195            FunctionKind::Static(_) => {
4196                self.ensure_local_resource_class(local_name.to_string());
4197                let method_name = func.item_name().to_lower_camel_case();
4198                uwrite!(
4199                    self.src.js,
4200                    "\n{local_name}.{method_name} = function {}",
4201                    if !is_js_reserved_word(&method_name) {
4202                        method_name.to_string()
4203                    } else {
4204                        format!("${method_name}")
4205                    }
4206                );
4207            }
4208            FunctionKind::Constructor(_) => {
4209                if self.defined_resource_classes.contains(local_name) {
4210                    panic!(
4211                        "Internal error: Resource constructor must be defined before other methods and statics"
4212                    );
4213                }
4214                uwrite!(
4215                    self.src.js,
4216                    "
4217                    class {local_name} {{
4218                        constructor"
4219                );
4220                self.defined_resource_classes.insert(local_name.to_string());
4221            }
4222            FunctionKind::AsyncFreestanding => {
4223                uwrite!(self.src.js, "\nasync function {local_name}")
4224            }
4225            FunctionKind::AsyncMethod(_) => {
4226                self.ensure_local_resource_class(local_name.to_string());
4227                let method_name = func.item_name().to_lower_camel_case();
4228                let fn_name = if !is_js_reserved_word(&method_name) {
4229                    method_name.to_string()
4230                } else {
4231                    format!("${method_name}")
4232                };
4233                uwrite!(
4234                    self.src.js,
4235                    "\n{local_name}.prototype.{method_name} = async function {fn_name}",
4236                );
4237            }
4238            FunctionKind::AsyncStatic(_) => {
4239                self.ensure_local_resource_class(local_name.to_string());
4240                let method_name = func.item_name().to_lower_camel_case();
4241                let fn_name = if !is_js_reserved_word(&method_name) {
4242                    method_name.to_string()
4243                } else {
4244                    format!("${method_name}")
4245                };
4246                uwrite!(
4247                    self.src.js,
4248                    "\n{local_name}.{method_name} = async function {fn_name}",
4249                );
4250            }
4251        };
4252
4253        // Perform bindgen
4254        self.bindgen(JsFunctionBindgenArgs {
4255            nparams: func.params.len(),
4256            call_type: match func.kind {
4257                FunctionKind::Method(_) => CallType::FirstArgIsThis,
4258                FunctionKind::AsyncMethod(_) => CallType::AsyncFirstArgIsThis,
4259                FunctionKind::Freestanding
4260                | FunctionKind::Static(_)
4261                | FunctionKind::Constructor(_) => CallType::Standard,
4262                FunctionKind::AsyncFreestanding | FunctionKind::AsyncStatic(_) => {
4263                    CallType::AsyncStandard
4264                }
4265            },
4266            iface_name: iface_name.map(|v| v.as_str()),
4267            callee: &callee,
4268            opts: options,
4269            func,
4270            resource_map: export_resource_map,
4271            abi: AbiVariant::GuestExport,
4272            requires_async_porcelain,
4273            is_async,
4274        });
4275
4276        // End the function
4277        match func.kind {
4278            FunctionKind::AsyncFreestanding | FunctionKind::Freestanding => self.src.js("\n"),
4279            FunctionKind::AsyncMethod(_)
4280            | FunctionKind::AsyncStatic(_)
4281            | FunctionKind::Method(_)
4282            | FunctionKind::Static(_) => self.src.js(";\n"),
4283            FunctionKind::Constructor(_) => self.src.js("\n}\n"),
4284        }
4285    }
4286}
4287
4288#[derive(Default)]
4289pub struct Source {
4290    pub js: source::Source,
4291    pub js_init: source::Source,
4292}
4293
4294impl Source {
4295    pub fn js(&mut self, s: &str) {
4296        self.js.push_str(s);
4297    }
4298    pub fn js_init(&mut self, s: &str) {
4299        self.js_init.push_str(s);
4300    }
4301}
4302
4303/// Compute the semver "compatibility track" for a version string.
4304/// Mirrors wasmtime's `alternate_lookup_key()` logic.
4305///
4306/// Returns the compat key and parsed `Version` on success.
4307///
4308/// Examples (showing just the key):
4309///   "1.2.3"  → Some(("1", ..))     — major > 0, compat within major
4310///   "0.2.10" → Some(("0.2", ..))   — minor > 0, compat within 0.minor
4311///   "0.0.1"  → None                — no semver compat
4312///   "1.0.0-rc.1" → None            — pre-release, no compat
4313fn semver_compat_key(version_str: &str) -> Option<(String, Version)> {
4314    let version = Version::parse(version_str).ok()?;
4315    if !version.pre.is_empty() {
4316        None
4317    } else if version.major != 0 {
4318        Some((format!("{}", version.major), version))
4319    } else if version.minor != 0 {
4320        Some((format!("0.{}", version.minor), version))
4321    } else {
4322        None
4323    }
4324}
4325
4326fn parse_mapping(mapping: &str) -> (String, Option<String>) {
4327    if mapping.len() > 1
4328        && let Some(hash_idx) = mapping[1..].find('#')
4329    {
4330        return (
4331            mapping[0..hash_idx + 1].to_string(),
4332            Some(mapping[hash_idx + 2..].into()),
4333        );
4334    }
4335    (mapping.into(), None)
4336}
4337
4338fn map_import(map: &Option<HashMap<String, String>>, impt: &str) -> (String, Option<String>) {
4339    let impt_sans_version = match impt.find('@') {
4340        Some(version_idx) => &impt[0..version_idx],
4341        None => impt,
4342    };
4343    if let Some(map) = map.as_ref() {
4344        // Exact match (including version)
4345        if let Some(mapping) = map.get(impt) {
4346            return parse_mapping(mapping);
4347        }
4348        // Match without version
4349        if let Some(mapping) = map.get(impt_sans_version) {
4350            return parse_mapping(mapping);
4351        }
4352        // Wildcard matching (version-stripped and full)
4353        for (key, mapping) in map {
4354            if let Some(wildcard_idx) = key.find('*') {
4355                let lhs = &key[0..wildcard_idx];
4356                let rhs = &key[wildcard_idx + 1..];
4357                if impt_sans_version.starts_with(lhs) && impt_sans_version.ends_with(rhs) {
4358                    let matched = &impt_sans_version[wildcard_idx
4359                        ..wildcard_idx + impt_sans_version.len() - lhs.len() - rhs.len()];
4360                    let mapping = mapping.replace('*', matched);
4361                    return parse_mapping(&mapping);
4362                }
4363                if impt.starts_with(lhs) && impt.ends_with(rhs) {
4364                    let matched =
4365                        &impt[wildcard_idx..wildcard_idx + impt.len() - lhs.len() - rhs.len()];
4366                    let mapping = mapping.replace('*', matched);
4367                    return parse_mapping(&mapping);
4368                }
4369            }
4370        }
4371        // Semver-compatible matching for versioned map entries.
4372        // If the import has a parseable version and earlier steps didn't match,
4373        // try matching against map entries with compatible versions.
4374        if let Some(at) = impt.find('@') {
4375            let impt_ver_str = &impt[at + 1..];
4376            if let Some((impt_compat, _)) = semver_compat_key(impt_ver_str) {
4377                let mut best_match: Option<(String, Version)> = None;
4378
4379                for (key, mapping) in map {
4380                    // Only consider map entries that have a version
4381                    let key_at = match key.find('@') {
4382                        Some(at) => at,
4383                        None => continue,
4384                    };
4385                    let key_base = &key[..key_at];
4386                    let key_ver_str = &key[key_at + 1..];
4387
4388                    // Check version compatibility
4389                    let (key_compat, key_ver) = match semver_compat_key(key_ver_str) {
4390                        Some(k) => k,
4391                        None => continue,
4392                    };
4393                    if impt_compat != key_compat {
4394                        continue;
4395                    }
4396
4397                    // Versions are on the same compatibility track.
4398                    // Now check if the base (sans version) matches.
4399                    let resolved = if let Some(wildcard_idx) = key_base.find('*') {
4400                        let lhs = &key_base[..wildcard_idx];
4401                        let rhs = &key_base[wildcard_idx + 1..];
4402                        if impt_sans_version.starts_with(lhs) && impt_sans_version.ends_with(rhs) {
4403                            let matched = &impt_sans_version[wildcard_idx
4404                                ..wildcard_idx + impt_sans_version.len() - lhs.len() - rhs.len()];
4405                            Some(mapping.replace('*', matched))
4406                        } else {
4407                            None
4408                        }
4409                    } else if key_base == impt_sans_version {
4410                        Some(mapping.clone())
4411                    } else {
4412                        None
4413                    };
4414
4415                    if let Some(resolved_mapping) = resolved {
4416                        // Prefer the highest compatible version
4417                        match &best_match {
4418                            Some((_, prev_ver)) if key_ver <= *prev_ver => {}
4419                            _ => {
4420                                best_match = Some((resolved_mapping, key_ver));
4421                            }
4422                        }
4423                    }
4424                }
4425
4426                if let Some((mapping, _)) = best_match {
4427                    return parse_mapping(&mapping);
4428                }
4429            }
4430        }
4431    }
4432    (impt_sans_version.to_string(), None)
4433}
4434
4435pub fn parse_world_key(name: &str) -> Option<(&str, &str, &str)> {
4436    let registry_idx = name.find(':')?;
4437    let ns = &name[0..registry_idx];
4438    match name.rfind('/') {
4439        Some(sep_idx) => {
4440            let end = if let Some(version_idx) = name.rfind('@') {
4441                version_idx
4442            } else {
4443                name.len()
4444            };
4445            Some((
4446                ns,
4447                &name[registry_idx + 1..sep_idx],
4448                &name[sep_idx + 1..end],
4449            ))
4450        }
4451        // interface is a namespace, function is a default export
4452        None => Some((ns, &name[registry_idx + 1..], "")),
4453    }
4454}
4455
4456fn core_file_name(name: &str, idx: u32) -> String {
4457    let i_str = if idx == 0 {
4458        String::from("")
4459    } else {
4460        (idx + 1).to_string()
4461    };
4462    format!("{name}.core{i_str}.wasm")
4463}
4464
4465/// Encode a [`StringEncoding`] as a string that can be used in Javascript
4466fn string_encoding_js_literal(val: &wasmtime_environ::component::StringEncoding) -> &'static str {
4467    match val {
4468        wasmtime_environ::component::StringEncoding::Utf8 => "'utf8'",
4469        wasmtime_environ::component::StringEncoding::Utf16 => "'utf16'",
4470        wasmtime_environ::component::StringEncoding::CompactUtf16 => "'compact-utf16'",
4471    }
4472}
4473
4474/// Generate the javascript that corresponds to a list of lifting functions for a given list of types
4475pub fn gen_flat_lift_fn_list_js_expr(
4476    intrinsic_mgr: &mut Instantiator,
4477    types: &[InterfaceType],
4478    canon_opts: &CanonicalOptions,
4479) -> String {
4480    let mut lift_fns: Vec<String> = Vec::with_capacity(types.len());
4481    for ty in types.iter() {
4482        lift_fns.push(gen_flat_lift_fn_js_expr(
4483            intrinsic_mgr,
4484            ty,
4485            &canon_opts.string_encoding,
4486        ));
4487    }
4488    format!("[{}]", lift_fns.join(","))
4489}
4490
4491/// Generate the javascript lifting function for a given type
4492///
4493/// This function will a function object that can be executed with the right
4494/// context in order to perform the lift. For example, running this for bool
4495/// will produce the following:
4496///
4497/// ```
4498/// _liftFlatBool
4499/// ```
4500///
4501/// This is becasue all it takes to lift a flat boolean is to run the _liftFlatBool function intrinsic.
4502///
4503/// The intrinsic it guaranteed to be in scope once execution time because it wlil be used in the relevant branch.
4504///
4505pub fn gen_flat_lift_fn_js_expr(
4506    intrinsic_mgr: &mut Instantiator,
4507    ty: &InterfaceType,
4508    string_encoding: &wasmtime_environ::component::StringEncoding,
4509) -> String {
4510    let component_types = intrinsic_mgr.types;
4511
4512    match ty {
4513        InterfaceType::Bool => {
4514            intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatBool));
4515            Intrinsic::Lift(LiftIntrinsic::LiftFlatBool).name().into()
4516        }
4517
4518        InterfaceType::S8 => {
4519            intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS8));
4520            Intrinsic::Lift(LiftIntrinsic::LiftFlatS8).name().into()
4521        }
4522
4523        InterfaceType::U8 => {
4524            intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU8));
4525            Intrinsic::Lift(LiftIntrinsic::LiftFlatU8).name().into()
4526        }
4527
4528        InterfaceType::S16 => {
4529            intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS16));
4530            Intrinsic::Lift(LiftIntrinsic::LiftFlatS16).name().into()
4531        }
4532
4533        InterfaceType::U16 => {
4534            intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU16));
4535            Intrinsic::Lift(LiftIntrinsic::LiftFlatU16).name().into()
4536        }
4537
4538        InterfaceType::S32 => {
4539            intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS32));
4540            Intrinsic::Lift(LiftIntrinsic::LiftFlatS32).name().into()
4541        }
4542
4543        InterfaceType::U32 => {
4544            intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU32));
4545            Intrinsic::Lift(LiftIntrinsic::LiftFlatU32).name().into()
4546        }
4547
4548        InterfaceType::S64 => {
4549            intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS64));
4550            Intrinsic::Lift(LiftIntrinsic::LiftFlatS64).name().into()
4551        }
4552
4553        InterfaceType::U64 => {
4554            intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU64));
4555            Intrinsic::Lift(LiftIntrinsic::LiftFlatU64).name().into()
4556        }
4557
4558        InterfaceType::Float32 => {
4559            intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFloat32));
4560            Intrinsic::Lift(LiftIntrinsic::LiftFlatFloat32)
4561                .name()
4562                .into()
4563        }
4564
4565        InterfaceType::Float64 => {
4566            intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFloat64));
4567            Intrinsic::Lift(LiftIntrinsic::LiftFlatFloat64)
4568                .name()
4569                .into()
4570        }
4571
4572        InterfaceType::Char => {
4573            intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatChar));
4574            Intrinsic::Lift(LiftIntrinsic::LiftFlatChar).name().into()
4575        }
4576
4577        InterfaceType::String => match string_encoding {
4578            wasmtime_environ::component::StringEncoding::Utf8 => {
4579                intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatStringUtf8));
4580                Intrinsic::Lift(LiftIntrinsic::LiftFlatStringUtf8)
4581                    .name()
4582                    .into()
4583            }
4584            wasmtime_environ::component::StringEncoding::Utf16 => {
4585                intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatStringUtf16));
4586                Intrinsic::Lift(LiftIntrinsic::LiftFlatStringUtf16)
4587                    .name()
4588                    .into()
4589            }
4590            wasmtime_environ::component::StringEncoding::CompactUtf16 => {
4591                todo!("latin1+utf8 not supported")
4592            }
4593        },
4594
4595        InterfaceType::Record(ty_idx) => {
4596            intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatRecord));
4597            let lift_fn = Intrinsic::Lift(LiftIntrinsic::LiftFlatRecord).name();
4598            let record_ty = &component_types[*ty_idx];
4599            let mut keys_and_lifts_expr = String::from("[");
4600            // For each field we build a list of [name, liftFn, 32bit alignment]
4601            // so that the record lifting function (which is a higher level function)
4602            // can properly generate a function that lifts the fields.
4603            for f in &record_ty.fields {
4604                keys_and_lifts_expr.push_str(&format!(
4605                    "['{}', {}, {}, {}],",
4606                    f.name.to_lower_camel_case(),
4607                    gen_flat_lift_fn_js_expr(intrinsic_mgr, &f.ty, string_encoding),
4608                    component_types.canonical_abi(ty).size32,
4609                    component_types.canonical_abi(ty).align32,
4610                ));
4611            }
4612            keys_and_lifts_expr.push(']');
4613            format!("{lift_fn}({keys_and_lifts_expr})")
4614        }
4615
4616        InterfaceType::Variant(ty_idx) => {
4617            intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatVariant));
4618            let lift_fn = Intrinsic::Lift(LiftIntrinsic::LiftFlatVariant).name();
4619            let variant_ty = &component_types[*ty_idx];
4620            let mut cases_and_lifts_expr = String::from("[");
4621            for (name, maybe_ty) in &variant_ty.cases {
4622                let lift_args = match maybe_ty {
4623                    None => format!("['{}', null, 0, 0, 0],", name),
4624                    Some(ty) => {
4625                        format!(
4626                            "['{name}', {}, {}, {}, {}],",
4627                            gen_flat_lift_fn_js_expr(intrinsic_mgr, ty, string_encoding),
4628                            variant_ty.abi.size32,
4629                            variant_ty.abi.align32,
4630                            variant_ty.info.payload_offset32,
4631                        )
4632                    }
4633                };
4634                cases_and_lifts_expr.push_str(&lift_args);
4635            }
4636            cases_and_lifts_expr.push(']');
4637            format!("{lift_fn}({cases_and_lifts_expr})")
4638        }
4639
4640        InterfaceType::List(ty_idx) => {
4641            intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatList));
4642            let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatList).name();
4643            let list_ty = &component_types[*ty_idx];
4644            let lift_fn_expr =
4645                gen_flat_lift_fn_js_expr(intrinsic_mgr, &list_ty.element, string_encoding);
4646            let elem_cabi = component_types.canonical_abi(&list_ty.element);
4647            let align_32 = elem_cabi.align32;
4648            let size_32 = elem_cabi.size32;
4649            format!("{f}({{ elemLiftFn: {lift_fn_expr}, align32: {align_32}, size32: {size_32} }})")
4650        }
4651
4652        InterfaceType::FixedLengthList(ty_idx) => {
4653            intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatList));
4654            let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatList).name();
4655            let list_ty = &component_types[*ty_idx];
4656            let lift_fn_expr =
4657                gen_flat_lift_fn_js_expr(intrinsic_mgr, &list_ty.element, string_encoding);
4658            let list_len = list_ty.size;
4659            let elem_cabi = component_types.canonical_abi(&list_ty.element);
4660            let align_32 = elem_cabi.align32;
4661            let size_32 = elem_cabi.size32;
4662            format!(
4663                "{f}({{ elemLiftFn: {lift_fn_expr}, align32: {align_32}, size32: {size_32}, knownLen: {list_len} }})"
4664            )
4665        }
4666
4667        InterfaceType::Tuple(ty_idx) => {
4668            intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatTuple));
4669            let tuple_ty = &component_types[*ty_idx];
4670            let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatTuple).name();
4671            let size_u32 = tuple_ty.abi.size32;
4672            let align_u32 = tuple_ty.abi.align32;
4673
4674            let mut elem_lifts_expr = String::from("[");
4675            for ty in &tuple_ty.types {
4676                let lift_fn_js = gen_flat_lift_fn_js_expr(intrinsic_mgr, ty, string_encoding);
4677                elem_lifts_expr.push_str(&format!("[{lift_fn_js}, {size_u32}, {align_u32}],"));
4678            }
4679            elem_lifts_expr.push(']');
4680
4681            format!("{f}({elem_lifts_expr})")
4682        }
4683
4684        InterfaceType::Flags(ty_idx) => {
4685            intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFlags));
4686            let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatFlags).name();
4687            let flags_ty = &component_types[*ty_idx];
4688            let size_u32 = flags_ty.abi.size32;
4689            let align_u32 = flags_ty.abi.align32;
4690            let names_expr = format!(
4691                "[{}]",
4692                flags_ty
4693                    .names
4694                    .iter()
4695                    .map(|s| format!("'{s}'"))
4696                    .collect::<Vec<_>>()
4697                    .join(",")
4698            );
4699            let num_flags = flags_ty.names.len();
4700            let elem_size = if num_flags <= 8 {
4701                1
4702            } else if num_flags <= 16 {
4703                2
4704            } else {
4705                4
4706            };
4707            format!(
4708                "{f}({{ names: {names_expr}, size32: {size_u32}, align32: {align_u32}, intSize: {elem_size} }})"
4709            )
4710        }
4711
4712        InterfaceType::Enum(ty_idx) => {
4713            intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatEnum));
4714            let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatEnum).name();
4715            let enum_ty = &component_types[*ty_idx];
4716            let size_32 = enum_ty.abi.size32;
4717            let align_32 = enum_ty.abi.align32;
4718            let payload_offset_32 = enum_ty.info.payload_offset32;
4719
4720            let mut elem_lifts_expr = String::from("[");
4721            for name in &enum_ty.names {
4722                elem_lifts_expr.push_str(&format!(
4723                    "['{name}', null, {size_32}, {align_32}, {payload_offset_32}],"
4724                ));
4725            }
4726            elem_lifts_expr.push(']');
4727
4728            format!("{f}({elem_lifts_expr})")
4729        }
4730
4731        InterfaceType::Option(ty_idx) => {
4732            intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatOption));
4733            let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatOption).name();
4734            let option_ty = &component_types[*ty_idx];
4735            let payload_offset_32 = option_ty.info.payload_offset32;
4736            let align_32 = option_ty.abi.align32;
4737            let size_32 = option_ty.abi.size32;
4738            let lift_fn_js =
4739                gen_flat_lift_fn_js_expr(intrinsic_mgr, &option_ty.ty, string_encoding);
4740            // NOTE: options are treated as variants
4741            format!(
4742                "{f}([
4743                     ['none', null, {size_32}, {align_32}, {payload_offset_32} ],
4744                     ['some', {lift_fn_js}, {size_32}, {align_32}, {payload_offset_32} ],
4745                 ])"
4746            )
4747        }
4748
4749        InterfaceType::Result(ty_idx) => {
4750            intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatResult));
4751            let lift_fn = Intrinsic::Lift(LiftIntrinsic::LiftFlatResult).name();
4752            let result_ty = &component_types[*ty_idx];
4753            let mut cases_and_lifts_expr = String::from("[");
4754
4755            if let Some(ok_ty) = result_ty.ok {
4756                cases_and_lifts_expr.push_str(&format!(
4757                    "['ok', {}, {}, {}, {}],",
4758                    gen_flat_lift_fn_js_expr(intrinsic_mgr, &ok_ty, string_encoding),
4759                    result_ty.abi.size32,
4760                    result_ty.abi.align32,
4761                    result_ty.info.payload_offset32,
4762                ))
4763            } else {
4764                cases_and_lifts_expr.push_str("['ok', null, 0, 0, 0],");
4765            }
4766
4767            if let Some(err_ty) = &result_ty.err {
4768                cases_and_lifts_expr.push_str(&format!(
4769                    "['err', {}, {}, {}, {}],",
4770                    gen_flat_lift_fn_js_expr(intrinsic_mgr, err_ty, string_encoding),
4771                    result_ty.abi.size32,
4772                    result_ty.abi.align32,
4773                    result_ty.info.payload_offset32,
4774                ))
4775            } else {
4776                cases_and_lifts_expr.push_str("['err', null, 0, 0, 0],");
4777            }
4778
4779            cases_and_lifts_expr.push(']');
4780            format!("{lift_fn}({cases_and_lifts_expr})")
4781        }
4782
4783        InterfaceType::Own(ty_idx) => {
4784            intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatOwn));
4785            intrinsic_mgr.add_intrinsic(Intrinsic::JsHelper(JsHelperIntrinsic::EmptyFunc));
4786            intrinsic_mgr.add_intrinsic(Intrinsic::SymbolResourceHandle);
4787            intrinsic_mgr.add_intrinsic(Intrinsic::SymbolDispose);
4788            intrinsic_mgr
4789                .add_intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableRemove));
4790            intrinsic_mgr.add_intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableFlag));
4791            let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatOwn).name();
4792            let table_ty = &component_types[*ty_idx];
4793            let component_idx = table_ty.unwrap_concrete_instance().as_u32();
4794            let resource_idx = table_ty.unwrap_concrete_ty();
4795
4796            if let Some(resource_typedef) = intrinsic_mgr
4797                .exports_resource_index_types
4798                .get(&resource_idx)
4799                && let Some(ResourceTable { data, .. }) =
4800                    intrinsic_mgr.resource_exports.get(resource_typedef)
4801            {
4802                let (resource_class_name, create_resource_fn_js) = match data {
4803                    ResourceData::Guest { .. } => {
4804                        unimplemented!(
4805                            "owned resources created by guests should must have host-side data"
4806                        )
4807                    }
4808                    ResourceData::Host {
4809                        tid,
4810                        local_name,
4811                        dtor_name,
4812                        ..
4813                    } => {
4814                        let empty_func = JsHelperIntrinsic::EmptyFunc.name();
4815                        let symbol_resource_handle = Intrinsic::SymbolResourceHandle.name();
4816                        let symbol_dispose = Intrinsic::SymbolDispose.name();
4817                        let rsc_table_remove = ResourceIntrinsic::ResourceTableRemove.name();
4818                        let tid = tid.as_u32();
4819                        let rsc_flag = ResourceIntrinsic::ResourceTableFlag.name();
4820
4821                        let dtor_setup_js = dtor_name.as_ref().map(|dtor|
4822                                format!(
4823                                    r#"
4824                                      Object.defineProperty(
4825                                          resourceObj,
4826                                          {symbol_dispose},
4827                                          {{
4828                                              writable: true,
4829                                              value: function() {{
4830                                                  finalizationRegistry{tid}.unregister(resourceObj);
4831                                                  {rsc_table_remove}(handleTable{tid}, handle);
4832                                                  resourceObj[{symbol_dispose}] = {empty_func};
4833                                                  resourceObj[{symbol_resource_handle}] = undefined;
4834                                                  {dtor}(handleTable{tid}[(handle << 1) + 1] & ~{rsc_flag});
4835                                              }}
4836                                         }}
4837                                     );
4838                                    "#
4839                                )
4840                            ).unwrap_or_default();
4841
4842                        let create_resource_fn_js = format!(
4843                            r#"
4844                                  (handle) => {{
4845                                      const resourceObj = Object.create({local_name}.prototype);
4846                                      Object.defineProperty(resourceObj, {symbol_resource_handle}, {{
4847                                          writable: true,
4848                                          value: handle,
4849                                      }});
4850                                      finalizationRegistry{tid}.register(resourceObj, handle, resourceObj);
4851                                      {dtor_setup_js}
4852                                      return resourceObj;
4853                                  }}
4854                                 "#
4855                        );
4856
4857                        (local_name.to_string(), create_resource_fn_js)
4858                    }
4859                };
4860
4861                format!(
4862                    r#"{f}({{
4863                       componentIdx: {component_idx},
4864                       className: {resource_class_name},
4865                       createResourceFn: {create_resource_fn_js},
4866                    }})
4867                "#,
4868                )
4869            } else {
4870                // In this case we couldn't find required type information
4871                format!(
4872                    r#"{f}({{
4873                       componentIdx: {component_idx},
4874                       className: null,
4875                       createResourceFn: () => {{ throw new Error('invalid/missing resource type data'); }},
4876                    }})
4877                "#,
4878                )
4879            }
4880        }
4881
4882        InterfaceType::Borrow(ty_idx) => {
4883            intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatBorrow));
4884            let table_idx = ty_idx.as_u32();
4885            let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatBorrow).name();
4886            format!("{f}.bind(null, {table_idx})")
4887        }
4888
4889        InterfaceType::Future(ty_idx) => {
4890            intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFuture));
4891            let table_idx = ty_idx.as_u32();
4892            let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatFuture).name();
4893            format!("{f}.bind(null, {table_idx})")
4894        }
4895
4896        InterfaceType::Stream(ty_idx) => {
4897            intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatStream));
4898            let table_idx = ty_idx.as_u32();
4899            let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatStream).name();
4900            let table_ty = &component_types[*ty_idx];
4901            let component_idx = table_ty.instance.as_u32();
4902            let stream_ty_idx = table_ty.ty;
4903            let stream_ty = &component_types[stream_ty_idx];
4904            let payload = stream_ty.payload;
4905            let (is_borrowed, is_none_type_js, is_numeric_type_js) = match payload {
4906                None => (false, true, false),
4907                Some(t) => (
4908                    matches!(t, InterfaceType::Borrow(_)),
4909                    false,
4910                    matches!(
4911                        ty,
4912                        InterfaceType::U8
4913                            | InterfaceType::U16
4914                            | InterfaceType::U32
4915                            | InterfaceType::U64
4916                            | InterfaceType::S8
4917                            | InterfaceType::S16
4918                            | InterfaceType::S32
4919                            | InterfaceType::S64
4920                            | InterfaceType::Float32
4921                            | InterfaceType::Float64
4922                    ),
4923                ),
4924            };
4925
4926            format!(
4927                r#"{f}({{
4928                 streamTableIdx: {table_idx},
4929                 componentIdx: {component_idx},
4930                 isBorrowedType: {is_borrowed},
4931                 isNoneType: {is_none_type_js},
4932                 isNumericTypeJs: {is_numeric_type_js},
4933             }})"#
4934            )
4935        }
4936
4937        InterfaceType::ErrorContext(ty_idx) => {
4938            intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatErrorContext));
4939            let table_idx = ty_idx.as_u32();
4940            let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatErrorContext).name();
4941            format!("{f}.bind(null, {table_idx})")
4942        }
4943    }
4944}
4945
4946/// Generate the javascript that corresponds to a list of lowering functions for a given list of types
4947pub fn gen_flat_lower_fn_list_js_expr(
4948    intrinsic_mgr: &mut impl ManagesIntrinsics,
4949    component_types: &ComponentTypes,
4950    types: &[InterfaceType],
4951    string_encoding: &wasmtime_environ::component::StringEncoding,
4952) -> String {
4953    let mut lower_fns: Vec<String> = Vec::with_capacity(types.len());
4954    for ty in types.iter() {
4955        lower_fns.push(gen_flat_lower_fn_js_expr(
4956            intrinsic_mgr,
4957            component_types,
4958            ty,
4959            string_encoding,
4960        ));
4961    }
4962    format!("[{}]", lower_fns.join(","))
4963}
4964
4965/// Generate the javascript lowering function for a given type
4966///
4967/// This function will a function object that can be executed with the right
4968/// context in order to perform the lower. For example, running this for bool
4969/// will produce the following:
4970///
4971/// ```
4972/// _lowerFlatBool
4973/// ```
4974///
4975/// This is becasue all it takes to lower a flat boolean is to run the _lowerFlatBool function intrinsic.
4976///
4977/// The intrinsic it guaranteed to be in scope once execution time because it wlil be used in the relevant branch.
4978///
4979pub fn gen_flat_lower_fn_js_expr(
4980    intrinsic_mgr: &mut impl ManagesIntrinsics,
4981    component_types: &ComponentTypes,
4982    ty: &InterfaceType,
4983    string_encoding: &wasmtime_environ::component::StringEncoding,
4984) -> String {
4985    match ty {
4986        InterfaceType::Bool => {
4987            intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatBool));
4988            Intrinsic::Lower(LowerIntrinsic::LowerFlatBool)
4989                .name()
4990                .into()
4991        }
4992
4993        InterfaceType::S8 => {
4994            intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatS8));
4995            Intrinsic::Lower(LowerIntrinsic::LowerFlatS8).name().into()
4996        }
4997
4998        InterfaceType::U8 => {
4999            intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatU8));
5000            Intrinsic::Lower(LowerIntrinsic::LowerFlatU8).name().into()
5001        }
5002
5003        InterfaceType::S16 => {
5004            intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatS16));
5005            Intrinsic::Lower(LowerIntrinsic::LowerFlatS16).name().into()
5006        }
5007
5008        InterfaceType::U16 => {
5009            intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatU16));
5010            Intrinsic::Lower(LowerIntrinsic::LowerFlatU16).name().into()
5011        }
5012
5013        InterfaceType::S32 => {
5014            intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatS32));
5015            Intrinsic::Lower(LowerIntrinsic::LowerFlatS32).name().into()
5016        }
5017
5018        InterfaceType::U32 => {
5019            intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatU32));
5020            Intrinsic::Lower(LowerIntrinsic::LowerFlatU32).name().into()
5021        }
5022
5023        InterfaceType::S64 => {
5024            intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatS64));
5025            Intrinsic::Lower(LowerIntrinsic::LowerFlatS64).name().into()
5026        }
5027
5028        InterfaceType::U64 => {
5029            intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatU64));
5030            Intrinsic::Lower(LowerIntrinsic::LowerFlatU64).name().into()
5031        }
5032
5033        InterfaceType::Float32 => {
5034            intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatFloat32));
5035            Intrinsic::Lower(LowerIntrinsic::LowerFlatFloat32)
5036                .name()
5037                .into()
5038        }
5039
5040        InterfaceType::Float64 => {
5041            intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatFloat64));
5042            Intrinsic::Lower(LowerIntrinsic::LowerFlatFloat64)
5043                .name()
5044                .into()
5045        }
5046
5047        InterfaceType::Char => {
5048            intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatChar));
5049            Intrinsic::Lower(LowerIntrinsic::LowerFlatChar)
5050                .name()
5051                .into()
5052        }
5053
5054        InterfaceType::String => match string_encoding {
5055            wasmtime_environ::component::StringEncoding::Utf8 => {
5056                intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatStringUtf8));
5057                Intrinsic::Lower(LowerIntrinsic::LowerFlatStringUtf8)
5058                    .name()
5059                    .into()
5060            }
5061            wasmtime_environ::component::StringEncoding::Utf16 => {
5062                intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatStringUtf16));
5063                Intrinsic::Lower(LowerIntrinsic::LowerFlatStringUtf16)
5064                    .name()
5065                    .into()
5066            }
5067            wasmtime_environ::component::StringEncoding::CompactUtf16 => {
5068                todo!("latin1+utf8 not supported")
5069            }
5070        },
5071
5072        InterfaceType::Record(ty_idx) => {
5073            intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatRecord));
5074            let lower_fn = Intrinsic::Lower(LowerIntrinsic::LowerFlatRecord).name();
5075            let record_ty = &component_types[*ty_idx];
5076            let mut keys_and_lowers_expr = String::from("[");
5077            for f in &record_ty.fields {
5078                // For each field we build a list of [name, lowerFn, 32bit alignment]
5079                // so that the record lowering function (which is a higher level function)
5080                // can properly generate a function that lowers the fields.
5081                keys_and_lowers_expr.push_str(&format!(
5082                    "['{}', {}, {}, {} ],",
5083                    f.name.to_lower_camel_case(),
5084                    gen_flat_lower_fn_js_expr(
5085                        intrinsic_mgr,
5086                        component_types,
5087                        &f.ty,
5088                        string_encoding
5089                    ),
5090                    component_types.canonical_abi(ty).size32,
5091                    component_types.canonical_abi(ty).align32,
5092                ));
5093            }
5094            keys_and_lowers_expr.push(']');
5095            format!("{lower_fn}.bind(null, {keys_and_lowers_expr})")
5096        }
5097
5098        InterfaceType::Variant(ty_idx) => {
5099            intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatVariant));
5100            let lower_fn = Intrinsic::Lower(LowerIntrinsic::LowerFlatVariant).name();
5101            let variant_ty = &component_types[*ty_idx];
5102            let size32 = variant_ty.abi.size32;
5103            let align32 = variant_ty.abi.align32;
5104            let payload_offset32 = variant_ty.info.payload_offset32;
5105
5106            let mut lower_metas_expr = String::from("[");
5107            for (name, maybe_ty) in variant_ty.cases.iter() {
5108                lower_metas_expr.push_str(&format!(
5109                    "[ '{name}', {}, {size32}, {align32}, {payload_offset32} ],",
5110                    maybe_ty
5111                        .map(|ty| gen_flat_lower_fn_js_expr(
5112                            intrinsic_mgr,
5113                            component_types,
5114                            &ty,
5115                            string_encoding,
5116                        ))
5117                        .unwrap_or_else(|| "null".into()),
5118                ));
5119            }
5120            lower_metas_expr.push(']');
5121
5122            format!("{lower_fn}({lower_metas_expr})")
5123        }
5124
5125        InterfaceType::List(ty_idx) => {
5126            intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatList));
5127            let list_ty = &component_types[*ty_idx];
5128            let elem_ty_lower_expr = gen_flat_lower_fn_js_expr(
5129                intrinsic_mgr,
5130                component_types,
5131                &list_ty.element,
5132                string_encoding,
5133            );
5134            let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatList).name();
5135            let ty_idx = ty_idx.as_u32();
5136            format!("{f}({{ elemLowerFn: {elem_ty_lower_expr}, typeIdx: {ty_idx} }})")
5137        }
5138
5139        InterfaceType::FixedLengthList(ty_idx) => {
5140            // TODO(fix): add more robust handling for fixed length lists
5141            intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatList));
5142            let list_ty = &component_types[*ty_idx];
5143            let elem_ty_lower_expr = gen_flat_lower_fn_js_expr(
5144                intrinsic_mgr,
5145                component_types,
5146                &list_ty.element,
5147                string_encoding,
5148            );
5149            let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatList).name();
5150            let ty_idx = ty_idx.as_u32();
5151            format!("{f}({{ elemLowerFn: {elem_ty_lower_expr}, typeIdx: {ty_idx} }})")
5152        }
5153
5154        InterfaceType::Tuple(ty_idx) => {
5155            intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatTuple));
5156            let ty_idx = ty_idx.as_u32();
5157            let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatTuple).name();
5158            format!("{f}.bind(null, {ty_idx})")
5159        }
5160
5161        InterfaceType::Flags(ty_idx) => {
5162            intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatFlags));
5163            let ty_idx = ty_idx.as_u32();
5164            let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatFlags).name();
5165            format!("{f}.bind(null, {ty_idx})")
5166        }
5167
5168        InterfaceType::Enum(ty_idx) => {
5169            intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatEnum));
5170            let ty_idx = ty_idx.as_u32();
5171            let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatEnum).name();
5172            format!("{f}.bind(null, {ty_idx})")
5173        }
5174
5175        InterfaceType::Option(ty_idx) => {
5176            intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatOption));
5177            let option_ty = &component_types[*ty_idx];
5178            let size32 = option_ty.abi.size32;
5179            let align32 = option_ty.abi.align32;
5180            let payload_offset32 = option_ty.info.payload_offset32;
5181
5182            let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatOption).name();
5183
5184            let mut cases_and_lowers_expr = String::from("[");
5185            cases_and_lowers_expr.push_str(&format!(
5186                "[ 'some', {}, {size32}, {align32}, {payload_offset32} ],",
5187                gen_flat_lower_fn_js_expr(
5188                    intrinsic_mgr,
5189                    component_types,
5190                    &option_ty.ty,
5191                    string_encoding,
5192                )
5193            ));
5194            cases_and_lowers_expr.push_str(&format!(
5195                "[ 'none', null, {size32}, {align32}, {payload_offset32} ],",
5196            ));
5197            cases_and_lowers_expr.push(']');
5198
5199            format!("{f}({cases_and_lowers_expr})")
5200        }
5201
5202        InterfaceType::Result(ty_idx) => {
5203            intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatResult));
5204            let lower_fn = Intrinsic::Lower(LowerIntrinsic::LowerFlatResult).name();
5205            let result_ty = &component_types[*ty_idx];
5206            let size32 = result_ty.abi.size32;
5207            let align32 = result_ty.abi.align32;
5208            let payload_offset32 = result_ty.info.payload_offset32;
5209
5210            let mut cases_and_lowers_expr = String::from("[");
5211            cases_and_lowers_expr.push_str(&format!(
5212                "[ 'ok', {}, {size32}, {align32}, {payload_offset32} ],",
5213                result_ty
5214                    .ok
5215                    .map(|ty| gen_flat_lower_fn_js_expr(
5216                        intrinsic_mgr,
5217                        component_types,
5218                        &ty,
5219                        string_encoding,
5220                    ))
5221                    .unwrap_or_else(|| "null".into())
5222            ));
5223            cases_and_lowers_expr.push_str(&format!(
5224                "[ 'err', {}, {size32}, {align32}, {payload_offset32} ],",
5225                result_ty
5226                    .err
5227                    .map(|ty| gen_flat_lower_fn_js_expr(
5228                        intrinsic_mgr,
5229                        component_types,
5230                        &ty,
5231                        string_encoding,
5232                    ))
5233                    .unwrap_or_else(|| "null".into())
5234            ));
5235            cases_and_lowers_expr.push(']');
5236            format!("{lower_fn}({cases_and_lowers_expr})")
5237        }
5238
5239        InterfaceType::Own(ty_idx) => {
5240            intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatOwn));
5241            let table_idx = ty_idx.as_u32();
5242            let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatOwn).name();
5243            format!("{f}.bind(null, {table_idx})")
5244        }
5245
5246        InterfaceType::Borrow(ty_idx) => {
5247            intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatBorrow));
5248            let table_idx = ty_idx.as_u32();
5249            let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatBorrow).name();
5250            format!("{f}.bind(null, {table_idx})")
5251        }
5252
5253        InterfaceType::Future(ty_idx) => {
5254            intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatFuture));
5255            let table_idx = ty_idx.as_u32();
5256            let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatFuture).name();
5257
5258            format!("{f}.bind(null, {table_idx})")
5259        }
5260
5261        InterfaceType::Stream(ty_idx) => {
5262            intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatStream));
5263            let table_idx = ty_idx.as_u32();
5264            let lower_flat_stream_fn = Intrinsic::Lower(LowerIntrinsic::LowerFlatStream).name();
5265            format!("{lower_flat_stream_fn}.bind(null, {table_idx})")
5266        }
5267
5268        InterfaceType::ErrorContext(ty_idx) => {
5269            intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatErrorContext));
5270            let table_idx = ty_idx.as_u32();
5271            let lower_flat_err_ctx_fn =
5272                Intrinsic::Lower(LowerIntrinsic::LowerFlatErrorContext).name();
5273            format!("{lower_flat_err_ctx_fn}.bind(null, {table_idx})")
5274        }
5275    }
5276}
5277
5278#[cfg(test)]
5279mod tests {
5280    use super::*;
5281
5282    /// Helper to extract just the compat key string for cleaner test assertions.
5283    fn compat_key(version_str: &str) -> Option<String> {
5284        semver_compat_key(version_str).map(|(key, _)| key)
5285    }
5286
5287    #[test]
5288    fn test_semver_compat_key() {
5289        assert_eq!(compat_key("1.0.0"), Some("1".into()));
5290        assert_eq!(compat_key("1.2.3"), Some("1".into()));
5291        assert_eq!(compat_key("2.0.0"), Some("2".into()));
5292        assert_eq!(compat_key("0.2.0"), Some("0.2".into()));
5293        assert_eq!(compat_key("0.2.10"), Some("0.2".into()));
5294        assert_eq!(compat_key("0.1.0"), Some("0.1".into()));
5295        assert_eq!(compat_key("0.0.1"), None);
5296        assert_eq!(compat_key("1.0.0-rc.1"), None);
5297        assert_eq!(compat_key("0.2.0-pre"), None);
5298        assert_eq!(compat_key("not-a-version"), None);
5299    }
5300
5301    #[test]
5302    fn test_semver_compat_key_returns_parsed_version() {
5303        let (key, ver) = semver_compat_key("1.2.3").unwrap();
5304        assert_eq!(key, "1");
5305        assert_eq!(ver, Version::new(1, 2, 3));
5306    }
5307
5308    #[test]
5309    fn test_map_import_exact_match() {
5310        let mut map = HashMap::new();
5311        map.insert("wasi:http/types@0.2.0".into(), "./http.js#types".into());
5312        let map = Some(map);
5313        assert_eq!(
5314            map_import(&map, "wasi:http/types@0.2.0"),
5315            ("./http.js".into(), Some("types".into()))
5316        );
5317    }
5318
5319    #[test]
5320    fn test_map_import_sans_version_match() {
5321        let mut map = HashMap::new();
5322        map.insert("wasi:http/types".into(), "./http.js".into());
5323        let map = Some(map);
5324        assert_eq!(
5325            map_import(&map, "wasi:http/types@0.2.10"),
5326            ("./http.js".into(), None)
5327        );
5328    }
5329
5330    #[test]
5331    fn test_map_import_wildcard_sans_version() {
5332        // Unversioned wildcard key matches via version-stripped path (pre-existing logic)
5333        let mut map = HashMap::new();
5334        map.insert("wasi:http/*".into(), "./http.js#*".into());
5335        let map = Some(map);
5336        assert_eq!(
5337            map_import(&map, "wasi:http/types@0.2.10"),
5338            ("./http.js".into(), Some("types".into()))
5339        );
5340    }
5341
5342    #[test]
5343    fn test_map_import_semver_exact_key() {
5344        // Map has @0.2.0, import is @0.2.10 — should match via semver
5345        let mut map = HashMap::new();
5346        map.insert("wasi:http/types@0.2.0".into(), "./http.js".into());
5347        let map = Some(map);
5348        assert_eq!(
5349            map_import(&map, "wasi:http/types@0.2.10"),
5350            ("./http.js".into(), None)
5351        );
5352    }
5353
5354    #[test]
5355    fn test_map_import_semver_wildcard_key() {
5356        // Map has wasi:http/*@0.2.0, import is @0.2.10 — should match via semver
5357        let mut map = HashMap::new();
5358        map.insert("wasi:http/*@0.2.1".into(), "./http.js#*".into());
5359        let map = Some(map);
5360        assert_eq!(
5361            map_import(&map, "wasi:http/types@0.2.10"),
5362            ("./http.js".into(), Some("types".into()))
5363        );
5364    }
5365
5366    #[test]
5367    fn test_map_import_semver_lower_import_version() {
5368        // Import version (0.2.1) is lower than map entry (0.2.10) — same compat track
5369        let mut map = HashMap::new();
5370        map.insert("wasi:http/types@0.2.10".into(), "./http.js".into());
5371        let map = Some(map);
5372        assert_eq!(
5373            map_import(&map, "wasi:http/types@0.2.1"),
5374            ("./http.js".into(), None)
5375        );
5376    }
5377
5378    #[test]
5379    fn test_map_import_semver_no_cross_minor() {
5380        // 0.2.x should NOT match 0.3.x
5381        let mut map = HashMap::new();
5382        map.insert("wasi:http/types@0.3.0".into(), "./http.js".into());
5383        let map = Some(map);
5384        assert_eq!(
5385            map_import(&map, "wasi:http/types@0.2.10"),
5386            ("wasi:http/types".into(), None)
5387        );
5388    }
5389
5390    #[test]
5391    fn test_map_import_semver_prefers_highest() {
5392        // Multiple compatible versions — should prefer highest
5393        let mut map = HashMap::new();
5394        map.insert("wasi:http/types@0.2.1".into(), "./http-old.js".into());
5395        map.insert("wasi:http/types@0.2.5".into(), "./http-new.js".into());
5396        let map = Some(map);
5397        assert_eq!(
5398            map_import(&map, "wasi:http/types@0.2.10"),
5399            ("./http-new.js".into(), None)
5400        );
5401    }
5402
5403    #[test]
5404    fn test_map_import_no_match_prerelease() {
5405        let mut map = HashMap::new();
5406        map.insert("wasi:http/types@0.2.0-rc.1".into(), "./http.js".into());
5407        let map = Some(map);
5408        assert_eq!(
5409            map_import(&map, "wasi:http/types@0.2.0"),
5410            ("wasi:http/types".into(), None)
5411        );
5412    }
5413
5414    #[test]
5415    fn test_map_import_no_match_zero_zero() {
5416        let mut map = HashMap::new();
5417        map.insert("wasi:http/types@0.0.1".into(), "./http.js".into());
5418        let map = Some(map);
5419        assert_eq!(
5420            map_import(&map, "wasi:http/types@0.0.2"),
5421            ("wasi:http/types".into(), None)
5422        );
5423    }
5424
5425    #[test]
5426    fn test_map_import_semver_major_version() {
5427        // Major version compat: 1.0.0 and 1.2.3 share compat key "1"
5428        let mut map = HashMap::new();
5429        map.insert("wasi:http/types@1.0.0".into(), "./http.js".into());
5430        let map = Some(map);
5431        assert_eq!(
5432            map_import(&map, "wasi:http/types@1.2.3"),
5433            ("./http.js".into(), None)
5434        );
5435    }
5436
5437    #[test]
5438    fn test_map_import_semver_no_cross_major() {
5439        // 1.x.y should NOT match 2.x.y
5440        let mut map = HashMap::new();
5441        map.insert("wasi:http/types@1.0.0".into(), "./http.js".into());
5442        let map = Some(map);
5443        assert_eq!(
5444            map_import(&map, "wasi:http/types@2.0.0"),
5445            ("wasi:http/types".into(), None)
5446        );
5447    }
5448
5449    #[test]
5450    fn test_map_import_no_map() {
5451        // No map provided — returns import sans version
5452        assert_eq!(
5453            map_import(&None, "wasi:http/types@0.2.0"),
5454            ("wasi:http/types".into(), None)
5455        );
5456    }
5457
5458    #[test]
5459    fn test_map_import_no_map_unversioned() {
5460        // No map, no version — returns import as-is
5461        assert_eq!(
5462            map_import(&None, "wasi:http/types"),
5463            ("wasi:http/types".into(), None)
5464        );
5465    }
5466
5467    #[test]
5468    fn test_parse_mapping_with_hash() {
5469        assert_eq!(
5470            parse_mapping("./http.js#types"),
5471            ("./http.js".into(), Some("types".into()))
5472        );
5473    }
5474
5475    #[test]
5476    fn test_parse_mapping_without_hash() {
5477        assert_eq!(parse_mapping("./http.js"), ("./http.js".into(), None));
5478    }
5479
5480    #[test]
5481    fn test_parse_mapping_leading_hash() {
5482        // Leading '#' should not be treated as a separator
5483        assert_eq!(parse_mapping("#foo"), ("#foo".into(), None));
5484    }
5485
5486    #[test]
5487    fn test_parse_mapping_empty() {
5488        assert_eq!(parse_mapping(""), ("".into(), None));
5489    }
5490}