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