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