js_component_bindgen/
transpile_bindgen.rs

1use std::cell::RefCell;
2use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
3use std::fmt::Write;
4use std::mem;
5
6use anyhow::{bail, Result};
7use base64::engine::general_purpose;
8use base64::Engine as _;
9use heck::{ToKebabCase, ToLowerCamelCase, ToUpperCamelCase};
10use wasmtime_environ::component::{
11    CanonicalOptions, CanonicalOptionsDataModel, Component, ComponentTranslation, ComponentTypes,
12    CoreDef, CoreExport, Export, ExportItem, FixedEncoding, GlobalInitializer, InstantiateModule,
13    InterfaceType, LinearMemoryOptions, LoweredIndex, ResourceIndex, RuntimeComponentInstanceIndex,
14    RuntimeImportIndex, RuntimeInstanceIndex, StaticModuleIndex, Trampoline, TrampolineIndex,
15    TypeDef, TypeFuncIndex, TypeResourceTableIndex,
16};
17use wasmtime_environ::component::{
18    ExportIndex, ExtractCallback, NameMap, NameMapNoIntern, Transcode,
19    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, SizeAlign, Type, TypeDefKind, TypeId, WorldId,
27    WorldItem, WorldKey,
28};
29
30use crate::esm_bindgen::EsmBindgen;
31use crate::files::Files;
32use crate::function_bindgen::{
33    ErrHandling, FunctionBindgen, RemoteResourceMap, ResourceData, ResourceMap, ResourceTable,
34};
35use crate::intrinsics::component::ComponentIntrinsic;
36use crate::intrinsics::lift::LiftIntrinsic;
37use crate::intrinsics::p3::async_future::AsyncFutureIntrinsic;
38use crate::intrinsics::p3::async_stream::AsyncStreamIntrinsic;
39use crate::intrinsics::p3::async_task::AsyncTaskIntrinsic;
40use crate::intrinsics::p3::error_context::ErrCtxIntrinsic;
41use crate::intrinsics::p3::waitable::WaitableIntrinsic;
42use crate::intrinsics::resource::ResourceIntrinsic;
43use crate::intrinsics::string::StringIntrinsic;
44use crate::intrinsics::webidl::WebIdlIntrinsic;
45use crate::intrinsics::{
46    render_intrinsics, AsyncDeterminismProfile, Intrinsic, RenderIntrinsicsArgs,
47};
48use crate::names::{is_js_reserved_word, maybe_quote_id, maybe_quote_member, LocalNames};
49use crate::{
50    core, get_thrown_type, is_guest_async_lifted_fn, requires_async_porcelain, source, uwrite,
51    uwriteln, FunctionIdentifier,
52};
53
54#[derive(Debug, Default, Clone)]
55pub struct TranspileOpts {
56    pub name: String,
57    /// Disables generation of `*.d.ts` files and instead only generates `*.js`
58    /// source files.
59    pub no_typescript: bool,
60    /// Provide a custom JS instantiation API for the component instead
61    /// of the direct importable native ESM output.
62    pub instantiation: Option<InstantiationMode>,
63    /// Configure how import bindings are provided, as high-level JS bindings,
64    /// or as hybrid optimized bindings.
65    pub import_bindings: Option<BindingsMode>,
66    /// Comma-separated list of "from-specifier=./to-specifier.js" mappings of
67    /// component import specifiers to JS import specifiers.
68    pub map: Option<HashMap<String, String>>,
69    /// Disables compatibility in Node.js without a fetch global.
70    pub no_nodejs_compat: bool,
71    /// Set the cutoff byte size for base64 inlining core Wasm in instantiation mode
72    /// (set to 0 to disable all base64 inlining)
73    pub base64_cutoff: usize,
74    /// Enables compatibility for JS environments without top-level await support
75    /// via an async $init promise export to wait for instead.
76    pub tla_compat: bool,
77    /// Disable verification of component Wasm data structures when
78    /// lifting as a production optimization
79    pub valid_lifting_optimization: bool,
80    /// Whether or not to emit `tracing` calls on function entry/exit.
81    pub tracing: bool,
82    /// Whether to generate namespaced exports like `foo as "local:package/foo"`.
83    /// These exports can break typescript builds.
84    pub no_namespaced_exports: bool,
85    /// Whether to output core Wasm utilizing multi-memory or to polyfill
86    /// this handling.
87    pub multi_memory: bool,
88    /// Whether to generate types for a guest module using module declarations.
89    pub guest: bool,
90    /// Configure whether to use `async` imports or exports with
91    /// JavaScript Promise Integration (JSPI).
92    pub async_mode: Option<AsyncMode>,
93}
94
95#[derive(Default, Clone, Debug)]
96pub enum AsyncMode {
97    #[default]
98    Sync,
99    JavaScriptPromiseIntegration {
100        imports: Vec<String>,
101        exports: Vec<String>,
102    },
103}
104
105#[derive(Default, Clone, Debug)]
106pub enum InstantiationMode {
107    #[default]
108    Async,
109    Sync,
110}
111
112/// Internal Bindgen calling convention
113enum CallType {
114    /// Standard calls - inner function is called directly with parameters
115    Standard,
116    /// Exported resource method calls - this is passed as the first argument
117    FirstArgIsThis,
118    /// Imported resource method calls - callee is a member of the parameter
119    CalleeResourceDispatch,
120}
121
122#[derive(Default, Clone, Debug)]
123pub enum BindingsMode {
124    Hybrid,
125    #[default]
126    Js,
127    Optimized,
128    DirectOptimized,
129}
130
131struct JsBindgen<'a> {
132    local_names: LocalNames,
133
134    esm_bindgen: EsmBindgen,
135
136    /// The source code for the "main" file that's going to be created for the
137    /// component we're generating bindings for. This is incrementally added to
138    /// over time and primarily contains the main `instantiate` function as well
139    /// as a type-description of the input/output interfaces.
140    src: Source,
141
142    /// Core module count
143    core_module_cnt: usize,
144
145    /// Various options for code generation.
146    opts: &'a TranspileOpts,
147
148    /// List of all intrinsics emitted to `src` so far.
149    all_intrinsics: BTreeSet<Intrinsic>,
150
151    /// List of all core Wasm exported functions (and if is async) referenced in
152    /// `src` so far.
153    all_core_exported_funcs: Vec<(String, bool)>,
154}
155
156/// Arguments provided to `JSBindgen::bindgen`, normally called to perform bindgen on a given function
157struct JsFunctionBindgenArgs<'a> {
158    /// Number of params that the function expects
159    nparams: usize,
160    /// Internal convention for function calls (ex. whether the first argument is known to be 'this')
161    call_type: CallType,
162    /// Interface name (if inside an interface)
163    iface_name: Option<&'a str>,
164    /// Callee of the function
165    callee: &'a str,
166    /// Canon opts provided for the functions
167    opts: &'a CanonicalOptions,
168    /// Parsed function metadata
169    func: &'a Function,
170    resource_map: &'a ResourceMap,
171    remote_resource_map: &'a RemoteResourceMap,
172    /// ABI variant of the function
173    abi: AbiVariant,
174    /// Whether the function in question is a host async function (i.e. JSPI)
175    requires_async_porcelain: bool,
176    /// Whether the function in question is a guest async function (i.e. WASI P3)
177    is_guest_async_lifted: bool,
178}
179
180#[allow(clippy::too_many_arguments)]
181pub fn transpile_bindgen(
182    name: &str,
183    component: &ComponentTranslation,
184    modules: &PrimaryMap<StaticModuleIndex, core::Translation<'_>>,
185    types: &ComponentTypes,
186    resolve: &Resolve,
187    id: WorldId,
188    opts: TranspileOpts,
189    files: &mut Files,
190) -> (Vec<String>, Vec<(String, Export)>) {
191    let (async_imports, async_exports) = match opts.async_mode.clone() {
192        None | Some(AsyncMode::Sync) => (Default::default(), Default::default()),
193        Some(AsyncMode::JavaScriptPromiseIntegration { imports, exports }) => {
194            (imports.into_iter().collect(), exports.into_iter().collect())
195        }
196    };
197
198    let mut bindgen = JsBindgen {
199        local_names: LocalNames::default(),
200        src: Source::default(),
201        esm_bindgen: EsmBindgen::default(),
202        core_module_cnt: 0,
203        opts: &opts,
204        all_intrinsics: BTreeSet::new(),
205        all_core_exported_funcs: Vec::new(),
206    };
207    bindgen.local_names.exclude_globals(
208        &Intrinsic::get_global_names()
209            .into_iter()
210            .collect::<Vec<_>>(),
211    );
212    bindgen.core_module_cnt = modules.len();
213
214    // Bindings are generated when the `instantiate` method is called on the
215    // Instantiator structure created below
216    let mut instantiator = Instantiator {
217        src: Source::default(),
218        sizes: SizeAlign::default(),
219        gen: &mut bindgen,
220        modules,
221        instances: Default::default(),
222        error_context_component_initialized: (0..component
223            .component
224            .num_runtime_component_instances)
225            .map(|_| false)
226            .collect(),
227        error_context_component_table_initialized: (0..component
228            .component
229            .num_error_context_tables)
230            .map(|_| false)
231            .collect(),
232        resolve,
233        world: id,
234        translation: component,
235        component: &component.component,
236        types,
237        async_imports,
238        async_exports,
239        imports: Default::default(),
240        exports: Default::default(),
241        lowering_options: Default::default(),
242        used_instance_flags: Default::default(),
243        defined_resource_classes: Default::default(),
244        imports_resource_types: Default::default(),
245        exports_resource_types: Default::default(),
246        resources_initialized: BTreeMap::new(),
247        resource_tables_initialized: BTreeMap::new(),
248    };
249    instantiator.sizes.fill(resolve);
250    instantiator.initialize();
251    instantiator.instantiate();
252
253    let mut intrinsic_definitions = source::Source::default();
254
255    instantiator.resource_definitions(&mut intrinsic_definitions);
256    instantiator.instance_flags();
257
258    instantiator.gen.src.js(&instantiator.src.js);
259    instantiator.gen.src.js_init(&instantiator.src.js_init);
260
261    instantiator
262        .gen
263        .finish_component(name, files, &opts, intrinsic_definitions);
264
265    let exports = instantiator
266        .gen
267        .esm_bindgen
268        .exports()
269        .iter()
270        .map(|(export_name, canon_export_name)| {
271            let export = if canon_export_name.contains(':') {
272                instantiator
273                    .component
274                    .exports
275                    .get(canon_export_name, &NameMapNoIntern)
276                    .unwrap()
277            } else {
278                instantiator
279                    .component
280                    .exports
281                    .get(&canon_export_name.to_kebab_case(), &NameMapNoIntern)
282                    .unwrap()
283            };
284            (
285                export_name.to_string(),
286                instantiator.component.export_items[*export].clone(),
287            )
288        })
289        .collect();
290
291    (bindgen.esm_bindgen.import_specifiers(), exports)
292}
293
294impl JsBindgen<'_> {
295    fn finish_component(
296        &mut self,
297        name: &str,
298        files: &mut Files,
299        opts: &TranspileOpts,
300        intrinsic_definitions: source::Source,
301    ) {
302        let mut output = source::Source::default();
303        let mut compilation_promises = source::Source::default();
304        let mut core_exported_funcs = source::Source::default();
305
306        for (core_export_fn, is_async) in self.all_core_exported_funcs.iter() {
307            let local_name = self.local_names.get(core_export_fn);
308            if *is_async {
309                uwriteln!(
310                    core_exported_funcs,
311                    "{local_name} = WebAssembly.promising({core_export_fn});",
312                );
313            } else {
314                uwriteln!(core_exported_funcs, "{local_name} = {core_export_fn};",);
315            }
316        }
317
318        // adds a default implementation of `getCoreModule`
319        if matches!(self.opts.instantiation, Some(InstantiationMode::Async)) {
320            uwriteln!(
321                compilation_promises,
322                "if (!getCoreModule) getCoreModule = (name) => {}(new URL(`./${{name}}`, import.meta.url));",
323                self.intrinsic(Intrinsic::FetchCompile)
324            );
325        }
326
327        // Setup the compilation data and compilation promises
328        let mut removed = BTreeSet::new();
329        for i in 0..self.core_module_cnt {
330            let local_name = format!("module{i}");
331            let mut name_idx = core_file_name(name, i as u32);
332            if self.opts.instantiation.is_some() {
333                uwriteln!(
334                    compilation_promises,
335                    "const {local_name} = getCoreModule('{name_idx}');"
336                );
337            } else if files.get_size(&name_idx).unwrap() < self.opts.base64_cutoff {
338                assert!(removed.insert(i));
339                let data = files.remove(&name_idx).unwrap();
340                uwriteln!(
341                    compilation_promises,
342                    "const {local_name} = {}('{}');",
343                    self.intrinsic(Intrinsic::Base64Compile),
344                    general_purpose::STANDARD_NO_PAD.encode(&data),
345                );
346            } else {
347                // Maintain numerical file orderings when a previous file was
348                // inlined
349                if let Some(&replacement) = removed.iter().next() {
350                    assert!(removed.remove(&replacement) && removed.insert(i));
351                    let data = files.remove(&name_idx).unwrap();
352                    name_idx = core_file_name(name, replacement as u32);
353                    files.push(&name_idx, &data);
354                }
355                uwriteln!(
356                    compilation_promises,
357                    "const {local_name} = {}(new URL('./{name_idx}', import.meta.url));",
358                    self.intrinsic(Intrinsic::FetchCompile)
359                );
360            }
361        }
362
363        let js_intrinsics = render_intrinsics(RenderIntrinsicsArgs {
364            intrinsics: &mut self.all_intrinsics,
365            no_nodejs_compat: self.opts.no_nodejs_compat,
366            instantiation: self.opts.instantiation.is_some(),
367            determinism: AsyncDeterminismProfile::default(),
368        });
369
370        if let Some(instantiation) = &self.opts.instantiation {
371            uwrite!(
372                output,
373                "\
374                    export function instantiate(getCoreModule, imports, instantiateCore = {}) {{
375                        {}
376                        {}
377                        {}
378                ",
379                match instantiation {
380                    InstantiationMode::Async => "WebAssembly.instantiate",
381                    InstantiationMode::Sync =>
382                        "(module, importObject) => new WebAssembly.Instance(module, importObject)",
383                },
384                &js_intrinsics as &str,
385                &intrinsic_definitions as &str,
386                &compilation_promises as &str,
387            );
388        }
389
390        let imports_object = if self.opts.instantiation.is_some() {
391            Some("imports")
392        } else {
393            None
394        };
395        self.esm_bindgen
396            .render_imports(&mut output, imports_object, &mut self.local_names);
397
398        if self.opts.instantiation.is_some() {
399            uwrite!(&mut self.src.js, "{}", &core_exported_funcs as &str);
400            self.esm_bindgen.render_exports(
401                &mut self.src.js,
402                self.opts.instantiation.is_some(),
403                &mut self.local_names,
404                opts,
405            );
406            uwrite!(
407                output,
408                "\
409                        let gen = (function* init () {{
410                            {}\
411                            {};
412                        }})();
413                        let promise, resolve, reject;
414                        function runNext (value) {{
415                            try {{
416                                let done;
417                                do {{
418                                    ({{ value, done }} = gen.next(value));
419                                }} while (!(value instanceof Promise) && !done);
420                                if (done) {{
421                                    if (resolve) return resolve(value);
422                                    else return value;
423                                }}
424                                if (!promise) promise = new Promise((_resolve, _reject) => (resolve = _resolve, reject = _reject));
425                                value.then(nextVal => done ? resolve() : runNext(nextVal), reject);
426                            }}
427                            catch (e) {{
428                                if (reject) reject(e);
429                                else throw e;
430                            }}
431                        }}
432                        const maybeSyncReturn = runNext(null);
433                        return promise || maybeSyncReturn;
434                    }}
435                ",
436                &self.src.js_init as &str,
437                &self.src.js as &str,
438            );
439        } else {
440            let (maybe_init_export, maybe_init) =
441                if self.opts.tla_compat && opts.instantiation.is_none() {
442                    uwriteln!(self.src.js_init, "_initialized = true;");
443                    (
444                        "\
445                        let _initialized = false;
446                        export ",
447                        "",
448                    )
449                } else {
450                    (
451                        "",
452                        "
453                        await $init;
454                    ",
455                    )
456                };
457
458            uwrite!(
459                output,
460                "\
461                    {}
462                    {}
463                    {}
464                    {maybe_init_export}const $init = (() => {{
465                        let gen = (function* init () {{
466                            {}\
467                            {}\
468                            {}\
469                        }})();
470                        let promise, resolve, reject;
471                        function runNext (value) {{
472                            try {{
473                                let done;
474                                do {{
475                                    ({{ value, done }} = gen.next(value));
476                                }} while (!(value instanceof Promise) && !done);
477                                if (done) {{
478                                    if (resolve) resolve(value);
479                                    else return value;
480                                }}
481                                if (!promise) promise = new Promise((_resolve, _reject) => (resolve = _resolve, reject = _reject));
482                                value.then(runNext, reject);
483                            }}
484                            catch (e) {{
485                                if (reject) reject(e);
486                                else throw e;
487                            }}
488                        }}
489                        const maybeSyncReturn = runNext(null);
490                        return promise || maybeSyncReturn;
491                    }})();
492                    {maybe_init}\
493                ",
494                &js_intrinsics as &str,
495                &intrinsic_definitions as &str,
496                &self.src.js as &str,
497                &compilation_promises as &str,
498                &self.src.js_init as &str,
499                &core_exported_funcs as &str,
500            );
501
502            self.esm_bindgen.render_exports(
503                &mut output,
504                self.opts.instantiation.is_some(),
505                &mut self.local_names,
506                opts,
507            );
508        }
509
510        let mut bytes = output.as_bytes();
511        // strip leading newline
512        if bytes[0] == b'\n' {
513            bytes = &bytes[1..];
514        }
515        files.push(&format!("{name}.js"), bytes);
516    }
517
518    fn intrinsic(&mut self, intrinsic: Intrinsic) -> String {
519        self.all_intrinsics.insert(intrinsic);
520        intrinsic.name().to_string()
521    }
522}
523
524/// Helper structure used to generate the `instantiate` method of a component.
525///
526/// This is the main structure for parsing the output of Wasmtime.
527struct Instantiator<'a, 'b> {
528    src: Source,
529    gen: &'a mut JsBindgen<'b>,
530    modules: &'a PrimaryMap<StaticModuleIndex, core::Translation<'a>>,
531    instances: PrimaryMap<RuntimeInstanceIndex, StaticModuleIndex>,
532    types: &'a ComponentTypes,
533    resolve: &'a Resolve,
534    world: WorldId,
535    sizes: SizeAlign,
536    component: &'a Component,
537
538    /// Map of error contexts tables for a given component & error context index pair
539    /// that have been initialized
540    error_context_component_initialized: PrimaryMap<RuntimeComponentInstanceIndex, bool>,
541    error_context_component_table_initialized:
542        PrimaryMap<TypeComponentLocalErrorContextTableIndex, bool>,
543
544    /// Component-level translation information, including trampolines
545    translation: &'a ComponentTranslation,
546
547    exports_resource_types: BTreeMap<TypeId, ResourceIndex>,
548    imports_resource_types: BTreeMap<TypeId, ResourceIndex>,
549    resources_initialized: BTreeMap<ResourceIndex, bool>,
550    resource_tables_initialized: BTreeMap<TypeResourceTableIndex, bool>,
551
552    exports: BTreeMap<String, WorldKey>,
553    imports: BTreeMap<String, WorldKey>,
554    /// Instance flags which references have been emitted externally at least once.
555    used_instance_flags: RefCell<BTreeSet<RuntimeComponentInstanceIndex>>,
556    defined_resource_classes: BTreeSet<String>,
557    async_imports: HashSet<String>,
558    async_exports: HashSet<String>,
559    lowering_options:
560        PrimaryMap<LoweredIndex, (&'a CanonicalOptions, TrampolineIndex, TypeFuncIndex)>,
561}
562
563impl<'a> Instantiator<'a, '_> {
564    fn initialize(&mut self) {
565        // Populate reverse map from import and export names to world items
566        for (key, _) in &self.resolve.worlds[self.world].imports {
567            let name = &self.resolve.name_world_key(key);
568            self.imports.insert(name.to_string(), key.clone());
569        }
570        for (key, _) in &self.resolve.worlds[self.world].exports {
571            let name = &self.resolve.name_world_key(key);
572            self.exports.insert(name.to_string(), key.clone());
573        }
574
575        // Populate reverse map from TypeId to ResourceIndex
576        // Populate the resource type to resource index map
577        for (key, item) in &self.resolve.worlds[self.world].imports {
578            let name = &self.resolve.name_world_key(key);
579            let Some((_, (_, import))) = self
580                .component
581                .import_types
582                .iter()
583                .find(|(_, (impt_name, _))| impt_name == name)
584            else {
585                match item {
586                    WorldItem::Interface { .. } => {
587                        unreachable!("unexpected interface in import types during initialization")
588                    }
589                    WorldItem::Function(_) => {
590                        unreachable!("unexpected function in import types during initialization")
591                    }
592                    WorldItem::Type(ty) => {
593                        assert!(!matches!(
594                            self.resolve.types[*ty].kind,
595                            TypeDefKind::Resource
596                        ))
597                    }
598                }
599                continue;
600            };
601            match item {
602                WorldItem::Interface { id, stability: _ } => {
603                    let TypeDef::ComponentInstance(instance) = import else {
604                        unreachable!("unexpectedly non-component instance import in interface")
605                    };
606                    let import_ty = &self.types[*instance];
607                    let iface = &self.resolve.interfaces[*id];
608                    for (ty_name, ty) in &iface.types {
609                        match &import_ty.exports.get(ty_name) {
610                            Some(TypeDef::Resource(resource)) => {
611                                let ty = crate::dealias(self.resolve, *ty);
612                                let resource_idx = self.types[*resource].ty;
613                                self.imports_resource_types.insert(ty, resource_idx);
614                            }
615                            Some(TypeDef::Interface(_)) | None => {}
616                            Some(_) => unreachable!("unexpected type in interface"),
617                        }
618                    }
619                }
620                WorldItem::Function(_) => {}
621                WorldItem::Type(ty) => match import {
622                    TypeDef::Resource(resource) => {
623                        let ty = crate::dealias(self.resolve, *ty);
624                        let resource_idx = self.types[*resource].ty;
625                        self.imports_resource_types.insert(ty, resource_idx);
626                    }
627                    TypeDef::Interface(_) => {}
628                    _ => unreachable!("unexpected type in import world item"),
629                },
630            }
631        }
632        self.exports_resource_types = self.imports_resource_types.clone();
633
634        for (key, item) in &self.resolve.worlds[self.world].exports {
635            let name = &self.resolve.name_world_key(key);
636            let (_, export_idx) = self
637                .component
638                .exports
639                .raw_iter()
640                .find(|(expt_name, _)| *expt_name == name)
641                .unwrap();
642            let export = &self.component.export_items[*export_idx];
643            match item {
644                WorldItem::Interface { id, stability: _ } => {
645                    let iface = &self.resolve.interfaces[*id];
646                    let Export::Instance { exports, .. } = &export else {
647                        unreachable!("unexpectedly non export instance item")
648                    };
649                    for (ty_name, ty) in &iface.types {
650                        match self.component.export_items
651                            [*exports.get(ty_name, &NameMapNoIntern).unwrap()]
652                        {
653                            Export::Type(TypeDef::Resource(resource)) => {
654                                let ty = crate::dealias(self.resolve, *ty);
655                                let resource_idx = self.types[resource].ty;
656                                self.exports_resource_types.insert(ty, resource_idx);
657                            }
658                            Export::Type(_) => {}
659                            _ => unreachable!(
660                                "unexpected type in component export items on iface [{iface_name}]",
661                                iface_name = iface.name.as_deref().unwrap_or("<unknown>"),
662                            ),
663                        }
664                    }
665                }
666                WorldItem::Function(_) => {}
667                WorldItem::Type(_) => unreachable!("unexpected exported world item type"),
668            }
669        }
670    }
671
672    fn instantiate(&mut self) {
673        for (i, trampoline) in self.translation.trampolines.iter() {
674            let Trampoline::LowerImport {
675                index,
676                lower_ty,
677                options,
678            } = trampoline
679            else {
680                continue;
681            };
682
683            let options = self
684                .component
685                .options
686                .get(*options)
687                .expect("failed to find canon options");
688
689            let i = self.lowering_options.push((options, i, *lower_ty));
690            assert_eq!(i, *index);
691        }
692
693        if let Some(InstantiationMode::Async) = self.gen.opts.instantiation {
694            // To avoid uncaught promise rejection errors, we attach an intermediate
695            // Promise.all with a rejection handler, if there are multiple promises.
696            if self.modules.len() > 1 {
697                self.src.js_init.push_str("Promise.all([");
698                for i in 0..self.modules.len() {
699                    if i > 0 {
700                        self.src.js_init.push_str(", ");
701                    }
702                    self.src.js_init.push_str(&format!("module{i}"));
703                }
704                uwriteln!(self.src.js_init, "]).catch(() => {{}});");
705            }
706        }
707
708        // We push lower import initializers down to right before instantiate, so that the
709        // memory, realloc and postReturn functions are available to the import lowerings
710        // for optimized bindgen
711        let mut lower_import_initializers = Vec::new();
712        for init in self.component.initializers.iter() {
713            match init {
714                GlobalInitializer::InstantiateModule(_) => {
715                    for init in lower_import_initializers.drain(..) {
716                        self.instantiation_global_initializer(init);
717                    }
718                }
719                GlobalInitializer::LowerImport { .. } => {
720                    lower_import_initializers.push(init);
721                    continue;
722                }
723                _ => {}
724            }
725            self.instantiation_global_initializer(init);
726        }
727
728        for init in lower_import_initializers.drain(..) {
729            self.instantiation_global_initializer(init);
730        }
731
732        // Some trampolines that correspond to host-provided imports need to be defined before the
733        // instantiation bits since they are referred to.
734        for (i, trampoline) in self
735            .translation
736            .trampolines
737            .iter()
738            .filter(|(_, t)| Instantiator::is_early_trampoline(t))
739        {
740            self.trampoline(i, trampoline);
741        }
742
743        if self.gen.opts.instantiation.is_some() {
744            let js_init = mem::take(&mut self.src.js_init);
745            self.src.js.push_str(&js_init);
746        }
747
748        self.exports(&self.component.exports);
749
750        // Trampolines here so we have static module indices, and resource maps populated
751        // (both imports and exports may still be populting resource map)
752        for (i, trampoline) in self
753            .translation
754            .trampolines
755            .iter()
756            .filter(|(_, t)| !Instantiator::is_early_trampoline(t))
757        {
758            self.trampoline(i, trampoline);
759        }
760    }
761
762    fn ensure_local_resource_class(&mut self, local_name: String) {
763        if !self.defined_resource_classes.contains(&local_name) {
764            uwriteln!(
765                self.src.js,
766                "\nclass {local_name} {{
767                constructor () {{
768                    throw new Error('\"{local_name}\" resource does not define a constructor');
769                }}
770            }}"
771            );
772            self.defined_resource_classes.insert(local_name.to_string());
773        }
774    }
775
776    fn resource_definitions(&mut self, definitions: &mut source::Source) {
777        // It is theoretically possible for locally defined resources used in no functions
778        // to still be exported
779        for resource in 0..self.component.num_resources {
780            let resource = ResourceIndex::from_u32(resource);
781            let is_imported = self.component.defined_resource_index(resource).is_none();
782            if is_imported {
783                continue;
784            }
785            if let Some(local_name) = self.gen.local_names.try_get(resource) {
786                self.ensure_local_resource_class(local_name.to_string());
787            }
788        }
789
790        // Write out the defined resource table indices for the runtime
791        if self.gen.all_intrinsics.contains(&Intrinsic::Resource(
792            ResourceIntrinsic::ResourceTransferBorrow,
793        )) || self.gen.all_intrinsics.contains(&Intrinsic::Resource(
794            ResourceIntrinsic::ResourceTransferBorrowValidLifting,
795        )) {
796            let defined_resource_tables = Intrinsic::DefinedResourceTables.name();
797            uwrite!(definitions, "const {defined_resource_tables} = [");
798            // Table per-resource
799            for tidx in 0..self.component.num_resources {
800                let tid = TypeResourceTableIndex::from_u32(tidx);
801                let rid = self.types[tid].ty;
802                if let Some(defined_index) = self.component.defined_resource_index(rid) {
803                    if self.types[tid].instance
804                        == self.component.defined_resource_instances[defined_index]
805                    {
806                        uwrite!(definitions, "true,");
807                    }
808                } else {
809                    uwrite!(definitions, ",");
810                };
811            }
812            uwrite!(definitions, "];\n");
813        }
814    }
815
816    /// Ensure a component-local `error-context` table has been created
817    ///
818    /// # Arguments
819    ///
820    /// * `component_idx` - component index
821    /// * `err_ctx_tbl_idx` - The component-local error-context table index
822    ///
823    fn ensure_error_context_local_table(
824        &mut self,
825        component_idx: RuntimeComponentInstanceIndex,
826        err_ctx_tbl_idx: TypeComponentLocalErrorContextTableIndex,
827    ) {
828        if self.error_context_component_initialized[component_idx]
829            && self.error_context_component_table_initialized[err_ctx_tbl_idx]
830        {
831            return;
832        }
833        let err_ctx_local_tables = self
834            .gen
835            .intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::ComponentLocalTable));
836        let c = component_idx.as_u32();
837        if !self.error_context_component_initialized[component_idx] {
838            uwriteln!(self.src.js, "{err_ctx_local_tables}.set({c}, new Map());");
839            self.error_context_component_initialized[component_idx] = true;
840        }
841        if !self.error_context_component_table_initialized[err_ctx_tbl_idx] {
842            let t = err_ctx_tbl_idx.as_u32();
843            uwriteln!(
844                self.src.js,
845                "{err_ctx_local_tables}.get({c}).set({t}, new Map());"
846            );
847            self.error_context_component_table_initialized[err_ctx_tbl_idx] = true;
848        }
849    }
850
851    /// Ensure that a resource table has been initialized
852    ///
853    /// For the relevant resource table, this function will generate initialization
854    /// blocks, exactly once.
855    ///
856    /// This is not done for *all* resources, but instead for those that are explicitly used.
857    fn ensure_resource_table(&mut self, id: TypeResourceTableIndex) {
858        if self.resource_tables_initialized.contains_key(&id) {
859            return;
860        }
861
862        let resource = self.types[id].ty;
863
864        let (is_imported, maybe_dtor) =
865            if let Some(resource_idx) = self.component.defined_resource_index(resource) {
866                let resource_def = self
867                    .component
868                    .initializers
869                    .iter()
870                    .find_map(|i| match i {
871                        GlobalInitializer::Resource(r) if r.index == resource_idx => Some(r),
872                        _ => None,
873                    })
874                    .unwrap();
875
876                if let Some(dtor) = &resource_def.dtor {
877                    (false, format!("\n{}(rep);", self.core_def(dtor)))
878                } else {
879                    (false, "".into())
880                }
881            } else {
882                (true, "".into())
883            };
884
885        let handle_tables = self.gen.intrinsic(Intrinsic::HandleTables);
886        let rsc_table_flag = self
887            .gen
888            .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableFlag));
889        let rsc_table_remove = self
890            .gen
891            .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableRemove));
892
893        let rtid = id.as_u32();
894        if is_imported {
895            uwriteln!(
896                self.src.js,
897                "const handleTable{rtid} = [{rsc_table_flag}, 0];",
898            );
899            if !self.resources_initialized.contains_key(&resource) {
900                let ridx = resource.as_u32();
901                uwriteln!(
902                    self.src.js,
903                    "const captureTable{ridx} = new Map();
904                    let captureCnt{ridx} = 0;"
905                );
906                self.resources_initialized.insert(resource, true);
907            }
908        } else {
909            let finalization_registry_create =
910                self.gen.intrinsic(Intrinsic::FinalizationRegistryCreate);
911            uwriteln!(
912                self.src.js,
913                "const handleTable{rtid} = [{rsc_table_flag}, 0];
914                const finalizationRegistry{rtid} = {finalization_registry_create}((handle) => {{
915                    const {{ rep }} = {rsc_table_remove}(handleTable{rtid}, handle);{maybe_dtor}
916                }});
917                ",
918            );
919        }
920        uwriteln!(self.src.js, "{handle_tables}[{rtid}] = handleTable{rtid};");
921        self.resource_tables_initialized.insert(id, true);
922    }
923
924    fn instance_flags(&mut self) {
925        // SAFETY: short-lived borrow, and the refcell isn't mutably borrowed in the loop's body.
926        let mut instance_flag_defs = String::new();
927        for used in self.used_instance_flags.borrow().iter() {
928            let i = used.as_u32();
929            uwriteln!(
930                &mut instance_flag_defs,
931                "const instanceFlags{i} = new WebAssembly.Global({{ value: \"i32\", mutable: true }}, {});",
932                wasmtime_environ::component::FLAG_MAY_LEAVE
933                | wasmtime_environ::component::FLAG_MAY_ENTER);
934        }
935        self.src.js_init.prepend_str(&instance_flag_defs);
936    }
937
938    // Trampolines defined in trampoline() below that use:
939    //   const trampoline{} = ...
940    // require early initialization since their bindings aren't auto-hoisted
941    // like JS functions are in the JS runtime.
942    fn is_early_trampoline(trampoline: &Trampoline) -> bool {
943        matches!(
944            trampoline,
945            Trampoline::BackpressureSet { .. }
946                | Trampoline::ContextGet(_)
947                | Trampoline::ContextSet(_)
948                | Trampoline::ErrorContextDebugMessage { .. }
949                | Trampoline::ErrorContextDrop { .. }
950                | Trampoline::ErrorContextNew { .. }
951                | Trampoline::ResourceDrop(_)
952                | Trampoline::ResourceNew(_)
953                | Trampoline::ResourceRep(_)
954                | Trampoline::ResourceTransferBorrow
955                | Trampoline::ResourceTransferOwn
956                | Trampoline::SubtaskDrop { .. }
957                | Trampoline::StreamNew { .. }
958                | Trampoline::TaskCancel { .. }
959                | Trampoline::TaskReturn { .. }
960                | Trampoline::WaitableSetDrop { .. }
961                | Trampoline::WaitableSetNew { .. }
962                | Trampoline::WaitableJoin { .. },
963        )
964    }
965
966    fn trampoline(&mut self, i: TrampolineIndex, trampoline: &'a Trampoline) {
967        let i = i.as_u32();
968        match trampoline {
969            Trampoline::TaskCancel { instance } => {
970                let task_cancel_fn = self
971                    .gen
972                    .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::TaskCancel));
973                uwriteln!(
974                    self.src.js,
975                    "const trampoline{i} = {task_cancel_fn}.bind(null, {instance_idx});\n",
976                    instance_idx = instance.as_u32(),
977                );
978            }
979
980            Trampoline::SubtaskCancel { instance, async_ } => {
981                let task_cancel_fn = self
982                    .gen
983                    .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::SubtaskCancel));
984                uwriteln!(
985                    self.src.js,
986                    "const trampoline{i} = {task_cancel_fn}.bind(null, {instance_idx}, {async_});\n",
987                    instance_idx = instance.as_u32(),
988                );
989            }
990
991            Trampoline::SubtaskDrop { instance } => {
992                let component_idx = instance.as_u32();
993                let subtask_drop_fn = self
994                    .gen
995                    .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::SubtaskDrop));
996                uwriteln!(
997                    self.src.js,
998                    "const trampoline{i} = {subtask_drop_fn}.bind(
999                         null,
1000                         {component_idx},
1001                     );"
1002                );
1003            }
1004
1005            Trampoline::BackpressureSet { instance } => {
1006                let backpressure_set_fn = self
1007                    .gen
1008                    .intrinsic(Intrinsic::Component(ComponentIntrinsic::BackpressureSet));
1009                uwriteln!(
1010                    self.src.js,
1011                    "const trampoline{i} = {backpressure_set_fn}.bind(null, {});\n",
1012                    instance.as_u32(),
1013                );
1014            }
1015
1016            Trampoline::WaitableSetNew { instance } => {
1017                let waitable_set_new_fn = self
1018                    .gen
1019                    .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableSetNew));
1020                uwriteln!(
1021                    self.src.js,
1022                    "const trampoline{i} = {waitable_set_new_fn}.bind(null, {});\n",
1023                    instance.as_u32(),
1024                );
1025            }
1026
1027            Trampoline::WaitableSetWait { options } => {
1028                let CanonicalOptions {
1029                    instance,
1030                    async_,
1031                    data_model:
1032                        CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, .. }),
1033                    ..
1034                } = self
1035                    .component
1036                    .options
1037                    .get(*options)
1038                    .expect("failed to find options")
1039                else {
1040                    panic!("unexpected/missing memory data model during waitable-set.wait");
1041                };
1042
1043                let waitable_set_wait_fn = self
1044                    .gen
1045                    .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableSetWait));
1046                uwriteln!(
1047                    self.src.js,
1048                    "const trampoline{i} = {waitable_set_wait_fn}.bind(null, {instance_idx}, {async_}, memory{memory_idx});\n",
1049                    instance_idx = instance.as_u32(),
1050                    memory_idx = memory.expect("missing memory idx for waitable-set.wait").as_u32(),
1051                );
1052            }
1053
1054            Trampoline::WaitableSetPoll { options } => {
1055                let CanonicalOptions {
1056                    instance,
1057                    async_,
1058                    data_model:
1059                        CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, .. }),
1060                    ..
1061                } = self
1062                    .component
1063                    .options
1064                    .get(*options)
1065                    .expect("failed to find options")
1066                else {
1067                    panic!("unexpected memory data model during waitable-set.poll");
1068                };
1069
1070                let waitable_set_poll_fn = self
1071                    .gen
1072                    .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableSetPoll));
1073                uwriteln!(
1074                    self.src.js,
1075                    "const trampoline{i} = {waitable_set_poll_fn}.bind(null, {instance_idx}, {async_}, memory{memory_idx});\n",
1076                    instance_idx = instance.as_u32(),
1077                    memory_idx = memory.expect("missing memory idx for waitable-set.poll").as_u32(),
1078                );
1079            }
1080
1081            Trampoline::WaitableSetDrop { instance } => {
1082                let waitable_set_drop_fn = self
1083                    .gen
1084                    .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableSetDrop));
1085                uwriteln!(
1086                    self.src.js,
1087                    "const trampoline{i} = {waitable_set_drop_fn}.bind(null, {instance_idx});\n",
1088                    instance_idx = instance.as_u32(),
1089                );
1090            }
1091
1092            Trampoline::WaitableJoin { instance } => {
1093                let waitable_join_fn = self
1094                    .gen
1095                    .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableJoin));
1096                uwriteln!(
1097                    self.src.js,
1098                    "const trampoline{i} = {waitable_join_fn}.bind(null, {instance_idx});\n",
1099                    instance_idx = instance.as_u32(),
1100                );
1101            }
1102
1103            Trampoline::Yield { async_ } => {
1104                let yield_fn = self
1105                    .gen
1106                    .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::Yield));
1107                uwriteln!(
1108                    self.src.js,
1109                    "const trampoline{i} = {yield_fn}.bind(null, {async_});\n",
1110                );
1111            }
1112
1113            // TODO: build a lookup of types that could be used in streams for a given component?
1114            // Need to have a way to look up/serialize the type indices per component into
1115            // a lookup of lifting functions? Or just use the cabiLower?
1116            Trampoline::StreamNew { ty } => {
1117                let stream_new_fn = self
1118                    .gen
1119                    .intrinsic(Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamNew));
1120                uwriteln!(
1121                    self.src.js,
1122                    "const trampoline{i} = {stream_new_fn}.bind(null, {});\n",
1123                    ty.as_u32(),
1124                );
1125            }
1126
1127            Trampoline::StreamRead { ty, options } => {
1128                let options = self
1129                    .component
1130                    .options
1131                    .get(*options)
1132                    .expect("failed to find options");
1133
1134                is_valid_canonopt(options).expect("invalid canonopts");
1135
1136                let stream_idx = ty.as_u32();
1137                let CanonicalOptions {
1138                    instance,
1139                    string_encoding,
1140                    async_,
1141                    data_model:
1142                        CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1143                    ..
1144                } = options
1145                else {
1146                    unreachable!("missing/invalid data model for options during stream.read")
1147                };
1148                let component_instance_id = instance.as_u32();
1149                let memory_idx = memory.expect("missing memory idx for stream.read").as_u32();
1150                let realloc_idx = realloc
1151                    .expect("missing realloc idx for stream.read")
1152                    .as_u32();
1153                let string_encoding = string_encoding_js_literal(string_encoding);
1154
1155                let stream_read_fn = self
1156                    .gen
1157                    .intrinsic(Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamRead));
1158                uwriteln!(
1159                    self.src.js,
1160                    r#"const trampoline{i} = {stream_read_fn}.bind(
1161                         null,
1162                         {component_instance_id},
1163                         {memory_idx},
1164                         {realloc_idx},
1165                         {string_encoding},
1166                         {async_},
1167                         {stream_idx},
1168                     );
1169                    "#,
1170                );
1171            }
1172
1173            Trampoline::StreamWrite { ty, options } => {
1174                let options = self
1175                    .component
1176                    .options
1177                    .get(*options)
1178                    .expect("failed to find options");
1179
1180                is_valid_canonopt(options).expect("invalid canonopts");
1181
1182                let stream_idx = ty.as_u32();
1183                let CanonicalOptions {
1184                    instance,
1185                    string_encoding,
1186                    async_,
1187                    data_model:
1188                        CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1189                    ..
1190                } = options
1191                else {
1192                    unreachable!("unexpected memory data model during stream.write");
1193                };
1194                let component_instance_id = instance.as_u32();
1195                let memory_idx = memory
1196                    .expect("missing memory idx for stream.write")
1197                    .as_u32();
1198                let realloc_idx = realloc
1199                    .expect("missing realloc idx for stream.write")
1200                    .as_u32();
1201                let string_encoding = string_encoding_js_literal(string_encoding);
1202
1203                let stream_write_fn = self
1204                    .gen
1205                    .intrinsic(Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamWrite));
1206                uwriteln!(
1207                    self.src.js,
1208                    r#"const trampoline{i} = {stream_write_fn}.bind(
1209                         null,
1210                         {component_instance_id},
1211                         {memory_idx},
1212                         {realloc_idx},
1213                         {string_encoding},
1214                         {async_},
1215                         {stream_idx},
1216                     );
1217                    "#,
1218                );
1219            }
1220
1221            Trampoline::StreamCancelRead { ty, async_ } => {
1222                let stream_cancel_read_fn = self.gen.intrinsic(Intrinsic::AsyncStream(
1223                    AsyncStreamIntrinsic::StreamCancelRead,
1224                ));
1225                uwriteln!(
1226                    self.src.js,
1227                    "const trampoline{i} = {stream_cancel_read_fn}.bind(null, {stream_idx}, {async_});\n",
1228                    stream_idx = ty.as_u32(),
1229                );
1230            }
1231
1232            Trampoline::StreamCancelWrite { ty, async_ } => {
1233                let stream_cancel_write_fn = self.gen.intrinsic(Intrinsic::AsyncStream(
1234                    AsyncStreamIntrinsic::StreamCancelWrite,
1235                ));
1236                uwriteln!(
1237                    self.src.js,
1238                    "const trampoline{i} = {stream_cancel_write_fn}.bind(null, {stream_idx}, {async_});\n",
1239                    stream_idx = ty.as_u32(),
1240                );
1241            }
1242
1243            Trampoline::StreamDropReadable { ty } => {
1244                let stream_drop_readable_fn = self.gen.intrinsic(Intrinsic::AsyncStream(
1245                    AsyncStreamIntrinsic::StreamDropReadable,
1246                ));
1247                uwriteln!(
1248                    self.src.js,
1249                    "const trampoline{i} = {stream_drop_readable_fn}.bind(null, {stream_idx});\n",
1250                    stream_idx = ty.as_u32(),
1251                );
1252            }
1253
1254            Trampoline::StreamDropWritable { ty } => {
1255                let stream_drop_writable_fn = self.gen.intrinsic(Intrinsic::AsyncStream(
1256                    AsyncStreamIntrinsic::StreamDropWritable,
1257                ));
1258                uwriteln!(
1259                    self.src.js,
1260                    "const trampoline{i} = {stream_drop_writable_fn}.bind(null, {stream_idx});\n",
1261                    stream_idx = ty.as_u32(),
1262                );
1263            }
1264
1265            Trampoline::StreamTransfer => todo!("Trampoline::StreamTransfer"),
1266
1267            Trampoline::FutureNew { ty } => {
1268                let future_new_fn = self
1269                    .gen
1270                    .intrinsic(Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureNew));
1271                uwriteln!(
1272                    self.src.js,
1273                    "const trampoline{i} = {future_new_fn}.bind(null, {});\n",
1274                    ty.as_u32(),
1275                );
1276            }
1277
1278            Trampoline::FutureRead { ty, options } => {
1279                let options = self
1280                    .component
1281                    .options
1282                    .get(*options)
1283                    .expect("failed to find options");
1284
1285                let future_idx = ty.as_u32();
1286
1287                let CanonicalOptions {
1288                    instance,
1289                    string_encoding,
1290                    callback,
1291                    post_return,
1292                    async_,
1293                    data_model:
1294                        CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1295                    ..
1296                } = options
1297                else {
1298                    unreachable!("unexpected memory data model during future.read");
1299                };
1300                let component_instance_id = instance.as_u32();
1301                let memory_idx = memory.expect("missing memory idx for future.read").as_u32();
1302                let realloc_idx = realloc
1303                    .expect("missing realloc idx for future.read")
1304                    .as_u32();
1305                let string_encoding = string_encoding_js_literal(string_encoding);
1306
1307                assert!(
1308                    callback.is_none(),
1309                    "callback should not be present for future read"
1310                );
1311                assert!(
1312                    post_return.is_none(),
1313                    "post_return should not be present for future read"
1314                );
1315
1316                let future_read_fn = self
1317                    .gen
1318                    .intrinsic(Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureRead));
1319                uwriteln!(
1320                    self.src.js,
1321                    r#"const trampoline{i} = {future_read_fn}.bind(
1322                         null,
1323                         {component_instance_id},
1324                         {memory_idx},
1325                         {realloc_idx},
1326                         {string_encoding},
1327                         {async_},
1328                         {future_idx},
1329                     );
1330                    "#,
1331                );
1332            }
1333
1334            Trampoline::FutureWrite { ty, options } => {
1335                let options = self
1336                    .component
1337                    .options
1338                    .get(*options)
1339                    .expect("failed to find options");
1340
1341                is_valid_canonopt(options).expect("invalid canonopts");
1342
1343                let future_idx = ty.as_u32();
1344                let CanonicalOptions {
1345                    instance,
1346                    string_encoding,
1347                    async_,
1348                    data_model:
1349                        CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1350                    ..
1351                } = options
1352                else {
1353                    unreachable!("unexpected memory data model during future.write");
1354                };
1355                let component_instance_id = instance.as_u32();
1356                let memory_idx = memory
1357                    .expect("missing memory idx for future.write")
1358                    .as_u32();
1359                let realloc_idx = realloc
1360                    .expect("missing realloc idx for future.write")
1361                    .as_u32();
1362                let string_encoding = string_encoding_js_literal(string_encoding);
1363
1364                let future_write_fn = self
1365                    .gen
1366                    .intrinsic(Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureWrite));
1367                uwriteln!(
1368                    self.src.js,
1369                    r#"const trampoline{i} = {future_write_fn}.bind(
1370                         null,
1371                         {component_instance_id},
1372                         {memory_idx},
1373                         {realloc_idx},
1374                         {string_encoding},
1375                         {async_},
1376                         {future_idx},
1377                     );
1378                    "#,
1379                );
1380            }
1381
1382            Trampoline::FutureCancelRead { ty, async_ } => {
1383                let future_cancel_read_fn = self.gen.intrinsic(Intrinsic::AsyncFuture(
1384                    AsyncFutureIntrinsic::FutureCancelRead,
1385                ));
1386                uwriteln!(
1387                    self.src.js,
1388                    "const trampoline{i} = {future_cancel_read_fn}.bind(null, {future_idx}, {async_});\n",
1389                    future_idx = ty.as_u32(),
1390                );
1391            }
1392
1393            Trampoline::FutureCancelWrite { ty, async_ } => {
1394                let future_cancel_write_fn = self.gen.intrinsic(Intrinsic::AsyncFuture(
1395                    AsyncFutureIntrinsic::FutureCancelWrite,
1396                ));
1397                uwriteln!(
1398                    self.src.js,
1399                    "const trampoline{i} = {future_cancel_write_fn}.bind(null, {future_idx}, {async_});\n",
1400                    future_idx = ty.as_u32(),
1401                );
1402            }
1403
1404            Trampoline::FutureDropReadable { ty } => {
1405                let future_drop_readable_fn = self.gen.intrinsic(Intrinsic::AsyncFuture(
1406                    AsyncFutureIntrinsic::FutureDropReadable,
1407                ));
1408                uwriteln!(
1409                    self.src.js,
1410                    "const trampoline{i} = {future_drop_readable_fn}.bind(null, {future_idx});\n",
1411                    future_idx = ty.as_u32(),
1412                );
1413            }
1414
1415            Trampoline::FutureDropWritable { ty } => {
1416                let future_drop_writable_fn = self.gen.intrinsic(Intrinsic::AsyncFuture(
1417                    AsyncFutureIntrinsic::FutureDropWritable,
1418                ));
1419                uwriteln!(
1420                    self.src.js,
1421                    "const trampoline{i} = {future_drop_writable_fn}.bind(null, {future_idx});\n",
1422                    future_idx = ty.as_u32(),
1423                );
1424            }
1425
1426            Trampoline::FutureTransfer => todo!("Trampoline::FutureTransfer"),
1427
1428            Trampoline::ErrorContextNew { ty, options } => {
1429                let CanonicalOptions {
1430                    instance,
1431                    string_encoding,
1432                    data_model:
1433                        CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, .. }),
1434                    ..
1435                } = self
1436                    .component
1437                    .options
1438                    .get(*options)
1439                    .expect("failed to find options")
1440                else {
1441                    panic!("unexpected memory data model during error-context.new");
1442                };
1443
1444                self.ensure_error_context_local_table(*instance, *ty);
1445
1446                let local_err_tbl_idx = ty.as_u32();
1447                let component_idx = instance.as_u32();
1448
1449                let memory_idx = memory
1450                    .expect("missing realloc fn idx for error-context.debug-message")
1451                    .as_u32();
1452
1453                // Generate a string decoding function to match this trampoline that does appropriate encoding
1454                let decoder = match string_encoding {
1455                    wasmtime_environ::component::StringEncoding::Utf8 => self
1456                        .gen
1457                        .intrinsic(Intrinsic::String(StringIntrinsic::Utf8Decoder)),
1458                    wasmtime_environ::component::StringEncoding::Utf16 => self
1459                        .gen
1460                        .intrinsic(Intrinsic::String(StringIntrinsic::Utf16Decoder)),
1461                    enc => panic!(
1462                        "unsupported string encoding [{enc:?}] for error-context.debug-message"
1463                    ),
1464                };
1465                uwriteln!(
1466                    self.src.js,
1467                    "function trampoline{i}InputStr(ptr, len) {{
1468                         return {decoder}.decode(new DataView(memory{memory_idx}.buffer, ptr, len));
1469                    }}"
1470                );
1471
1472                let err_ctx_new_fn = self.gen.intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::New));
1473                // Store the options associated with this new error context for later use in the global array
1474                uwriteln!(
1475                    self.src.js,
1476                    "const trampoline{i} = {err_ctx_new_fn}.bind(
1477                         null,
1478                         {component_idx},
1479                         {local_err_tbl_idx},
1480                         trampoline{i}InputStr,
1481                     );
1482                    "
1483                );
1484            }
1485
1486            Trampoline::ErrorContextDebugMessage { ty, options } => {
1487                let CanonicalOptions {
1488                    instance,
1489                    async_,
1490                    callback,
1491                    post_return,
1492                    string_encoding,
1493                    data_model:
1494                        CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1495                    ..
1496                } = self
1497                    .component
1498                    .options
1499                    .get(*options)
1500                    .expect("failed to find options")
1501                else {
1502                    panic!("unexpected memory data model during error-context.debug-message");
1503                };
1504
1505                let debug_message_fn = self
1506                    .gen
1507                    .intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::DebugMessage));
1508
1509                let realloc_fn_idx = realloc
1510                    .expect("missing realloc fn idx for error-context.debug-message")
1511                    .as_u32();
1512                let memory_idx = memory
1513                    .expect("missing realloc fn idx for error-context.debug-message")
1514                    .as_u32();
1515
1516                // Generate a string encoding function to match this trampoline that does appropriate encoding
1517                match string_encoding {
1518                    wasmtime_environ::component::StringEncoding::Utf8 => {
1519                        let encode_fn = self
1520                            .gen
1521                            .intrinsic(Intrinsic::String(StringIntrinsic::Utf8Encode));
1522                        let encode_len_var =
1523                            Intrinsic::String(StringIntrinsic::Utf8EncodedLen).name();
1524                        uwriteln!(
1525                            self.src.js,
1526                            "function trampoline{i}OutputStr(s, outputPtr) {{
1527                                 const memory = memory{memory_idx};
1528                                 const reallocFn = realloc{realloc_fn_idx};
1529                                 let ptr = {encode_fn}(s, reallocFn, memory);
1530                                 let len = {encode_len_var};
1531                                 new DataView(memory.buffer).setUint32(outputPtr, ptr, true)
1532                                 new DataView(memory.buffer).setUint32(outputPtr + 4, len, true)
1533                             }}"
1534                        );
1535                    }
1536                    wasmtime_environ::component::StringEncoding::Utf16 => {
1537                        let encode_fn = self
1538                            .gen
1539                            .intrinsic(Intrinsic::String(StringIntrinsic::Utf16Encode));
1540                        uwriteln!(
1541                            self.src.js,
1542                            "function trampoline{i}OutputStr(s, outputPtr) {{
1543                                 const memory = memory{memory_idx};
1544                                 const reallocFn = realloc{realloc_fn_idx};
1545                                 let ptr = {encode_fn}(s, reallocFn, memory);
1546                                 let len = s.length;
1547                                 new DataView(memory.buffer).setUint32(outputPtr, ptr, true)
1548                                 new DataView(memory.buffer).setUint32(outputPtr + 4, len, true)
1549                             }}"
1550                        );
1551                    }
1552                    enc => panic!(
1553                        "unsupported string encoding [{enc:?}] for error-context.debug-message"
1554                    ),
1555                };
1556
1557                let options_obj = format!(
1558                    "{{callback:{callback}, postReturn: {post_return}, async: {async_}}}",
1559                    callback = callback
1560                        .map(|v| v.as_u32().to_string())
1561                        .unwrap_or_else(|| "null".into()),
1562                    post_return = post_return
1563                        .map(|v| v.as_u32().to_string())
1564                        .unwrap_or_else(|| "null".into()),
1565                );
1566
1567                let component_idx = instance.as_u32();
1568                let local_err_tbl_idx = ty.as_u32();
1569                uwriteln!(
1570                    self.src.js,
1571                    "const trampoline{i} = {debug_message_fn}.bind(
1572                         null,
1573                         {component_idx},
1574                         {local_err_tbl_idx},
1575                         {options_obj},
1576                         trampoline{i}OutputStr,
1577                      );"
1578                );
1579            }
1580
1581            Trampoline::ErrorContextDrop { ty } => {
1582                let drop_fn = self.gen.intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::Drop));
1583                let local_err_tbl_idx = ty.as_u32();
1584                let component_idx = self.types[*ty].instance.as_u32();
1585                uwriteln!(
1586                            self.src.js,
1587                            "const trampoline{i} = {drop_fn}.bind(null, {component_idx}, {local_err_tbl_idx});"
1588                        );
1589            }
1590
1591            Trampoline::ErrorContextTransfer => {
1592                let transfer_fn = self
1593                    .gen
1594                    .intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::Transfer));
1595                uwriteln!(self.src.js, "const trampoline{i} = {transfer_fn};");
1596            }
1597
1598            Trampoline::SyncStartCall { callback } => {
1599                let _ = callback;
1600                todo!("sync-start-call not implemented");
1601            }
1602
1603            Trampoline::AsyncStartCall {
1604                callback,
1605                post_return,
1606            } => {
1607                let _ = (callback, post_return);
1608                todo!("async-start-call not implemented");
1609            }
1610
1611            Trampoline::LowerImport {
1612                index,
1613                lower_ty,
1614                options,
1615            } => {
1616                let _ = (index, lower_ty, options);
1617                // TODO: implement (can't build without, empty does work)
1618            }
1619
1620            Trampoline::AlwaysTrap => {
1621                uwrite!(
1622                    self.src.js,
1623                    "function trampoline{i}(rep) {{
1624                        throw new TypeError('AlwaysTrap');
1625                    }}
1626                "
1627                );
1628            }
1629
1630            Trampoline::Transcoder {
1631                op,
1632                from,
1633                from64,
1634                to,
1635                to64,
1636            } => {
1637                if *from64 || *to64 {
1638                    unimplemented!("memory 64 transcoder");
1639                }
1640                let from = from.as_u32();
1641                let to = to.as_u32();
1642                match op {
1643                    Transcode::Copy(FixedEncoding::Utf8) => {
1644                        uwriteln!(
1645                                            self.src.js,
1646                                            "function trampoline{i} (from_ptr, len, to_ptr) {{
1647                                new Uint8Array(memory{to}.buffer, to_ptr, len).set(new Uint8Array(memory{from}.buffer, from_ptr, len));
1648                            }}
1649                            "
1650                                        );
1651                    }
1652                    Transcode::Copy(FixedEncoding::Utf16) => unimplemented!("utf16 copier"),
1653                    Transcode::Copy(FixedEncoding::Latin1) => unimplemented!("latin1 copier"),
1654                    Transcode::Latin1ToUtf16 => unimplemented!("latin to utf16 transcoder"),
1655                    Transcode::Latin1ToUtf8 => unimplemented!("latin to utf8 transcoder"),
1656                    Transcode::Utf16ToCompactProbablyUtf16 => {
1657                        unimplemented!("utf16 to compact wtf16 transcoder")
1658                    }
1659                    Transcode::Utf16ToCompactUtf16 => {
1660                        unimplemented!("utf16 to compact utf16 transcoder")
1661                    }
1662                    Transcode::Utf16ToLatin1 => unimplemented!("utf16 to latin1 transcoder"),
1663                    Transcode::Utf16ToUtf8 => {
1664                        uwriteln!(
1665                                            self.src.js,
1666                                            "function trampoline{i} (src, src_len, dst, dst_len) {{
1667                                const encoder = new TextEncoder();
1668                                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));
1669                                return [read, written];
1670                            }}
1671                            "
1672                                        );
1673                    }
1674                    Transcode::Utf8ToCompactUtf16 => {
1675                        unimplemented!("utf8 to compact utf16 transcoder")
1676                    }
1677                    Transcode::Utf8ToLatin1 => unimplemented!("utf8 to latin1 transcoder"),
1678                    Transcode::Utf8ToUtf16 => {
1679                        uwriteln!(
1680                                            self.src.js,
1681                                            "function trampoline{i} (from_ptr, len, to_ptr) {{
1682                                const decoder = new TextDecoder();
1683                                const content = decoder.decode(new Uint8Array(memory{from}.buffer, from_ptr, len));
1684                                const strlen = content.length
1685                                const view = new Uint16Array(memory{to}.buffer, to_ptr, strlen * 2)
1686                                for (var i = 0; i < strlen; i++) {{
1687                                    view[i] = content.charCodeAt(i);
1688                                }}
1689                                return strlen;
1690                            }}
1691                            "
1692                                        );
1693                    }
1694                };
1695            }
1696
1697            Trampoline::ResourceNew(resource) => {
1698                self.ensure_resource_table(*resource);
1699                let rid = resource.as_u32();
1700                let rsc_table_create_own = self.gen.intrinsic(Intrinsic::Resource(
1701                    ResourceIntrinsic::ResourceTableCreateOwn,
1702                ));
1703                uwriteln!(
1704                    self.src.js,
1705                    "const trampoline{i} = {rsc_table_create_own}.bind(null, handleTable{rid});"
1706                );
1707            }
1708
1709            Trampoline::ResourceRep(resource) => {
1710                self.ensure_resource_table(*resource);
1711                let rid = resource.as_u32();
1712                let rsc_flag = self
1713                    .gen
1714                    .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableFlag));
1715                uwriteln!(
1716                    self.src.js,
1717                    "function trampoline{i} (handle) {{
1718                        return handleTable{rid}[(handle << 1) + 1] & ~{rsc_flag};
1719                    }}"
1720                );
1721            }
1722
1723            Trampoline::ResourceDrop(resource) => {
1724                self.ensure_resource_table(*resource);
1725                let tid = resource.as_u32();
1726                let resource_ty = &self.types[*resource];
1727                let rid = resource_ty.ty.as_u32();
1728
1729                // Build the code fragment that encapsulates calling the destructor
1730                let dtor = if let Some(resource_idx) =
1731                    self.component.defined_resource_index(resource_ty.ty)
1732                {
1733                    let resource_def = self
1734                        .component
1735                        .initializers
1736                        .iter()
1737                        .find_map(|i| match i {
1738                            GlobalInitializer::Resource(r) if r.index == resource_idx => Some(r),
1739                            _ => None,
1740                        })
1741                        .unwrap();
1742
1743                    // If a destructor index is defined for the resource, call it
1744                    if let Some(dtor) = &resource_def.dtor {
1745                        format!(
1746                            "
1747                            {}(handleEntry.rep);",
1748                            self.core_def(dtor)
1749                        )
1750                    } else {
1751                        "".into()
1752                    }
1753                } else {
1754                    // Imported resource is one without a defined resource index.
1755                    // If it is a captured instance (class instance was created externally so had to
1756                    // be assigned a rep), and there is a Symbol.dispose handler, call it explicitly
1757                    // for imported resources when the resource is dropped.
1758                    // Otherwise if it is an instance without a captured class definition, then
1759                    // call the low-level bindgen destructor.
1760                    let symbol_dispose = self.gen.intrinsic(Intrinsic::SymbolDispose);
1761                    let symbol_cabi_dispose = self.gen.intrinsic(Intrinsic::SymbolCabiDispose);
1762
1763                    // previous imports walk should define all imported resources which are accessible
1764                    if let Some(imported_resource_local_name) =
1765                        self.gen.local_names.try_get(resource_ty.ty)
1766                    {
1767                        format!(
1768                                            "
1769                            const rsc = captureTable{rid}.get(handleEntry.rep);
1770                            if (rsc) {{
1771                                if (rsc[{symbol_dispose}]) rsc[{symbol_dispose}]();
1772                                captureTable{rid}.delete(handleEntry.rep);
1773                            }} else if ({imported_resource_local_name}[{symbol_cabi_dispose}]) {{
1774                                {imported_resource_local_name}[{symbol_cabi_dispose}](handleEntry.rep);
1775                            }}"
1776                                        )
1777                    } else {
1778                        // If not, then capture / disposal paths are never called
1779                        format!(
1780                            "throw new TypeError('unreachable trampoline for resource [{:?}]')",
1781                            resource_ty.ty
1782                        )
1783                    }
1784                };
1785
1786                let rsc_table_remove = self
1787                    .gen
1788                    .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableRemove));
1789                uwrite!(
1790                    self.src.js,
1791                    "function trampoline{i}(handle) {{
1792                        const handleEntry = {rsc_table_remove}(handleTable{tid}, handle);
1793                        if (handleEntry.own) {{
1794                            {dtor}
1795                        }}
1796                    }}
1797                    ",
1798                );
1799            }
1800
1801            Trampoline::ResourceTransferOwn => {
1802                let resource_transfer = self
1803                    .gen
1804                    .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTransferOwn));
1805                uwriteln!(self.src.js, "const trampoline{i} = {resource_transfer};");
1806            }
1807
1808            Trampoline::ResourceTransferBorrow => {
1809                let resource_transfer =
1810                    self.gen
1811                        .intrinsic(if self.gen.opts.valid_lifting_optimization {
1812                            Intrinsic::Resource(
1813                                ResourceIntrinsic::ResourceTransferBorrowValidLifting,
1814                            )
1815                        } else {
1816                            Intrinsic::Resource(ResourceIntrinsic::ResourceTransferBorrow)
1817                        });
1818                uwriteln!(self.src.js, "const trampoline{i} = {resource_transfer};");
1819            }
1820
1821            Trampoline::ResourceEnterCall => {
1822                let scope_id = self.gen.intrinsic(Intrinsic::ScopeId);
1823                uwrite!(
1824                    self.src.js,
1825                    "function trampoline{i}() {{
1826                        {scope_id}++;
1827                    }}
1828                    ",
1829                );
1830            }
1831
1832            Trampoline::ResourceExitCall => {
1833                let scope_id = self.gen.intrinsic(Intrinsic::ScopeId);
1834                let resource_borrows = self
1835                    .gen
1836                    .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceCallBorrows));
1837                let handle_tables = self.gen.intrinsic(Intrinsic::HandleTables);
1838                // To verify that borrows are dropped, it is enough to verify that the handle
1839                // either no longer exists (part of free list) or belongs to another scope, since
1840                // the enter call closed off the ability to create new handles in the parent scope
1841                uwrite!(
1842                    self.src.js,
1843                    "function trampoline{i}() {{
1844                        {scope_id}--;
1845                        for (const {{ rid, handle }} of {resource_borrows}) {{
1846                            if ({handle_tables}[rid][handle << 1] === {scope_id})
1847                                throw new TypeError('borrows not dropped for resource call');
1848                        }}
1849                        {resource_borrows} = [];
1850                    }}
1851                    ",
1852                );
1853            }
1854
1855            Trampoline::PrepareCall { memory } => {
1856                let _ = memory;
1857                todo!("prepare-call not implemented")
1858            }
1859
1860            Trampoline::ContextSet(slot) => {
1861                let context_set_fn = self
1862                    .gen
1863                    .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::ContextSet));
1864                uwriteln!(
1865                    self.src.js,
1866                    "const trampoline{i} = {context_set_fn}.bind(null, {slot});"
1867                );
1868            }
1869
1870            Trampoline::ContextGet(slot) => {
1871                let context_get_fn = self
1872                    .gen
1873                    .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::ContextGet));
1874                uwriteln!(
1875                    self.src.js,
1876                    "const trampoline{i} = {context_get_fn}.bind(null, {slot});"
1877                );
1878            }
1879
1880            Trampoline::TaskReturn { results, options } => {
1881                let CanonicalOptions {
1882                    instance,
1883                    async_,
1884                    data_model:
1885                        CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1886                    string_encoding,
1887                    callback,
1888                    post_return,
1889                    ..
1890                } = self
1891                    .component
1892                    .options
1893                    .get(*options)
1894                    .expect("failed to find options")
1895                else {
1896                    unreachable!("unexpected memory data model during task.return");
1897                };
1898
1899                // Validate canonopts
1900                // TODO: these should be traps at runtime rather than failures at transpilation time
1901                if realloc.is_some() && memory.is_none() {
1902                    panic!("memory must be present if realloc is");
1903                }
1904                if *async_ && post_return.is_some() {
1905                    panic!("async and post return must not be specified together");
1906                }
1907                if *async_ && callback.is_none() {
1908                    panic!("callback must be specified for async");
1909                }
1910                if let Some(cb_idx) = callback {
1911                    let cb_fn = &self.types[TypeFuncIndex::from_u32(cb_idx.as_u32())];
1912                    match self.types[cb_fn.params].types[..] {
1913                        [InterfaceType::S32, InterfaceType::S32, InterfaceType::S32] => {}
1914                        _ => panic!("unexpected params for async callback fn"),
1915                    }
1916                    match self.types[cb_fn.results].types[..] {
1917                        [InterfaceType::S32] => {}
1918                        _ => panic!("unexpected results for async callback fn"),
1919                    }
1920                }
1921
1922                // TODO: async callbacks always have a result, but that's not the *actual* return,
1923                // it's the async one.
1924
1925                // Build up a list of all the lifting functions that will be needed for the types
1926                // that are actually being task.return'd by this call
1927                let result_types = &self.types[*results].types;
1928                let mut lift_fns: Vec<String> = Vec::with_capacity(result_types.len());
1929                for result_ty in result_types {
1930                    let result_ty_abi = self.types.canonical_abi(result_ty);
1931                    lift_fns.push(match result_ty {
1932                        InterfaceType::Bool => self
1933                            .gen
1934                            .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatBool)),
1935                        InterfaceType::S8 => self
1936                            .gen
1937                            .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS8)),
1938                        InterfaceType::U8 => self
1939                            .gen
1940                            .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU8)),
1941                        InterfaceType::S16 => self
1942                            .gen
1943                            .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS16)),
1944                        InterfaceType::U16 => self
1945                            .gen
1946                            .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU16)),
1947                        InterfaceType::S32 => self
1948                            .gen
1949                            .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS32)),
1950                        InterfaceType::U32 => self
1951                            .gen
1952                            .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU32)),
1953                        InterfaceType::S64 => self
1954                            .gen
1955                            .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS64)),
1956                        InterfaceType::U64 => self
1957                            .gen
1958                            .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU64)),
1959                        InterfaceType::Float32 => self
1960                            .gen
1961                            .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFloat32)),
1962                        InterfaceType::Float64 => self
1963                            .gen
1964                            .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFloat64)),
1965                        InterfaceType::Char => match string_encoding {
1966                            wasmtime_environ::component::StringEncoding::Utf8 => self
1967                                .gen
1968                                .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatCharUtf8)),
1969                            wasmtime_environ::component::StringEncoding::Utf16 => self
1970                                .gen
1971                                .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatCharUtf16)),
1972                            wasmtime_environ::component::StringEncoding::CompactUtf16 => {
1973                                todo!("latin1+utf8 not supported")
1974                            }
1975                        },
1976                        InterfaceType::String => match string_encoding {
1977                            wasmtime_environ::component::StringEncoding::Utf8 => self
1978                                .gen
1979                                .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatStringUtf8)),
1980                            wasmtime_environ::component::StringEncoding::Utf16 => self
1981                                .gen
1982                                .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatStringUtf16)),
1983                            wasmtime_environ::component::StringEncoding::CompactUtf16 => {
1984                                todo!("latin1+utf8 not supported")
1985                            }
1986                        },
1987                        InterfaceType::Record(_ty_idx) => {
1988                            let lift_fn = self
1989                                .gen
1990                                .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatRecord));
1991                            format!("{lift_fn}.bind(null, {})", result_ty_abi.size32)
1992                        }
1993                        InterfaceType::Variant(_ty_idx) => {
1994                            let lift_fn = self
1995                                .gen
1996                                .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatVariant));
1997                            format!("{lift_fn}.bind(null, {})", result_ty_abi.size32)
1998                        }
1999                        InterfaceType::List(_ty_idx) => {
2000                            let lift_fn = self
2001                                .gen
2002                                .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatList));
2003                            format!("{lift_fn}.bind(null, {})", result_ty_abi.size32)
2004                        }
2005                        InterfaceType::Tuple(_ty_idx) => {
2006                            let lift_fn = self
2007                                .gen
2008                                .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatTuple));
2009                            format!("{lift_fn}.bind(null, {})", result_ty_abi.size32)
2010                        }
2011                        InterfaceType::Flags(_ty_idx) => {
2012                            let lift_fn = self
2013                                .gen
2014                                .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFlags));
2015                            format!("{lift_fn}.bind(null, {})", result_ty_abi.size32)
2016                        }
2017                        InterfaceType::Enum(_ty_idx) => {
2018                            let lift_fn = self
2019                                .gen
2020                                .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatEnum));
2021                            format!("{lift_fn}.bind(null, {})", result_ty_abi.size32)
2022                        }
2023                        InterfaceType::Option(_ty_idx) => {
2024                            let lift_fn = self
2025                                .gen
2026                                .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatOption));
2027                            format!("{lift_fn}.bind(null, {})", result_ty_abi.size32)
2028                        }
2029                        InterfaceType::Result(_ty_idx) => {
2030                            let lift_fn = self
2031                                .gen
2032                                .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatResult));
2033                            format!("{lift_fn}.bind(null, {})", result_ty_abi.size32)
2034                        }
2035                        InterfaceType::Own(_ty_idx) => {
2036                            let lift_fn = self
2037                                .gen
2038                                .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatOwn));
2039                            format!("{lift_fn}.bind(null, {})", result_ty_abi.size32)
2040                        }
2041                        InterfaceType::Borrow(_ty_idx) => {
2042                            let lift_fn = self
2043                                .gen
2044                                .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatOwn));
2045                            format!("{lift_fn}.bind(null, {})", result_ty_abi.size32)
2046                        }
2047                        InterfaceType::Future(_ty_idx) => {
2048                            let lift_fn = self
2049                                .gen
2050                                .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFuture));
2051                            format!("{lift_fn}.bind(null, 4)")
2052                        }
2053                        InterfaceType::Stream(_ty_idx) => {
2054                            let lift_fn = self
2055                                .gen
2056                                .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatStream));
2057                            format!("{lift_fn}.bind(null {})", result_ty_abi.size32)
2058                        }
2059                        InterfaceType::ErrorContext(_ty_idx) => {
2060                            let lift_fn = self
2061                                .gen
2062                                .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatErrorContext));
2063                            format!("{lift_fn}.bind(null, {})", result_ty_abi.size32)
2064                        }
2065                    });
2066                }
2067                let lift_fns_js = format!("[{}]", lift_fns.join(","));
2068
2069                // TODO: get the current in-flight call/task (currently passing null!) -- we'll need it to know
2070                // what component to return the results to. This might be stored in the trampoline data
2071
2072                // TODO: should be (task, result_ty, liftoptions, flat_args)
2073
2074                let memory_js = memory
2075                    .map(|idx| format!("memory{}", idx.as_u32()))
2076                    .unwrap_or_else(|| "null".into());
2077                let component_idx = instance.as_u32();
2078                let task_return_fn = self
2079                    .gen
2080                    .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::TaskReturn));
2081                let callback_fn_idx = callback
2082                    .map(|v| v.as_u32().to_string())
2083                    .unwrap_or_else(|| "null".into());
2084
2085                uwriteln!(
2086                    self.src.js,
2087                    "const trampoline{i} = {task_return_fn}.bind(
2088                         null,
2089                         {component_idx},
2090                         {memory_js},
2091                         {callback_fn_idx},
2092                         {lift_fns_js},
2093                     );",
2094                );
2095            }
2096        }
2097    }
2098
2099    fn instantiation_global_initializer(&mut self, init: &GlobalInitializer) {
2100        match init {
2101            // Extracting callbacks is a part of the async support for hosts -- it ensures that
2102            // a given core export can be turned into a callback function that will be used
2103            // later.
2104            //
2105            // Generally what we have to do here is to create a callback that can be called upon re-entrance
2106            // into the component after a related suspension.
2107            GlobalInitializer::ExtractCallback(ExtractCallback { index, def }) => {
2108                let callback_idx = index.as_u32();
2109                let core_def = self.core_def(def);
2110                uwriteln!(self.src.js, "let callback_{callback_idx};",);
2111                uwriteln!(self.src.js_init, "callback_{callback_idx} = {core_def}",);
2112            }
2113
2114            GlobalInitializer::InstantiateModule(m) => match m {
2115                InstantiateModule::Static(idx, args) => self.instantiate_static_module(*idx, args),
2116                // This is only needed when instantiating an imported core wasm
2117                // module which while easy to implement here is not possible to
2118                // test at this time so it's left unimplemented.
2119                InstantiateModule::Import(..) => unimplemented!(),
2120            },
2121            GlobalInitializer::LowerImport { index, import } => {
2122                self.lower_import(*index, *import);
2123            }
2124            GlobalInitializer::ExtractMemory(m) => {
2125                let def = self.core_export_var_name(&m.export);
2126                let idx = m.index.as_u32();
2127                uwriteln!(self.src.js, "let memory{idx};");
2128                uwriteln!(self.src.js_init, "memory{idx} = {def};");
2129            }
2130            GlobalInitializer::ExtractRealloc(r) => {
2131                let def = self.core_def(&r.def);
2132                let idx = r.index.as_u32();
2133                uwriteln!(self.src.js, "let realloc{idx};");
2134                uwriteln!(self.src.js_init, "realloc{idx} = {def};",);
2135            }
2136            GlobalInitializer::ExtractPostReturn(p) => {
2137                let def = self.core_def(&p.def);
2138                let idx = p.index.as_u32();
2139                uwriteln!(self.src.js, "let postReturn{idx};");
2140                uwriteln!(self.src.js_init, "postReturn{idx} = {def};");
2141            }
2142            GlobalInitializer::Resource(_) => {}
2143            GlobalInitializer::ExtractTable(extract_table) => {
2144                let _ = extract_table;
2145            }
2146        }
2147    }
2148
2149    fn instantiate_static_module(&mut self, idx: StaticModuleIndex, args: &[CoreDef]) {
2150        // Build a JS "import object" which represents `args`. The `args` is a
2151        // flat representation which needs to be zip'd with the list of names to
2152        // correspond to the JS wasm embedding API. This is one of the major
2153        // differences between Wasmtime's and JS's embedding API.
2154        let mut import_obj = BTreeMap::new();
2155        for (module, name, arg) in self.modules[idx].imports(args) {
2156            let def = self.augmented_import_def(arg);
2157            let dst = import_obj.entry(module).or_insert(BTreeMap::new());
2158            let prev = dst.insert(name, def);
2159            assert!(
2160                prev.is_none(),
2161                "unsupported duplicate import of `{module}::{name}`"
2162            );
2163            assert!(prev.is_none());
2164        }
2165        let mut imports = String::new();
2166        if !import_obj.is_empty() {
2167            imports.push_str(", {\n");
2168            for (module, names) in import_obj {
2169                imports.push_str(&maybe_quote_id(module));
2170                imports.push_str(": {\n");
2171                for (name, val) in names {
2172                    imports.push_str(&maybe_quote_id(name));
2173                    uwriteln!(imports, ": {val},");
2174                }
2175                imports.push_str("},\n");
2176            }
2177            imports.push('}');
2178        }
2179
2180        let i = self.instances.push(idx);
2181        let iu32 = i.as_u32();
2182        let instantiate = self.gen.intrinsic(Intrinsic::InstantiateCore);
2183        uwriteln!(self.src.js, "let exports{iu32};");
2184
2185        match self.gen.opts.instantiation {
2186            Some(InstantiationMode::Async) | None => {
2187                uwriteln!(
2188                    self.src.js_init,
2189                    "({{ exports: exports{iu32} }} = yield {instantiate}(yield module{}{imports}));",
2190                    idx.as_u32(),
2191                )
2192            }
2193
2194            Some(InstantiationMode::Sync) => {
2195                uwriteln!(
2196                    self.src.js_init,
2197                    "({{ exports: exports{iu32} }} = {instantiate}(module{}{imports}));",
2198                    idx.as_u32(),
2199                )
2200            }
2201        }
2202    }
2203
2204    /// Map all types in parameters and results to local resource types
2205    ///
2206    /// # Arguments
2207    ///
2208    /// * `func` - The function in question
2209    /// * `ty_func_idx` - Type index of the function
2210    /// * `resource_map` - resource map of locally resolved types
2211    /// * `remote_resource_map` - resource map of types only known to the remote (ex. an `error-context` for which we only hold the relevant rep)
2212    ///
2213    fn create_resource_fn_map(
2214        &mut self,
2215        func: &Function,
2216        ty_func_idx: TypeFuncIndex,
2217        resource_map: &mut ResourceMap,
2218        remote_resource_map: &mut RemoteResourceMap,
2219    ) {
2220        // Connect resources used in parameters
2221        let params_ty = &self.types[self.types[ty_func_idx].params];
2222        for ((_, ty), iface_ty) in func.params.iter().zip(params_ty.types.iter()) {
2223            if let Type::Id(id) = ty {
2224                self.connect_resource_types(*id, iface_ty, resource_map, remote_resource_map);
2225            }
2226        }
2227        // Connect resources used in results
2228        let results_ty = &self.types[self.types[ty_func_idx].results];
2229        if let (Some(Type::Id(id)), Some(iface_ty)) = (func.result, results_ty.types.first()) {
2230            self.connect_resource_types(id, iface_ty, resource_map, remote_resource_map);
2231        }
2232    }
2233
2234    fn resource_name(
2235        resolve: &Resolve,
2236        local_names: &'a mut LocalNames,
2237        resource: TypeId,
2238        resource_map: &BTreeMap<TypeId, ResourceIndex>,
2239    ) -> &'a str {
2240        let resource = crate::dealias(resolve, resource);
2241        local_names
2242            .get_or_create(
2243                resource_map[&resource],
2244                &resolve.types[resource]
2245                    .name
2246                    .as_ref()
2247                    .unwrap()
2248                    .to_upper_camel_case(),
2249            )
2250            .0
2251    }
2252
2253    fn lower_import(&mut self, index: LoweredIndex, import: RuntimeImportIndex) {
2254        let (options, trampoline, func_ty) = self.lowering_options[index];
2255
2256        let (import_index, path) = &self.component.imports[import];
2257        let (import_name, _) = &self.component.import_types[*import_index];
2258        let world_key = &self.imports[import_name];
2259
2260        let (func, func_name, iface_name) =
2261            match &self.resolve.worlds[self.world].imports[world_key] {
2262                WorldItem::Function(func) => {
2263                    assert_eq!(path.len(), 0);
2264                    (func, import_name, None)
2265                }
2266                WorldItem::Interface { id, stability: _ } => {
2267                    assert_eq!(path.len(), 1);
2268                    let iface = &self.resolve.interfaces[*id];
2269                    let func = &iface.functions[&path[0]];
2270                    let bundle = (
2271                        func,
2272                        &path[0],
2273                        Some(iface.name.as_deref().unwrap_or_else(|| import_name)),
2274                    );
2275                    bundle
2276                }
2277                WorldItem::Type(_) => unreachable!("unexpected imported world item type"),
2278            };
2279
2280        // WASI P3 async lifted functions should not have post returns
2281        let is_guest_async_lifted = is_guest_async_lifted_fn(func, options);
2282        if options.async_ {
2283            assert!(
2284                options.post_return.is_none(),
2285                "async function {func_name} (import {import_name}) can't have post return",
2286            );
2287        }
2288
2289        // Host lifted async (i.e. JSPI)
2290        let requires_async_porcelain = requires_async_porcelain(
2291            FunctionIdentifier::Fn(func),
2292            import_name,
2293            &self.async_imports,
2294        );
2295
2296        // nested interfaces only currently possible through mapping
2297        let (import_specifier, maybe_iface_member) = map_import(
2298            &self.gen.opts.map,
2299            if iface_name.is_some() {
2300                import_name
2301            } else {
2302                match func.kind {
2303                    FunctionKind::Method(_) => {
2304                        let stripped = import_name.strip_prefix("[method]").unwrap();
2305                        &stripped[0..stripped.find(".").unwrap()]
2306                    }
2307                    FunctionKind::Static(_) => {
2308                        let stripped = import_name.strip_prefix("[static]").unwrap();
2309                        &stripped[0..stripped.find(".").unwrap()]
2310                    }
2311                    FunctionKind::Constructor(_) => {
2312                        import_name.strip_prefix("[constructor]").unwrap()
2313                    }
2314                    FunctionKind::Freestanding => import_name,
2315                    FunctionKind::AsyncFreestanding => {
2316                        todo!("[lower_import()] FunctionKind::AsyncFreeStanding (import specifier)")
2317                    }
2318                    FunctionKind::AsyncMethod(id) => {
2319                        let _ = id;
2320                        todo!("[lower_import()] FunctionKind::AsyncMethod (import specifier)")
2321                    }
2322                    FunctionKind::AsyncStatic(id) => {
2323                        let _ = id;
2324                        todo!("[lower_import()] FunctionKind::AsyncStatic (import specifier)")
2325                    }
2326                }
2327            },
2328        );
2329
2330        let mut import_resource_map = ResourceMap::new();
2331        let mut import_remote_resource_map = RemoteResourceMap::new();
2332        self.create_resource_fn_map(
2333            func,
2334            func_ty,
2335            &mut import_resource_map,
2336            &mut import_remote_resource_map,
2337        );
2338
2339        let (callee_name, call_type) = match func.kind {
2340            FunctionKind::Freestanding => (
2341                self.gen
2342                    .local_names
2343                    .get_or_create(
2344                        format!(
2345                            "import:{}-{}-{}",
2346                            import_specifier,
2347                            maybe_iface_member.as_deref().unwrap_or(""),
2348                            &func.name
2349                        ),
2350                        &func.name,
2351                    )
2352                    .0
2353                    .to_string(),
2354                CallType::Standard,
2355            ),
2356            FunctionKind::Method(_) => (
2357                func.item_name().to_lower_camel_case(),
2358                CallType::CalleeResourceDispatch,
2359            ),
2360            FunctionKind::Static(resource_id) => (
2361                format!(
2362                    "{}.{}",
2363                    Instantiator::resource_name(
2364                        self.resolve,
2365                        &mut self.gen.local_names,
2366                        resource_id,
2367                        &self.imports_resource_types
2368                    ),
2369                    func.item_name().to_lower_camel_case()
2370                ),
2371                CallType::Standard,
2372            ),
2373
2374            FunctionKind::Constructor(resource_id) => (
2375                format!(
2376                    "new {}",
2377                    Instantiator::resource_name(
2378                        self.resolve,
2379                        &mut self.gen.local_names,
2380                        resource_id,
2381                        &self.imports_resource_types
2382                    )
2383                ),
2384                CallType::Standard,
2385            ),
2386
2387            FunctionKind::AsyncFreestanding => {
2388                todo!("[lower_import()] FunctionKind::AsyncFreeStanding")
2389            }
2390            FunctionKind::AsyncMethod(id) => {
2391                let _ = id;
2392                todo!("[lower_import()] FunctionKind::AsyncMethod");
2393            }
2394            FunctionKind::AsyncStatic(id) => {
2395                let _ = id;
2396                todo!("[lower_import()] FunctionKind::AsyncStatic");
2397            }
2398        };
2399
2400        let nparams = self
2401            .resolve
2402            .wasm_signature(AbiVariant::GuestImport, func)
2403            .params
2404            .len();
2405        match self.gen.opts.import_bindings {
2406            None | Some(BindingsMode::Js) | Some(BindingsMode::Hybrid) => {
2407                if requires_async_porcelain {
2408                    uwrite!(
2409                        self.src.js,
2410                        "\nconst trampoline{} = new WebAssembly.Suspending(async function",
2411                        trampoline.as_u32()
2412                    );
2413                } else {
2414                    uwrite!(self.src.js, "\nfunction trampoline{}", trampoline.as_u32());
2415                }
2416                self.bindgen(JsFunctionBindgenArgs {
2417                    nparams,
2418                    call_type,
2419                    iface_name: if import_name.is_empty() {
2420                        None
2421                    } else {
2422                        Some(import_name)
2423                    },
2424                    callee: &callee_name,
2425                    opts: options,
2426                    func,
2427                    resource_map: &import_resource_map,
2428                    remote_resource_map: &import_remote_resource_map,
2429                    abi: AbiVariant::GuestImport,
2430                    requires_async_porcelain,
2431                    is_guest_async_lifted,
2432                });
2433
2434                uwriteln!(self.src.js, "");
2435                if requires_async_porcelain {
2436                    uwriteln!(self.src.js, ");");
2437                } else {
2438                    uwriteln!(self.src.js, "");
2439                }
2440            }
2441            Some(BindingsMode::Optimized) | Some(BindingsMode::DirectOptimized) => {
2442                uwriteln!(self.src.js, "let trampoline{};", trampoline.as_u32());
2443            }
2444        };
2445
2446        // Build import bindings & trampolines for the import
2447        //
2448        // This is only necessary if an import binding mode is specified and not JS (the default),
2449        // (e.g. Optimized, Direct, Hybrid).
2450        if !matches!(self.gen.opts.import_bindings, None | Some(BindingsMode::Js)) {
2451            let (memory, realloc) =
2452                if let CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions {
2453                    memory,
2454                    realloc,
2455                }) = options.data_model
2456                {
2457                    (
2458                        memory.map(|idx| format!(" memory: memory{},", idx.as_u32())),
2459                        realloc.map(|idx| format!(" realloc: realloc{},", idx.as_u32())),
2460                    )
2461                } else {
2462                    (None, None)
2463                };
2464            let memory = memory.unwrap_or_default();
2465            let realloc = realloc.unwrap_or_default();
2466
2467            let post_return = options
2468                .post_return
2469                .map(|idx| format!(" postReturn: postReturn{},", idx.as_u32()))
2470                .unwrap_or("".into());
2471            let string_encoding = match options.string_encoding {
2472                wasmtime_environ::component::StringEncoding::Utf8 => "",
2473                wasmtime_environ::component::StringEncoding::Utf16 => " stringEncoding: 'utf16',",
2474                wasmtime_environ::component::StringEncoding::CompactUtf16 => {
2475                    " stringEncoding: 'compact-utf16',"
2476                }
2477            };
2478
2479            let callee_name = match func.kind {
2480                FunctionKind::Static(_) | FunctionKind::Freestanding => callee_name.to_string(),
2481                FunctionKind::Method(resource_id) => format!(
2482                    "{}.prototype.{callee_name}",
2483                    Instantiator::resource_name(
2484                        self.resolve,
2485                        &mut self.gen.local_names,
2486                        resource_id,
2487                        &self.imports_resource_types
2488                    )
2489                ),
2490                FunctionKind::Constructor(_) => callee_name[4..].to_string(),
2491                FunctionKind::AsyncFreestanding => {
2492                    todo!("[lower_import()] FunctionKind::AsyncFreeStanding (callee name gen)")
2493                }
2494                FunctionKind::AsyncMethod(id) => {
2495                    let _ = id;
2496                    todo!("[lower_import()] FunctionKind::AsyncMethod (callee name gen)")
2497                }
2498                FunctionKind::AsyncStatic(id) => {
2499                    let _ = id;
2500                    todo!("[lower_import()] FunctionKind::AsyncFreeStanding (callee name gen)")
2501                }
2502            };
2503
2504            let resource_tables = {
2505                let mut resource_tables: Vec<TypeResourceTableIndex> = Vec::new();
2506
2507                for (_, data) in import_resource_map {
2508                    let ResourceTable {
2509                        data: ResourceData::Host { tid, .. },
2510                        ..
2511                    } = &data
2512                    else {
2513                        unreachable!("unexpected non-host resource table");
2514                    };
2515                    resource_tables.push(*tid);
2516                }
2517
2518                if resource_tables.is_empty() {
2519                    "".to_string()
2520                } else {
2521                    format!(
2522                        " resourceTables: [{}],",
2523                        resource_tables
2524                            .iter()
2525                            .map(|x| format!("handleTable{}", x.as_u32()))
2526                            .collect::<Vec<String>>()
2527                            .join(", ")
2528                    )
2529                }
2530            };
2531
2532            // Build trampolines for the import
2533            match self.gen.opts.import_bindings {
2534                Some(BindingsMode::Hybrid) => {
2535                    let symbol_cabi_lower = self.gen.intrinsic(Intrinsic::SymbolCabiLower);
2536                    uwriteln!(self.src.js_init, "if ({callee_name}[{symbol_cabi_lower}]) {{
2537                        trampoline{} = {callee_name}[{symbol_cabi_lower}]({{{memory}{realloc}{post_return}{string_encoding}{resource_tables}}});
2538                    }}", trampoline.as_u32());
2539                }
2540                Some(BindingsMode::Optimized) => {
2541                    let symbol_cabi_lower = self.gen.intrinsic(Intrinsic::SymbolCabiLower);
2542                    if !self.gen.opts.valid_lifting_optimization {
2543                        uwriteln!(self.src.js_init, "if (!{callee_name}[{symbol_cabi_lower}]) {{
2544                            throw new TypeError('import for \"{import_name}\" does not define a Symbol.for(\"cabiLower\") optimized binding');
2545                        }}");
2546                    }
2547                    uwriteln!(self.src.js_init, "trampoline{} = {callee_name}[{symbol_cabi_lower}]({{{memory}{realloc}{post_return}{string_encoding}{resource_tables}}});", trampoline.as_u32());
2548                }
2549                Some(BindingsMode::DirectOptimized) => {
2550                    uwriteln!(
2551                        self.src.js_init,
2552                        "trampoline{} = {callee_name}({{{memory}{realloc}{post_return}{string_encoding}}});",
2553                        trampoline.as_u32()
2554                    );
2555                }
2556                None | Some(BindingsMode::Js) => unreachable!("invalid bindings mode"),
2557            };
2558        }
2559
2560        // Generate import name
2561        let (import_name, binding_name) = match func.kind {
2562            FunctionKind::Freestanding => (func_name.to_lower_camel_case(), callee_name),
2563            FunctionKind::Method(tid)
2564            | FunctionKind::Static(tid)
2565            | FunctionKind::Constructor(tid) => {
2566                let ty = &self.resolve.types[tid];
2567                (
2568                    ty.name.as_ref().unwrap().to_upper_camel_case(),
2569                    Instantiator::resource_name(
2570                        self.resolve,
2571                        &mut self.gen.local_names,
2572                        tid,
2573                        &self.imports_resource_types,
2574                    )
2575                    .to_string(),
2576                )
2577            }
2578            FunctionKind::AsyncFreestanding => {
2579                todo!("[lower_import()] FunctionKind::AsyncFreeStanding (import name gen)")
2580            }
2581            FunctionKind::AsyncMethod(id) => {
2582                let _ = id;
2583                todo!("[lower_import()] FunctionKind::AsyncMethod (import name gen)")
2584            }
2585            FunctionKind::AsyncStatic(id) => {
2586                let _ = id;
2587                todo!("[lower_import()] FunctionKind::AsyncStatic (import name gen)")
2588            }
2589        };
2590
2591        self.ensure_import(
2592            import_specifier,
2593            iface_name,
2594            maybe_iface_member.as_deref(),
2595            if iface_name.is_some() {
2596                Some(import_name.to_string())
2597            } else {
2598                None
2599            },
2600            binding_name,
2601        );
2602    }
2603
2604    /// Process an import if it has not already been processed
2605    ///
2606    /// # Arguments
2607    ///
2608    /// * `import_specifier` - The specifier of the import as used in JS (ex. `"@bytecodealliance/preview2-shim/random"`)
2609    /// * `iface_name` - The name of the WIT interface related to this binding, if present (ex. `"random"`)
2610    /// * `iface_member` - The name of the interface member, if present (ex. `"random"`)
2611    /// * `import_binding` - The name of binding, if present (ex. `"getRandomBytes"`)
2612    /// * `local_name` - Local name of the import (ex. `"getRandomBytes"`)
2613    ///
2614    fn ensure_import(
2615        &mut self,
2616        import_specifier: String,
2617        iface_name: Option<&str>,
2618        iface_member: Option<&str>,
2619        import_binding: Option<String>,
2620        local_name: String,
2621    ) {
2622        if import_specifier.starts_with("webidl:") {
2623            self.gen
2624                .intrinsic(Intrinsic::WebIdl(WebIdlIntrinsic::GlobalThisIdlProxy));
2625        }
2626
2627        // Build the import path depending on the kind of interface
2628        let mut import_path = Vec::with_capacity(2);
2629        import_path.push(import_specifier);
2630        if let Some(_iface_name) = iface_name {
2631            // Mapping can be used to construct virtual nested namespaces
2632            // which is used eg to support WASI interface groupings
2633            if let Some(iface_member) = iface_member {
2634                import_path.push(iface_member.to_lower_camel_case());
2635            }
2636            import_path.push(import_binding.clone().unwrap());
2637        } else if let Some(iface_member) = iface_member {
2638            import_path.push(iface_member.into());
2639        } else if let Some(import_binding) = &import_binding {
2640            import_path.push(import_binding.into());
2641        }
2642
2643        // Add the import binding that represents this import
2644        self.gen
2645            .esm_bindgen
2646            .add_import_binding(&import_path, local_name);
2647    }
2648
2649    /// Connect resources that have no types
2650    ///
2651    /// Commonly this is used for resources that have a type on on the import side
2652    /// but no relevant type on the receiving side, for which local types must be generated locally:
2653    /// - `error-context`
2654    /// - `future<_>`
2655    /// - `stream<_>`
2656    ///
2657    fn connect_remote_resources(
2658        &mut self,
2659        iface_ty: &InterfaceType,
2660        remote_resource_map: &mut RemoteResourceMap,
2661    ) {
2662        // TODO: finish implementing resource table creation for this data
2663        let (idx, entry) = match iface_ty {
2664            InterfaceType::Future(idx) => {
2665                // Create an entry to represent this future
2666                (
2667                    idx.as_u32(),
2668                    ResourceTable {
2669                        imported: true,
2670                        data: ResourceData::Guest {
2671                            resource_name: "Future".into(),
2672                            prefix: Some(format!("${}", idx.as_u32())),
2673                        },
2674                    },
2675                )
2676            }
2677            InterfaceType::Stream(idx) => (
2678                idx.as_u32(),
2679                ResourceTable {
2680                    imported: true,
2681                    data: ResourceData::Guest {
2682                        resource_name: "Stream".into(),
2683                        prefix: Some(format!("${}", idx.as_u32())),
2684                    },
2685                },
2686            ),
2687            InterfaceType::ErrorContext(idx) => (
2688                idx.as_u32(),
2689                ResourceTable {
2690                    imported: true,
2691                    data: ResourceData::Guest {
2692                        resource_name: "ErrorContext".into(),
2693                        prefix: Some(format!("${}", idx.as_u32())),
2694                    },
2695                },
2696            ),
2697            _ => unreachable!("unexpected interface type [{iface_ty:?}] with no type"),
2698        };
2699
2700        remote_resource_map.insert(idx, entry);
2701    }
2702
2703    /// Connect two types as host resources
2704    ///
2705    /// # Arguments
2706    ///
2707    /// * `t` - the TypeId
2708    /// * `tid` - Index into the type resource table of the interface (foreign side)
2709    /// * `resource_map` - Resource map that holds resource pairings
2710    ///
2711    fn connect_host_resource(
2712        &mut self,
2713        t: TypeId,
2714        tid: TypeResourceTableIndex,
2715        resource_map: &mut ResourceMap,
2716    ) {
2717        self.ensure_resource_table(tid);
2718
2719        // Figure out whether the resource index we're dealing with is for an imported type
2720        let resource_idx = self.types[tid].ty;
2721        let imported = self
2722            .component
2723            .defined_resource_index(resource_idx)
2724            .is_none();
2725
2726        // Retrieve the resource id for the type definition
2727        let resource_id = crate::dealias(self.resolve, t);
2728        let ty = &self.resolve.types[resource_id];
2729
2730        // If the resource is defined by this component (i.e. exported/used internally, *not* imported),
2731        // then determine the destructor that should be run based on the relevant resource
2732        let mut dtor_str = None;
2733        if let Some(resource_idx) = self.component.defined_resource_index(resource_idx) {
2734            assert!(!imported);
2735            let resource_def = self
2736                .component
2737                .initializers
2738                .iter()
2739                .find_map(|i| match i {
2740                    GlobalInitializer::Resource(r) if r.index == resource_idx => Some(r),
2741                    _ => None,
2742                })
2743                .unwrap();
2744
2745            if let Some(dtor) = &resource_def.dtor {
2746                dtor_str = Some(self.core_def(dtor));
2747            }
2748        }
2749
2750        // Look up the local import name
2751        let resource_name = ty.name.as_ref().unwrap().to_upper_camel_case();
2752
2753        let local_name = if imported {
2754            let (world_key, iface_name) = match ty.owner {
2755                wit_parser::TypeOwner::World(world) => (
2756                    self.resolve.worlds[world]
2757                        .imports
2758                        .iter()
2759                        .find(|&(_, item)| *item == WorldItem::Type(t))
2760                        .unwrap()
2761                        .0
2762                        .clone(),
2763                    None,
2764                ),
2765                wit_parser::TypeOwner::Interface(iface) => {
2766                    match &self.resolve.interfaces[iface].name {
2767                        Some(name) => (WorldKey::Interface(iface), Some(name.as_str())),
2768                        None => {
2769                            let key = self.resolve.worlds[self.world]
2770                                .imports
2771                                .iter()
2772                                .find(|&(_, item)| match item {
2773                                    WorldItem::Interface { id, .. } => *id == iface,
2774                                    _ => false,
2775                                })
2776                                .unwrap()
2777                                .0;
2778                            (
2779                                key.clone(),
2780                                match key {
2781                                    WorldKey::Name(ref name) => Some(name.as_str()),
2782                                    WorldKey::Interface(_) => None,
2783                                },
2784                            )
2785                        }
2786                    }
2787                }
2788                wit_parser::TypeOwner::None => unimplemented!(),
2789            };
2790
2791            let import_name = self.resolve.name_world_key(&world_key);
2792            let (local_name, _) = self
2793                .gen
2794                .local_names
2795                .get_or_create(resource_idx, &resource_name);
2796
2797            let local_name_str = local_name.to_string();
2798
2799            // Nested interfaces only currently possible through mapping
2800            let (import_specifier, maybe_iface_member) =
2801                map_import(&self.gen.opts.map, &import_name);
2802
2803            // Ensure that the import exists
2804            self.ensure_import(
2805                import_specifier,
2806                iface_name,
2807                maybe_iface_member.as_deref(),
2808                iface_name.map(|_| resource_name),
2809                local_name_str.to_string(),
2810            );
2811            local_name_str
2812        } else {
2813            let (local_name, _) = self
2814                .gen
2815                .local_names
2816                .get_or_create(resource_idx, &resource_name);
2817            local_name.to_string()
2818        };
2819
2820        // Add a resource table to track the host resource
2821        let entry = ResourceTable {
2822            imported,
2823            data: ResourceData::Host {
2824                tid,
2825                rid: self.types[tid].ty,
2826                local_name,
2827                dtor_name: dtor_str,
2828            },
2829        };
2830
2831        // If the the resource already exists, then  ensure that it is exactly the same as the
2832        // value we're attempting to insert
2833        if let Some(existing) = resource_map.get(&resource_id) {
2834            assert_eq!(*existing, entry);
2835            return;
2836        }
2837
2838        // Insert the resource into the map,
2839        resource_map.insert(resource_id, entry);
2840    }
2841
2842    /// Connect resources that are defined at the type levels in `wit-parser`
2843    /// to their types as defined in `wamstime-environ`
2844    ///
2845    /// The types that are connected here are stored in the `resource_map` for
2846    /// use later.
2847    ///
2848    /// # Arguments
2849    ///
2850    /// * `id` - The ID of the type if present (can be missing when dealing with `error-context`s, `future<_>`, etc)
2851    /// * `iface_ty` - The relevant interface type
2852    /// * `resource_map` - Resource map that we will update with pairings
2853    ///
2854    fn connect_resource_types(
2855        &mut self,
2856        id: TypeId,
2857        iface_ty: &InterfaceType,
2858        resource_map: &mut ResourceMap,
2859        remote_resource_map: &mut RemoteResourceMap,
2860    ) {
2861        match (&self.resolve.types[id].kind, iface_ty) {
2862            // For flags and enums we can do nothing -- they're global (?)
2863            (TypeDefKind::Flags(_), InterfaceType::Flags(_))
2864            | (TypeDefKind::Enum(_), InterfaceType::Enum(_)) => {}
2865
2866            // Connect records to records
2867            (TypeDefKind::Record(t1), InterfaceType::Record(t2)) => {
2868                let t2 = &self.types[*t2];
2869                for (f1, f2) in t1.fields.iter().zip(t2.fields.iter()) {
2870                    if let Type::Id(id) = f1.ty {
2871                        self.connect_resource_types(id, &f2.ty, resource_map, remote_resource_map);
2872                    }
2873                }
2874            }
2875
2876            // Handle connecting owned/borrowed handles to owned/borrowed handles
2877            (
2878                TypeDefKind::Handle(Handle::Own(t1) | Handle::Borrow(t1)),
2879                InterfaceType::Own(t2) | InterfaceType::Borrow(t2),
2880            ) => {
2881                self.connect_host_resource(*t1, *t2, resource_map);
2882            }
2883
2884            // Connect tuples to interface tuples
2885            (TypeDefKind::Tuple(t1), InterfaceType::Tuple(t2)) => {
2886                let t2 = &self.types[*t2];
2887                for (f1, f2) in t1.types.iter().zip(t2.types.iter()) {
2888                    if let Type::Id(id) = f1 {
2889                        self.connect_resource_types(*id, f2, resource_map, remote_resource_map);
2890                    }
2891                }
2892            }
2893
2894            // Connect inner types of variants to their interface types
2895            (TypeDefKind::Variant(t1), InterfaceType::Variant(t2)) => {
2896                let t2 = &self.types[*t2];
2897                for (f1, f2) in t1.cases.iter().zip(t2.cases.iter()) {
2898                    if let Some(Type::Id(id)) = &f1.ty {
2899                        self.connect_resource_types(
2900                            *id,
2901                            f2.1.as_ref().unwrap(),
2902                            resource_map,
2903                            remote_resource_map,
2904                        );
2905                    }
2906                }
2907            }
2908
2909            // Connect option<t> to option<t>
2910            (TypeDefKind::Option(t1), InterfaceType::Option(t2)) => {
2911                let t2 = &self.types[*t2];
2912                if let Type::Id(id) = t1 {
2913                    self.connect_resource_types(*id, &t2.ty, resource_map, remote_resource_map);
2914                }
2915            }
2916
2917            // Connect result<t> to result<t>
2918            (TypeDefKind::Result(t1), InterfaceType::Result(t2)) => {
2919                let t2 = &self.types[*t2];
2920                if let Some(Type::Id(id)) = &t1.ok {
2921                    self.connect_resource_types(
2922                        *id,
2923                        &t2.ok.unwrap(),
2924                        resource_map,
2925                        remote_resource_map,
2926                    );
2927                }
2928                if let Some(Type::Id(id)) = &t1.err {
2929                    self.connect_resource_types(
2930                        *id,
2931                        &t2.err.unwrap(),
2932                        resource_map,
2933                        remote_resource_map,
2934                    );
2935                }
2936            }
2937
2938            // Connect list<t> to list types
2939            (TypeDefKind::List(t1), InterfaceType::List(t2)) => {
2940                let t2 = &self.types[*t2];
2941                if let Type::Id(id) = t1 {
2942                    self.connect_resource_types(
2943                        *id,
2944                        &t2.element,
2945                        resource_map,
2946                        remote_resource_map,
2947                    );
2948                }
2949            }
2950
2951            // Connect named types
2952            (TypeDefKind::Type(ty), _) => {
2953                if let Type::Id(id) = ty {
2954                    self.connect_resource_types(*id, iface_ty, resource_map, remote_resource_map);
2955                }
2956            }
2957
2958            // Connect futures & stream types
2959            (TypeDefKind::Future(maybe_ty), InterfaceType::Future(_))
2960            | (TypeDefKind::Stream(maybe_ty), InterfaceType::Stream(_)) => {
2961                match maybe_ty {
2962                    // The case of an empty future is the propagation of a `null`-like value, usually a simple signal
2963                    // which we'll connect with the *normally invalid* type value 0 as an indicator
2964                    None => {
2965                        self.connect_remote_resources(iface_ty, remote_resource_map);
2966                    }
2967                    // For
2968                    Some(Type::Id(t)) => {
2969                        self.connect_resource_types(*t, iface_ty, resource_map, remote_resource_map)
2970                    }
2971                    Some(_) => {
2972                        unreachable!("unexpected interface type [{iface_ty:?}]")
2973                    }
2974                }
2975            }
2976
2977            // These types should never need to be connected
2978            (TypeDefKind::Resource, _) => {
2979                unreachable!("resource types do not need to be connected")
2980            }
2981            (TypeDefKind::Unknown, _) => unreachable!("unknown types cannot be connected"),
2982            (tk1, tk2) => unreachable!("invalid typedef kind combination [{tk1:?}] [{tk2:?}]",),
2983        }
2984    }
2985
2986    fn bindgen(&mut self, args: JsFunctionBindgenArgs) {
2987        let JsFunctionBindgenArgs {
2988            nparams,
2989            call_type,
2990            iface_name,
2991            callee,
2992            opts,
2993            func,
2994            resource_map,
2995            remote_resource_map,
2996            abi,
2997            requires_async_porcelain,
2998            is_guest_async_lifted,
2999        } = args;
3000
3001        let (memory, realloc) =
3002            if let CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions {
3003                memory,
3004                realloc,
3005            }) = opts.data_model
3006            {
3007                (
3008                    memory.map(|idx| format!("memory{}", idx.as_u32())),
3009                    realloc.map(|idx| format!("realloc{}", idx.as_u32())),
3010                )
3011            } else {
3012                (None, None)
3013            };
3014
3015        let post_return = opts
3016            .post_return
3017            .map(|idx| format!("postReturn{}", idx.as_u32()));
3018
3019        let tracing_prefix = format!(
3020            "[iface=\"{}\", function=\"{}\"]",
3021            iface_name.unwrap_or("<no iface>"),
3022            func.name
3023        );
3024
3025        // Write the function argument list
3026        //
3027        // At this point, only the function preamble (e.g. 'function nameOfFunc()') has been written
3028        self.src.js("(");
3029        let mut params = Vec::new();
3030        let mut first = true;
3031        for i in 0..nparams {
3032            if i == 0 && matches!(call_type, CallType::FirstArgIsThis) {
3033                params.push("this".into());
3034                continue;
3035            }
3036            if !first {
3037                self.src.js(", ");
3038            } else {
3039                first = false;
3040            }
3041            let param = format!("arg{i}");
3042            self.src.js(&param);
3043            params.push(param);
3044        }
3045        uwriteln!(self.src.js, ") {{");
3046
3047        // If tracing is enabled, output a function entry tracing message
3048        if self.gen.opts.tracing {
3049            let event_fields = func
3050                .params
3051                .iter()
3052                .enumerate()
3053                .map(|(i, (name, _ty))| format!("{name}=${{arguments[{i}]}}"))
3054                .collect::<Vec<String>>();
3055            uwriteln!(
3056                self.src.js,
3057                "console.error(`{tracing_prefix} call {}`);",
3058                event_fields.join(", ")
3059            );
3060        }
3061
3062        // If TLA compat was enabled, ensure that it was initialized
3063        if self.gen.opts.tla_compat
3064            && matches!(abi, AbiVariant::GuestExport)
3065            && self.gen.opts.instantiation.is_none()
3066        {
3067            let throw_uninitialized = self.gen.intrinsic(Intrinsic::ThrowUninitialized);
3068            uwrite!(
3069                self.src.js,
3070                "\
3071                if (!_initialized) {throw_uninitialized}();
3072            "
3073            );
3074        }
3075
3076        // Generate function body
3077        let mut f = FunctionBindgen {
3078            resource_map,
3079            remote_resource_map,
3080            clear_resource_borrows: false,
3081            intrinsics: &mut self.gen.all_intrinsics,
3082            valid_lifting_optimization: self.gen.opts.valid_lifting_optimization,
3083            sizes: &self.sizes,
3084            err: if get_thrown_type(self.resolve, func.result).is_some() {
3085                match abi {
3086                    AbiVariant::GuestExport => ErrHandling::ThrowResultErr,
3087                    AbiVariant::GuestImport => ErrHandling::ResultCatchHandler,
3088                    AbiVariant::GuestImportAsync => todo!("[transpile_bindgen::bindgen()] GuestImportAsync (ERR) not yet implemented"),
3089                    AbiVariant::GuestExportAsync => todo!("[transpile_bindgen::bindgen()] GuestExportAsync (ERR) not yet implemented"),
3090                    AbiVariant::GuestExportAsyncStackful => todo!("[transpile_bindgen::bindgen()] GuestExportAsyncStackful (ERR) not yet implemented"),
3091                }
3092            } else {
3093                ErrHandling::None
3094            },
3095            block_storage: Vec::new(),
3096            blocks: Vec::new(),
3097            callee,
3098            callee_resource_dynamic: matches!(call_type, CallType::CalleeResourceDispatch),
3099            memory: memory.as_ref(),
3100            realloc: realloc.as_ref(),
3101            tmp: 0,
3102            params,
3103            post_return: post_return.as_ref(),
3104            tracing_prefix: &tracing_prefix,
3105            tracing_enabled: self.gen.opts.tracing,
3106            encoding: match opts.string_encoding {
3107                wasmtime_environ::component::StringEncoding::Utf8 => StringEncoding::UTF8,
3108                wasmtime_environ::component::StringEncoding::Utf16 => StringEncoding::UTF16,
3109                wasmtime_environ::component::StringEncoding::CompactUtf16 => {
3110                    StringEncoding::CompactUTF16
3111                }
3112            },
3113            src: source::Source::default(),
3114            resolve: self.resolve,
3115            requires_async_porcelain,
3116            is_guest_async_lifted,
3117            canon_opts: opts,
3118            iface_name,
3119        };
3120
3121        // Emit (and visit, via the `FunctionBindgen` object) an abstract sequence of
3122        // instructions which represents the function being generated.
3123        abi::call(
3124            self.resolve,
3125            abi,
3126            match abi {
3127                AbiVariant::GuestImport => LiftLower::LiftArgsLowerResults,
3128                AbiVariant::GuestExport => if is_guest_async_lifted {
3129                    LiftLower::LiftArgsLowerResults
3130                } else {
3131                    LiftLower::LowerArgsLiftResults
3132                },
3133                AbiVariant::GuestImportAsync => todo!("[transpile_bindgen::bindgen()] GuestImportAsync (LIFT_LOWER) not yet implemented"),
3134                AbiVariant::GuestExportAsync => todo!("[transpile_bindgen::bindgen()] GuestExportAsync (LIFT_LOWER) not yet implemented"),
3135                AbiVariant::GuestExportAsyncStackful => todo!("[transpile_bindgen::bindgen()] GuestExportAsyncStackful (LIFT_LOWER) not yet implemented"),
3136            },
3137            func,
3138            &mut f,
3139            is_guest_async_lifted,
3140        );
3141
3142        // Once visiting has completed, write the contents the `FunctionBindgen` generated to output
3143        self.src.js(&f.src);
3144
3145        // Close function body
3146        self.src.js("}");
3147    }
3148
3149    fn augmented_import_def(&self, def: core::AugmentedImport<'_>) -> String {
3150        match def {
3151            core::AugmentedImport::CoreDef(def) => self.core_def(def),
3152            core::AugmentedImport::Memory { mem, op } => {
3153                let mem = self.core_def(mem);
3154                match op {
3155                    core::AugmentedOp::I32Load => {
3156                        format!(
3157                            "(ptr, off) => new DataView({mem}.buffer).getInt32(ptr + off, true)"
3158                        )
3159                    }
3160                    core::AugmentedOp::I32Load8U => {
3161                        format!(
3162                            "(ptr, off) => new DataView({mem}.buffer).getUint8(ptr + off, true)"
3163                        )
3164                    }
3165                    core::AugmentedOp::I32Load8S => {
3166                        format!("(ptr, off) => new DataView({mem}.buffer).getInt8(ptr + off, true)")
3167                    }
3168                    core::AugmentedOp::I32Load16U => {
3169                        format!(
3170                            "(ptr, off) => new DataView({mem}.buffer).getUint16(ptr + off, true)"
3171                        )
3172                    }
3173                    core::AugmentedOp::I32Load16S => {
3174                        format!(
3175                            "(ptr, off) => new DataView({mem}.buffer).getInt16(ptr + off, true)"
3176                        )
3177                    }
3178                    core::AugmentedOp::I64Load => {
3179                        format!(
3180                            "(ptr, off) => new DataView({mem}.buffer).getBigInt64(ptr + off, true)"
3181                        )
3182                    }
3183                    core::AugmentedOp::F32Load => {
3184                        format!(
3185                            "(ptr, off) => new DataView({mem}.buffer).getFloat32(ptr + off, true)"
3186                        )
3187                    }
3188                    core::AugmentedOp::F64Load => {
3189                        format!(
3190                            "(ptr, off) => new DataView({mem}.buffer).getFloat64(ptr + off, true)"
3191                        )
3192                    }
3193                    core::AugmentedOp::I32Store8 => {
3194                        format!(
3195                            "(ptr, val, offset) => {{
3196                                new DataView({mem}.buffer).setInt8(ptr + offset, val, true);
3197                            }}"
3198                        )
3199                    }
3200                    core::AugmentedOp::I32Store16 => {
3201                        format!(
3202                            "(ptr, val, offset) => {{
3203                                new DataView({mem}.buffer).setInt16(ptr + offset, val, true);
3204                            }}"
3205                        )
3206                    }
3207                    core::AugmentedOp::I32Store => {
3208                        format!(
3209                            "(ptr, val, offset) => {{
3210                                new DataView({mem}.buffer).setInt32(ptr + offset, val, true);
3211                            }}"
3212                        )
3213                    }
3214                    core::AugmentedOp::I64Store => {
3215                        format!(
3216                            "(ptr, val, offset) => {{
3217                                new DataView({mem}.buffer).setBigInt64(ptr + offset, val, true);
3218                            }}"
3219                        )
3220                    }
3221                    core::AugmentedOp::F32Store => {
3222                        format!(
3223                            "(ptr, val, offset) => {{
3224                                new DataView({mem}.buffer).setFloat32(ptr + offset, val, true);
3225                            }}"
3226                        )
3227                    }
3228                    core::AugmentedOp::F64Store => {
3229                        format!(
3230                            "(ptr, val, offset) => {{
3231                                new DataView({mem}.buffer).setFloat64(ptr + offset, val, true);
3232                            }}"
3233                        )
3234                    }
3235                    core::AugmentedOp::MemorySize => {
3236                        format!("ptr => {mem}.buffer.byteLength / 65536")
3237                    }
3238                }
3239            }
3240        }
3241    }
3242
3243    fn core_def(&self, def: &CoreDef) -> String {
3244        match def {
3245            CoreDef::Export(e) => self.core_export_var_name(e),
3246            CoreDef::Trampoline(i) => format!("trampoline{}", i.as_u32()),
3247            CoreDef::InstanceFlags(i) => {
3248                // SAFETY: short-lived borrow-mut.
3249                self.used_instance_flags.borrow_mut().insert(*i);
3250                format!("instanceFlags{}", i.as_u32())
3251            }
3252        }
3253    }
3254
3255    fn core_export_var_name<T>(&self, export: &CoreExport<T>) -> String
3256    where
3257        T: Into<EntityIndex> + Copy,
3258    {
3259        let name = match &export.item {
3260            ExportItem::Index(idx) => {
3261                let module = &self.modules[self.instances[export.instance]];
3262                let idx = (*idx).into();
3263                module
3264                    .exports()
3265                    .iter()
3266                    .find_map(|(name, i)| if *i == idx { Some(name) } else { None })
3267                    .unwrap()
3268            }
3269            ExportItem::Name(s) => s,
3270        };
3271        let i = export.instance.as_u32() as usize;
3272        format!("exports{i}{}", maybe_quote_member(name))
3273    }
3274
3275    fn exports(&mut self, exports: &NameMap<String, ExportIndex>) {
3276        for (export_name, export_idx) in exports.raw_iter() {
3277            let export = &self.component.export_items[*export_idx];
3278            let world_key = &self.exports[export_name];
3279            let item = &self.resolve.worlds[self.world].exports[world_key];
3280            let mut export_resource_map = ResourceMap::new();
3281            let mut export_remote_resource_map = RemoteResourceMap::new();
3282            match export {
3283                Export::LiftedFunction {
3284                    func: def,
3285                    options,
3286                    ty: func_ty,
3287                } => {
3288                    let func = match item {
3289                        WorldItem::Function(f) => f,
3290                        WorldItem::Interface { .. } | WorldItem::Type(_) => {
3291                            unreachable!("unexpectedly non-function lifted function export")
3292                        }
3293                    };
3294                    self.create_resource_fn_map(
3295                        func,
3296                        *func_ty,
3297                        &mut export_resource_map,
3298                        &mut export_remote_resource_map,
3299                    );
3300
3301                    let local_name = if let FunctionKind::Constructor(resource_id)
3302                    | FunctionKind::Method(resource_id)
3303                    | FunctionKind::Static(resource_id) = func.kind
3304                    {
3305                        Instantiator::resource_name(
3306                            self.resolve,
3307                            &mut self.gen.local_names,
3308                            resource_id,
3309                            &self.exports_resource_types,
3310                        )
3311                    } else {
3312                        self.gen.local_names.create_once(export_name)
3313                    }
3314                    .to_string();
3315
3316                    let options = self
3317                        .component
3318                        .options
3319                        .get(*options)
3320                        .expect("failed to find options");
3321
3322                    self.export_bindgen(
3323                        &local_name,
3324                        def,
3325                        options,
3326                        func,
3327                        export_name,
3328                        &export_resource_map,
3329                        &export_remote_resource_map,
3330                    );
3331                    if let FunctionKind::Constructor(ty)
3332                    | FunctionKind::Method(ty)
3333                    | FunctionKind::Static(ty) = func.kind
3334                    {
3335                        let ty = &self.resolve.types[ty];
3336                        self.gen.esm_bindgen.add_export_binding(
3337                            None,
3338                            local_name,
3339                            ty.name.as_ref().unwrap().to_upper_camel_case(),
3340                        );
3341                    } else {
3342                        self.gen.esm_bindgen.add_export_binding(
3343                            None,
3344                            local_name,
3345                            export_name.to_lower_camel_case(),
3346                        );
3347                    }
3348                }
3349
3350                Export::Instance { exports, .. } => {
3351                    let id = match item {
3352                        WorldItem::Interface { id, stability: _ } => *id,
3353                        WorldItem::Function(_) | WorldItem::Type(_) => {
3354                            unreachable!("unexpectedly non-interface export instance")
3355                        }
3356                    };
3357                    for (func_name, export_idx) in exports.raw_iter() {
3358                        let export = &self.component.export_items[*export_idx];
3359                        let (def, options, func_ty) = match export {
3360                            Export::LiftedFunction { func, options, ty } => (func, options, ty),
3361                            Export::Type(_) => continue, // ignored
3362                            _ => unreachable!("unexpected non-lifted function export"),
3363                        };
3364
3365                        let func = &self.resolve.interfaces[id].functions[func_name];
3366
3367                        self.create_resource_fn_map(
3368                            func,
3369                            *func_ty,
3370                            &mut export_resource_map,
3371                            &mut export_remote_resource_map,
3372                        );
3373
3374                        let local_name = if let FunctionKind::Constructor(resource_id)
3375                        | FunctionKind::Method(resource_id)
3376                        | FunctionKind::Static(resource_id) = func.kind
3377                        {
3378                            Instantiator::resource_name(
3379                                self.resolve,
3380                                &mut self.gen.local_names,
3381                                resource_id,
3382                                &self.exports_resource_types,
3383                            )
3384                        } else {
3385                            self.gen.local_names.create_once(func_name)
3386                        }
3387                        .to_string();
3388
3389                        let options = self
3390                            .component
3391                            .options
3392                            .get(*options)
3393                            .expect("failed to find options");
3394
3395                        self.export_bindgen(
3396                            &local_name,
3397                            def,
3398                            options,
3399                            func,
3400                            export_name,
3401                            &export_resource_map,
3402                            &export_remote_resource_map,
3403                        );
3404
3405                        if let FunctionKind::Constructor(ty)
3406                        | FunctionKind::Method(ty)
3407                        | FunctionKind::Static(ty) = func.kind
3408                        {
3409                            let ty = &self.resolve.types[ty];
3410                            let resource = ty.name.as_ref().unwrap();
3411                            self.gen.esm_bindgen.add_export_binding(
3412                                Some(export_name),
3413                                local_name,
3414                                resource.to_upper_camel_case(),
3415                            );
3416                        } else {
3417                            self.gen.esm_bindgen.add_export_binding(
3418                                Some(export_name),
3419                                local_name,
3420                                func_name.to_lower_camel_case(),
3421                            );
3422                        }
3423                    }
3424                }
3425
3426                // ignore type exports for now
3427                Export::Type(_) => {}
3428
3429                // This can't be tested at this time so leave it unimplemented
3430                Export::ModuleStatic { .. } | Export::ModuleImport { .. } => unimplemented!(),
3431            }
3432        }
3433        self.gen.esm_bindgen.populate_export_aliases();
3434    }
3435
3436    #[allow(clippy::too_many_arguments)]
3437    fn export_bindgen(
3438        &mut self,
3439        local_name: &str,
3440        def: &CoreDef,
3441        options: &CanonicalOptions,
3442        func: &Function,
3443        export_name: &String,
3444        export_resource_map: &ResourceMap,
3445        export_remote_resource_map: &RemoteResourceMap,
3446    ) {
3447        // Determine whether the function should be generated as async
3448        let requires_async_porcelain = requires_async_porcelain(
3449            FunctionIdentifier::Fn(func),
3450            export_name,
3451            &self.async_exports,
3452        );
3453        // If the function is *also* async lifted, it
3454        if options.async_ {
3455            assert!(
3456                options.post_return.is_none(),
3457                "async function {local_name} (export {export_name}) can't have post return"
3458            );
3459        }
3460
3461        let maybe_async = if requires_async_porcelain {
3462            "async "
3463        } else {
3464            ""
3465        };
3466
3467        // Start building early variable declarations
3468        let core_export_fn = self.core_def(def);
3469        let callee = match self
3470            .gen
3471            .local_names
3472            .get_or_create(&core_export_fn, &core_export_fn)
3473        {
3474            (local_name, true) => local_name.to_string(),
3475            (local_name, false) => {
3476                let local_name = local_name.to_string();
3477                uwriteln!(self.src.js, "let {local_name};");
3478                self.gen
3479                    .all_core_exported_funcs
3480                    .push((core_export_fn.clone(), requires_async_porcelain));
3481                local_name
3482            }
3483        };
3484
3485        match func.kind {
3486            FunctionKind::Freestanding => {
3487                uwrite!(self.src.js, "\n{maybe_async}function {local_name}")
3488            }
3489            FunctionKind::Method(_) => {
3490                self.ensure_local_resource_class(local_name.to_string());
3491                let method_name = func.item_name().to_lower_camel_case();
3492
3493                uwrite!(
3494                    self.src.js,
3495                    "\n{local_name}.prototype.{method_name} = {maybe_async}function {}",
3496                    if !is_js_reserved_word(&method_name) {
3497                        method_name.to_string()
3498                    } else {
3499                        format!("${method_name}")
3500                    }
3501                );
3502            }
3503            FunctionKind::Static(_) => {
3504                self.ensure_local_resource_class(local_name.to_string());
3505                let method_name = func.item_name().to_lower_camel_case();
3506                uwrite!(
3507                    self.src.js,
3508                    "\n{local_name}.{method_name} = function {}",
3509                    if !is_js_reserved_word(&method_name) {
3510                        method_name.to_string()
3511                    } else {
3512                        format!("${method_name}")
3513                    }
3514                );
3515            }
3516            FunctionKind::Constructor(_) => {
3517                if self.defined_resource_classes.contains(local_name) {
3518                    panic!("Internal error: Resource constructor must be defined before other methods and statics");
3519                }
3520                uwrite!(
3521                    self.src.js,
3522                    "
3523                    class {local_name} {{
3524                        constructor"
3525                );
3526                self.defined_resource_classes.insert(local_name.to_string());
3527            }
3528            FunctionKind::AsyncFreestanding => {
3529                uwrite!(self.src.js, "\nasync function {local_name}")
3530            }
3531            FunctionKind::AsyncMethod(_) => {
3532                self.ensure_local_resource_class(local_name.to_string());
3533                let method_name = func.item_name().to_lower_camel_case();
3534                let fn_name = if !is_js_reserved_word(&method_name) {
3535                    method_name.to_string()
3536                } else {
3537                    format!("${method_name}")
3538                };
3539                uwrite!(
3540                    self.src.js,
3541                    "\n{local_name}.prototype.{method_name} = async function {fn_name}",
3542                );
3543            }
3544            FunctionKind::AsyncStatic(_) => {
3545                self.ensure_local_resource_class(local_name.to_string());
3546                let method_name = func.item_name().to_lower_camel_case();
3547                let fn_name = if !is_js_reserved_word(&method_name) {
3548                    method_name.to_string()
3549                } else {
3550                    format!("${method_name}")
3551                };
3552                uwrite!(
3553                    self.src.js,
3554                    "\n{local_name}.{method_name} = async function {fn_name}",
3555                );
3556            }
3557        }
3558
3559        // Perform bindgen
3560        self.bindgen(JsFunctionBindgenArgs {
3561            nparams: func.params.len(),
3562            call_type: match func.kind {
3563                FunctionKind::Method(_) => CallType::FirstArgIsThis,
3564                _ => CallType::Standard,
3565            },
3566            iface_name: if export_name.is_empty() {
3567                None
3568            } else {
3569                Some(export_name)
3570            },
3571            callee: &callee,
3572            opts: options,
3573            func,
3574            resource_map: export_resource_map,
3575            remote_resource_map: export_remote_resource_map,
3576            abi: AbiVariant::GuestExport,
3577            requires_async_porcelain,
3578            is_guest_async_lifted: is_guest_async_lifted_fn(func, options),
3579        });
3580
3581        // End the function
3582        match func.kind {
3583            FunctionKind::AsyncFreestanding | FunctionKind::Freestanding => self.src.js("\n"),
3584            FunctionKind::AsyncMethod(_)
3585            | FunctionKind::AsyncStatic(_)
3586            | FunctionKind::Method(_)
3587            | FunctionKind::Static(_) => self.src.js(";\n"),
3588            FunctionKind::Constructor(_) => self.src.js("\n}\n"),
3589        }
3590    }
3591}
3592
3593#[derive(Default)]
3594pub struct Source {
3595    pub js: source::Source,
3596    pub js_init: source::Source,
3597}
3598
3599impl Source {
3600    pub fn js(&mut self, s: &str) {
3601        self.js.push_str(s);
3602    }
3603    pub fn js_init(&mut self, s: &str) {
3604        self.js_init.push_str(s);
3605    }
3606}
3607
3608fn map_import(map: &Option<HashMap<String, String>>, impt: &str) -> (String, Option<String>) {
3609    let impt_sans_version = match impt.find('@') {
3610        Some(version_idx) => &impt[0..version_idx],
3611        None => impt,
3612    };
3613    if let Some(map) = map.as_ref() {
3614        if let Some(mapping) = map.get(impt) {
3615            return if let Some(hash_idx) = mapping[1..].find('#') {
3616                (
3617                    mapping[0..hash_idx + 1].to_string(),
3618                    Some(mapping[hash_idx + 2..].into()),
3619                )
3620            } else {
3621                (mapping.into(), None)
3622            };
3623        }
3624        if let Some(mapping) = map.get(impt_sans_version) {
3625            return if let Some(hash_idx) = mapping[1..].find('#') {
3626                (
3627                    mapping[0..hash_idx + 1].to_string(),
3628                    Some(mapping[hash_idx + 2..].into()),
3629                )
3630            } else {
3631                (mapping.into(), None)
3632            };
3633        }
3634        for (key, mapping) in map {
3635            if let Some(wildcard_idx) = key.find('*') {
3636                let lhs = &key[0..wildcard_idx];
3637                let rhs = &key[wildcard_idx + 1..];
3638                if impt_sans_version.starts_with(lhs) && impt_sans_version.ends_with(rhs) {
3639                    let matched = &impt_sans_version[wildcard_idx
3640                        ..wildcard_idx + impt_sans_version.len() - lhs.len() - rhs.len()];
3641                    let mapping = mapping.replace('*', matched);
3642                    return if let Some(hash_idx) = mapping[1..].find('#') {
3643                        (
3644                            mapping[0..hash_idx + 1].to_string(),
3645                            Some(mapping[hash_idx + 2..].into()),
3646                        )
3647                    } else {
3648                        (mapping, None)
3649                    };
3650                }
3651                if impt.starts_with(lhs) && impt.ends_with(rhs) {
3652                    let matched =
3653                        &impt[wildcard_idx..wildcard_idx + impt.len() - lhs.len() - rhs.len()];
3654                    let mapping = mapping.replace('*', matched);
3655                    return if let Some(hash_idx) = mapping[1..].find('#') {
3656                        (
3657                            mapping[0..hash_idx + 1].to_string(),
3658                            Some(mapping[hash_idx + 2..].into()),
3659                        )
3660                    } else {
3661                        (mapping, None)
3662                    };
3663                }
3664            }
3665        }
3666    }
3667    (impt_sans_version.to_string(), None)
3668}
3669
3670pub fn parse_world_key(name: &str) -> Option<(&str, &str, &str)> {
3671    let registry_idx = name.find(':')?;
3672    let ns = &name[0..registry_idx];
3673    match name.rfind('/') {
3674        Some(sep_idx) => {
3675            let end = if let Some(version_idx) = name.rfind('@') {
3676                version_idx
3677            } else {
3678                name.len()
3679            };
3680            Some((
3681                ns,
3682                &name[registry_idx + 1..sep_idx],
3683                &name[sep_idx + 1..end],
3684            ))
3685        }
3686        // interface is a namespace, function is a default export
3687        None => Some((ns, &name[registry_idx + 1..], "")),
3688    }
3689}
3690
3691fn core_file_name(name: &str, idx: u32) -> String {
3692    let i_str = if idx == 0 {
3693        String::from("")
3694    } else {
3695        (idx + 1).to_string()
3696    };
3697    format!("{name}.core{i_str}.wasm")
3698}
3699
3700/// Encode a [`StringEncoding`] as a string that can be used in Javascript
3701fn string_encoding_js_literal(val: &wasmtime_environ::component::StringEncoding) -> &'static str {
3702    match val {
3703        wasmtime_environ::component::StringEncoding::Utf8 => "'utf8'",
3704        wasmtime_environ::component::StringEncoding::Utf16 => "'utf16'",
3705        wasmtime_environ::component::StringEncoding::CompactUtf16 => "'compact-utf16'",
3706    }
3707}
3708
3709/// Perform basic canonical option validation
3710fn is_valid_canonopt(
3711    CanonicalOptions {
3712        data_model,
3713        callback,
3714        post_return,
3715        async_,
3716        ..
3717    }: &CanonicalOptions,
3718) -> Result<()> {
3719    if let CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }) =
3720        data_model
3721    {
3722        if realloc.is_some() && memory.is_none() {
3723            bail!("memory must be present if realloc is");
3724        }
3725    }
3726    if *async_ && post_return.is_some() {
3727        bail!("async and post return must not be specified together");
3728    }
3729    if *async_ && callback.is_none() {
3730        bail!("callback must be specified for async");
3731    }
3732    Ok(())
3733}