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