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