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