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