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