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