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