Skip to main content

js_component_bindgen/
transpile_bindgen.rs

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