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 mut instance_flag_defs = String::new();
1320 for used in self.used_instance_flags.borrow().iter() {
1321 let i = used.as_u32();
1322 uwriteln!(
1325 &mut instance_flag_defs,
1326 "const instanceFlags{i} = new WebAssembly.Global({{ value: \"i32\", mutable: true }}, 1);",
1327 );
1328 }
1329 self.src.js_init.prepend_str(&instance_flag_defs);
1330 }
1331
1332 fn is_early_trampoline(trampoline: &Trampoline) -> bool {
1337 matches!(
1338 trampoline,
1339 Trampoline::AsyncStartCall { .. }
1340 | Trampoline::BackpressureDec { .. }
1341 | Trampoline::BackpressureInc { .. }
1342 | Trampoline::EnterSyncCall
1343 | Trampoline::ErrorContextDebugMessage { .. }
1344 | Trampoline::ErrorContextDrop { .. }
1345 | Trampoline::ErrorContextNew { .. }
1346 | Trampoline::ErrorContextTransfer
1347 | Trampoline::ExitSyncCall
1348 | Trampoline::FutureCancelRead { .. }
1349 | Trampoline::FutureCancelWrite { .. }
1350 | Trampoline::FutureDropReadable { .. }
1351 | Trampoline::FutureDropWritable { .. }
1352 | Trampoline::FutureNew { .. }
1353 | Trampoline::FutureRead { .. }
1354 | Trampoline::FutureTransfer
1355 | Trampoline::FutureWrite { .. }
1356 | Trampoline::LowerImport { .. }
1357 | Trampoline::PrepareCall { .. }
1358 | Trampoline::ResourceDrop { .. }
1359 | Trampoline::ResourceNew { .. }
1360 | Trampoline::ResourceRep { .. }
1361 | Trampoline::ResourceTransferBorrow
1362 | Trampoline::ResourceTransferOwn
1363 | Trampoline::StreamCancelRead { .. }
1364 | Trampoline::StreamCancelWrite { .. }
1365 | Trampoline::StreamDropReadable { .. }
1366 | Trampoline::StreamDropWritable { .. }
1367 | Trampoline::StreamNew { .. }
1368 | Trampoline::StreamRead { .. }
1369 | Trampoline::StreamTransfer
1370 | Trampoline::StreamWrite { .. }
1371 | Trampoline::SubtaskCancel { .. }
1372 | Trampoline::SubtaskDrop { .. }
1373 | Trampoline::SyncStartCall { .. }
1374 | Trampoline::TaskCancel { .. }
1375 | Trampoline::TaskReturn { .. }
1376 | Trampoline::ThreadYield { .. }
1377 | Trampoline::ThreadYieldToSuspended { .. }
1378 | Trampoline::WaitableJoin { .. }
1379 | Trampoline::WaitableSetDrop { .. }
1380 | Trampoline::WaitableSetNew { .. }
1381 | Trampoline::WaitableSetPoll { .. }
1382 | Trampoline::WaitableSetWait { .. }
1383 )
1384 }
1385
1386 fn trampoline(&mut self, i: TrampolineIndex, trampoline: &'a Trampoline) {
1387 let i = i.as_u32();
1388 match trampoline {
1389 Trampoline::TaskCancel { instance } => {
1390 let task_cancel_fn = self
1391 .bindgen
1392 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::TaskCancel));
1393 uwriteln!(
1394 self.src.js,
1395 "const trampoline{i} = {task_cancel_fn}.bind(null, {instance_idx});\n",
1396 instance_idx = instance.as_u32(),
1397 );
1398 }
1399
1400 Trampoline::SubtaskCancel { instance, async_ } => {
1401 let subtask_cancel_fn = self
1402 .bindgen
1403 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::SubtaskCancel));
1404 let suspending_wrap_fn =
1405 self.bindgen.intrinsic(Intrinsic::SuspendingImportWrapperFn);
1406 uwriteln!(
1410 self.src.js,
1411 "const trampoline{i} = new WebAssembly.Suspending({suspending_wrap_fn}({instance_idx}, {subtask_cancel_fn}.bind(null, {instance_idx}, {async_})));\n",
1412 instance_idx = instance.as_u32(),
1413 );
1414 }
1415
1416 Trampoline::SubtaskDrop { instance } => {
1417 let component_idx = instance.as_u32();
1418 let subtask_drop_fn = self
1419 .bindgen
1420 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::SubtaskDrop));
1421 uwriteln!(
1422 self.src.js,
1423 "const trampoline{i} = {subtask_drop_fn}.bind(
1424 null,
1425 {component_idx},
1426 );"
1427 );
1428 }
1429
1430 Trampoline::WaitableSetNew { instance } => {
1431 let waitable_set_new_fn = self
1432 .bindgen
1433 .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableSetNew));
1434 uwriteln!(
1435 self.src.js,
1436 "const trampoline{i} = {waitable_set_new_fn}.bind(null, {});\n",
1437 instance.as_u32(),
1438 );
1439 }
1440
1441 Trampoline::WaitableSetWait { instance, options } => {
1442 let options = self
1443 .component
1444 .options
1445 .get(*options)
1446 .expect("failed to find options");
1447 assert_eq!(
1448 instance.as_u32(),
1449 options.instance.as_u32(),
1450 "options index instance must match trampoline"
1451 );
1452
1453 let CanonicalOptions {
1454 instance,
1455 async_,
1456 data_model:
1457 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, .. }),
1458 ..
1459 } = options
1460 else {
1461 panic!("unexpected/missing memory data model during waitable-set.wait");
1462 };
1463
1464 let instance_idx = instance.as_u32();
1465 let memory_idx = memory
1466 .expect("missing memory idx for waitable-set.wait")
1467 .as_u32();
1468 let waitable_set_wait_fn = self
1469 .bindgen
1470 .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableSetWait));
1471 let suspending_wrap_fn =
1472 self.bindgen.intrinsic(Intrinsic::SuspendingImportWrapperFn);
1473
1474 uwriteln!(
1475 self.src.js,
1476 r#"
1477 const trampoline{i} = new WebAssembly.Suspending({suspending_wrap_fn}({instance_idx}, {waitable_set_wait_fn}.bind(null, {{
1478 componentIdx: {instance_idx},
1479 isAsync: {async_},
1480 memoryIdx: {memory_idx},
1481 getMemoryFn: () => memory{memory_idx},
1482 }})));
1483 "#,
1484 );
1485 }
1486
1487 Trampoline::WaitableSetPoll { options, .. } => {
1488 let CanonicalOptions {
1489 instance,
1490 async_,
1491 data_model:
1492 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, .. }),
1493 cancellable,
1494 ..
1495 } = self
1496 .component
1497 .options
1498 .get(*options)
1499 .expect("failed to find options")
1500 else {
1501 panic!("unexpected memory data model during waitable-set.poll");
1502 };
1503
1504 let instance_idx = instance.as_u32();
1505 let memory_idx = memory
1506 .expect("missing memory idx for waitable-set.poll")
1507 .as_u32();
1508 let waitable_set_poll_fn = self
1509 .bindgen
1510 .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableSetPoll));
1511
1512 uwriteln!(
1513 self.src.js,
1514 r#"
1515 const trampoline{i} = {waitable_set_poll_fn}.bind(
1516 null,
1517 {{
1518 componentIdx: {instance_idx},
1519 isAsync: {async_},
1520 isCancellable: {cancellable},
1521 memoryIdx: {memory_idx},
1522 getMemoryFn: () => memory{memory_idx},
1523 }}
1524 );
1525 "#,
1526 );
1527 }
1528
1529 Trampoline::WaitableSetDrop { instance } => {
1530 let waitable_set_drop_fn = self
1531 .bindgen
1532 .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableSetDrop));
1533 uwriteln!(
1534 self.src.js,
1535 "const trampoline{i} = {waitable_set_drop_fn}.bind(null, {instance_idx});\n",
1536 instance_idx = instance.as_u32(),
1537 );
1538 }
1539
1540 Trampoline::WaitableJoin { instance } => {
1541 let waitable_join_fn = self
1542 .bindgen
1543 .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableJoin));
1544 uwriteln!(
1545 self.src.js,
1546 "const trampoline{i} = {waitable_join_fn}.bind(null, {instance_idx});\n",
1547 instance_idx = instance.as_u32(),
1548 );
1549 }
1550
1551 Trampoline::StreamNew { ty, instance } => {
1552 let stream_new_fn = self
1553 .bindgen
1554 .intrinsic(Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamNew));
1555 let instance_idx = instance.as_u32();
1556 let stream_table_idx = ty.as_u32();
1557
1558 let table_ty = &self.types[*ty];
1560 let stream_ty_idx = table_ty.ty;
1561 let stream_ty = &self.types[stream_ty_idx];
1562
1563 let payload_ty_name_js = stream_ty
1568 .payload
1569 .map(|iface_ty| format!("'{iface_ty:?}'"))
1570 .unwrap_or_else(|| "null".into());
1571
1572 let (
1574 align_32_js,
1575 size_32_js,
1576 flat_count_js,
1577 lift_fn_js,
1578 lower_fn_js,
1579 is_none_js,
1580 is_numeric_type_js,
1581 is_borrow_js,
1582 is_async_value_js,
1583 typed_array_js,
1584 ) = match stream_ty.payload {
1585 None => (
1587 "0".into(),
1588 "0".into(),
1589 "0".into(),
1590 "null".into(),
1591 "null".into(),
1592 "true",
1593 "false".into(),
1594 "false".into(),
1595 "false".into(),
1596 "undefined",
1597 ),
1598 Some(ty) => (
1600 self.types.canonical_abi(&ty).align32.to_string(),
1601 self.types.canonical_abi(&ty).size32.to_string(),
1602 self.types
1603 .canonical_abi(&ty)
1604 .flat_count
1605 .map(|v| v.to_string())
1606 .unwrap_or_else(|| "null".into()),
1607 gen_flat_lift_fn_js_expr(self, &ty, &None),
1608 gen_flat_lower_fn_js_expr(self, &ty, &None),
1609 "false",
1610 format!(
1611 "{}",
1612 matches!(
1613 ty,
1614 InterfaceType::U8
1615 | InterfaceType::U16
1616 | InterfaceType::U32
1617 | InterfaceType::U64
1618 | InterfaceType::S8
1619 | InterfaceType::S16
1620 | InterfaceType::S32
1621 | InterfaceType::S64
1622 | InterfaceType::Float32
1623 | InterfaceType::Float64
1624 )
1625 ),
1626 format!("{}", matches!(ty, InterfaceType::Borrow(_))),
1627 format!(
1628 "{}",
1629 matches!(ty, InterfaceType::Stream(_) | InterfaceType::Future(_))
1630 ),
1631 js_typed_array_ctor(&ty).unwrap_or("undefined"),
1632 ),
1633 };
1634
1635 uwriteln!(
1636 self.src.js,
1637 "const trampoline{i} = {stream_new_fn}.bind(null, {{
1638 streamTableIdx: {stream_table_idx},
1639 callerComponentIdx: {instance_idx},
1640 elemMeta: {{
1641 liftFn: {lift_fn_js},
1642 lowerFn: {lower_fn_js},
1643 payloadTypeName: {payload_ty_name_js},
1644 isNone: {is_none_js},
1645 isNumeric: {is_numeric_type_js},
1646 isBorrowed: {is_borrow_js},
1647 isAsyncValue: {is_async_value_js},
1648 typedArray: {typed_array_js},
1649 flatCount: {flat_count_js},
1650 align32: {align_32_js},
1651 size32: {size_32_js},
1652 }},
1653 }});\n",
1654 );
1655 }
1656
1657 Trampoline::StreamRead {
1658 instance,
1659 ty,
1660 options,
1661 } => {
1662 let options = self
1663 .component
1664 .options
1665 .get(*options)
1666 .expect("failed to find options");
1667 assert_eq!(
1668 instance.as_u32(),
1669 options.instance.as_u32(),
1670 "options index instance must match trampoline"
1671 );
1672
1673 let CanonicalOptions {
1674 instance,
1675 string_encoding,
1676 async_,
1677 data_model:
1678 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1679 ..
1680 } = options
1681 else {
1682 unreachable!("missing/invalid data model for options during stream.read")
1683 };
1684 let memory_idx = memory.expect("missing memory idx for stream.read").as_u32();
1685 let (realloc_idx, get_realloc_fn_js) = match realloc {
1686 Some(v) => {
1687 let v = v.as_u32().to_string();
1688 (v.to_string(), format!("() => realloc{v}"))
1689 }
1690 None => ("undefined".into(), "undefined".into()),
1691 };
1692
1693 let component_instance_id = instance.as_u32();
1694 let string_encoding = string_encoding_js_literal(string_encoding);
1695 let stream_table_idx = ty.as_u32();
1696 let stream_read_fn = self
1697 .bindgen
1698 .intrinsic(Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamRead));
1699
1700 let register_global_memory_for_component_fn = self
1705 .bindgen
1706 .intrinsic(Intrinsic::RegisterGlobalMemoryForComponent);
1707 uwriteln!(
1708 self.src.js_init,
1709 r#"{register_global_memory_for_component_fn}({{
1710 componentIdx: {component_instance_id},
1711 memoryIdx: {memory_idx},
1712 memory: memory{memory_idx},
1713 }});"#
1714 );
1715
1716 uwriteln!(
1717 self.src.js,
1718 r#"const trampoline{i} = new WebAssembly.Suspending({suspending_wrap_fn}({component_instance_id}, {stream_read_fn}.bind(
1719 null,
1720 {{
1721 componentIdx: {component_instance_id},
1722 memoryIdx: {memory_idx},
1723 getMemoryFn: () => memory{memory_idx},
1724 reallocIdx: {realloc_idx},
1725 getReallocFn: {get_realloc_fn_js},
1726 stringEncoding: {string_encoding},
1727 isAsync: {async_},
1728 streamTableIdx: {stream_table_idx},
1729 }}
1730 )));
1731 "#,
1732 suspending_wrap_fn =
1733 self.bindgen.intrinsic(Intrinsic::SuspendingImportWrapperFn),
1734 );
1735 }
1736
1737 Trampoline::StreamWrite {
1738 instance,
1739 ty,
1740 options,
1741 } => {
1742 let options = self
1743 .component
1744 .options
1745 .get(*options)
1746 .expect("failed to find options");
1747 assert_eq!(
1748 instance.as_u32(),
1749 options.instance.as_u32(),
1750 "options index instance must match trampoline"
1751 );
1752
1753 let CanonicalOptions {
1754 instance,
1755 string_encoding,
1756 async_,
1757 data_model:
1758 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1759 ..
1760 } = options
1761 else {
1762 unreachable!("unexpected memory data model during stream.write");
1763 };
1764 let component_instance_id = instance.as_u32();
1765 let memory_idx = memory
1766 .expect("missing memory idx for stream.write")
1767 .as_u32();
1768 let (realloc_idx, get_realloc_fn_js) = match realloc {
1769 Some(v) => {
1770 let v = v.as_u32().to_string();
1771 (v.to_string(), format!("() => realloc{v}"))
1772 }
1773 None => ("undefined".into(), "undefined".into()),
1774 };
1775
1776 let string_encoding = string_encoding_js_literal(string_encoding);
1777 let stream_table_idx = ty.as_u32();
1778 let stream_write_fn = self
1779 .bindgen
1780 .intrinsic(Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamWrite));
1781
1782 let register_global_memory_for_component_fn = self
1786 .bindgen
1787 .intrinsic(Intrinsic::RegisterGlobalMemoryForComponent);
1788 uwriteln!(
1789 self.src.js_init,
1790 r#"{register_global_memory_for_component_fn}({{
1791 componentIdx: {component_instance_id},
1792 memoryIdx: {memory_idx},
1793 memory: memory{memory_idx},
1794 }});"#
1795 );
1796
1797 uwriteln!(
1798 self.src.js,
1799 r#"
1800 const trampoline{i} = new WebAssembly.Suspending({suspending_wrap_fn}({component_instance_id}, {stream_write_fn}.bind(
1801 null,
1802 {{
1803 componentIdx: {component_instance_id},
1804 memoryIdx: {memory_idx},
1805 getMemoryFn: () => memory{memory_idx},
1806 reallocIdx: {realloc_idx},
1807 getReallocFn: {get_realloc_fn_js},
1808 stringEncoding: {string_encoding},
1809 isAsync: {async_},
1810 streamTableIdx: {stream_table_idx},
1811 }}
1812 )));
1813 "#,
1814 suspending_wrap_fn =
1815 self.bindgen.intrinsic(Intrinsic::SuspendingImportWrapperFn),
1816 );
1817 }
1818
1819 Trampoline::StreamCancelRead {
1820 instance,
1821 ty,
1822 async_,
1823 }
1824 | Trampoline::StreamCancelWrite {
1825 instance,
1826 ty,
1827 async_,
1828 } => {
1829 let stream_cancel_fn = match trampoline {
1830 Trampoline::StreamCancelRead { .. } => self.bindgen.intrinsic(
1831 Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamCancelRead),
1832 ),
1833 Trampoline::StreamCancelWrite { .. } => self.bindgen.intrinsic(
1834 Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamCancelWrite),
1835 ),
1836 _ => unreachable!("unexpected trampoline"),
1837 };
1838
1839 let stream_table_idx = ty.as_u32();
1840 let component_idx = instance.as_u32();
1841 uwriteln!(
1842 self.src.js,
1843 r#"
1844 const trampoline{i} = new WebAssembly.Suspending({suspending_wrap_fn}({component_idx}, {stream_cancel_fn}.bind(null, {{
1845 streamTableIdx: {stream_table_idx},
1846 isAsync: {async_},
1847 componentIdx: {component_idx},
1848 }})));
1849 "#,
1850 suspending_wrap_fn =
1851 self.bindgen.intrinsic(Intrinsic::SuspendingImportWrapperFn),
1852 );
1853 }
1854
1855 Trampoline::StreamDropReadable { ty, instance }
1856 | Trampoline::StreamDropWritable { ty, instance } => {
1857 let intrinsic_fn = match trampoline {
1858 Trampoline::StreamDropReadable { .. } => self.bindgen.intrinsic(
1859 Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamDropReadable),
1860 ),
1861 Trampoline::StreamDropWritable { .. } => self.bindgen.intrinsic(
1862 Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamDropWritable),
1863 ),
1864 _ => unreachable!("unexpected trampoline"),
1865 };
1866 let stream_idx = ty.as_u32();
1867 let instance_idx = instance.as_u32();
1868 uwriteln!(
1869 self.src.js,
1870 "const trampoline{i} = {intrinsic_fn}.bind(null, {{
1871 streamTableIdx: {stream_idx},
1872 componentIdx: {instance_idx},
1873 }});\n",
1874 );
1875 }
1876
1877 Trampoline::StreamTransfer => {
1878 let stream_transfer_fn = self
1879 .bindgen
1880 .intrinsic(Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamTransfer));
1881 uwriteln!(self.src.js, "const trampoline{i} = {stream_transfer_fn};\n",);
1882 }
1883
1884 Trampoline::FutureNew { instance, ty } => {
1885 let future_new_fn = self
1886 .bindgen
1887 .intrinsic(Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureNew));
1888 let future_table_idx = ty.as_u32();
1889 let component_idx = instance.as_u32();
1890
1891 let future_table_ty = &self.types[*ty];
1893 let future_ty = &self.types[future_table_ty.ty];
1894 let (
1895 payload_size32,
1896 payload_align32,
1897 payload_flat_count_js,
1898 payload_lift_fn_js,
1899 payload_lower_fn_js,
1900 is_borrowed,
1901 is_none_type,
1902 is_numeric_type,
1903 is_async_value,
1904 ) = match future_ty.payload {
1905 None => (
1906 0,
1907 0,
1908 "0".into(),
1909 "() => {{ throw new Error('empty future payload'); }}".into(),
1910 "() => {{ throw new Error('empty future payload'); }}".into(),
1911 false,
1912 true,
1913 false,
1914 false,
1915 ),
1916 Some(payload_ty) => {
1917 let cabi = self.types.canonical_abi(&payload_ty);
1918 (
1919 cabi.size32,
1920 cabi.align32,
1921 cabi.flat_count
1922 .map(|v| format!("{v}"))
1923 .unwrap_or_else(|| "null".into()),
1924 gen_flat_lift_fn_js_expr(self, &payload_ty, &None),
1925 gen_flat_lower_fn_js_expr(self, &payload_ty, &None),
1926 matches!(payload_ty, InterfaceType::Borrow(_)),
1927 false,
1928 matches!(
1929 payload_ty,
1930 InterfaceType::U8
1931 | InterfaceType::U16
1932 | InterfaceType::U32
1933 | InterfaceType::U64
1934 | InterfaceType::S8
1935 | InterfaceType::S16
1936 | InterfaceType::S32
1937 | InterfaceType::S64
1938 | InterfaceType::Float32
1939 | InterfaceType::Float64
1940 ),
1941 matches!(
1942 payload_ty,
1943 InterfaceType::Stream(_) | InterfaceType::Future(_)
1944 ),
1945 )
1946 }
1947 };
1948 let payload_ty_name_js = future_ty
1949 .payload
1950 .map(|iface_ty| format!("'{iface_ty:?}'"))
1951 .unwrap_or_else(|| "null".into());
1952
1953 uwriteln!(
1954 self.src.js,
1955 r#"
1956 const trampoline{i} = {future_new_fn}.bind(null, {{
1957 componentIdx: {component_idx},
1958 futureTableIdx: {future_table_idx},
1959 elemMeta: {{
1960 liftFn: {payload_lift_fn_js},
1961 lowerFn: {payload_lower_fn_js},
1962 payloadTypeName: {payload_ty_name_js},
1963 isNone: {is_none_type},
1964 isNumeric: {is_numeric_type},
1965 isBorrowed: {is_borrowed},
1966 isAsyncValue: {is_async_value},
1967 flatCount: {payload_flat_count_js},
1968 align32: {payload_align32},
1969 size32: {payload_size32},
1970 }},
1971 }});
1972 "#,
1973 );
1974 }
1975
1976 Trampoline::FutureWrite {
1977 instance,
1978 ty,
1979 options,
1980 }
1981 | Trampoline::FutureRead {
1982 instance,
1983 ty,
1984 options,
1985 } => {
1986 let intrinsic_fn = match trampoline {
1987 Trampoline::FutureRead { .. } => self
1988 .bindgen
1989 .intrinsic(Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureRead)),
1990 Trampoline::FutureWrite { .. } => self
1991 .bindgen
1992 .intrinsic(Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureWrite)),
1993 _ => unreachable!("invalid trampoline"),
1994 };
1995
1996 let options = self
1997 .component
1998 .options
1999 .get(*options)
2000 .expect("failed to find options");
2001 let CanonicalOptions {
2002 async_,
2003 string_encoding,
2004 callback,
2005 post_return,
2006 data_model:
2007 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
2008 ..
2009 } = options
2010 else {
2011 unreachable!("unexpected memory data model during future intrinsic");
2012 };
2013
2014 assert_eq!(
2015 *instance, options.instance,
2016 "component instances should match"
2017 );
2018 assert!(
2019 callback.is_none(),
2020 "callback should not be present for future intrinsic"
2021 );
2022 assert!(
2023 post_return.is_none(),
2024 "post_return should not be present for future intrinsic"
2025 );
2026
2027 let future_table_idx = ty.as_u32();
2028 let component_idx = instance.as_u32();
2029 let memory_idx = memory
2030 .expect("missing memory idx for future intrinsic")
2031 .as_u32();
2032 let (realloc_idx, get_realloc_fn_js) = match realloc {
2033 Some(idx) => (
2034 idx.as_u32().to_string(),
2035 format!("() => realloc{}", idx.as_u32()),
2036 ),
2037 None => ("undefined".into(), "undefined".to_string()),
2038 };
2039 let string_encoding = string_encoding_js_literal(string_encoding);
2040
2041 uwriteln!(
2042 self.src.js,
2043 r#"
2044 const trampoline{i} = new WebAssembly.Suspending({suspending_wrap_fn}({component_idx}, {intrinsic_fn}.bind(
2045 null,
2046 {{
2047 componentIdx: {component_idx},
2048 memoryIdx: {memory_idx},
2049 getMemoryFn: () => memory{memory_idx},
2050 reallocIdx: {realloc_idx},
2051 getReallocFn: {get_realloc_fn_js},
2052 stringEncoding: {string_encoding},
2053 futureTableIdx: {future_table_idx},
2054 isAsync: {async_},
2055 }},
2056 )));
2057 "#,
2058 suspending_wrap_fn =
2059 self.bindgen.intrinsic(Intrinsic::SuspendingImportWrapperFn),
2060 );
2061 }
2062
2063 Trampoline::FutureCancelRead {
2064 instance,
2065 ty,
2066 async_,
2067 }
2068 | Trampoline::FutureCancelWrite {
2069 instance,
2070 ty,
2071 async_,
2072 } => {
2073 let future_cancel_op_fn = match trampoline {
2074 Trampoline::FutureCancelRead { .. } => self.bindgen.intrinsic(
2075 Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureCancelRead),
2076 ),
2077 Trampoline::FutureCancelWrite { .. } => self.bindgen.intrinsic(
2078 Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureCancelWrite),
2079 ),
2080 _ => unreachable!(),
2081 };
2082
2083 let component_idx = instance.as_u32();
2084 let future_table_idx = ty.as_u32();
2085
2086 uwriteln!(
2087 self.src.js,
2088 r#"
2089 const trampoline{i} = new WebAssembly.Suspending({suspending_wrap_fn}({component_idx}, {future_cancel_op_fn}.bind(
2090 null,
2091 {{
2092 futureTableIdx: {future_table_idx},
2093 componentIdx: {component_idx},
2094 isAsync: {async_},
2095 }},
2096 )));
2097 "#,
2098 suspending_wrap_fn =
2099 self.bindgen.intrinsic(Intrinsic::SuspendingImportWrapperFn),
2100 );
2101 }
2102
2103 Trampoline::FutureDropReadable { instance, ty }
2104 | Trampoline::FutureDropWritable { instance, ty } => {
2105 let future_drop_op_fn = match trampoline {
2106 Trampoline::FutureDropReadable { .. } => self.bindgen.intrinsic(
2107 Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureDropReadable),
2108 ),
2109 Trampoline::FutureDropWritable { .. } => self.bindgen.intrinsic(
2110 Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureDropWritable),
2111 ),
2112 _ => unreachable!(),
2113 };
2114
2115 let component_idx = instance.as_u32();
2116 let future_table_idx = ty.as_u32();
2117
2118 uwriteln!(
2119 self.src.js,
2120 r#"
2121 const trampoline{i} = new WebAssembly.Suspending({suspending_wrap_fn}({component_idx}, {future_drop_op_fn}.bind(
2122 null,
2123 {{
2124 futureTableIdx: {future_table_idx},
2125 componentIdx: {component_idx},
2126 }},
2127 )));
2128 "#,
2129 suspending_wrap_fn =
2130 self.bindgen.intrinsic(Intrinsic::SuspendingImportWrapperFn),
2131 );
2132 }
2133
2134 Trampoline::FutureTransfer => {
2135 let future_transfer_fn = self
2136 .bindgen
2137 .intrinsic(Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureTransfer));
2138 uwriteln!(self.src.js, "const trampoline{i} = {future_transfer_fn};");
2139 }
2140
2141 Trampoline::ErrorContextNew { ty, options, .. } => {
2142 let CanonicalOptions {
2143 instance,
2144 string_encoding,
2145 data_model:
2146 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, .. }),
2147 ..
2148 } = self
2149 .component
2150 .options
2151 .get(*options)
2152 .expect("failed to find options")
2153 else {
2154 panic!("unexpected memory data model during error-context.new");
2155 };
2156
2157 self.ensure_error_context_local_table(*instance, *ty);
2158
2159 let local_err_tbl_idx = ty.as_u32();
2160 let component_idx = instance.as_u32();
2161
2162 let memory_idx = memory
2163 .expect("missing realloc fn idx for error-context.debug-message")
2164 .as_u32();
2165
2166 let decoder = match string_encoding {
2168 wasmtime_environ::component::StringEncoding::Utf8 => self
2169 .bindgen
2170 .intrinsic(Intrinsic::String(StringIntrinsic::GlobalTextDecoderUtf8)),
2171 wasmtime_environ::component::StringEncoding::Utf16 => self
2172 .bindgen
2173 .intrinsic(Intrinsic::String(StringIntrinsic::Utf16Decoder)),
2174 enc => panic!(
2175 "unsupported string encoding [{enc:?}] for error-context.debug-message"
2176 ),
2177 };
2178 uwriteln!(
2179 self.src.js,
2180 "function trampoline{i}InputStr(ptr, len) {{
2181 return {decoder}.decode(new DataView(memory{memory_idx}.buffer, ptr, len));
2182 }}"
2183 );
2184
2185 let err_ctx_new_fn = self
2186 .bindgen
2187 .intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::ErrorContextNew));
2188 uwriteln!(
2190 self.src.js,
2191 "const trampoline{i} = {err_ctx_new_fn}.bind(
2192 null,
2193 {{
2194 componentIdx: {component_idx},
2195 localTableIdx: {local_err_tbl_idx},
2196 readStrFn: trampoline{i}InputStr,
2197 }}
2198 );
2199 "
2200 );
2201 }
2202
2203 Trampoline::ErrorContextDebugMessage {
2204 instance, options, ..
2205 } => {
2206 let CanonicalOptions {
2207 async_,
2208 callback,
2209 post_return,
2210 string_encoding,
2211 data_model:
2212 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
2213 ..
2214 } = self
2215 .component
2216 .options
2217 .get(*options)
2218 .expect("failed to find options")
2219 else {
2220 panic!("unexpected memory data model during error-context.debug-message");
2221 };
2222
2223 let debug_message_fn = self
2224 .bindgen
2225 .intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::ErrorContextDebugMessage));
2226
2227 let realloc_fn_idx = realloc
2228 .expect("missing realloc fn idx for error-context.debug-message")
2229 .as_u32();
2230 let memory_idx = memory
2231 .expect("missing realloc fn idx for error-context.debug-message")
2232 .as_u32();
2233
2234 match string_encoding {
2236 wasmtime_environ::component::StringEncoding::Utf8 => {
2237 let encode_fn = self
2238 .bindgen
2239 .intrinsic(Intrinsic::String(StringIntrinsic::Utf8Encode));
2240 uwriteln!(
2241 self.src.js,
2242 "function trampoline{i}OutputStr(s, outputPtr) {{
2243 const memory = memory{memory_idx};
2244 const reallocFn = realloc{realloc_fn_idx};
2245 let {{ ptr, len }} = {encode_fn}(s, reallocFn, memory);
2246 new DataView(memory.buffer).setUint32(outputPtr, ptr, true)
2247 new DataView(memory.buffer).setUint32(outputPtr + 4, len, true)
2248 }}"
2249 );
2250 }
2251 wasmtime_environ::component::StringEncoding::Utf16 => {
2252 let encode_fn = self
2253 .bindgen
2254 .intrinsic(Intrinsic::String(StringIntrinsic::Utf16Encode));
2255 uwriteln!(
2256 self.src.js,
2257 "function trampoline{i}OutputStr(s, outputPtr) {{
2258 const memory = memory{memory_idx};
2259 const reallocFn = realloc{realloc_fn_idx};
2260 let ptr = {encode_fn}(s, reallocFn, memory);
2261 let len = s.length;
2262 new DataView(memory.buffer).setUint32(outputPtr, ptr, true)
2263 new DataView(memory.buffer).setUint32(outputPtr + 4, len, true)
2264 }}"
2265 );
2266 }
2267 enc => panic!(
2268 "unsupported string encoding [{enc:?}] for error-context.debug-message"
2269 ),
2270 };
2271
2272 let options_obj = format!(
2273 "{{callback:{callback}, postReturn: {post_return}, async: {async_}}}",
2274 callback = callback
2275 .map(|v| v.as_u32().to_string())
2276 .unwrap_or_else(|| "null".into()),
2277 post_return = post_return
2278 .map(|v| v.as_u32().to_string())
2279 .unwrap_or_else(|| "null".into()),
2280 );
2281
2282 let component_idx = instance.as_u32();
2283 uwriteln!(
2284 self.src.js,
2285 "const trampoline{i} = {debug_message_fn}.bind(
2286 null,
2287 {{
2288 componentIdx: {component_idx},
2289 options: {options_obj},
2290 writeStrFn: trampoline{i}OutputStr,
2291 }}
2292 );"
2293 );
2294 }
2295
2296 Trampoline::ErrorContextDrop { instance, ty } => {
2297 let drop_fn = self
2298 .bindgen
2299 .intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::ErrorContextDrop));
2300 let local_err_tbl_idx = ty.as_u32();
2301 let component_idx = instance.as_u32();
2302 uwriteln!(
2303 self.src.js,
2304 r#"
2305 const trampoline{i} = {drop_fn}.bind(
2306 null,
2307 {{ componentIdx: {component_idx}, localTableIdx: {local_err_tbl_idx} }},
2308 );
2309 "#
2310 );
2311 }
2312
2313 Trampoline::ErrorContextTransfer => {
2314 let transfer_fn = self
2315 .bindgen
2316 .intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::ErrorContextTransfer));
2317 uwriteln!(self.src.js, "const trampoline{i} = {transfer_fn};");
2318 }
2319
2320 Trampoline::PrepareCall { memory } => {
2322 let prepare_call_fn = self
2323 .bindgen
2324 .intrinsic(Intrinsic::Host(HostIntrinsic::PrepareCall));
2325 let (memory_idx_js, memory_fn_js) = memory
2326 .map(|v| {
2327 (
2328 v.as_u32().to_string(),
2329 format!("() => memory{}", v.as_u32()),
2330 )
2331 })
2332 .unwrap_or_else(|| ("null".into(), "() => null".into()));
2333 uwriteln!(
2334 self.src.js,
2335 "const trampoline{i} = {prepare_call_fn}.bind(null, {memory_idx_js}, {memory_fn_js});",
2336 )
2337 }
2338
2339 Trampoline::SyncStartCall { callback } => {
2340 let sync_start_call_fn = self
2341 .bindgen
2342 .intrinsic(Intrinsic::Host(HostIntrinsic::SyncStartCall));
2343 let (callback_idx, callback_fn) = callback
2344 .map(|v| (v.as_u32().to_string(), format!("callback_{}", v.as_u32())))
2345 .unwrap_or_else(|| ("null".into(), "null".into()));
2346
2347 uwriteln!(
2351 self.src.js,
2352 "const trampoline{i} = new WebAssembly.Suspending({sync_start_call_fn}.bind(
2353 null,
2354 {{
2355 callbackIdx: {callback_idx},
2356 getCallbackFn: () => {callback_fn},
2357 }},
2358 ));",
2359 );
2360 }
2361
2362 Trampoline::AsyncStartCall {
2365 callback,
2366 post_return,
2367 } => {
2368 let async_start_call_fn = self
2369 .bindgen
2370 .intrinsic(Intrinsic::Host(HostIntrinsic::AsyncStartCall));
2371 let (callback_idx, callback_fn) = callback
2372 .map(|v| (v.as_u32().to_string(), format!("callback_{}", v.as_u32())))
2373 .unwrap_or_else(|| ("null".into(), "null".into()));
2374 let (post_return_idx, post_return_fn) = post_return
2375 .map(|v| (v.as_u32().to_string(), format!("postReturn{}", v.as_u32())))
2376 .unwrap_or_else(|| ("null".into(), "null".into()));
2377
2378 uwriteln!(
2379 self.src.js,
2380 "const trampoline{i} = {async_start_call_fn}.bind(
2381 null,
2382 {{
2383 postReturnIdx: {post_return_idx},
2384 getPostReturnFn: () => {post_return_fn},
2385 callbackIdx: {callback_idx},
2386 getCallbackFn: () => {callback_fn},
2387 }},
2388 );",
2389 );
2390 }
2391
2392 Trampoline::LowerImport {
2393 index: _,
2394 lower_ty,
2395 options,
2396 } => {
2397 let canon_opts = self
2398 .component
2399 .options
2400 .get(*options)
2401 .expect("failed to find options");
2402
2403 let component_idx = canon_opts.instance.as_u32();
2409 let is_async = canon_opts.async_;
2410
2411 let cancellable = canon_opts.cancellable;
2412
2413 let func_ty = self.types.index(*lower_ty);
2414
2415 let param_types = &self.types.index(func_ty.params).types;
2417 let param_lift_fns_js =
2418 gen_flat_lift_fn_list_js_expr(self, param_types.iter().as_slice(), &None);
2419
2420 let result_types = &self.types.index(func_ty.results).types;
2422 let result_lower_fns_js =
2423 gen_flat_lower_fn_list_js_expr(self, result_types.iter().as_slice(), &None);
2424 let result_flat_count = result_types.iter().try_fold(0usize, |count, ty| {
2425 self.types
2426 .canonical_abi(ty)
2427 .flat_count
2428 .map(|flat_count| count + usize::from(flat_count))
2429 });
2430
2431 let get_callback_fn_js = canon_opts
2432 .callback
2433 .map(|idx| format!("() => callback_{}", idx.as_u32()))
2434 .unwrap_or_else(|| "() => null".into());
2435 let get_post_return_fn_js = canon_opts
2436 .post_return
2437 .map(|idx| format!("() => postReturn{}", idx.as_u32()))
2438 .unwrap_or_else(|| "() => null".into());
2439
2440 let (memory_exprs, realloc_expr_js) =
2442 if let CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions {
2443 memory,
2444 realloc,
2445 }) = canon_opts.data_model
2446 {
2447 (
2448 memory.map(|idx| {
2449 (
2450 idx.as_u32().to_string(),
2451 format!("() => memory{}", idx.as_u32()),
2452 )
2453 }),
2454 realloc.map(|idx| format!("() => realloc{}", idx.as_u32())),
2455 )
2456 } else {
2457 (None, None)
2458 };
2459 let (memory_idx_js, memory_expr_js) =
2460 memory_exprs.unwrap_or_else(|| ("null".into(), "() => null".into()));
2461 let realloc_expr_js = realloc_expr_js.unwrap_or_else(|| "undefined".into());
2462 let string_encoding_js = string_encoding_js_literal(&canon_opts.string_encoding);
2463
2464 let func_ty_async = func_ty.async_;
2466 let max_direct_results = if is_async || func_ty_async {
2467 0
2468 } else {
2469 MAX_FLAT_RESULTS
2470 };
2471 let has_result_pointer = result_flat_count
2472 .map(|count| count > max_direct_results)
2473 .unwrap_or(true);
2474 let call = format!(
2475 r#"{lower_import_intrinsic}.bind(
2476 null,
2477 {{
2478 trampolineIdx: {i},
2479 componentIdx: {component_idx},
2480 isAsync: {is_async},
2481 isManualAsync: _trampoline{i}.manuallyAsync,
2482 paramLiftFns: {param_lift_fns_js},
2483 resultLowerFns: {result_lower_fns_js},
2484 hasResultPointer: {has_result_pointer},
2485 funcTypeIsAsync: {func_ty_async},
2486 getCallbackFn: {get_callback_fn_js},
2487 getPostReturnFn: {get_post_return_fn_js},
2488 isCancellable: {cancellable},
2489 memoryIdx: {memory_idx_js},
2490 stringEncoding: {string_encoding_js},
2491 getMemoryFn: {memory_expr_js},
2492 getReallocFn: {realloc_expr_js},
2493 importFn: _trampoline{i},
2494 }},
2495 )"#,
2496 lower_import_intrinsic = if is_async || func_ty_async {
2497 self.bindgen
2498 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::LowerImport))
2499 } else {
2500 self.bindgen.intrinsic(Intrinsic::AsyncTask(
2501 AsyncTaskIntrinsic::LowerImportBackwardsCompat,
2502 ))
2503 }
2504 );
2505
2506 let suspending_wrap_fn =
2509 self.bindgen.intrinsic(Intrinsic::SuspendingImportWrapperFn);
2510 if is_async || func_ty_async {
2511 uwriteln!(
2512 self.src.js,
2513 "let trampoline{i} = new WebAssembly.Suspending({suspending_wrap_fn}({component_idx}, {call}));"
2514 );
2515 } else {
2516 uwriteln!(
2519 self.src.js,
2520 "let trampoline{i} = _trampoline{i}.manuallyAsync ? new WebAssembly.Suspending({suspending_wrap_fn}({component_idx}, {call})) : {call};"
2521 );
2522 }
2523 }
2524
2525 Trampoline::Transcoder {
2526 op,
2527 from,
2528 from64,
2529 to,
2530 to64,
2531 } => {
2532 if *from64 || *to64 {
2533 unimplemented!("memory 64 transcoder");
2534 }
2535 let from = from.as_u32();
2536 let to = to.as_u32();
2537 match op {
2538 Transcode::Copy(FixedEncoding::Utf8) => {
2539 uwriteln!(
2540 self.src.js,
2541 r#"
2542 function trampoline{i} (from_ptr, len, to_ptr) {{
2543 new Uint8Array(memory{to}.buffer, to_ptr, len).set(new Uint8Array(memory{from}.buffer, from_ptr, len));
2544 }}
2545 "#
2546 );
2547 }
2548 Transcode::Copy(FixedEncoding::Utf16) => unimplemented!("utf16 copier"),
2549 Transcode::Copy(FixedEncoding::Latin1) => unimplemented!("latin1 copier"),
2550 Transcode::Latin1ToUtf16 => unimplemented!("latin to utf16 transcoder"),
2551 Transcode::Latin1ToUtf8 => unimplemented!("latin to utf8 transcoder"),
2552 Transcode::Utf16ToCompactProbablyUtf16 => {
2553 unimplemented!("utf16 to compact wtf16 transcoder")
2554 }
2555 Transcode::Utf16ToCompactUtf16 => {
2556 unimplemented!("utf16 to compact utf16 transcoder")
2557 }
2558 Transcode::Utf16ToLatin1 => unimplemented!("utf16 to latin1 transcoder"),
2559 Transcode::Utf16ToUtf8 => {
2560 uwriteln!(
2561 self.src.js,
2562 r#"
2563 function trampoline{i} (src, src_len, dst, dst_len) {{
2564 const encoder = new TextEncoder();
2565 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));
2566 return [read, written];
2567 }}
2568 "#,
2569 );
2570 }
2571 Transcode::Utf8ToCompactUtf16 => {
2572 unimplemented!("utf8 to compact utf16 transcoder")
2573 }
2574 Transcode::Utf8ToLatin1 => unimplemented!("utf8 to latin1 transcoder"),
2575 Transcode::Utf8ToUtf16 => {
2576 uwriteln!(
2577 self.src.js,
2578 r#"
2579 function trampoline{i} (from_ptr, len, to_ptr) {{
2580 const decoder = new TextDecoder();
2581 const content = decoder.decode(new Uint8Array(memory{from}.buffer, from_ptr, len));
2582 const codeUnits = content.length;
2583 const view = new Uint16Array(memory{to}.buffer, to_ptr, codeUnits);
2584 for (var i = 0; i < codeUnits; i++) {{
2585 view[i] = content.charCodeAt(i);
2586 }}
2587 return codeUnits;
2588 }}
2589 "#,
2590 );
2591 }
2592 };
2593 }
2594
2595 Trampoline::ResourceNew {
2596 ty: resource_ty_idx,
2597 ..
2598 } => {
2599 self.ensure_resource_table(*resource_ty_idx);
2600 let rid = resource_ty_idx.as_u32();
2601 let rsc_table_create_own = self.bindgen.intrinsic(Intrinsic::Resource(
2602 ResourceIntrinsic::ResourceTableCreateOwn,
2603 ));
2604 uwriteln!(
2605 self.src.js,
2606 "const trampoline{i} = {rsc_table_create_own}.bind(null, handleTable{rid});"
2607 );
2608 }
2609
2610 Trampoline::ResourceRep {
2611 ty: resource_ty_idx,
2612 ..
2613 } => {
2614 self.ensure_resource_table(*resource_ty_idx);
2615 let rid = resource_ty_idx.as_u32();
2616 let rsc_table_get = self
2617 .bindgen
2618 .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableGet));
2619 uwriteln!(
2620 self.src.js,
2621 "function trampoline{i} (handle) {{
2622 return {rsc_table_get}(handleTable{rid}, handle).rep;
2623 }}"
2624 );
2625 }
2626
2627 Trampoline::ResourceDrop {
2628 ty: resource_table_ty_idx,
2629 ..
2630 } => {
2631 self.ensure_resource_table(*resource_table_ty_idx);
2632 let tid = resource_table_ty_idx.as_u32();
2633 let resource_table_ty = &self.types[*resource_table_ty_idx];
2634 let resource_ty = resource_table_ty.unwrap_concrete_ty();
2635 let rid = resource_ty.as_u32();
2636
2637 let dtor = if let Some(resource_idx) =
2639 self.component.defined_resource_index(resource_ty)
2640 {
2641 let resource_def = self
2642 .component
2643 .initializers
2644 .iter()
2645 .find_map(|i| match i {
2646 GlobalInitializer::Resource(r) if r.index == resource_idx => Some(r),
2647 _ => None,
2648 })
2649 .unwrap();
2650
2651 if let Some(dtor) = &resource_def.dtor {
2653 format!(
2654 "
2655 {}(handleEntry.rep);",
2656 self.core_def(dtor)
2657 )
2658 } else {
2659 "".into()
2660 }
2661 } else {
2662 let symbol_dispose = self.bindgen.intrinsic(Intrinsic::SymbolDispose);
2669 let symbol_cabi_dispose = self.bindgen.intrinsic(Intrinsic::SymbolCabiDispose);
2670
2671 if let Some(imported_resource_local_name) =
2673 self.bindgen.local_names.try_get(resource_ty)
2674 {
2675 format!(
2676 "
2677 const rsc = captureTable{rid}.get(handleEntry.rep);
2678 if (rsc) {{
2679 if (rsc[{symbol_dispose}]) rsc[{symbol_dispose}]();
2680 captureTable{rid}.delete(handleEntry.rep);
2681 }} else if ({imported_resource_local_name}[{symbol_cabi_dispose}]) {{
2682 {imported_resource_local_name}[{symbol_cabi_dispose}](handleEntry.rep);
2683 }}"
2684 )
2685 } else {
2686 format!(
2688 "throw new TypeError('unreachable trampoline for resource [{:?}]')",
2689 resource_ty
2690 )
2691 }
2692 };
2693
2694 let rsc_table_remove = self
2695 .bindgen
2696 .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableRemove));
2697 uwrite!(
2698 self.src.js,
2699 "function trampoline{i}(handle) {{
2700 const handleEntry = {rsc_table_remove}(handleTable{tid}, handle);
2701 if (handleEntry.own) {{
2702 {dtor}
2703 }}
2704 }}
2705 ",
2706 );
2707 }
2708
2709 Trampoline::ResourceTransferOwn => {
2710 let resource_transfer = self
2711 .bindgen
2712 .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTransferOwn));
2713 uwriteln!(self.src.js, "const trampoline{i} = {resource_transfer};");
2714 }
2715
2716 Trampoline::ResourceTransferBorrow => {
2717 let resource_transfer =
2718 self.bindgen
2719 .intrinsic(if self.bindgen.opts.valid_lifting_optimization {
2720 Intrinsic::Resource(
2721 ResourceIntrinsic::ResourceTransferBorrowValidLifting,
2722 )
2723 } else {
2724 Intrinsic::Resource(ResourceIntrinsic::ResourceTransferBorrow)
2725 });
2726 uwriteln!(self.src.js, "const trampoline{i} = {resource_transfer};");
2727 }
2728
2729 Trampoline::TaskReturn {
2730 results, options, ..
2731 } => {
2732 let canon_opts = self
2733 .component
2734 .options
2735 .get(*options)
2736 .expect("failed to find options");
2737 let CanonicalOptions {
2738 instance,
2739 async_,
2740 data_model:
2741 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
2742 callback,
2743 post_return,
2744 string_encoding,
2745 ..
2746 } = canon_opts
2747 else {
2748 unreachable!("unexpected memory data model during task.return");
2749 };
2750
2751 if realloc.is_some() && memory.is_none() {
2753 panic!("memory must be present if realloc is");
2754 }
2755 if *async_ && post_return.is_some() {
2756 panic!("async and post return must not be specified together");
2757 }
2758 if *async_ && callback.is_none() {
2759 panic!("callback must be specified for async");
2760 }
2761 if let Some(cb_idx) = callback {
2762 let cb_fn = &self.types[TypeFuncIndex::from_u32(cb_idx.as_u32())];
2763 match self.types[cb_fn.params].types[..] {
2764 [InterfaceType::S32, InterfaceType::S32, InterfaceType::S32] => {}
2765 _ => panic!("unexpected params for async callback fn"),
2766 }
2767 match self.types[cb_fn.results].types[..] {
2768 [InterfaceType::S32] => {}
2769 _ => panic!("unexpected results for async callback fn"),
2770 }
2771 }
2772
2773 let result_types = &self.types[*results].types;
2774
2775 let result_flat_param_total: usize = result_types
2778 .iter()
2779 .map(|t| {
2780 self.types
2781 .canonical_abi(t)
2782 .flat_count
2783 .map(usize::from)
2784 .unwrap_or(0)
2785 })
2786 .sum();
2787 let use_direct_params = result_flat_param_total < MAX_FLAT_PARAMS;
2788
2789 let mut lift_fns: Vec<String> = Vec::with_capacity(result_types.len());
2792 for result_ty in result_types {
2793 lift_fns.push(gen_flat_lift_fn_js_expr(self, result_ty, &None));
2794 }
2795 let lift_fns_js = format!("[{}]", lift_fns.join(","));
2796
2797 let mut lower_fns: Vec<String> = Vec::with_capacity(result_types.len());
2803 for result_ty in result_types {
2804 lower_fns.push(gen_flat_lower_fn_js_expr(self, result_ty, &None));
2805 }
2806 let lower_fns_js = format!("[{}]", lower_fns.join(","));
2807
2808 let get_memory_fn_js = memory
2809 .map(|idx| format!("() => memory{}", idx.as_u32()))
2810 .unwrap_or_else(|| "() => null".into());
2811 let memory_idx_js = memory
2812 .map(|idx| idx.as_u32().to_string())
2813 .unwrap_or_else(|| "null".into());
2814 let component_idx = instance.as_u32();
2815 let task_return_fn = self
2816 .bindgen
2817 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::TaskReturn));
2818 let callback_fn_idx = callback
2819 .map(|v| v.as_u32().to_string())
2820 .unwrap_or_else(|| "null".into());
2821 let string_encoding_js = string_encoding_js_literal(string_encoding);
2822
2823 uwriteln!(
2824 self.src.js,
2825 "const trampoline{i} = {task_return_fn}.bind(
2826 null,
2827 {{
2828 componentIdx: {component_idx},
2829 useDirectParams: {use_direct_params},
2830 getMemoryFn: {get_memory_fn_js},
2831 memoryIdx: {memory_idx_js},
2832 callbackFnIdx: {callback_fn_idx},
2833 liftFns: {lift_fns_js},
2834 lowerFns: {lower_fns_js},
2835 stringEncoding: {string_encoding_js},
2836 }},
2837 );",
2838 );
2839 }
2840
2841 Trampoline::BackpressureInc { instance } => {
2842 let backpressure_inc_fn = self
2843 .bindgen
2844 .intrinsic(Intrinsic::Component(ComponentIntrinsic::BackpressureInc));
2845 uwriteln!(
2846 self.src.js,
2847 "const trampoline{i} = {backpressure_inc_fn}.bind(null, {instance});\n",
2848 instance = instance.as_u32(),
2849 );
2850 }
2851
2852 Trampoline::BackpressureDec { instance } => {
2853 let backpressure_dec_fn = self
2854 .bindgen
2855 .intrinsic(Intrinsic::Component(ComponentIntrinsic::BackpressureDec));
2856 uwriteln!(
2857 self.src.js,
2858 "const trampoline{i} = {backpressure_dec_fn}.bind(null, {instance});\n",
2859 instance = instance.as_u32(),
2860 );
2861 }
2862
2863 Trampoline::ThreadYield {
2864 cancellable,
2865 instance,
2866 } => {
2867 let yield_fn = self
2868 .bindgen
2869 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::Yield));
2870 let suspending_wrap_fn =
2871 self.bindgen.intrinsic(Intrinsic::SuspendingImportWrapperFn);
2872 let component_instance_idx = instance.as_u32();
2873 uwriteln!(
2874 self.src.js,
2875 r#"
2876 const trampoline{i} = new WebAssembly.Suspending({suspending_wrap_fn}({component_instance_idx}, {yield_fn}.bind(null, {{
2877 isCancellable: {cancellable},
2878 componentIdx: {component_instance_idx},
2879 }})));
2880 "#,
2881 );
2882 }
2883 Trampoline::ThreadIndex => todo!("Trampoline::ThreadIndex"),
2884 Trampoline::ThreadNewIndirect { .. } => todo!("Trampoline::ThreadNewIndirect"),
2885 Trampoline::ThreadSuspend { .. } => todo!("Trampoline::ThreadSuspend"),
2886 Trampoline::ThreadSuspendTo { .. } => todo!("Trampoline::ThreadSuspendTo"),
2887 Trampoline::ThreadUnsuspend { .. } => todo!("Trampoline::ThreadUnsuspend"),
2888 Trampoline::ThreadYieldToSuspended { .. } => {
2889 todo!("Trampoline::ThreadYieldToSuspended")
2890 }
2891 Trampoline::ThreadSuspendToSuspended { .. } => {
2892 todo!("Trampoline::ThreadYieldToSuspended")
2893 }
2894
2895 Trampoline::Trap => {
2896 uwriteln!(
2897 self.src.js,
2898 "function trampoline{i}(rep) {{ throw new TypeError('Trap'); }}"
2899 );
2900 }
2901
2902 Trampoline::EnterSyncCall => {
2903 let enter_symmetric_sync_guest_call_fn = self.bindgen.intrinsic(
2904 Intrinsic::AsyncTask(AsyncTaskIntrinsic::EnterSymmetricSyncGuestCall),
2905 );
2906 let uses_jspi = matches!(
2915 self.bindgen.opts.async_mode,
2916 Some(AsyncMode::JavaScriptPromiseIntegration { .. })
2917 );
2918 if uses_jspi {
2919 uwriteln!(
2920 self.src.js,
2921 r#"
2922 const trampoline{i} = new WebAssembly.Suspending({enter_symmetric_sync_guest_call_fn});
2923 "#,
2924 );
2925 } else {
2926 uwriteln!(
2927 self.src.js,
2928 r#"
2929 const trampoline{i} = {enter_symmetric_sync_guest_call_fn};
2930 "#,
2931 );
2932 }
2933 }
2934
2935 Trampoline::ExitSyncCall => {
2936 let exit_symmetric_sync_guest_call_fn = self.bindgen.intrinsic(
2937 Intrinsic::AsyncTask(AsyncTaskIntrinsic::ExitSymmetricSyncGuestCall),
2938 );
2939 uwriteln!(
2940 self.src.js,
2941 "const trampoline{i} = {exit_symmetric_sync_guest_call_fn};\n",
2942 );
2943 }
2944 }
2945 }
2946
2947 fn instantiation_global_initializer(&mut self, init: &GlobalInitializer) {
2948 match init {
2949 GlobalInitializer::ExtractCallback(ExtractCallback { index, def }) => {
2956 let callback_idx = index.as_u32();
2957 let core_def = self.core_def(def);
2958
2959 uwriteln!(self.src.js, "let callback_{callback_idx};",);
2960
2961 uwriteln!(
2970 self.src.js_init,
2971 r#"
2972 callback_{callback_idx} = WebAssembly.promising({core_def});
2973 callback_{callback_idx}.fnName = "{core_def}";
2974 "#
2975 );
2976 }
2977
2978 GlobalInitializer::InstantiateModule(m, instance) => {
2979 self.init_current_module = *instance;
2983
2984 match m {
2985 InstantiateModule::Static(idx, args) => {
2986 self.instantiate_static_module(*idx, args, *instance);
2987 }
2988 InstantiateModule::Import(..) => unimplemented!(),
2992 }
2993 }
2994
2995 GlobalInitializer::LowerImport { index, import } => {
2996 self.lower_import(*index, *import);
2997 }
2998
2999 GlobalInitializer::ExtractMemory(m) => {
3000 let def = self.core_export_var_name(&m.export);
3001 let idx = m.index.as_u32();
3002 uwriteln!(self.src.js, "let memory{idx};");
3003 uwriteln!(self.src.js_init, "memory{idx} = {def};");
3004 }
3005
3006 GlobalInitializer::ExtractRealloc(r) => {
3007 let def = self.core_def(&r.def);
3008 let idx = r.index.as_u32();
3009 uwriteln!(self.src.js, "let realloc{idx};");
3010 uwriteln!(self.src.js, "let realloc{idx}Async;");
3011 uwriteln!(self.src.js_init, "realloc{idx} = {def};",);
3012 uwriteln!(
3015 self.src.js_init,
3016 r#"
3017 try {{
3018 realloc{idx}Async = WebAssembly.promising({def});
3019 }} catch(err) {{
3020 realloc{idx}Async = {def};
3021 }}
3022 "#
3023 );
3024 }
3025
3026 GlobalInitializer::ExtractPostReturn(p) => {
3027 let def = self.core_def(&p.def);
3028 let idx = p.index.as_u32();
3029 uwriteln!(self.src.js, "let postReturn{idx};");
3030 uwriteln!(self.src.js, "let postReturn{idx}Async;");
3031 uwriteln!(self.src.js_init, "postReturn{idx} = {def};");
3032 uwriteln!(
3035 self.src.js_init,
3036 r#"
3037 try {{
3038 postReturn{idx}Async = WebAssembly.promising({def});
3039 }} catch(err) {{
3040 postReturn{idx}Async = {def};
3041 }}
3042 "#
3043 );
3044 }
3045
3046 GlobalInitializer::Resource(_) => {}
3047
3048 GlobalInitializer::ExtractTable(_) => {}
3049 }
3050 }
3051
3052 fn instantiate_static_module(
3053 &mut self,
3054 module_idx: StaticModuleIndex,
3055 args: &[CoreDef],
3056 instance: Option<RuntimeComponentInstanceIndex>,
3057 ) {
3058 let mut import_obj = BTreeMap::new();
3063 for (module, name, arg) in self.modules[module_idx].imports(args) {
3064 let def = self.augmented_import_def(&arg);
3065 let dst = import_obj.entry(module).or_insert(BTreeMap::new());
3066 let prev = dst.insert(name, def);
3067 assert!(
3068 prev.is_none(),
3069 "unsupported duplicate import of `{module}::{name}`"
3070 );
3071 assert!(prev.is_none());
3072 }
3073
3074 if self.bindgen.opts.asmjs {
3075 let component_instance_idx = instance
3076 .expect("missing runtime component index during static module instantiation")
3077 .as_u32();
3078
3079 self.add_intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::GetCurrentTask));
3080 self.add_intrinsic(Intrinsic::GetGlobalCurrentTaskMetaFn);
3081 let current_task_get_fn =
3082 Intrinsic::AsyncTask(AsyncTaskIntrinsic::GetCurrentTask).name();
3083 let get_global_current_task_meta_fn = Intrinsic::GetGlobalCurrentTaskMetaFn.name();
3084
3085 let dst = import_obj.entry("env").or_insert(BTreeMap::new());
3086 let prev = dst.insert(
3087 "setTempRet0",
3088 format!(
3089 "(x) => {{
3090 const {{ taskID }} = {get_global_current_task_meta_fn}({component_instance_idx});
3091
3092 const taskMeta = {current_task_get_fn}({component_instance_idx}, taskID);
3093 if (!taskMeta) {{ throw new Error('invalid/missing async task meta'); }}
3094
3095 const task = taskMeta.task;
3096 if (!task) {{ throw new Error('invalid/missing async task'); }}
3097
3098 task.tmpRetI64HighBits = x|0;
3099 }}"
3100 ),
3101 );
3102 assert!(
3103 prev.is_none(),
3104 "unsupported duplicate import of `env::setTempRet0`"
3105 );
3106 assert!(prev.is_none());
3107 }
3108
3109 let mut imports = String::new();
3111 if !import_obj.is_empty() {
3112 imports.push_str(", {\n");
3113 for (module, names) in import_obj {
3114 imports.push_str(&maybe_quote_id(module));
3115 imports.push_str(": {\n");
3116 for (name, val) in names {
3117 imports.push_str(&maybe_quote_id(name));
3118 uwriteln!(imports, ": {val},");
3119 }
3120 imports.push_str("},\n");
3121 }
3122 imports.push('}');
3123 }
3124
3125 let i = self.instances.push(module_idx);
3126 let iu32 = i.as_u32();
3127 let instantiate = self.bindgen.intrinsic(Intrinsic::InstantiateCore);
3128 uwriteln!(self.src.js, "let exports{iu32};");
3129
3130 match self.bindgen.opts.instantiation_mode {
3131 Some(InstantiationMode::Async) | None => {
3132 uwriteln!(
3133 self.src.js_init,
3134 "({{ exports: exports{iu32} }} = yield {instantiate}(yield module{}{imports}));",
3135 module_idx.as_u32(),
3136 )
3137 }
3138
3139 Some(InstantiationMode::Sync) => {
3140 uwriteln!(
3141 self.src.js_init,
3142 "({{ exports: exports{iu32} }} = {instantiate}(module{}{imports}));",
3143 module_idx.as_u32(),
3144 );
3145 }
3146 }
3147 }
3148
3149 fn create_resource_fn_map(
3157 &mut self,
3158 func: &Function,
3159 ty_func_idx: TypeFuncIndex,
3160 resource_map: &mut ResourceMap,
3161 ) {
3162 let params_ty = &self.types[self.types[ty_func_idx].params];
3164 for (p, iface_ty) in func.params.iter().zip(params_ty.types.iter()) {
3165 if let Type::Id(id) = p.ty {
3166 self.connect_resource_types(id, iface_ty, resource_map);
3167 }
3168 }
3169 let results_ty = &self.types[self.types[ty_func_idx].results];
3171 if let (Some(Type::Id(id)), Some(iface_ty)) = (func.result, results_ty.types.first()) {
3172 self.connect_resource_types(id, iface_ty, resource_map);
3173 }
3174 }
3175
3176 fn resource_name(
3177 resolve: &Resolve,
3178 local_names: &'a mut LocalNames,
3179 resource: TypeId,
3180 resource_map: &BTreeMap<TypeId, ResourceIndex>,
3181 ) -> &'a str {
3182 let resource = crate::dealias(resolve, resource);
3183 local_names
3184 .get_or_create(
3185 resource_map[&resource],
3186 &resolve.types[resource]
3187 .name
3188 .as_ref()
3189 .unwrap()
3190 .to_upper_camel_case(),
3191 )
3192 .0
3193 }
3194
3195 fn imported_resource_name(&mut self, import_index: ImportIndex, resource: TypeId) -> String {
3206 let resolve = self.resolve;
3207 let types = self.types;
3208 let component = self.component;
3209 let resource = crate::dealias(resolve, resource);
3210 let resource_wit_name = resolve.types[resource].name.as_ref().unwrap();
3211 if let (
3212 _,
3213 ComponentExtern {
3214 ty: TypeDef::ComponentInstance(inst),
3215 ..
3216 },
3217 ) = &component.import_types[import_index]
3218 && let Some(ComponentExtern {
3219 ty: TypeDef::Resource(rt_idx),
3220 ..
3221 }) = types[*inst].exports.get(resource_wit_name)
3222 {
3223 let rid = types[*rt_idx].unwrap_concrete_ty();
3224 return self
3225 .bindgen
3226 .local_names
3227 .get_or_create(rid, &resource_wit_name.to_upper_camel_case())
3228 .0
3229 .to_string();
3230 }
3231 Instantiator::resource_name(
3234 resolve,
3235 &mut self.bindgen.local_names,
3236 resource,
3237 &self.imports_resource_types,
3238 )
3239 .to_string()
3240 }
3241
3242 fn find_import_providing_resource(
3254 &self,
3255 resource_idx: ResourceIndex,
3256 ) -> Option<(&'a str, bool)> {
3257 let component = self.component;
3258 let types = self.types;
3259 for (_, (imp_name, extern_)) in component.import_types.iter() {
3260 match &extern_.ty {
3261 TypeDef::ComponentInstance(inst) => {
3262 for (_, export) in types[*inst].exports.iter() {
3263 if let TypeDef::Resource(rt) = &export.ty
3264 && types[*rt].unwrap_concrete_ty() == resource_idx
3265 {
3266 return Some((imp_name.as_str(), true));
3267 }
3268 }
3269 }
3270 TypeDef::Resource(rt) if types[*rt].unwrap_concrete_ty() == resource_idx => {
3271 return Some((imp_name.as_str(), false));
3272 }
3273 _ => {}
3274 }
3275 }
3276 None
3277 }
3278
3279 fn lower_import(&mut self, index: LoweredIndex, import: RuntimeImportIndex) {
3280 let (options, trampoline, func_ty) = self.lowering_options[index];
3281
3282 let (import_index, path) = &self.component.imports[import];
3284 let (import_name, _) = &self.component.import_types[*import_index];
3285 let world_key = &self.imports[import_name];
3286
3287 let (func, func_name, iface_name) =
3289 match &self.resolve.worlds[self.world].imports[world_key] {
3290 WorldItem::Function(func) => {
3291 assert_eq!(path.len(), 0);
3292 (func, import_name, None)
3293 }
3294 WorldItem::Interface { id, .. } => {
3295 assert_eq!(path.len(), 1);
3296 let iface = &self.resolve.interfaces[*id];
3297 let func = &iface.functions[&path[0]];
3298 (
3299 func,
3300 &path[0],
3301 Some(iface.name.as_deref().unwrap_or_else(|| import_name)),
3302 )
3303 }
3304 WorldItem::Type { .. } => unreachable!("unexpected imported world item type"),
3305 };
3306
3307 let is_async = is_async_fn(func, options);
3308
3309 if options.async_ {
3310 assert!(
3311 options.post_return.is_none(),
3312 "async function {func_name} (import {import_name}) can't have post return",
3313 );
3314 }
3315
3316 let requires_async_porcelain = requires_async_porcelain(
3318 FunctionIdentifier::Fn(func),
3319 import_name,
3320 &self.async_imports,
3321 );
3322
3323 let implements = self.resolve.implements_value(
3328 world_key,
3329 &self.resolve.worlds[self.world].imports[world_key],
3330 );
3331
3332 let (import_specifier, maybe_iface_member) = map_import_with_implements(
3334 &self.bindgen.opts.map,
3335 if iface_name.is_some() {
3336 import_name
3337 } else {
3338 match func.kind {
3339 FunctionKind::Method(_) => {
3340 let stripped = import_name.strip_prefix("[method]").unwrap();
3341 &stripped[0..stripped.find(".").unwrap()]
3342 }
3343 FunctionKind::AsyncMethod(_) => {
3344 let stripped = import_name.strip_prefix("[async method]").unwrap();
3345 &stripped[0..stripped.find(".").unwrap()]
3346 }
3347 FunctionKind::Static(_) => {
3348 let stripped = import_name.strip_prefix("[static]").unwrap();
3349 &stripped[0..stripped.find(".").unwrap()]
3350 }
3351 FunctionKind::AsyncStatic(_) => {
3352 let stripped = import_name.strip_prefix("[async static]").unwrap();
3353 &stripped[0..stripped.find(".").unwrap()]
3354 }
3355 FunctionKind::Constructor(_) => {
3356 import_name.strip_prefix("[constructor]").unwrap()
3357 }
3358 FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => import_name,
3359 }
3360 },
3361 implements.as_deref(),
3362 );
3363
3364 let mut import_resource_map = ResourceMap::new();
3366
3367 self.create_resource_fn_map(func, func_ty, &mut import_resource_map);
3368
3369 let (callee_name, call_type) = match func.kind {
3370 FunctionKind::Freestanding => (
3371 self.bindgen
3372 .local_names
3373 .get_or_create(
3374 format!(
3375 "import:{import}-{maybe_iface_member}-{func_name}",
3376 import = import_specifier,
3377 maybe_iface_member = maybe_iface_member.as_deref().unwrap_or(""),
3378 func_name = func.name
3379 ),
3380 &func.name,
3381 )
3382 .0
3383 .to_string(),
3384 CallType::Standard,
3385 ),
3386
3387 FunctionKind::AsyncFreestanding => (
3388 self.bindgen
3389 .local_names
3390 .get_or_create(
3391 format!(
3392 "import:async-{import}-{maybe_iface_member}-{func_name}",
3393 import = import_specifier,
3394 maybe_iface_member = maybe_iface_member.as_deref().unwrap_or(""),
3395 func_name = func.name
3396 ),
3397 &func.name,
3398 )
3399 .0
3400 .to_string(),
3401 CallType::AsyncStandard,
3402 ),
3403
3404 FunctionKind::Method(_) => (
3405 func.item_name().to_lower_camel_case(),
3406 CallType::CalleeResourceDispatch,
3407 ),
3408
3409 FunctionKind::AsyncMethod(_) => (
3410 func.item_name().to_lower_camel_case(),
3411 CallType::AsyncCalleeResourceDispatch,
3412 ),
3413
3414 FunctionKind::Static(resource_id) => (
3415 format!(
3416 "{}.{}",
3417 self.imported_resource_name(*import_index, resource_id),
3418 func.item_name().to_lower_camel_case()
3419 ),
3420 CallType::Standard,
3421 ),
3422
3423 FunctionKind::AsyncStatic(resource_id) => (
3424 format!(
3425 "{}.{}",
3426 self.imported_resource_name(*import_index, resource_id),
3427 func.item_name().to_lower_camel_case()
3428 ),
3429 CallType::AsyncStandard,
3430 ),
3431
3432 FunctionKind::Constructor(resource_id) => (
3433 format!(
3434 "new {}",
3435 self.imported_resource_name(*import_index, resource_id)
3436 ),
3437 CallType::Standard,
3438 ),
3439 };
3440
3441 let abi = if options.async_ {
3447 AbiVariant::GuestImportAsync
3448 } else {
3449 AbiVariant::GuestImport
3450 };
3451
3452 let core_ty = self.types[options.core_type].unwrap_func();
3457 let wasm_signature = self.resolve.wasm_signature(abi, func);
3458 assert_eq!(wasm_signature.params.len(), core_ty.params().len());
3459 assert_eq!(wasm_signature.results.len(), core_ty.results().len());
3460 let nparams = core_ty.params().len();
3461
3462 let trampoline_idx = trampoline.as_u32();
3464 match self.bindgen.opts.import_bindings {
3465 None | Some(BindingsMode::Js) | Some(BindingsMode::Hybrid) => {
3466 if is_async | requires_async_porcelain {
3468 uwrite!(
3473 self.src.js,
3474 "\nconst _trampoline{trampoline_idx} = async function"
3475 );
3476 } else {
3477 uwrite!(
3478 self.src.js,
3479 "\nconst _trampoline{trampoline_idx} = function"
3480 );
3481 }
3482
3483 let iface_name = if import_name.is_empty() {
3484 None
3485 } else {
3486 Some(import_name.to_string())
3487 };
3488
3489 self.bindgen(JsFunctionBindgenArgs {
3491 nparams,
3492 call_type,
3493 iface_name: iface_name.as_deref(),
3494 callee: &callee_name,
3495 opts: options,
3496 func,
3497 resource_map: &import_resource_map,
3498 abi,
3499 requires_async_porcelain,
3500 is_async,
3501 wrap_async_future_result: false,
3502 for_import: true,
3503 });
3504 uwriteln!(self.src.js, "");
3505
3506 uwriteln!(
3507 self.src.js,
3508 "_trampoline{trampoline_idx}.fnName = '{}#{callee_name}';",
3509 iface_name.unwrap_or_default(),
3510 );
3511
3512 if requires_async_porcelain {
3514 uwriteln!(
3515 self.src.js,
3516 "_trampoline{trampoline_idx}.manuallyAsync = true;"
3517 );
3518 }
3519 }
3520
3521 Some(BindingsMode::Optimized) | Some(BindingsMode::DirectOptimized) => {
3522 uwriteln!(self.src.js, "let trampoline{trampoline_idx};");
3523 }
3524 };
3525
3526 if !matches!(
3531 self.bindgen.opts.import_bindings,
3532 None | Some(BindingsMode::Js)
3533 ) {
3534 let (memory, realloc) =
3535 if let CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions {
3536 memory,
3537 realloc,
3538 }) = options.data_model
3539 {
3540 (
3541 memory.map(|idx| format!(" memory: memory{},", idx.as_u32())),
3542 realloc.map(|idx| format!(" realloc: realloc{},", idx.as_u32())),
3543 )
3544 } else {
3545 (None, None)
3546 };
3547 let memory = memory.unwrap_or_default();
3548 let realloc = realloc.unwrap_or_default();
3549
3550 let post_return = options
3551 .post_return
3552 .map(|idx| format!(" postReturn: postReturn{},", idx.as_u32()))
3553 .unwrap_or("".into());
3554 let string_encoding = match options.string_encoding {
3555 wasmtime_environ::component::StringEncoding::Utf8 => "",
3556 wasmtime_environ::component::StringEncoding::Utf16 => " stringEncoding: 'utf16',",
3557 wasmtime_environ::component::StringEncoding::CompactUtf16 => {
3558 " stringEncoding: 'compact-utf16',"
3559 }
3560 };
3561
3562 let callee_name = match func.kind {
3563 FunctionKind::Constructor(_) => callee_name[4..].to_string(),
3564
3565 FunctionKind::Static(_)
3566 | FunctionKind::AsyncStatic(_)
3567 | FunctionKind::Freestanding
3568 | FunctionKind::AsyncFreestanding => callee_name.to_string(),
3569
3570 FunctionKind::Method(resource_id) | FunctionKind::AsyncMethod(resource_id) => {
3571 format!(
3572 "{}.prototype.{callee_name}",
3573 self.imported_resource_name(*import_index, resource_id)
3574 )
3575 }
3576 };
3577
3578 self.resource_imports.extend(import_resource_map.clone());
3580
3581 let resource_tables = {
3582 let mut resource_table_ids: Vec<TypeResourceTableIndex> = Vec::new();
3583
3584 for (_, data) in import_resource_map {
3585 let ResourceTable {
3586 data: ResourceData::Host { tid, .. },
3587 ..
3588 } = &data
3589 else {
3590 unreachable!("unexpected non-host resource table");
3591 };
3592 resource_table_ids.push(*tid);
3593 }
3594
3595 if resource_table_ids.is_empty() {
3596 "".to_string()
3597 } else {
3598 format!(
3599 " resourceTables: [{}],",
3600 resource_table_ids
3601 .iter()
3602 .map(|x| format!("handleTable{}", x.as_u32()))
3603 .collect::<Vec<String>>()
3604 .join(", ")
3605 )
3606 }
3607 };
3608
3609 match self.bindgen.opts.import_bindings {
3611 Some(BindingsMode::Hybrid) => {
3612 let symbol_cabi_lower = self.bindgen.intrinsic(Intrinsic::SymbolCabiLower);
3613 uwriteln!(self.src.js_init, "if ({callee_name}[{symbol_cabi_lower}]) {{
3614 trampoline{} = {callee_name}[{symbol_cabi_lower}]({{{memory}{realloc}{post_return}{string_encoding}{resource_tables}}});
3615 }}", trampoline.as_u32());
3616 }
3617 Some(BindingsMode::Optimized) => {
3618 let symbol_cabi_lower = self.bindgen.intrinsic(Intrinsic::SymbolCabiLower);
3619 if !self.bindgen.opts.valid_lifting_optimization {
3620 uwriteln!(self.src.js_init, "if (!{callee_name}[{symbol_cabi_lower}]) {{
3621 throw new TypeError('import for \"{import_name}\" does not define a Symbol.for(\"cabiLower\") optimized binding');
3622 }}");
3623 }
3624 uwriteln!(
3625 self.src.js_init,
3626 "trampoline{} = {callee_name}[{symbol_cabi_lower}]({{{memory}{realloc}{post_return}{string_encoding}{resource_tables}}});",
3627 trampoline.as_u32()
3628 );
3629 }
3630 Some(BindingsMode::DirectOptimized) => {
3631 uwriteln!(
3632 self.src.js_init,
3633 "trampoline{} = {callee_name}({{{memory}{realloc}{post_return}{string_encoding}}});",
3634 trampoline.as_u32()
3635 );
3636 }
3637 None | Some(BindingsMode::Js) => unreachable!("invalid bindings mode"),
3638 };
3639 }
3640
3641 let (import_name, binding_name) = match func.kind {
3643 FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
3644 (func_name.to_lower_camel_case(), callee_name)
3645 }
3646
3647 FunctionKind::Method(tid)
3648 | FunctionKind::AsyncMethod(tid)
3649 | FunctionKind::Static(tid)
3650 | FunctionKind::AsyncStatic(tid)
3651 | FunctionKind::Constructor(tid) => {
3652 let ty = &self.resolve.types[tid];
3653 let class_name = ty.name.as_ref().unwrap().to_upper_camel_case();
3654 let resource_name = self.imported_resource_name(*import_index, tid);
3655 (class_name, resource_name)
3656 }
3657 };
3658
3659 self.ensure_import(
3660 import_specifier,
3661 iface_name,
3662 maybe_iface_member.as_deref(),
3663 if iface_name.is_some() {
3664 Some(import_name.to_string())
3665 } else {
3666 None
3667 },
3668 binding_name,
3669 );
3670 }
3671
3672 fn ensure_import(
3683 &mut self,
3684 import_specifier: String,
3685 iface_name: Option<&str>,
3686 iface_member: Option<&str>,
3687 import_binding: Option<String>,
3688 local_name: String,
3689 ) {
3690 if import_specifier.starts_with("webidl:") {
3691 self.bindgen
3692 .intrinsic(Intrinsic::WebIdl(WebIdlIntrinsic::GlobalThisIdlProxy));
3693 }
3694
3695 let mut import_path = Vec::with_capacity(2);
3697 import_path.push(import_specifier);
3698 if let Some(_iface_name) = iface_name {
3699 if let Some(iface_member) = iface_member {
3702 import_path.push(iface_member.to_lower_camel_case());
3703 }
3704 import_path.push(import_binding.clone().unwrap());
3705 } else if let Some(iface_member) = iface_member {
3706 import_path.push(iface_member.into());
3707 } else if let Some(import_binding) = &import_binding {
3708 import_path.push(import_binding.into());
3709 }
3710
3711 self.bindgen
3713 .esm_bindgen
3714 .add_import_binding(&import_path, local_name);
3715 }
3716
3717 fn connect_p3_resources(
3726 &mut self,
3727 id: &TypeId,
3728 maybe_elem_ty: &Option<Type>,
3729 iface_ty: &InterfaceType,
3730 resource_map: &mut ResourceMap,
3731 ) {
3732 let remote_resource = match iface_ty {
3733 InterfaceType::Future(table_idx) => {
3734 let future_table_ty = &self.types[*table_idx];
3735 let future_ty = &self.types[future_table_ty.ty];
3736
3737 let mut future_nesting_level = 0;
3739 let mut payload_ty = future_ty.payload;
3740 while let Some(InterfaceType::Future(inner_ty)) = payload_ty {
3741 future_nesting_level += 1;
3742 payload_ty = self.types[self.types[inner_ty].ty].payload;
3743 }
3744
3745 ResourceTable {
3746 imported: true,
3747 data: ResourceData::Guest {
3748 resource_name: "Future".into(),
3749 prefix: Some(format!("${}", table_idx.as_u32())),
3750 extra: Some(ResourceExtraData::Future {
3751 table_idx: *table_idx,
3752 nesting_level: future_nesting_level,
3753 elem_ty: maybe_elem_ty.map(|ty| {
3754 let table_ty = &self.types[*table_idx];
3755 let future_ty_idx = table_ty.ty;
3756 let future_ty = &self.types[future_ty_idx];
3757 let iface_ty = future_ty.payload.expect(
3758 "missing future payload despite elem type being present",
3759 );
3760 let abi = self.types.canonical_abi(&iface_ty);
3761 PayloadTypeMetadata {
3762 ty,
3763 iface_ty,
3764
3765 lift_js_expr: gen_flat_lift_fn_js_expr(
3772 self,
3773 &iface_ty,
3774 &Some(resource_map),
3775 ),
3776 lower_js_expr: gen_flat_lower_fn_js_expr(
3777 self,
3778 &iface_ty,
3779 &Some(resource_map),
3780 ),
3781 size32: abi.size32,
3782 align32: abi.align32,
3783 flat_count: abi.flat_count,
3784 }
3785 }),
3786 }),
3787 },
3788 }
3789 }
3790 InterfaceType::Stream(table_idx) => ResourceTable {
3791 imported: true,
3792 data: ResourceData::Guest {
3793 resource_name: "Stream".into(),
3794 prefix: Some(format!("${}", table_idx.as_u32())),
3795 extra: Some(ResourceExtraData::Stream {
3796 table_idx: *table_idx,
3797 elem_ty: maybe_elem_ty.map(|ty| {
3798 let table_ty = &self.types[*table_idx];
3799 let stream_ty_idx = table_ty.ty;
3800 let stream_ty = &self.types[stream_ty_idx];
3801 let iface_ty = stream_ty
3802 .payload
3803 .expect("missing payload despite elem type being present");
3804 let abi = self.types.canonical_abi(&iface_ty);
3805 PayloadTypeMetadata {
3806 ty,
3807 iface_ty,
3808 lift_js_expr: gen_flat_lift_fn_js_expr(
3809 self,
3810 &iface_ty,
3811 &Some(resource_map),
3812 ),
3813 lower_js_expr: gen_flat_lower_fn_js_expr(
3814 self,
3815 &iface_ty,
3816 &Some(resource_map),
3817 ),
3818 size32: abi.size32,
3819 align32: abi.align32,
3820 flat_count: abi.flat_count,
3821 }
3822 }),
3823 }),
3824 },
3825 },
3826 InterfaceType::ErrorContext(table_idx) => ResourceTable {
3827 imported: true,
3828 data: ResourceData::Guest {
3829 resource_name: "ErrorContext".into(),
3830 prefix: Some(format!("${}", table_idx.as_u32())),
3831 extra: Some(ResourceExtraData::ErrorContext {
3832 table_idx: *table_idx,
3833 }),
3834 },
3835 },
3836 _ => unreachable!("unexpected interface type [{iface_ty:?}] with no type"),
3837 };
3838
3839 resource_map.insert(*id, remote_resource);
3840 }
3841
3842 fn connect_host_resource(
3851 &mut self,
3852 t: TypeId,
3853 resource_table_ty_idx: TypeResourceTableIndex,
3854 resource_map: &mut ResourceMap,
3855 ) {
3856 self.ensure_resource_table(resource_table_ty_idx);
3857
3858 let resource_table_ty = &self.types[resource_table_ty_idx];
3860 let resource_idx = resource_table_ty.unwrap_concrete_ty();
3861 let imported = self
3862 .component
3863 .defined_resource_index(resource_idx)
3864 .is_none();
3865
3866 let resource_id = crate::dealias(self.resolve, t);
3868 let ty = &self.resolve.types[resource_id];
3869
3870 let mut dtor_str = None;
3873 if let Some(resource_idx) = self.component.defined_resource_index(resource_idx) {
3874 assert!(!imported);
3875 let resource_def = self
3876 .component
3877 .initializers
3878 .iter()
3879 .find_map(|i| match i {
3880 GlobalInitializer::Resource(r) if r.index == resource_idx => Some(r),
3881 _ => None,
3882 })
3883 .unwrap();
3884
3885 if let Some(dtor) = &resource_def.dtor {
3886 dtor_str = Some(self.core_def(dtor));
3887 }
3888 }
3889
3890 let resource_name = ty.name.as_ref().unwrap().to_upper_camel_case();
3892
3893 let local_name = if imported {
3894 let imported_resource_entry = self.find_import_providing_resource(resource_idx);
3897
3898 let (world_key, iface_name) = match imported_resource_entry {
3899 Some((imp_name, _is_from_instance @ true)) => {
3902 let key = self.imports[imp_name].clone();
3903 let iface_name = match &key {
3904 WorldKey::Name(name) => Some(name.clone()),
3905 WorldKey::Interface(_) => {
3906 match &self.resolve.worlds[self.world].imports[&key] {
3907 WorldItem::Interface { id, .. } => {
3908 self.resolve.interfaces[*id].name.clone()
3909 }
3910 _ => None,
3911 }
3912 }
3913 };
3914 (key, iface_name)
3915 }
3916 Some((imp_name, _is_from_instance @ false)) => {
3918 (self.imports[imp_name].clone(), None)
3919 }
3920 None => match ty.owner {
3924 wit_parser::TypeOwner::World(world) => (
3925 self.resolve.worlds[world]
3926 .imports
3927 .iter()
3928 .find(
3929 |&(_, item)| matches!(item, WorldItem::Type { id, .. } if *id == t),
3930 )
3931 .unwrap()
3932 .0
3933 .clone(),
3934 None,
3935 ),
3936 wit_parser::TypeOwner::Interface(iface) => {
3937 let key = self.resolve.worlds[self.world]
3938 .imports
3939 .iter()
3940 .find(|&(_, item)| match item {
3941 WorldItem::Interface { id, .. } => *id == iface,
3942 _ => false,
3943 })
3944 .map(|(key, _)| key)
3945 .unwrap_or_else(|| {
3946 panic!(
3947 "unable to find world import for interface [{}]",
3948 self.resolve.interfaces[iface]
3949 .name
3950 .as_deref()
3951 .unwrap_or("<unnamed>")
3952 )
3953 });
3954 (
3955 key.clone(),
3956 match key {
3957 WorldKey::Name(name) => Some(name.clone()),
3958 WorldKey::Interface(_) => {
3959 self.resolve.interfaces[iface].name.clone()
3960 }
3961 },
3962 )
3963 }
3964 wit_parser::TypeOwner::None => unimplemented!(),
3965 },
3966 };
3967 let iface_name = iface_name.as_deref();
3968
3969 let import_name = self.resolve.name_world_key(&world_key);
3970 let implements = self.resolve.worlds[self.world]
3971 .imports
3972 .get(&world_key)
3973 .and_then(|item| self.resolve.implements_value(&world_key, item));
3974 let (local_name, _) = self
3975 .bindgen
3976 .local_names
3977 .get_or_create(resource_idx, &resource_name);
3978
3979 let local_name_str = local_name.to_string();
3980
3981 let (import_specifier, maybe_iface_member) = map_import_with_implements(
3985 &self.bindgen.opts.map,
3986 &import_name,
3987 implements.as_deref(),
3988 );
3989
3990 self.ensure_import(
3992 import_specifier,
3993 iface_name,
3994 maybe_iface_member.as_deref(),
3995 iface_name.map(|_| resource_name),
3996 local_name_str.to_string(),
3997 );
3998 local_name_str
3999 } else {
4000 let (local_name, _) = self
4001 .bindgen
4002 .local_names
4003 .get_or_create(resource_idx, &resource_name);
4004 local_name.to_string()
4005 };
4006
4007 let entry = ResourceTable {
4009 imported,
4010 data: ResourceData::Host {
4011 tid: resource_table_ty_idx,
4012 rid: resource_idx,
4013 local_name,
4014 dtor_name: dtor_str,
4015 },
4016 };
4017
4018 if let Some(existing) = resource_map.get(&resource_id) {
4021 if *existing != entry {
4027 assert!(
4028 imported && existing.imported,
4029 "conflicting resource tables for non-imported resource"
4030 );
4031 }
4032 return;
4033 }
4034
4035 resource_map.insert(resource_id, entry);
4037 }
4038
4039 fn connect_resource_types(
4052 &mut self,
4053 id: TypeId,
4054 iface_ty: &InterfaceType,
4055 resource_map: &mut ResourceMap,
4056 ) {
4057 let kind = &self.resolve.types[id].kind;
4058 match (kind, iface_ty) {
4059 (TypeDefKind::Flags(_), InterfaceType::Flags(_))
4061 | (TypeDefKind::Enum(_), InterfaceType::Enum(_)) => {}
4062
4063 (TypeDefKind::Record(t1), InterfaceType::Record(t2)) => {
4065 let t2 = &self.types[*t2];
4066 for (f1, f2) in t1.fields.iter().zip(t2.fields.iter()) {
4067 if let Type::Id(id) = f1.ty {
4068 self.connect_resource_types(id, &f2.ty, resource_map);
4069 }
4070 }
4071 }
4072
4073 (
4075 TypeDefKind::Handle(Handle::Own(t1) | Handle::Borrow(t1)),
4076 InterfaceType::Own(t2) | InterfaceType::Borrow(t2),
4077 ) => {
4078 self.connect_host_resource(*t1, *t2, resource_map);
4079 }
4080
4081 (TypeDefKind::Tuple(t1), InterfaceType::Tuple(t2)) => {
4083 let t2 = &self.types[*t2];
4084 for (f1, f2) in t1.types.iter().zip(t2.types.iter()) {
4085 if let Type::Id(id) = f1 {
4086 self.connect_resource_types(*id, f2, resource_map);
4087 }
4088 }
4089 }
4090
4091 (TypeDefKind::Variant(t1), InterfaceType::Variant(t2)) => {
4093 let t2 = &self.types[*t2];
4094 for (f1, f2) in t1.cases.iter().zip(t2.cases.iter()) {
4095 if let Some(Type::Id(id)) = &f1.ty {
4096 self.connect_resource_types(*id, f2.1.as_ref().unwrap(), resource_map);
4097 }
4098 }
4099 }
4100
4101 (TypeDefKind::Option(t1), InterfaceType::Option(t2)) => {
4103 let t2 = &self.types[*t2];
4104 if let Type::Id(id) = t1 {
4105 self.connect_resource_types(*id, &t2.ty, resource_map);
4106 }
4107 }
4108
4109 (TypeDefKind::Result(t1), InterfaceType::Result(t2)) => {
4111 let t2 = &self.types[*t2];
4112 if let Some(Type::Id(id)) = &t1.ok {
4113 self.connect_resource_types(*id, &t2.ok.unwrap(), resource_map);
4114 }
4115 if let Some(Type::Id(id)) = &t1.err {
4116 self.connect_resource_types(*id, &t2.err.unwrap(), resource_map);
4117 }
4118 }
4119
4120 (TypeDefKind::List(t1), InterfaceType::List(t2)) => {
4122 let t2 = &self.types[*t2];
4123 if let Type::Id(id) = t1 {
4124 self.connect_resource_types(*id, &t2.element, resource_map);
4125 }
4126 }
4127
4128 (TypeDefKind::Map(key, value), InterfaceType::Map(map)) => {
4130 let map = &self.types[*map];
4131 if let Type::Id(id) = key {
4132 self.connect_resource_types(*id, &map.key, resource_map);
4133 }
4134 if let Type::Id(id) = value {
4135 self.connect_resource_types(*id, &map.value, resource_map);
4136 }
4137 }
4138
4139 (TypeDefKind::FixedLengthList(t1, _len), InterfaceType::FixedLengthList(t2)) => {
4141 let t2 = &self.types[*t2];
4142 if let Type::Id(id) = t1 {
4143 self.connect_resource_types(*id, &t2.element, resource_map);
4144 }
4145 }
4146
4147 (TypeDefKind::Type(ty), _) => {
4149 if let Type::Id(id) = ty {
4150 self.connect_resource_types(*id, iface_ty, resource_map);
4151 }
4152 }
4153
4154 (TypeDefKind::Future(maybe_elem_ty), container_iface_ty)
4156 | (TypeDefKind::Stream(maybe_elem_ty), container_iface_ty) => {
4157 match maybe_elem_ty {
4158 None => {
4161 self.connect_p3_resources(&id, maybe_elem_ty, iface_ty, resource_map);
4162 }
4163 Some(elem_ty @ Type::Id(elem_ty_id)) => {
4165 let maybe_elem_iface_ty = match container_iface_ty {
4170 InterfaceType::Future(future_table_ty_idx) => {
4171 let future_table_ty = &self.types[*future_table_ty_idx];
4172 let future = &self.types[future_table_ty.ty];
4173 future.payload
4174 }
4175 InterfaceType::Stream(stream_table_ty_idx) => {
4176 let stream_table_ty = &self.types[*stream_table_ty_idx];
4177 let stream = &self.types[stream_table_ty.ty];
4178 stream.payload
4179 }
4180 _ => unreachable!("unexpected iface type"),
4181 };
4182 if let Some(elem_iface_ty) = maybe_elem_iface_ty {
4183 self.connect_resource_types(*elem_ty_id, &elem_iface_ty, resource_map);
4191 }
4192
4193 self.connect_p3_resources(&id, &Some(*elem_ty), iface_ty, resource_map);
4194 }
4195 Some(_) => {
4197 self.connect_p3_resources(&id, maybe_elem_ty, iface_ty, resource_map);
4198 }
4199 }
4200 }
4201
4202 (
4204 TypeDefKind::Result(Result_ { ok, err }),
4205 tk2 @ (InterfaceType::Future(_) | InterfaceType::Stream(_)),
4206 ) => {
4207 if let Some(Type::Id(ok_t)) = ok {
4208 self.connect_resource_types(*ok_t, tk2, resource_map)
4209 }
4210 if let Some(Type::Id(err_t)) = err {
4211 self.connect_resource_types(*err_t, tk2, resource_map)
4212 }
4213 }
4214
4215 (
4217 TypeDefKind::Option(ty),
4218 tk2 @ (InterfaceType::Future(_) | InterfaceType::Stream(_)),
4219 ) => {
4220 if let Type::Id(some_t) = ty {
4221 self.connect_resource_types(*some_t, tk2, resource_map)
4222 }
4223 }
4224
4225 (
4227 TypeDefKind::Handle(Handle::Own(t1) | Handle::Borrow(t1)),
4228 tk2 @ (InterfaceType::Future(_) | InterfaceType::Stream(_)),
4229 ) => self.connect_resource_types(*t1, tk2, resource_map),
4230
4231 (TypeDefKind::Resource, InterfaceType::Future(_) | InterfaceType::Stream(_)) => {}
4232
4233 (
4235 TypeDefKind::Variant(variant),
4236 tk2 @ (InterfaceType::Future(_) | InterfaceType::Stream(_)),
4237 ) => {
4238 for f1 in variant.cases.iter() {
4239 if let Some(Type::Id(id)) = &f1.ty {
4240 self.connect_resource_types(*id, tk2, resource_map);
4241 }
4242 }
4243 }
4244
4245 (
4247 TypeDefKind::Record(record),
4248 tk2 @ (InterfaceType::Future(_) | InterfaceType::Stream(_)),
4249 ) => {
4250 for f1 in record.fields.iter() {
4251 if let Type::Id(id) = f1.ty {
4252 self.connect_resource_types(id, tk2, resource_map);
4253 }
4254 }
4255 }
4256
4257 (
4260 TypeDefKind::Enum(_) | TypeDefKind::Flags(_),
4261 InterfaceType::Future(_) | InterfaceType::Stream(_),
4262 ) => {}
4263
4264 (TypeDefKind::Resource, tk2) => {
4265 unreachable!(
4266 "resource types do not need to be connected (in this case, to [{tk2:?}])"
4267 )
4268 }
4269
4270 (TypeDefKind::Unknown, tk2) => {
4271 unreachable!("unknown types cannot be connected (in this case to [{tk2:?}])")
4272 }
4273
4274 (tk1, tk2) => unreachable!("invalid typedef kind combination [{tk1:?}] [{tk2:?}]",),
4275 }
4276 }
4277
4278 fn bindgen(&mut self, args: JsFunctionBindgenArgs) {
4279 let JsFunctionBindgenArgs {
4280 nparams,
4281 call_type,
4282 iface_name,
4283 callee,
4284 opts,
4285 func,
4286 resource_map,
4287 abi,
4288 requires_async_porcelain,
4289 is_async,
4290 wrap_async_future_result,
4291 for_import,
4292 } = args;
4293
4294 let (memory, realloc) =
4295 if let CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions {
4296 memory,
4297 realloc,
4298 }) = opts.data_model
4299 {
4300 (
4301 memory.map(|idx| format!("memory{}", idx.as_u32())),
4302 realloc.map(|idx| {
4303 format!(
4304 "realloc{}{}",
4305 idx.as_u32(),
4306 if is_async {
4307 "Async"
4308 } else {
4309 Default::default()
4310 }
4311 )
4312 }),
4313 )
4314 } else {
4315 (None, None)
4316 };
4317
4318 let post_return = opts.post_return.map(|idx| {
4319 format!(
4320 "postReturn{}{}",
4321 idx.as_u32(),
4322 if is_async {
4323 "Async"
4324 } else {
4325 Default::default()
4326 }
4327 )
4328 });
4329
4330 let tracing_prefix = format!(
4331 "[iface=\"{}\", function=\"{}\"]",
4332 iface_name.unwrap_or("<no iface>"),
4333 func.name
4334 );
4335
4336 self.src.js("(");
4340 let mut params = Vec::new();
4341 let mut first = true;
4342 for i in 0..nparams {
4343 if i == 0
4344 && matches!(
4345 call_type,
4346 CallType::FirstArgIsThis | CallType::AsyncFirstArgIsThis
4347 )
4348 {
4349 params.push("this".into());
4350 continue;
4351 }
4352 if !first {
4353 self.src.js(", ");
4354 } else {
4355 first = false;
4356 }
4357 let param = format!("arg{i}");
4358 self.src.js(¶m);
4359 params.push(param);
4360 }
4361 uwriteln!(self.src.js, ") {{");
4362 if wrap_async_future_result {
4363 let future_value = self.bindgen.intrinsic(Intrinsic::AsyncFuture(
4364 AsyncFutureIntrinsic::FutureValueClass,
4365 ));
4366 uwriteln!(
4367 self.src.js,
4368 "return new {future_value}(() => (async () => {{"
4369 );
4370 }
4371
4372 if self.bindgen.opts.tracing {
4374 let event_fields = func
4375 .params
4376 .iter()
4377 .enumerate()
4378 .map(|(i, p)| format!("{}=${{arguments[{i}]}}", p.name))
4379 .collect::<Vec<String>>();
4380 uwriteln!(
4381 self.src.js,
4382 "console.error(`{tracing_prefix} call {}`);",
4383 event_fields.join(", ")
4384 );
4385 }
4386
4387 if self.bindgen.opts.tla_compat
4389 && matches!(abi, AbiVariant::GuestExport)
4390 && self.bindgen.opts.instantiation_mode.is_none()
4391 {
4392 let throw_uninitialized = self.bindgen.intrinsic(Intrinsic::ThrowUninitialized);
4393 uwrite!(
4394 self.src.js,
4395 "\
4396 if (!_initialized) {throw_uninitialized}();
4397 "
4398 );
4399 }
4400
4401 let mut f = FunctionBindgen {
4403 resource_map,
4404 clear_resource_borrows: false,
4405 intrinsics: &mut self.bindgen.all_intrinsics,
4406 valid_lifting_optimization: self.bindgen.opts.valid_lifting_optimization,
4407 flags_as_bigint: self.bindgen.opts.flags_as_bigint,
4408 enum_values_screaming_snake_case: self.bindgen.opts.enum_values_screaming_snake_case,
4409 sizes: &self.sizes,
4410 err: if get_thrown_type(self.resolve, func.result).is_some() {
4411 match abi {
4412 AbiVariant::GuestExport
4413 | AbiVariant::GuestExportAsync
4414 | AbiVariant::GuestExportAsyncStackful => ErrHandling::ThrowResultErr,
4415 AbiVariant::GuestImport | AbiVariant::GuestImportAsync => {
4416 ErrHandling::ResultCatchHandler
4417 }
4418 }
4419 } else {
4420 ErrHandling::None
4421 },
4422 block_storage: Vec::new(),
4423 blocks: Vec::new(),
4424 callee,
4425 callee_resource_dynamic: matches!(
4426 call_type,
4427 CallType::CalleeResourceDispatch | CallType::AsyncCalleeResourceDispatch
4428 ),
4429 memory: memory.as_ref(),
4430 realloc: realloc.as_ref(),
4431 tmp: 0,
4432 params,
4433 post_return: post_return.as_ref(),
4434 tracing_prefix: &tracing_prefix,
4435 tracing_enabled: self.bindgen.opts.tracing,
4436 no_component_error_wrapping: self.bindgen.opts.no_component_error_wrapping,
4437 encoding: match opts.string_encoding {
4438 wasmtime_environ::component::StringEncoding::Utf8 => StringEncoding::UTF8,
4439 wasmtime_environ::component::StringEncoding::Utf16 => StringEncoding::UTF16,
4440 wasmtime_environ::component::StringEncoding::CompactUtf16 => {
4441 StringEncoding::CompactUTF16
4442 }
4443 },
4444 src: source::Source::default(),
4445 resolve: self.resolve,
4446 requires_async_porcelain,
4447 is_async,
4448 wrap_async_future_result,
4449 iface_name,
4450 asmjs: self.bindgen.opts.asmjs,
4451 component_state: Some(FunctionBindgenComponentState {
4452 component_idx: opts.instance,
4453 realloc_fn_idx: if let CanonicalOptionsDataModel::LinearMemory(
4454 LinearMemoryOptions { realloc, .. },
4455 ) = opts.data_model
4456 {
4457 realloc
4458 } else {
4459 None
4460 },
4461 memory_idx: opts.memory(),
4462 callback_fn_idx: opts.callback,
4463 }),
4464 for_import: Some(for_import),
4465 };
4466
4467 let is_guest_export = matches!(
4468 abi,
4469 AbiVariant::GuestExport
4470 | AbiVariant::GuestExportAsync
4471 | AbiVariant::GuestExportAsyncStackful
4472 );
4473 if is_guest_export {
4474 f.start_wasm_export_task();
4475 f.begin_wasm_export_body();
4476 }
4477
4478 abi::call(
4481 self.resolve,
4482 abi,
4483 match abi {
4484 AbiVariant::GuestImport | AbiVariant::GuestImportAsync => {
4485 LiftLower::LiftArgsLowerResults
4486 }
4487 AbiVariant::GuestExport
4488 | AbiVariant::GuestExportAsync
4489 | AbiVariant::GuestExportAsyncStackful => LiftLower::LowerArgsLiftResults,
4490 },
4491 func,
4492 &mut f,
4493 is_async,
4494 );
4495
4496 if is_guest_export {
4497 f.end_wasm_export_body();
4498 }
4499
4500 self.src.js(&f.src);
4502 if wrap_async_future_result {
4503 self.src.js("})());");
4504 }
4505
4506 self.src.js("}");
4508 }
4509
4510 fn augmented_import_def(&mut self, def: &core::AugmentedImport<'_>) -> String {
4511 match def {
4512 core::AugmentedImport::CoreDef(def) => self.core_def(def),
4513 core::AugmentedImport::Memory { mem, op } => {
4514 let mem = self.core_def(mem);
4515 match op {
4516 core::AugmentedOp::I32Load => {
4517 format!(
4518 "(ptr, off) => new DataView({mem}.buffer).getInt32(ptr + off, true)"
4519 )
4520 }
4521 core::AugmentedOp::I32Load8U => {
4522 format!(
4523 "(ptr, off) => new DataView({mem}.buffer).getUint8(ptr + off, true)"
4524 )
4525 }
4526 core::AugmentedOp::I32Load8S => {
4527 format!("(ptr, off) => new DataView({mem}.buffer).getInt8(ptr + off, true)")
4528 }
4529 core::AugmentedOp::I32Load16U => {
4530 format!(
4531 "(ptr, off) => new DataView({mem}.buffer).getUint16(ptr + off, true)"
4532 )
4533 }
4534 core::AugmentedOp::I32Load16S => {
4535 format!(
4536 "(ptr, off) => new DataView({mem}.buffer).getInt16(ptr + off, true)"
4537 )
4538 }
4539 core::AugmentedOp::I64Load => {
4540 format!(
4541 "(ptr, off) => new DataView({mem}.buffer).getBigInt64(ptr + off, true)"
4542 )
4543 }
4544 core::AugmentedOp::F32Load => {
4545 format!(
4546 "(ptr, off) => new DataView({mem}.buffer).getFloat32(ptr + off, true)"
4547 )
4548 }
4549 core::AugmentedOp::F64Load => {
4550 format!(
4551 "(ptr, off) => new DataView({mem}.buffer).getFloat64(ptr + off, true)"
4552 )
4553 }
4554 core::AugmentedOp::I32Store8 => {
4555 format!(
4556 "(ptr, val, offset) => {{
4557 new DataView({mem}.buffer).setInt8(ptr + offset, val, true);
4558 }}"
4559 )
4560 }
4561 core::AugmentedOp::I32Store16 => {
4562 format!(
4563 "(ptr, val, offset) => {{
4564 new DataView({mem}.buffer).setInt16(ptr + offset, val, true);
4565 }}"
4566 )
4567 }
4568 core::AugmentedOp::I32Store => {
4569 format!(
4570 "(ptr, val, offset) => {{
4571 new DataView({mem}.buffer).setInt32(ptr + offset, val, true);
4572 }}"
4573 )
4574 }
4575 core::AugmentedOp::I64Store => {
4576 format!(
4577 "(ptr, val, offset) => {{
4578 new DataView({mem}.buffer).setBigInt64(ptr + offset, val, true);
4579 }}"
4580 )
4581 }
4582 core::AugmentedOp::F32Store => {
4583 format!(
4584 "(ptr, val, offset) => {{
4585 new DataView({mem}.buffer).setFloat32(ptr + offset, val, true);
4586 }}"
4587 )
4588 }
4589 core::AugmentedOp::F64Store => {
4590 format!(
4591 "(ptr, val, offset) => {{
4592 new DataView({mem}.buffer).setFloat64(ptr + offset, val, true);
4593 }}"
4594 )
4595 }
4596 core::AugmentedOp::MemorySize => {
4597 format!("ptr => {mem}.buffer.byteLength / 65536")
4598 }
4599 }
4600 }
4601 }
4602 }
4603
4604 fn core_def(&mut self, def: &CoreDef) -> String {
4605 match def {
4606 CoreDef::Export(e) => self.core_export_var_name(e),
4607 CoreDef::TaskMayBlock => self
4608 .bindgen
4609 .intrinsic(AsyncTaskIntrinsic::CurrentTaskMayBlock.into()),
4610 CoreDef::Trampoline(i) => format!("trampoline{}", i.as_u32()),
4611 CoreDef::InstanceFlags(i) => {
4612 self.used_instance_flags.borrow_mut().insert(*i);
4614 format!("instanceFlags{}", i.as_u32())
4615 }
4616 CoreDef::UnsafeIntrinsic(ui) => match ui {
4617 wasmtime_environ::component::UnsafeIntrinsic::ContextGetI32_0 => {
4618 let context_get_fn = self
4619 .bindgen
4620 .intrinsic(AsyncTaskIntrinsic::ContextGet.into());
4621 let component_idx = self.init_current_module.expect("missing current module");
4622 self.init_context_components
4623 .borrow_mut()
4624 .insert(component_idx);
4625 format!(
4626 "{context_get_fn}.bind(null, {{ componentIdx: {}, slot: 0 }})",
4627 component_idx.as_u32(),
4628 )
4629 }
4630 wasmtime_environ::component::UnsafeIntrinsic::ContextSetI32_0 => {
4631 let context_set_fn = self
4632 .bindgen
4633 .intrinsic(AsyncTaskIntrinsic::ContextSet.into());
4634 let component_idx = self.init_current_module.expect("missing current module");
4635 self.init_context_components
4636 .borrow_mut()
4637 .insert(component_idx);
4638 format!(
4639 "{context_set_fn}.bind(null, {{ componentIdx: {}, slot: 0 }})",
4640 component_idx.as_u32(),
4641 )
4642 }
4643 wasmtime_environ::component::UnsafeIntrinsic::ContextGetI32_1 => {
4644 let context_get_fn = self
4645 .bindgen
4646 .intrinsic(AsyncTaskIntrinsic::ContextGet.into());
4647 let component_idx = self.init_current_module.expect("missing current module");
4648 self.init_context_components
4649 .borrow_mut()
4650 .insert(component_idx);
4651 format!(
4652 "{context_get_fn}.bind(null, {{ componentIdx: {}, slot: 1 }})",
4653 component_idx.as_u32(),
4654 )
4655 }
4656 wasmtime_environ::component::UnsafeIntrinsic::ContextSetI32_1 => {
4657 let context_set_fn = self
4658 .bindgen
4659 .intrinsic(AsyncTaskIntrinsic::ContextSet.into());
4660 let component_idx = self.init_current_module.expect("missing current module");
4661 self.init_context_components
4662 .borrow_mut()
4663 .insert(component_idx);
4664 format!(
4665 "{context_set_fn}.bind(null, {{ componentIdx: {}, slot: 1 }})",
4666 component_idx.as_u32(),
4667 )
4668 }
4669
4670 ui => {
4672 let idx = ui.index();
4673 format!("unsafeIntrinsic{idx}")
4674 }
4675 },
4676 }
4677 }
4678
4679 fn core_export_var_name<T>(&self, export: &CoreExport<T>) -> String
4680 where
4681 T: Into<EntityIndex> + Copy,
4682 {
4683 let name = match &export.item {
4684 ExportItem::Index(idx) => {
4685 let module_idx = self
4686 .instances
4687 .get(export.instance)
4688 .expect("unexpectedly missing export instance");
4689 let module = &self
4690 .modules
4691 .get(*module_idx)
4692 .expect("unexpectedly missing module by idx");
4693 let idx = (*idx).into();
4694 module
4695 .exports()
4696 .iter()
4697 .find_map(|(name, i)| if *i == idx { Some(name) } else { None })
4698 .unwrap()
4699 .to_string()
4700 }
4701 ExportItem::Name(s) => s.to_string(),
4702 };
4703 let i = export.instance.as_u32() as usize;
4704 let quoted = maybe_quote_member(&name);
4705 format!("exports{i}{quoted}")
4706 }
4707
4708 fn process_imports(&mut self) {
4710 let mut import_resource_map = ResourceMap::new();
4711 for (_import_name, (import_idx, _import_path)) in self.component.imports.iter() {
4712 let (import_name, import_type_def) = &self.component.import_types[*import_idx];
4713 let import_world_key = &self
4714 .imports
4715 .get(import_name)
4716 .expect("missing import mapping");
4717 let import_world_item = &self
4718 .resolve
4719 .worlds
4720 .get(self.world)
4721 .expect("missing world")
4722 .imports
4723 .get(*import_world_key)
4724 .expect("missing import in world for import");
4725
4726 match import_world_item {
4728 WorldItem::Interface { id: iface_id, .. } => {
4729 let iface = &self.resolve.interfaces[*iface_id];
4730
4731 for (fn_name, iface_fn) in iface.functions.iter() {
4734 match import_type_def {
4735 ComponentExtern {
4736 ty: TypeDef::ComponentInstance(instance_ty),
4737 ..
4738 } => {
4739 if let Some(ComponentExtern {
4740 ty: TypeDef::ComponentFunc(type_func_index),
4741 ..
4742 }) = &self.types[*instance_ty].exports.get(fn_name)
4743 {
4744 self.create_resource_fn_map(
4745 iface_fn,
4746 *type_func_index,
4747 &mut import_resource_map,
4748 );
4749 }
4750 }
4751 ComponentExtern {
4752 ty: TypeDef::ComponentFunc(type_func_idx),
4753 ..
4754 } => {
4755 self.create_resource_fn_map(
4756 iface_fn,
4757 *type_func_idx,
4758 &mut import_resource_map,
4759 );
4760 }
4761 _ => {}
4762 }
4763 }
4764 }
4765
4766 WorldItem::Function(func) => {
4768 let TypeDef::ComponentFunc(func_ty_idx) = &import_type_def.ty else {
4769 unreachable!("invalid fn export");
4770 };
4771 self.create_resource_fn_map(func, *func_ty_idx, &mut import_resource_map);
4772 }
4773 WorldItem::Type { .. } => {}
4775 }
4776 }
4777
4778 self.resource_imports.extend(import_resource_map);
4779 }
4780
4781 fn process_exports(&mut self) {
4783 self.resource_exports.extend(self.resource_imports.clone());
4785
4786 for (export_name, (export_idx, _extern_data)) in self.component.exports.raw_iter() {
4788 let export_name = export_name.as_ref().to_string();
4789 let export = &self.component.export_items[*export_idx];
4790 let world_key = &self.exports[&export_name];
4791 let item = &self.resolve.worlds[self.world].exports[world_key];
4792 let mut export_resource_map = ResourceMap::new();
4793
4794 match export {
4795 Export::LiftedFunction {
4796 func: def,
4797 options,
4798 ty: func_ty,
4799 } => {
4800 let func = match item {
4801 WorldItem::Function(f) => f,
4802 WorldItem::Interface { .. } | WorldItem::Type { .. } => {
4803 unreachable!("unexpectedly non-function lifted function export")
4804 }
4805 };
4806
4807 self.create_resource_fn_map(func, *func_ty, &mut export_resource_map);
4808
4809 let local_name = String::from(match func.kind {
4810 FunctionKind::Constructor(resource_id)
4812 | FunctionKind::Method(resource_id)
4813 | FunctionKind::AsyncMethod(resource_id)
4814 | FunctionKind::Static(resource_id)
4815 | FunctionKind::AsyncStatic(resource_id) => Instantiator::resource_name(
4816 self.resolve,
4817 &mut self.bindgen.local_names,
4818 resource_id,
4819 &self.exports_resource_types,
4820 ),
4821 FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
4823 self.bindgen.local_names.create_once(&export_name)
4824 }
4825 });
4826
4827 let options = self
4828 .component
4829 .options
4830 .get(*options)
4831 .expect("failed to find options");
4832
4833 self.export_bindgen(
4834 &local_name,
4835 def,
4836 options,
4837 func,
4838 func_ty,
4839 &export_name,
4840 &export_resource_map,
4841 );
4842
4843 let js_binding_name = match func.kind {
4844 FunctionKind::Constructor(ty)
4846 | FunctionKind::Method(ty)
4847 | FunctionKind::AsyncMethod(ty)
4848 | FunctionKind::Static(ty)
4849 | FunctionKind::AsyncStatic(ty) => self.resolve.types[ty]
4850 .name
4851 .as_ref()
4852 .unwrap()
4853 .to_upper_camel_case(),
4854 FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
4856 export_name.to_lower_camel_case()
4857 }
4858 };
4859
4860 self.bindgen.esm_bindgen.add_export_binding(
4862 None,
4863 local_name,
4864 js_binding_name,
4865 func,
4866 );
4867 }
4868
4869 Export::Instance { exports, .. } => {
4870 let iface_id = match item {
4871 WorldItem::Interface { id, .. } => *id,
4872 WorldItem::Function(_) | WorldItem::Type { .. } => {
4873 unreachable!("unexpectedly non-interface export instance")
4874 }
4875 };
4876
4877 if self.bindgen.opts.flags_as_bigint || self.bindgen.opts.use_namespace_objects
4878 {
4879 let mut namespace_locals = BTreeMap::<TypeId, String>::new();
4880 for (type_name, type_id) in &self.resolve.interfaces[iface_id].types {
4881 let type_id = crate::dealias(self.resolve, *type_id);
4882 let kind = &self.resolve.types[type_id].kind;
4883 let generate = matches!(kind, TypeDefKind::Flags(_))
4884 && self.bindgen.opts.flags_as_bigint
4885 || matches!(kind, TypeDefKind::Enum(_) | TypeDefKind::Variant(_))
4886 && self.bindgen.opts.use_namespace_objects;
4887 if !generate {
4888 continue;
4889 }
4890
4891 let local_name = self
4892 .bindgen
4893 .local_names
4894 .create_once(&type_name.to_upper_camel_case())
4895 .to_string();
4896 if let Some(existing) = namespace_locals.get(&type_id) {
4897 uwriteln!(self.src.js, "const {local_name} = {existing};");
4898 } else {
4899 uwriteln!(self.src.js, "const {local_name} = Object.freeze({{");
4900 match kind {
4901 TypeDefKind::Flags(flags) => {
4902 for (index, flag) in flags.flags.iter().enumerate() {
4903 uwriteln!(
4904 self.src.js,
4905 "{}: 1n << {index}n,",
4906 flag.name.to_upper_camel_case()
4907 );
4908 }
4909 }
4910 TypeDefKind::Enum(enum_) => {
4911 for case in &enum_.cases {
4912 let case_value = crate::enum_case_name(
4913 &case.name,
4914 self.bindgen.opts.enum_values_screaming_snake_case,
4915 );
4916 uwriteln!(
4917 self.src.js,
4918 "{}: '{}',",
4919 case.name.to_upper_camel_case(),
4920 case_value
4921 );
4922 }
4923 }
4924 TypeDefKind::Variant(variant) => {
4925 for case in &variant.cases {
4926 let case_name = case.name.to_upper_camel_case();
4927 if case.ty.is_some() {
4928 uwriteln!(
4929 self.src.js,
4930 "{case_name}: (val) => ({{ tag: '{}', val }}),",
4931 case.name
4932 );
4933 } else {
4934 uwriteln!(
4935 self.src.js,
4936 "{case_name}: () => ({{ tag: '{}' }}),",
4937 case.name
4938 );
4939 }
4940 }
4941 }
4942 _ => unreachable!(),
4943 }
4944 uwriteln!(self.src.js, "}});");
4945 namespace_locals.insert(type_id, local_name.clone());
4946 }
4947 self.bindgen.esm_bindgen.add_export_constant(
4948 &export_name,
4949 local_name,
4950 type_name.to_upper_camel_case(),
4951 );
4952 }
4953 }
4954
4955 for (func_name, (export_idx, _extern_data)) in exports.raw_iter() {
4957 let func_name = func_name.as_ref().to_string();
4958 let export = &self.component.export_items[*export_idx];
4959
4960 let (def, options, func_ty) = match export {
4962 Export::LiftedFunction { func, options, ty } => (func, options, ty),
4963 Export::Type(_) => continue, _ => unreachable!("unexpected non-lifted function export"),
4965 };
4966
4967 let func = &self.resolve.interfaces[iface_id].functions[&func_name];
4968
4969 self.create_resource_fn_map(func, *func_ty, &mut export_resource_map);
4970
4971 let local_name = String::from(match func.kind {
4972 FunctionKind::Constructor(resource_id)
4974 | FunctionKind::Method(resource_id)
4975 | FunctionKind::AsyncMethod(resource_id)
4976 | FunctionKind::Static(resource_id)
4977 | FunctionKind::AsyncStatic(resource_id) => {
4978 Instantiator::resource_name(
4979 self.resolve,
4980 &mut self.bindgen.local_names,
4981 resource_id,
4982 &self.exports_resource_types,
4983 )
4984 }
4985 FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
4987 self.bindgen.local_names.create_once(&func_name)
4988 }
4989 });
4990
4991 let options = self
4992 .component
4993 .options
4994 .get(*options)
4995 .expect("failed to find options");
4996
4997 self.export_bindgen(
4998 &local_name,
4999 def,
5000 options,
5001 func,
5002 func_ty,
5003 &export_name,
5004 &export_resource_map,
5005 );
5006
5007 let export_binding_name = match func.kind {
5009 FunctionKind::Constructor(ty)
5011 | FunctionKind::Method(ty)
5012 | FunctionKind::AsyncMethod(ty)
5013 | FunctionKind::Static(ty)
5014 | FunctionKind::AsyncStatic(ty) => self.resolve.types[ty]
5015 .name
5016 .as_ref()
5017 .unwrap()
5018 .to_upper_camel_case(),
5019 FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
5021 func_name.to_lower_camel_case()
5022 }
5023 };
5024
5025 self.bindgen.esm_bindgen.add_export_binding(
5027 Some(&export_name),
5028 local_name,
5029 export_binding_name,
5030 func,
5031 );
5032 }
5033 }
5034
5035 Export::Type(_) => {}
5037
5038 Export::ModuleStatic { .. } | Export::ModuleImport { .. } => unimplemented!(),
5040 }
5041
5042 self.resource_exports.extend(export_resource_map);
5044 }
5045
5046 self.bindgen.esm_bindgen.populate_export_aliases();
5047 }
5048
5049 #[allow(clippy::too_many_arguments)]
5050 fn export_bindgen(
5051 &mut self,
5052 local_name: &str,
5053 def: &CoreDef,
5054 options: &CanonicalOptions,
5055 func: &Function,
5056 _func_ty_idx: &TypeFuncIndex,
5057 export_name: &String,
5058 export_resource_map: &ResourceMap,
5059 ) {
5060 let requires_async_porcelain = requires_async_porcelain(
5062 FunctionIdentifier::Fn(func),
5063 export_name,
5064 &self.async_exports,
5065 );
5066 if options.async_ {
5068 assert!(
5069 options.post_return.is_none(),
5070 "async function {local_name} (export {export_name}) can't have post return"
5071 );
5072 }
5073
5074 let is_async = is_async_fn(func, options);
5075
5076 let wrap_async_future_result = (requires_async_porcelain || is_async)
5077 && matches!(
5078 func.result.as_ref(),
5079 Some(Type::Id(id))
5080 if matches!(
5081 self.resolve.types[crate::dealias(self.resolve, *id)].kind,
5082 TypeDefKind::Future(_)
5083 )
5084 );
5085
5086 let maybe_async = if (requires_async_porcelain || is_async) && !wrap_async_future_result {
5087 "async "
5088 } else {
5089 ""
5090 };
5091 let wrapped_function_target = wrap_async_future_result.then(|| match func.kind {
5092 FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => local_name.to_string(),
5093 FunctionKind::Method(_) | FunctionKind::AsyncMethod(_) => format!(
5094 "{local_name}.prototype.{}",
5095 func.item_name().to_lower_camel_case()
5096 ),
5097 FunctionKind::Static(_) | FunctionKind::AsyncStatic(_) => {
5098 format!("{local_name}.{}", func.item_name().to_lower_camel_case())
5099 }
5100 FunctionKind::Constructor(_) => {
5101 unreachable!("constructors cannot return futures")
5102 }
5103 });
5104
5105 let core_export_fn = self.core_def(def);
5107 let callee = match self
5108 .bindgen
5109 .local_names
5110 .get_or_create(&core_export_fn, &core_export_fn)
5111 {
5112 (local_name, true) => local_name.to_string(),
5113 (local_name, false) => {
5114 let local_name = local_name.to_string();
5115 uwriteln!(self.src.js, "let {local_name};");
5116 self.bindgen
5117 .all_core_exported_funcs
5118 .push((core_export_fn.clone(), is_async | requires_async_porcelain));
5122 local_name
5123 }
5124 };
5125
5126 let iface_name = if export_name.is_empty() {
5127 None
5128 } else {
5129 Some(export_name)
5130 };
5131
5132 match func.kind {
5134 FunctionKind::Freestanding => {
5135 uwrite!(self.src.js, "\n{maybe_async}function {local_name}")
5136 }
5137 FunctionKind::Method(_) => {
5138 self.ensure_local_resource_class(local_name.to_string());
5139 let method_name = func.item_name().to_lower_camel_case();
5140
5141 uwrite!(
5142 self.src.js,
5143 "\n{local_name}.prototype.{method_name} = {maybe_async}function {}",
5144 if !is_js_reserved_word(&method_name) {
5145 method_name.to_string()
5146 } else {
5147 format!("${method_name}")
5148 }
5149 );
5150 }
5151 FunctionKind::Static(_) => {
5152 self.ensure_local_resource_class(local_name.to_string());
5153 let method_name = func.item_name().to_lower_camel_case();
5154 uwrite!(
5155 self.src.js,
5156 "\n{local_name}.{method_name} = function {}",
5157 if !is_js_reserved_word(&method_name) {
5158 method_name.to_string()
5159 } else {
5160 format!("${method_name}")
5161 }
5162 );
5163 }
5164 FunctionKind::Constructor(_) => {
5165 if self.defined_resource_classes.contains(local_name) {
5166 panic!(
5167 "Internal error: Resource constructor must be defined before other methods and statics"
5168 );
5169 }
5170 uwrite!(
5171 self.src.js,
5172 "
5173 class {local_name} {{
5174 constructor"
5175 );
5176 self.defined_resource_classes.insert(local_name.to_string());
5177 }
5178 FunctionKind::AsyncFreestanding => {
5179 uwrite!(self.src.js, "\n{maybe_async}function {local_name}")
5180 }
5181 FunctionKind::AsyncMethod(_) => {
5182 self.ensure_local_resource_class(local_name.to_string());
5183 let method_name = func.item_name().to_lower_camel_case();
5184 let fn_name = if !is_js_reserved_word(&method_name) {
5185 method_name.to_string()
5186 } else {
5187 format!("${method_name}")
5188 };
5189 uwrite!(
5190 self.src.js,
5191 "\n{local_name}.prototype.{method_name} = {maybe_async}function {fn_name}",
5192 );
5193 }
5194 FunctionKind::AsyncStatic(_) => {
5195 self.ensure_local_resource_class(local_name.to_string());
5196 let method_name = func.item_name().to_lower_camel_case();
5197 let fn_name = if !is_js_reserved_word(&method_name) {
5198 method_name.to_string()
5199 } else {
5200 format!("${method_name}")
5201 };
5202 uwrite!(
5203 self.src.js,
5204 "\n{local_name}.{method_name} = {maybe_async}function {fn_name}",
5205 );
5206 }
5207 };
5208
5209 self.bindgen(JsFunctionBindgenArgs {
5211 nparams: func.params.len(),
5212 call_type: match func.kind {
5213 FunctionKind::Method(_) => CallType::FirstArgIsThis,
5214 FunctionKind::AsyncMethod(_) => CallType::AsyncFirstArgIsThis,
5215 FunctionKind::Freestanding
5216 | FunctionKind::Static(_)
5217 | FunctionKind::Constructor(_) => CallType::Standard,
5218 FunctionKind::AsyncFreestanding | FunctionKind::AsyncStatic(_) => {
5219 CallType::AsyncStandard
5220 }
5221 },
5222 iface_name: iface_name.map(|v| v.as_str()),
5223 callee: &callee,
5224 opts: options,
5225 func,
5226 resource_map: export_resource_map,
5227 abi: AbiVariant::GuestExport,
5228 requires_async_porcelain,
5229 is_async,
5230 wrap_async_future_result,
5231 for_import: false,
5232 });
5233 if let Some(target) = wrapped_function_target {
5234 let async_fn_ctor = self.bindgen.intrinsic(Intrinsic::AsyncFunctionCtor);
5235 uwriteln!(
5236 self.src.js,
5237 "\nObject.setPrototypeOf({target}, {async_fn_ctor}.prototype);"
5238 );
5239 }
5240
5241 match func.kind {
5243 FunctionKind::AsyncFreestanding | FunctionKind::Freestanding => self.src.js("\n"),
5244 FunctionKind::AsyncMethod(_)
5245 | FunctionKind::AsyncStatic(_)
5246 | FunctionKind::Method(_)
5247 | FunctionKind::Static(_) => self.src.js(";\n"),
5248 FunctionKind::Constructor(_) => self.src.js("\n}\n"),
5249 }
5250 }
5251}
5252
5253#[derive(Default)]
5254pub struct Source {
5255 pub js: source::Source,
5256 pub js_init: source::Source,
5257}
5258
5259impl Source {
5260 pub fn js(&mut self, s: &str) {
5261 self.js.push_str(s);
5262 }
5263 pub fn js_init(&mut self, s: &str) {
5264 self.js_init.push_str(s);
5265 }
5266}
5267
5268fn semver_compat_key(version_str: &str) -> Option<(String, Version)> {
5279 let version = Version::parse(version_str).ok()?;
5280 if !version.pre.is_empty() {
5281 None
5282 } else if version.major != 0 {
5283 Some((format!("{}", version.major), version))
5284 } else if version.minor != 0 {
5285 Some((format!("0.{}", version.minor), version))
5286 } else {
5287 None
5288 }
5289}
5290
5291fn parse_mapping(mapping: &str) -> (String, Option<String>) {
5292 if mapping.len() > 1
5293 && let Some(hash_idx) = mapping[1..].find('#')
5294 {
5295 return (
5296 mapping[0..hash_idx + 1].to_string(),
5297 Some(mapping[hash_idx + 2..].into()),
5298 );
5299 }
5300 (mapping.into(), None)
5301}
5302
5303fn resolve_wildcard_mapping(key: &str, mapping: &str, impt: &str) -> Option<String> {
5304 let idx = key.find('*')?;
5305 let lhs = &key[..idx];
5306 let rhs = &key[idx + 1..];
5307
5308 if !impt.starts_with(lhs) || !impt.ends_with(rhs) {
5309 return None;
5310 }
5311
5312 let matched_len = impt.len() - lhs.len() - rhs.len();
5313 let matched = &impt[lhs.len()..lhs.len() + matched_len];
5314 Some(mapping.replace('*', matched))
5315}
5316
5317fn map_import_with_implements(
5322 map: &Option<HashMap<String, String>>,
5323 impt: &str,
5324 implements: Option<&str>,
5325) -> (String, Option<String>) {
5326 let (specifier, iface_member) = map_import(map, impt);
5327 if specifier == impt
5328 && iface_member.is_none()
5329 && let Some(target) = implements
5330 {
5331 let (mapped, member) = map_import(map, target);
5332 let target_sans_version = match target.find('@') {
5334 Some(version_idx) => &target[0..version_idx],
5335 None => target,
5336 };
5337 if mapped != target_sans_version || member.is_some() {
5338 return (mapped, member);
5339 }
5340 }
5341 (specifier, iface_member)
5342}
5343
5344fn map_import(map: &Option<HashMap<String, String>>, impt: &str) -> (String, Option<String>) {
5345 let impt_sans_version = match impt.find('@') {
5346 Some(version_idx) => &impt[0..version_idx],
5347 None => impt,
5348 };
5349 if let Some(map) = map.as_ref() {
5350 if let Some(mapping) = map.get(impt) {
5352 return parse_mapping(mapping);
5353 }
5354
5355 if let Some(mapping) = map.get(impt_sans_version) {
5357 return parse_mapping(mapping);
5358 }
5359
5360 for (key, mapping) in map {
5362 if !key.contains('@') {
5363 continue;
5364 }
5365 if let Some(mapping) = resolve_wildcard_mapping(key, mapping, impt) {
5366 return parse_mapping(&mapping);
5367 }
5368 }
5369
5370 for (key, mapping) in map {
5372 if key.contains('@') {
5373 continue;
5374 }
5375 if let Some(mapping) = resolve_wildcard_mapping(key, mapping, impt_sans_version) {
5376 return parse_mapping(&mapping);
5377 }
5378 }
5379
5380 if let Some(at) = impt.find('@') {
5383 let impt_ver_str = &impt[at + 1..];
5384 if let Some((impt_compat, _)) = semver_compat_key(impt_ver_str) {
5385 let mut best_match: Option<(String, Version)> = None;
5386
5387 for (key, mapping) in map {
5388 let key_at = match key.find('@') {
5389 Some(at) => at,
5390 None => continue,
5391 };
5392 let key_base = &key[..key_at];
5393 let key_ver_str = &key[key_at + 1..];
5394
5395 let (key_compat, key_ver) = match semver_compat_key(key_ver_str) {
5396 Some(k) => k,
5397 None => continue,
5398 };
5399 if impt_compat != key_compat {
5400 continue;
5401 }
5402
5403 let resolved = if let Some(mapping) =
5404 resolve_wildcard_mapping(key_base, mapping, impt_sans_version)
5405 {
5406 Some(mapping)
5407 } else if key_base == impt_sans_version {
5408 Some(mapping.clone())
5409 } else {
5410 None
5411 };
5412
5413 if let Some(resolved_mapping) = resolved {
5414 match &best_match {
5415 Some((_, prev_ver)) if key_ver <= *prev_ver => {}
5416 _ => {
5417 best_match = Some((resolved_mapping, key_ver));
5418 }
5419 }
5420 }
5421 }
5422
5423 if let Some((mapping, _)) = best_match {
5424 return parse_mapping(&mapping);
5425 }
5426 }
5427 }
5428 }
5429 (impt_sans_version.to_string(), None)
5430}
5431
5432pub fn parse_world_key(name: &str) -> Option<(&str, &str, &str)> {
5433 let registry_idx = name.find(':')?;
5434 let ns = &name[0..registry_idx];
5435 match name.rfind('/') {
5436 Some(sep_idx) => {
5437 let end = if let Some(version_idx) = name.rfind('@') {
5438 version_idx
5439 } else {
5440 name.len()
5441 };
5442 Some((
5443 ns,
5444 &name[registry_idx + 1..sep_idx],
5445 &name[sep_idx + 1..end],
5446 ))
5447 }
5448 None => Some((ns, &name[registry_idx + 1..], "")),
5450 }
5451}
5452
5453fn core_file_name(name: &str, idx: u32) -> String {
5454 let i_str = if idx == 0 {
5455 String::from("")
5456 } else {
5457 (idx + 1).to_string()
5458 };
5459 format!("{name}.core{i_str}.wasm")
5460}
5461
5462fn string_encoding_js_literal(val: &wasmtime_environ::component::StringEncoding) -> &'static str {
5464 match val {
5465 wasmtime_environ::component::StringEncoding::Utf8 => "'utf8'",
5466 wasmtime_environ::component::StringEncoding::Utf16 => "'utf16'",
5467 wasmtime_environ::component::StringEncoding::CompactUtf16 => "'compact-utf16'",
5468 }
5469}
5470
5471pub fn gen_flat_lift_fn_list_js_expr(
5480 instantiator: &mut Instantiator,
5481 types: &[InterfaceType],
5482 extra_resource_map: &Option<&mut ResourceMap>,
5483) -> String {
5484 let mut lift_fns: Vec<String> = Vec::with_capacity(types.len());
5485 for ty in types.iter() {
5486 lift_fns.push(gen_flat_lift_fn_js_expr(
5487 instantiator,
5488 ty,
5489 extra_resource_map,
5490 ));
5491 }
5492 format!("[{}]", lift_fns.join(","))
5493}
5494
5495fn flat_count_js_expr(flat_count: &Option<u8>) -> String {
5496 flat_count
5497 .map(|count| count.to_string())
5498 .unwrap_or_else(|| "null".into())
5499}
5500
5501fn join_flat_core_types(a: &'static str, b: &'static str) -> &'static str {
5504 if a == b {
5505 a
5506 } else if (a == "i32" && b == "f32") || (a == "f32" && b == "i32") {
5507 "i32"
5508 } else {
5509 "i64"
5510 }
5511}
5512
5513fn flat_core_types(
5519 component_types: &ComponentTypes,
5520 ty: &InterfaceType,
5521) -> Option<Vec<&'static str>> {
5522 component_types
5523 .canonical_abi(ty)
5524 .flat_count(MAX_FLAT_PARAMS)?;
5525 let mut flat = Vec::new();
5526 push_flat_core_types(component_types, ty, &mut flat);
5527 Some(flat)
5528}
5529
5530fn flat_core_types_variant_payload_join<'a>(
5534 component_types: &ComponentTypes,
5535 cases: impl Iterator<Item = Option<&'a InterfaceType>>,
5536) -> Vec<&'static str> {
5537 let mut joined: Vec<&'static str> = Vec::new();
5538 for maybe_ty in cases {
5539 let Some(ty) = maybe_ty else { continue };
5540 let mut case_flat = Vec::new();
5541 push_flat_core_types(component_types, ty, &mut case_flat);
5542 for (idx, flat_ty) in case_flat.into_iter().enumerate() {
5543 match joined.get_mut(idx) {
5544 Some(existing) => {
5545 *existing = join_flat_core_types(existing, flat_ty);
5546 }
5547 None => joined.push(flat_ty),
5548 }
5549 }
5550 }
5551 joined
5552}
5553
5554fn push_flat_core_types(
5555 component_types: &ComponentTypes,
5556 ty: &InterfaceType,
5557 flat: &mut Vec<&'static str>,
5558) {
5559 match ty {
5560 InterfaceType::Bool
5561 | InterfaceType::S8
5562 | InterfaceType::U8
5563 | InterfaceType::S16
5564 | InterfaceType::U16
5565 | InterfaceType::S32
5566 | InterfaceType::U32
5567 | InterfaceType::Char
5568 | InterfaceType::Flags(_)
5569 | InterfaceType::Enum(_)
5570 | InterfaceType::Own(_)
5571 | InterfaceType::Borrow(_)
5572 | InterfaceType::Future(_)
5573 | InterfaceType::Stream(_)
5574 | InterfaceType::ErrorContext(_) => flat.push("i32"),
5575
5576 InterfaceType::S64 | InterfaceType::U64 => flat.push("i64"),
5577
5578 InterfaceType::Float32 => flat.push("f32"),
5579 InterfaceType::Float64 => flat.push("f64"),
5580
5581 InterfaceType::String | InterfaceType::List(_) | InterfaceType::Map(_) => {
5582 flat.push("i32");
5583 flat.push("i32");
5584 }
5585
5586 InterfaceType::Record(ty_idx) => {
5587 for field in &component_types[*ty_idx].fields {
5588 push_flat_core_types(component_types, &field.ty, flat);
5589 }
5590 }
5591
5592 InterfaceType::Tuple(ty_idx) => {
5593 for ty in &component_types[*ty_idx].types {
5594 push_flat_core_types(component_types, ty, flat);
5595 }
5596 }
5597
5598 InterfaceType::FixedLengthList(ty_idx) => {
5599 let list_ty = &component_types[*ty_idx];
5600 for _ in 0..list_ty.size {
5601 push_flat_core_types(component_types, &list_ty.element, flat);
5602 }
5603 }
5604
5605 InterfaceType::Variant(ty_idx) => {
5606 let variant_ty = &component_types[*ty_idx];
5607 flat.push("i32");
5608 flat.extend(flat_core_types_variant_payload_join(
5609 component_types,
5610 variant_ty.cases.iter().map(|(_, ty)| ty.as_ref()),
5611 ));
5612 }
5613
5614 InterfaceType::Option(ty_idx) => {
5615 let option_ty = &component_types[*ty_idx];
5616 flat.push("i32");
5617 flat.extend(flat_core_types_variant_payload_join(
5618 component_types,
5619 [None, Some(&option_ty.ty)].into_iter(),
5620 ));
5621 }
5622
5623 InterfaceType::Result(ty_idx) => {
5624 let result_ty = &component_types[*ty_idx];
5625 flat.push("i32");
5626 flat.extend(flat_core_types_variant_payload_join(
5627 component_types,
5628 [result_ty.ok.as_ref(), result_ty.err.as_ref()].into_iter(),
5629 ));
5630 }
5631 }
5632}
5633
5634fn flat_core_types_js_expr(flat: &Option<Vec<&'static str>>) -> String {
5636 match flat {
5637 Some(flat) => format!(
5638 "[{}]",
5639 flat.iter()
5640 .map(|t| format!("'{t}'"))
5641 .collect::<Vec<_>>()
5642 .join(",")
5643 ),
5644 None => "null".into(),
5645 }
5646}
5647
5648pub fn gen_flat_lift_fn_js_expr(
5669 instantiator: &mut Instantiator,
5670 ty: &InterfaceType,
5671 extra_resource_map: &Option<&mut ResourceMap>,
5672) -> String {
5673 let component_types = instantiator.types;
5674
5675 match ty {
5676 InterfaceType::Bool => {
5677 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatBool));
5678 Intrinsic::Lift(LiftIntrinsic::LiftFlatBool).name().into()
5679 }
5680
5681 InterfaceType::S8 => {
5682 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS8));
5683 Intrinsic::Lift(LiftIntrinsic::LiftFlatS8).name().into()
5684 }
5685
5686 InterfaceType::U8 => {
5687 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU8));
5688 Intrinsic::Lift(LiftIntrinsic::LiftFlatU8).name().into()
5689 }
5690
5691 InterfaceType::S16 => {
5692 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS16));
5693 Intrinsic::Lift(LiftIntrinsic::LiftFlatS16).name().into()
5694 }
5695
5696 InterfaceType::U16 => {
5697 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU16));
5698 Intrinsic::Lift(LiftIntrinsic::LiftFlatU16).name().into()
5699 }
5700
5701 InterfaceType::S32 => {
5702 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS32));
5703 Intrinsic::Lift(LiftIntrinsic::LiftFlatS32).name().into()
5704 }
5705
5706 InterfaceType::U32 => {
5707 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU32));
5708 Intrinsic::Lift(LiftIntrinsic::LiftFlatU32).name().into()
5709 }
5710
5711 InterfaceType::S64 => {
5712 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS64));
5713 Intrinsic::Lift(LiftIntrinsic::LiftFlatS64).name().into()
5714 }
5715
5716 InterfaceType::U64 => {
5717 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU64));
5718 Intrinsic::Lift(LiftIntrinsic::LiftFlatU64).name().into()
5719 }
5720
5721 InterfaceType::Float32 => {
5722 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFloat32));
5723 Intrinsic::Lift(LiftIntrinsic::LiftFlatFloat32)
5724 .name()
5725 .into()
5726 }
5727
5728 InterfaceType::Float64 => {
5729 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFloat64));
5730 Intrinsic::Lift(LiftIntrinsic::LiftFlatFloat64)
5731 .name()
5732 .into()
5733 }
5734
5735 InterfaceType::Char => {
5736 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatChar));
5737 Intrinsic::Lift(LiftIntrinsic::LiftFlatChar).name().into()
5738 }
5739
5740 InterfaceType::String => {
5741 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatStringAny));
5742 Intrinsic::Lift(LiftIntrinsic::LiftFlatStringAny)
5743 .name()
5744 .into()
5745 }
5746
5747 InterfaceType::Record(ty_idx) => {
5748 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatRecord));
5749 let lift_fn = Intrinsic::Lift(LiftIntrinsic::LiftFlatRecord).name();
5750 let record_ty = &component_types[*ty_idx];
5751 let size32 = record_ty.abi.size32;
5752 let align32 = record_ty.abi.align32;
5753 let mut keys_and_lifts_expr = String::from("[");
5754 for f in &record_ty.fields {
5758 let field_abi = component_types.canonical_abi(&f.ty);
5759 let field_size32 = field_abi.size32;
5760 let field_align32 = field_abi.align32;
5761 keys_and_lifts_expr.push_str(&format!(
5762 "['{}', {}, {}, {}],",
5763 f.name.to_lower_camel_case(),
5764 gen_flat_lift_fn_js_expr(instantiator, &f.ty, extra_resource_map),
5765 field_size32,
5766 field_align32,
5767 ));
5768 }
5769 keys_and_lifts_expr.push(']');
5770 format!(
5771 "{lift_fn}({{ fieldMetas: {keys_and_lifts_expr}, size32: {size32}, align32: {align32} }})"
5772 )
5773 }
5774
5775 InterfaceType::Variant(ty_idx) => {
5776 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatVariant));
5777 let lift_fn = Intrinsic::Lift(LiftIntrinsic::LiftFlatVariant).name();
5778 let variant_ty = &component_types[*ty_idx];
5779 let variant_flat_count = flat_count_js_expr(&variant_ty.abi.flat_count);
5780 let variant_size32 = variant_ty.abi.size32;
5781 let variant_align32 = variant_ty.abi.align32;
5782 let variant_payload_offset32 = variant_ty.info.payload_offset32;
5783 let variant_payload_flat_types = flat_core_types_js_expr(
5784 &flat_core_types(component_types, ty).map(|flat| flat[1..].to_vec()),
5785 );
5786
5787 let mut lift_metas_expr = String::from("[");
5788 for (name, maybe_ty) in &variant_ty.cases {
5789 let (lift_fn_js, case_size32, case_align32, case_flat_count, case_flat_types) =
5790 match maybe_ty {
5791 Some(ty) => {
5792 let cabi_info = component_types.canonical_abi(ty);
5793 (
5794 gen_flat_lift_fn_js_expr(instantiator, ty, extra_resource_map),
5795 cabi_info.size32.to_string(),
5796 cabi_info.align32.to_string(),
5797 cabi_info
5798 .flat_count(MAX_FLAT_PARAMS)
5799 .map(|v| v.to_string())
5800 .unwrap_or_else(|| "null".into()),
5801 flat_core_types_js_expr(&flat_core_types(component_types, ty)),
5802 )
5803 }
5804 None => (
5805 "null".into(),
5806 "0".into(),
5807 "0".into(),
5808 "0".into(),
5809 "[]".into(),
5810 ),
5811 };
5812
5813 lift_metas_expr.push_str(&format!(
5814 "['{name}', {lift_fn_js}, {case_size32}, {case_align32}, {case_flat_count}, {case_flat_types}],",
5815 ));
5816 }
5817 lift_metas_expr.push(']');
5818
5819 format!(
5820 "{lift_fn}({{
5821 caseMetas: {lift_metas_expr},
5822 variantSize32: {variant_size32},
5823 variantAlign32: {variant_align32},
5824 variantPayloadOffset32: {variant_payload_offset32},
5825 variantFlatCount: {variant_flat_count},
5826 variantPayloadFlatTypes: {variant_payload_flat_types},
5827 }} )"
5828 )
5829 }
5830
5831 InterfaceType::List(ty_idx) => {
5832 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatList));
5833 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatList).name();
5834 let list_ty = &component_types[*ty_idx];
5835 let lift_fn_expr =
5836 gen_flat_lift_fn_js_expr(instantiator, &list_ty.element, extra_resource_map);
5837 let elem_cabi = component_types.canonical_abi(&list_ty.element);
5838 let elem_align32 = elem_cabi.align32;
5839 let elem_size32 = elem_cabi.size32;
5840 let typed_array = js_typed_array_ctor(&list_ty.element).unwrap_or("undefined");
5841 format!(
5842 "{f}({{
5843 elemLiftFn: {lift_fn_expr},
5844 elemAlign32: {elem_align32},
5845 elemSize32: {elem_size32},
5846 typedArray: {typed_array},
5847 }})"
5848 )
5849 }
5850
5851 InterfaceType::FixedLengthList(ty_idx) => {
5852 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatList));
5853 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatList).name();
5854 let list_ty = &component_types[*ty_idx];
5855 let list_size32 = list_ty.abi.size32;
5856 let list_align32 = list_ty.abi.align32;
5857 let lift_fn_expr =
5858 gen_flat_lift_fn_js_expr(instantiator, &list_ty.element, extra_resource_map);
5859 let list_len = list_ty.size;
5860 let elem_cabi = component_types.canonical_abi(&list_ty.element);
5861 let elem_align32 = elem_cabi.align32;
5862 let elem_size32 = elem_cabi.size32;
5863 format!(
5864 "{f}({{
5865 elemLiftFn: {lift_fn_expr},
5866 elemAlign32: {elem_align32},
5867 elemSize32: {elem_size32},
5868 listSize32: {list_size32},
5869 listAlign32: {list_align32},
5870 knownLen: {list_len},
5871 }})"
5872 )
5873 }
5874
5875 InterfaceType::Tuple(ty_idx) => {
5876 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatTuple));
5877 let tuple_ty = &component_types[*ty_idx];
5878 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatTuple).name();
5879 let size_u32 = tuple_ty.abi.size32;
5880 let align_u32 = tuple_ty.abi.align32;
5881
5882 let mut elem_lifts_expr = String::from("[");
5883 for ty in &tuple_ty.types {
5884 let lift_fn_js = gen_flat_lift_fn_js_expr(instantiator, ty, extra_resource_map);
5885 let elem_abi = component_types.canonical_abi(ty);
5886 let elem_size32 = elem_abi.size32;
5887 let elem_align32 = elem_abi.align32;
5888 elem_lifts_expr
5889 .push_str(&format!("[{lift_fn_js}, {elem_size32}, {elem_align32}],"));
5890 }
5891 elem_lifts_expr.push(']');
5892
5893 format!(
5894 "{f}({{ elemLiftFns: {elem_lifts_expr}, size32: {size_u32}, align32: {align_u32} }})"
5895 )
5896 }
5897
5898 InterfaceType::Flags(ty_idx) => {
5899 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFlags));
5900 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatFlags).name();
5901 let flags_ty = &component_types[*ty_idx];
5902 let size_u32 = flags_ty.abi.size32;
5903 let align_u32 = flags_ty.abi.align32;
5904 let names_expr = format!(
5905 "[{}]",
5906 flags_ty
5907 .names
5908 .iter()
5909 .map(|s| format!("'{}'", s.to_lower_camel_case()))
5910 .collect::<Vec<_>>()
5911 .join(",")
5912 );
5913 let num_flags = flags_ty.names.len();
5914 let elem_size = if num_flags <= 8 {
5915 1
5916 } else if num_flags <= 16 {
5917 2
5918 } else {
5919 4
5920 };
5921
5922 format!(
5923 "{f}({{ names: {names_expr}, size32: {size_u32}, align32: {align_u32}, intSizeBytes: {elem_size} }})"
5924 )
5925 }
5926
5927 InterfaceType::Enum(ty_idx) => {
5928 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatEnum));
5929 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatEnum).name();
5930 let enum_ty = &component_types[*ty_idx];
5931 let enum_size32 = enum_ty.abi.size32;
5932 let enum_align32 = enum_ty.abi.align32;
5933 let enum_payload_offset32 = enum_ty.info.payload_offset32;
5934 let enum_flat_count = flat_count_js_expr(&enum_ty.abi.flat_count);
5935
5936 let mut elem_lifts_expr = String::from("[");
5937 for name in &enum_ty.names {
5938 let name = crate::enum_case_name(
5939 name,
5940 instantiator.bindgen.opts.enum_values_screaming_snake_case,
5941 );
5942 elem_lifts_expr.push_str(&format!(
5943 "['{name}', null, {enum_size32}, {enum_align32}, {enum_payload_offset32}],"
5944 ));
5945 }
5946 elem_lifts_expr.push(']');
5947
5948 format!(
5949 r#"
5950 {f}({{
5951 caseMetas: {elem_lifts_expr},
5952 variantSize32: {enum_size32},
5953 variantAlign32: {enum_align32},
5954 variantPayloadOffset32: {enum_payload_offset32},
5955 variantFlatCount: {enum_flat_count},
5956 }})
5957 "#
5958 )
5959 }
5960
5961 InterfaceType::Option(ty_idx) => {
5962 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatOption));
5963 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatOption).name();
5964 let option_ty = &component_types[*ty_idx];
5965 let option_payload_offset32 = option_ty.info.payload_offset32;
5966 let option_align32 = option_ty.abi.align32;
5967 let option_size32 = option_ty.abi.size32;
5968 let option_flat_count = flat_count_js_expr(&option_ty.abi.flat_count);
5969 let option_payload_flat_types = flat_core_types_js_expr(
5970 &flat_core_types(component_types, ty).map(|flat| flat[1..].to_vec()),
5971 );
5972
5973 let some_ty_abi = component_types.canonical_abi(&option_ty.ty);
5974 let some_ty_flat_count = flat_count_js_expr(&some_ty_abi.flat_count);
5975 let some_ty_size32 = some_ty_abi.size32;
5976 let some_ty_align32 = some_ty_abi.align32;
5977 let some_ty_flat_types =
5978 flat_core_types_js_expr(&flat_core_types(component_types, &option_ty.ty));
5979 let some_ty_lift_fn_js =
5980 gen_flat_lift_fn_js_expr(instantiator, &option_ty.ty, extra_resource_map);
5981
5982 format!(
5983 r#"
5984 {f}({{
5985 caseMetas: [
5986 ['none', null, 0, 0, 0, [] ],
5987 ['some', {some_ty_lift_fn_js}, {some_ty_size32}, {some_ty_align32}, {some_ty_flat_count}, {some_ty_flat_types} ],
5988 ],
5989 variantSize32: {option_size32},
5990 variantAlign32: {option_align32},
5991 variantPayloadOffset32: {option_payload_offset32},
5992 variantFlatCount: {option_flat_count},
5993 variantPayloadFlatTypes: {option_payload_flat_types},
5994 }})
5995 "#
5996 )
5997 }
5998
5999 InterfaceType::Result(ty_idx) => {
6000 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatResult));
6001 let lift_fn = Intrinsic::Lift(LiftIntrinsic::LiftFlatResult).name();
6002 let result_ty = &component_types[*ty_idx];
6003 let result_size32 = result_ty.abi.size32;
6004 let result_align32 = result_ty.abi.align32;
6005 let result_payload_offset32 = result_ty.info.payload_offset32;
6006 let result_flat_count = flat_count_js_expr(&result_ty.abi.flat_count);
6007 let result_payload_flat_types = flat_core_types_js_expr(
6008 &flat_core_types(component_types, ty).map(|flat| flat[1..].to_vec()),
6009 );
6010
6011 let mut cases_and_lifts_expr = String::from("[");
6012 if let Some(ok_ty) = result_ty.ok {
6013 let ok_ty_abi = component_types.canonical_abi(&ok_ty);
6014 let ok_ty_size32 = ok_ty_abi.size32;
6015 let ok_ty_align32 = ok_ty_abi.align32;
6016 let ok_flat_count = flat_count_js_expr(&ok_ty_abi.flat_count);
6017 let ok_ty_flat_types =
6018 flat_core_types_js_expr(&flat_core_types(component_types, &ok_ty));
6019 let ok_ty_lift_fn =
6020 gen_flat_lift_fn_js_expr(instantiator, &ok_ty, extra_resource_map);
6021 cases_and_lifts_expr.push_str(&format!(
6022 "['ok', {ok_ty_lift_fn}, {ok_ty_size32}, {ok_ty_align32}, {ok_flat_count}, {ok_ty_flat_types}],",
6023 ))
6024 } else {
6025 cases_and_lifts_expr.push_str("['ok', null, 0, 0, 0, []],");
6026 }
6027
6028 if let Some(err_ty) = &result_ty.err {
6029 let err_ty_abi = component_types.canonical_abi(err_ty);
6030 let err_ty_size32 = err_ty_abi.size32;
6031 let err_ty_align32 = err_ty_abi.align32;
6032 let err_ty_flat_count = flat_count_js_expr(&err_ty_abi.flat_count);
6033 let err_ty_flat_types =
6034 flat_core_types_js_expr(&flat_core_types(component_types, err_ty));
6035 let err_ty_lift_fn =
6036 gen_flat_lift_fn_js_expr(instantiator, err_ty, extra_resource_map);
6037 cases_and_lifts_expr.push_str(&format!(
6038 "['err', {err_ty_lift_fn}, {err_ty_size32}, {err_ty_align32}, {err_ty_flat_count}, {err_ty_flat_types}],",
6039 ))
6040 } else {
6041 cases_and_lifts_expr.push_str("['err', null, 0, 0, 0, []],");
6042 }
6043 cases_and_lifts_expr.push(']');
6044
6045 format!(
6046 r#"
6047 {lift_fn}({{
6048 caseMetas: {cases_and_lifts_expr},
6049 variantSize32: {result_size32},
6050 variantAlign32: {result_align32},
6051 variantPayloadOffset32: {result_payload_offset32},
6052 variantFlatCount: {result_flat_count},
6053 variantPayloadFlatTypes: {result_payload_flat_types},
6054 }})
6055 "#
6056 )
6057 }
6058
6059 InterfaceType::Own(ty_idx) => {
6060 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatOwn));
6061 instantiator.add_intrinsic(Intrinsic::JsHelper(JsHelperIntrinsic::EmptyFunc));
6062 instantiator.add_intrinsic(Intrinsic::SymbolResourceHandle);
6063 instantiator.add_intrinsic(Intrinsic::SymbolResourceRep);
6064 instantiator.add_intrinsic(Intrinsic::SymbolDispose);
6065 instantiator.add_intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableRemove));
6066 instantiator.add_intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableFlag));
6067 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatOwn).name();
6068 let table_ty = &component_types[*ty_idx];
6069 let component_idx = table_ty.unwrap_concrete_instance().as_u32();
6070 let resource_idx = table_ty.unwrap_concrete_ty();
6071
6072 match instantiator.exports_resource_index_types.get(&resource_idx) {
6074 None => format!(
6076 r#"{f}({{
6077 componentIdx: {component_idx},
6078 classNameFn: () => null,
6079 createResourceFn: () => {{ throw new Error('invalid/missing resource type data'); }},
6080 }})
6081 "#,
6082 ),
6083
6084 Some(resource_typedef) => {
6087 let (resource_class_name, create_resource_fn_js) = match (
6089 instantiator.resource_exports.get(resource_typedef),
6090 extra_resource_map
6091 .as_ref()
6092 .and_then(|v| v.get(resource_typedef)),
6093 ) {
6094 (None, None) => (
6096 "null".into(),
6097 "() => {{ throw new Error('missing resource information'); }}".into(),
6098 ),
6099
6100 (Some(ResourceTable { imported, data }), _)
6102 | (_, Some(ResourceTable { imported, data })) => match data {
6103 ResourceData::Guest { .. } => {
6104 unimplemented!(
6105 "owned resources created by guests should must have host-side data"
6106 )
6107 }
6108 ResourceData::Host {
6109 tid,
6110 rid,
6111 local_name,
6112 dtor_name,
6113 } => {
6114 let empty_func = JsHelperIntrinsic::EmptyFunc.name();
6115 let symbol_resource_handle = Intrinsic::SymbolResourceHandle.name();
6116 let symbol_dispose = Intrinsic::SymbolDispose.name();
6117 let rsc_table_remove =
6118 ResourceIntrinsic::ResourceTableRemove.name();
6119 let tid = tid.as_u32();
6120 let rsc_flag = ResourceIntrinsic::ResourceTableFlag.name();
6121
6122 let create_resource_fn_js = if *imported {
6124 let symbol_resource_rep = Intrinsic::SymbolResourceRep.name();
6125 let rid = rid.as_u32();
6126 format!(
6127 r#"
6128 (handle) => {{
6129 const rep = handleTable{tid}[(handle << 1) + 1] & ~{rsc_flag};
6130 let resourceObj = captureTable{rid}.get(rep);
6131 if (!resourceObj) {{
6132 resourceObj = Object.create({local_name}.prototype);
6133 Object.defineProperty(resourceObj, {symbol_resource_handle}, {{ writable: true, value: handle }});
6134 Object.defineProperty(resourceObj, {symbol_resource_rep}, {{ writable: true, value: rep }});
6135 }} else {{
6136 captureTable{rid}.delete(rep);
6137 }}
6138 {rsc_table_remove}(handleTable{tid}, handle);
6139 return resourceObj;
6140 }}
6141 "#
6142 )
6143 } else {
6144 let dtor_setup_js = dtor_name
6145 .as_ref()
6146 .map(|dtor|
6147 format!(
6148 r#"
6149 Object.defineProperty(
6150 resourceObj,
6151 {symbol_dispose},
6152 {{
6153 writable: true,
6154 value: function() {{
6155 finalizationRegistry{tid}.unregister(resourceObj);
6156 {rsc_table_remove}(handleTable{tid}, handle);
6157 resourceObj[{symbol_dispose}] = {empty_func};
6158 resourceObj[{symbol_resource_handle}] = undefined;
6159 {dtor}(handleTable{tid}[(handle << 1) + 1] & ~{rsc_flag});
6160 }}
6161 }}
6162 );
6163 "#
6164 )
6165 ).unwrap_or_default();
6166
6167 format!(
6168 r#"
6169 (handle) => {{
6170 const resourceObj = Object.create({local_name}.prototype);
6171 Object.defineProperty(resourceObj, {symbol_resource_handle}, {{
6172 writable: true,
6173 value: handle,
6174 }});
6175 finalizationRegistry{tid}.register(resourceObj, handle, resourceObj);
6176 {dtor_setup_js}
6177 return resourceObj;
6178 }}
6179 "#
6180 )
6181 };
6182
6183 (local_name.to_string(), create_resource_fn_js)
6184 }
6185 },
6186 };
6187
6188 format!(
6189 r#"{f}({{
6190 componentIdx: {component_idx},
6191 classNameFn: () => {resource_class_name},
6192 createResourceFn: {create_resource_fn_js},
6193 }})
6194 "#,
6195 )
6196 }
6197 }
6198 }
6199
6200 InterfaceType::Borrow(ty_idx) => {
6201 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatBorrow));
6202 let table_idx = ty_idx.as_u32();
6203 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatBorrow).name();
6204 format!("{f}.bind(null, {table_idx})")
6205 }
6206
6207 InterfaceType::Future(ty_idx) => {
6208 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFuture));
6209 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatFuture).name();
6210 let table_idx = ty_idx.as_u32();
6211 let table_ty = &component_types[*ty_idx];
6212 let component_idx = table_ty.instance.as_u32();
6213 format!("{f}({{ futureTableIdx: {table_idx}, componentIdx: {component_idx} }})")
6214 }
6215
6216 InterfaceType::Stream(ty_idx) => {
6217 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatStream));
6218 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatStream).name();
6219 let table_idx = ty_idx.as_u32();
6220 let table_ty = &component_types[*ty_idx];
6221 let component_idx = table_ty.instance.as_u32();
6222 format!("{f}({{ streamTableIdx: {table_idx}, componentIdx: {component_idx} }})")
6223 }
6224
6225 InterfaceType::ErrorContext(ty_idx) => {
6226 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatErrorContext));
6227 let table_idx = ty_idx.as_u32();
6228 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatErrorContext).name();
6229 format!("{f}.bind(null, {table_idx})")
6230 }
6231
6232 InterfaceType::Map(ty_idx) => {
6233 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatMap));
6234 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatMap).name();
6235 let map_ty = &component_types[*ty_idx];
6236 let key_lift = gen_flat_lift_fn_js_expr(instantiator, &map_ty.key, extra_resource_map);
6237 let value_lift =
6238 gen_flat_lift_fn_js_expr(instantiator, &map_ty.value, extra_resource_map);
6239 let entry_size32 = map_ty.entry_abi.size32;
6240 let entry_align32 = map_ty.entry_abi.align32;
6241 let value_offset32 = map_ty.value_offset32;
6242 format!(
6243 "{f}({{
6244 keyLiftFn: {key_lift},
6245 valueLiftFn: {value_lift},
6246 entrySize32: {entry_size32},
6247 entryAlign32: {entry_align32},
6248 valueOffset32: {value_offset32},
6249 }})"
6250 )
6251 }
6252 }
6253}
6254
6255fn js_typed_array_ctor(ty: &InterfaceType) -> Option<&'static str> {
6256 match ty {
6257 InterfaceType::U8 => Some("Uint8Array"),
6258 InterfaceType::S8 => Some("Int8Array"),
6259 InterfaceType::U16 => Some("Uint16Array"),
6260 InterfaceType::S16 => Some("Int16Array"),
6261 InterfaceType::U32 => Some("Uint32Array"),
6262 InterfaceType::S32 => Some("Int32Array"),
6263 InterfaceType::U64 => Some("BigUint64Array"),
6264 InterfaceType::S64 => Some("BigInt64Array"),
6265 InterfaceType::Float32 => Some("Float32Array"),
6266 InterfaceType::Float64 => Some("Float64Array"),
6267 _ => None,
6268 }
6269}
6270
6271pub fn gen_flat_lower_fn_list_js_expr(
6280 instantiator: &mut Instantiator,
6281 types: &[InterfaceType],
6282 extra_import_map: &Option<&mut ResourceMap>,
6283) -> String {
6284 let mut lower_fns: Vec<String> = Vec::with_capacity(types.len());
6285 for ty in types.iter() {
6286 lower_fns.push(gen_flat_lower_fn_js_expr(
6287 instantiator,
6288 ty,
6289 extra_import_map,
6290 ));
6291 }
6292 format!("[{}]", lower_fns.join(","))
6293}
6294
6295pub fn gen_flat_lower_fn_js_expr(
6316 instantiator: &mut Instantiator,
6317 ty: &InterfaceType,
6318 extra_resource_map: &Option<&mut ResourceMap>,
6319) -> String {
6320 let component_types = instantiator.types;
6321 match ty {
6322 InterfaceType::Bool => {
6323 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatBool));
6324 Intrinsic::Lower(LowerIntrinsic::LowerFlatBool)
6325 .name()
6326 .into()
6327 }
6328
6329 InterfaceType::S8 => {
6330 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatS8));
6331 Intrinsic::Lower(LowerIntrinsic::LowerFlatS8).name().into()
6332 }
6333
6334 InterfaceType::U8 => {
6335 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatU8));
6336 Intrinsic::Lower(LowerIntrinsic::LowerFlatU8).name().into()
6337 }
6338
6339 InterfaceType::S16 => {
6340 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatS16));
6341 Intrinsic::Lower(LowerIntrinsic::LowerFlatS16).name().into()
6342 }
6343
6344 InterfaceType::U16 => {
6345 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatU16));
6346 Intrinsic::Lower(LowerIntrinsic::LowerFlatU16).name().into()
6347 }
6348
6349 InterfaceType::S32 => {
6350 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatS32));
6351 Intrinsic::Lower(LowerIntrinsic::LowerFlatS32).name().into()
6352 }
6353
6354 InterfaceType::U32 => {
6355 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatU32));
6356 Intrinsic::Lower(LowerIntrinsic::LowerFlatU32).name().into()
6357 }
6358
6359 InterfaceType::S64 => {
6360 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatS64));
6361 Intrinsic::Lower(LowerIntrinsic::LowerFlatS64).name().into()
6362 }
6363
6364 InterfaceType::U64 => {
6365 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatU64));
6366 Intrinsic::Lower(LowerIntrinsic::LowerFlatU64).name().into()
6367 }
6368
6369 InterfaceType::Float32 => {
6370 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatFloat32));
6371 Intrinsic::Lower(LowerIntrinsic::LowerFlatFloat32)
6372 .name()
6373 .into()
6374 }
6375
6376 InterfaceType::Float64 => {
6377 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatFloat64));
6378 Intrinsic::Lower(LowerIntrinsic::LowerFlatFloat64)
6379 .name()
6380 .into()
6381 }
6382
6383 InterfaceType::Char => {
6384 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatChar));
6385 Intrinsic::Lower(LowerIntrinsic::LowerFlatChar)
6386 .name()
6387 .into()
6388 }
6389
6390 InterfaceType::String => {
6391 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatStringAny));
6392 Intrinsic::Lower(LowerIntrinsic::LowerFlatStringAny)
6393 .name()
6394 .into()
6395 }
6396
6397 InterfaceType::Record(ty_idx) => {
6398 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatRecord));
6399 let lower_fn = Intrinsic::Lower(LowerIntrinsic::LowerFlatRecord).name();
6400 let record_ty = &component_types[*ty_idx];
6401 let size32 = record_ty.abi.size32;
6402 let align32 = record_ty.abi.align32;
6403 let mut keys_and_lowers_expr = String::from("[");
6404 for f in &record_ty.fields {
6405 let field_abi = component_types.canonical_abi(&f.ty);
6409 let field_size32 = field_abi.size32;
6410 let field_align32 = field_abi.align32;
6411 keys_and_lowers_expr.push_str(&format!(
6412 "['{}', {}, {}, {} ],",
6413 f.name.to_lower_camel_case(),
6414 gen_flat_lower_fn_js_expr(instantiator, &f.ty, &None),
6415 field_size32,
6416 field_align32,
6417 ));
6418 }
6419 keys_and_lowers_expr.push(']');
6420 format!(
6421 "{lower_fn}({{ fieldMetas: {keys_and_lowers_expr}, size32: {size32}, align32: {align32} }})"
6422 )
6423 }
6424
6425 InterfaceType::Variant(ty_idx) => {
6426 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatVariant));
6427 let lower_fn = Intrinsic::Lower(LowerIntrinsic::LowerFlatVariant).name();
6428 let variant_ty = &component_types[*ty_idx];
6429 let variant_flat_count = flat_count_js_expr(&variant_ty.abi.flat_count);
6430 let size32 = variant_ty.abi.size32;
6431 let align32 = variant_ty.abi.align32;
6432 let payload_offset32 = variant_ty.info.payload_offset32;
6433
6434 let mut lower_metas_expr = String::from("[");
6435 for (name, maybe_ty) in variant_ty.cases.iter() {
6436 let (case_size32, case_align32, case_flat_count) = if let Some(iface_ty) = maybe_ty
6437 {
6438 let cabi_info = component_types.canonical_abi(iface_ty);
6439 (
6440 cabi_info.size32.to_string(),
6441 cabi_info.align32.to_string(),
6442 cabi_info
6443 .flat_count(MAX_FLAT_PARAMS)
6444 .map(|v| v.to_string())
6445 .unwrap_or_else(|| "null".into()),
6446 )
6447 } else {
6448 ("0".into(), "0".into(), "0".into())
6449 };
6450
6451 lower_metas_expr.push_str(&format!(
6452 "[ '{name}', {}, {case_size32}, {case_align32}, {case_flat_count} ],",
6453 maybe_ty
6454 .map(|ty| gen_flat_lower_fn_js_expr(instantiator, &ty, &None))
6455 .unwrap_or_else(|| "null".into()),
6456 ));
6457 }
6458 lower_metas_expr.push(']');
6459
6460 format!(
6461 "{lower_fn}({{
6462 caseMetas: {lower_metas_expr},
6463 variantSize32: {size32},
6464 variantAlign32: {align32},
6465 variantPayloadOffset32: {payload_offset32},
6466 variantFlatCount: {variant_flat_count},
6467 }} )"
6468 )
6469 }
6470
6471 InterfaceType::List(ty_idx) => {
6472 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatList));
6473 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatList).name();
6474 let list_ty = &component_types[*ty_idx];
6475 let elem_ty_lower_expr =
6476 gen_flat_lower_fn_js_expr(instantiator, &list_ty.element, extra_resource_map);
6477 let elem_cabi = component_types.canonical_abi(&list_ty.element);
6478 let elem_align32 = elem_cabi.align32;
6479 let elem_size32 = elem_cabi.size32;
6480
6481 format!(
6482 "{f}({{
6483 elemLowerFn: {elem_ty_lower_expr},
6484 elemSize32: {elem_size32},
6485 elemAlign32: {elem_align32},
6486 }})"
6487 )
6488 }
6489
6490 InterfaceType::FixedLengthList(ty_idx) => {
6491 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatList));
6492 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatList).name();
6493 let list_ty = &component_types[*ty_idx];
6494 let elem_ty_lower_expr =
6495 gen_flat_lower_fn_js_expr(instantiator, &list_ty.element, extra_resource_map);
6496 let list_len = list_ty.size;
6497 let list_align32 = list_ty.abi.size32;
6498 let list_size32 = list_ty.abi.size32;
6499 let elem_cabi = component_types.canonical_abi(&list_ty.element);
6500 let elem_align32 = elem_cabi.align32;
6501 let elem_size32 = elem_cabi.size32;
6502
6503 format!(
6504 r#"{f}({{
6505 elemLowerFn: {elem_ty_lower_expr},
6506 elemAlign32: {elem_align32},
6507 elemSize32: {elem_size32},
6508 align32: {list_align32},
6509 size32: {list_size32},
6510 knownLen: {list_len},
6511 }})"#
6512 )
6513 }
6514
6515 InterfaceType::Tuple(ty_idx) => {
6516 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatTuple));
6517 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatTuple).name();
6518 let tuple_ty = &component_types[*ty_idx];
6519 let size_u32 = tuple_ty.abi.size32;
6520 let align_u32 = tuple_ty.abi.align32;
6521
6522 let mut elem_lowers_expr = String::from("[");
6523 for ty in &tuple_ty.types {
6524 let lower_fn_js = gen_flat_lower_fn_js_expr(instantiator, ty, extra_resource_map);
6525 let elem_abi = component_types.canonical_abi(ty);
6526 let elem_size32 = elem_abi.size32;
6527 let elem_align32 = elem_abi.align32;
6528 elem_lowers_expr
6529 .push_str(&format!("[{lower_fn_js}, {elem_size32}, {elem_align32}],"));
6530 }
6531 elem_lowers_expr.push(']');
6532
6533 format!(
6534 "{f}({{ elemLowerMetas: {elem_lowers_expr}, size32: {size_u32}, align32: {align_u32} }})"
6535 )
6536 }
6537
6538 InterfaceType::Flags(ty_idx) => {
6539 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatFlags));
6540 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatFlags).name();
6541 let flags_ty = &component_types[*ty_idx];
6542 let size32 = flags_ty.abi.size32;
6543 let align32 = flags_ty.abi.align32;
6544 let names_list_js = format!(
6545 "[{}]",
6546 flags_ty
6547 .names
6548 .iter()
6549 .map(|s| format!("'{}'", s.to_lower_camel_case()))
6550 .collect::<Vec<_>>()
6551 .join(",")
6552 );
6553 let num_flags = flags_ty.names.len();
6554 let elem_size = if num_flags <= 8 {
6555 1
6556 } else if num_flags <= 16 {
6557 2
6558 } else {
6559 4
6560 };
6561
6562 format!(
6563 "{f}({{ names: {names_list_js}, size32: {size32}, align32: {align32}, intSizeBytes: {elem_size} }})"
6564 )
6565 }
6566
6567 InterfaceType::Enum(ty_idx) => {
6568 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatEnum));
6569 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatEnum).name();
6570 let enum_ty = &component_types[*ty_idx];
6571 let enum_size32 = enum_ty.abi.size32;
6572 let enum_align32 = enum_ty.abi.align32;
6573 let enum_flat_count = flat_count_js_expr(&enum_ty.abi.flat_count);
6574 let enum_payload_offset32 = enum_ty.info.payload_offset32;
6575
6576 let mut elem_lowers_expr = String::from("[");
6577 for name in &enum_ty.names {
6578 let name = crate::enum_case_name(
6579 name,
6580 instantiator.bindgen.opts.enum_values_screaming_snake_case,
6581 );
6582 elem_lowers_expr.push_str(&format!(
6583 "['{name}', null, {enum_size32}, {enum_align32}, {enum_payload_offset32}],"
6584 ));
6585 }
6586 elem_lowers_expr.push(']');
6587
6588 format!(
6589 r#"
6590 {f}({{
6591 caseMetas: {elem_lowers_expr},
6592 variantSize32: {enum_size32},
6593 variantAlign32: {enum_align32},
6594 variantPayloadOffset32: {enum_payload_offset32},
6595 variantFlatCount: {enum_flat_count},
6596 }})
6597 "#
6598 )
6599 }
6600
6601 InterfaceType::Option(ty_idx) => {
6602 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatOption));
6603 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatOption).name();
6604 let option_ty = &component_types[*ty_idx];
6605 let option_size32 = option_ty.abi.size32;
6606 let option_align32 = option_ty.abi.align32;
6607 let option_payload_offset32 = option_ty.info.payload_offset32;
6608 let option_flat_count = flat_count_js_expr(&option_ty.abi.flat_count);
6609
6610 let some_ty_abi = component_types.canonical_abi(&option_ty.ty);
6611 let some_ty_flat_count = flat_count_js_expr(&some_ty_abi.flat_count);
6612 let some_ty_size32 = some_ty_abi.size32;
6613 let some_ty_align32 = some_ty_abi.align32;
6614 let some_ty_lower_fn_js =
6615 gen_flat_lower_fn_js_expr(instantiator, &option_ty.ty, extra_resource_map);
6616
6617 format!(
6618 r#"
6619 {f}({{
6620 caseMetas: [
6621 [ 'none', null, 0, 0, 0 ],
6622 [ 'some', {some_ty_lower_fn_js}, {some_ty_size32}, {some_ty_align32}, {some_ty_flat_count}],
6623 ],
6624 variantSize32: {option_size32},
6625 variantAlign32: {option_align32},
6626 variantPayloadOffset32: {option_payload_offset32},
6627 variantFlatCount: {option_flat_count},
6628 }})
6629 "#
6630 )
6631 }
6632
6633 InterfaceType::Result(ty_idx) => {
6634 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatResult));
6635 let lower_fn = Intrinsic::Lower(LowerIntrinsic::LowerFlatResult).name();
6636 let result_ty = &component_types[*ty_idx];
6637 let result_size32 = result_ty.abi.size32;
6638 let result_align32 = result_ty.abi.align32;
6639 let result_payload_offset32 = result_ty.info.payload_offset32;
6640 let result_flat_count = flat_count_js_expr(&result_ty.abi.flat_count);
6641
6642 let ok_lower_fn_js = result_ty
6643 .ok
6644 .map(|ty| gen_flat_lower_fn_js_expr(instantiator, &ty, extra_resource_map))
6645 .unwrap_or_else(|| "null".into());
6646 let err_lower_fn_js = result_ty
6647 .err
6648 .map(|ty| gen_flat_lower_fn_js_expr(instantiator, &ty, extra_resource_map))
6649 .unwrap_or_else(|| "null".into());
6650
6651 format!(
6652 r#"
6653 {lower_fn}({{
6654 caseMetas: [
6655 [ 'ok', {ok_lower_fn_js}, {result_size32}, {result_align32}, {result_payload_offset32} ],
6656 [ 'err', {err_lower_fn_js}, {result_size32}, {result_align32}, {result_payload_offset32} ],
6657 ],
6658 variantSize32: {result_size32},
6659 variantAlign32: {result_align32},
6660 variantPayloadOffset32: {result_payload_offset32},
6661 variantFlatCount: {result_flat_count},
6662 }})
6663 "#
6664 )
6665 }
6666
6667 InterfaceType::Own(ty_idx) => {
6668 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatOwn));
6669 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatOwn).name();
6670 let resource_table_ty = &component_types[*ty_idx];
6671 let component_idx = resource_table_ty.unwrap_concrete_instance().as_u32();
6672 let resource_idx = resource_table_ty.unwrap_concrete_ty();
6673
6674 let (_, ResourceTable { imported, data }) = match (
6678 instantiator.imports_resource_index_types.get(&resource_idx),
6679 instantiator.exports_resource_index_types.get(&resource_idx),
6680 ) {
6681 (Some(import_ty_id), _) => {
6682 let ty = crate::dealias(instantiator.resolve, *import_ty_id);
6683 let maybe_resource_table =
6684 instantiator.resource_imports.get(&ty).or(extra_resource_map
6685 .as_ref()
6686 .and_then(|m| m.get(import_ty_id)));
6687 (
6688 ty,
6689 maybe_resource_table.expect("missing imported resource table information"),
6690 )
6691 }
6692 (_, Some(export_ty_id)) => {
6693 let ty = crate::dealias(instantiator.resolve, *export_ty_id);
6694 let maybe_resource_table =
6695 instantiator.resource_exports.get(&ty).or(extra_resource_map
6696 .as_ref()
6697 .and_then(|m| m.get(export_ty_id)));
6698 (
6699 ty,
6700 maybe_resource_table.expect("missing exported resource table information"),
6701 )
6702 }
6703
6704 (None, None) => {
6706 return format!(
6707 "{f}({{
6708 componentIdx: {component_idx},
6709 lowerFn: () => {{ throw new Error('missing/invalid resource metadata'); }}
6710 }})"
6711 );
6712 }
6713 };
6714
6715 let lower_fn_js = match data {
6717 ResourceData::Host {
6719 tid,
6720 rid,
6721 local_name,
6722 ..
6723 } => {
6724 let tid = tid.as_u32();
6725 let rid = rid.as_u32();
6726 let symbol_resource_rep =
6727 instantiator.bindgen.intrinsic(Intrinsic::SymbolResourceRep);
6728 let symbol_resource_handle = instantiator
6729 .bindgen
6730 .intrinsic(Intrinsic::SymbolResourceHandle);
6731 let symbol_dispose = instantiator.bindgen.intrinsic(Intrinsic::SymbolDispose);
6732
6733 if *imported {
6734 let create_own_fn = instantiator.bindgen.intrinsic(Intrinsic::Resource(
6737 ResourceIntrinsic::ResourceTableCreateOwn,
6738 ));
6739 format!(
6740 r#"
6741 function lowerImportedOwnedHost_{local_name}(obj) {{
6742 if (!(obj instanceof {local_name})) {{
6743 throw new TypeError('Resource error: Not a valid \"{local_name}\" resource.');
6744 }}
6745 let handle = obj[{symbol_resource_handle}];
6746 if (!handle) {{
6747 const rep = obj[{symbol_resource_rep}] || ++captureCnt{rid};
6748 captureTable{rid}.set(rep, obj);
6749 handle = {create_own_fn}(handleTable{tid}, rep);
6750 }}
6751 return handle;
6752 }}
6753 "#
6754 )
6755 } else {
6756 let empty_func = instantiator
6762 .bindgen
6763 .intrinsic(Intrinsic::JsHelper(JsHelperIntrinsic::EmptyFunc));
6764 format!(
6765 r#"
6766 function lowerExportedOwnedHost_{local_name}(obj) {{
6767 let handle = obj[{symbol_resource_handle}];
6768 if (!handle) {{
6769 throw new TypeError('Resource error: Not a valid \"{local_name}\" resource.');
6770 }}
6771 finalizationRegistry{tid}.unregister(obj);
6772 obj[{symbol_dispose}] = {empty_func};
6773 obj[{symbol_resource_handle}] = undefined;
6774 return handle;
6775 }}
6776 "#
6777 )
6778 }
6779 }
6780
6781 ResourceData::Guest {
6783 resource_name,
6784 prefix,
6785 extra,
6786 } => {
6787 assert!(
6788 extra.is_none(),
6789 "plain resource handles do not carry extra data"
6790 );
6791
6792 let upper_camel = resource_name.to_upper_camel_case();
6793 let lower_camel = resource_name.to_lower_camel_case();
6794 let prefix = prefix.as_deref().unwrap_or("");
6795
6796 if *imported {
6797 let symbol_resource_handle = instantiator
6800 .bindgen
6801 .intrinsic(Intrinsic::SymbolResourceHandle);
6802 format!(
6803 r#"
6804 function lowerImportedOwnedGuest_{upper_camel}(obj) {{
6805 const handle = obj[{symbol_resource_handle}];
6806 finalizationRegistry_import${prefix}{lower_camel}.unregister(obj);
6807 return handle;
6808 }}
6809 "#
6810 )
6811 } else {
6812 let symbol_resource_handle = instantiator
6816 .bindgen
6817 .intrinsic(Intrinsic::SymbolResourceHandle);
6818 format!(
6819 r#"
6820 function lowerExportedOwnedGuest_{upper_camel}(obj) {{
6821 if (!(obj instanceof {upper_camel})) {{
6822 throw new TypeError('Resource error: Not a valid \"{upper_camel}\" resource.');
6823 }}
6824 let handle = obj[{symbol_resource_handle}];
6825 if (handle === undefined) {{
6826 const localRep = repCnt++;
6827 repTable.set(localRep, {{ rep: obj, own: true }});
6828 handle = $resource_{prefix}new${lower_camel}(localRep);
6829 obj[{symbol_resource_handle}] = handle;
6830 finalizationRegistry_export${prefix}{lower_camel}.register(obj, handle, obj);
6831 }}
6832 return handle;
6833 }}
6834 "#
6835 )
6836 }
6837 }
6838 };
6839
6840 format!(
6841 "{f}({{
6842 componentIdx: {component_idx},
6843 lowerFn: {lower_fn_js},
6844 }})"
6845 )
6846 }
6847
6848 InterfaceType::Borrow(ty_idx) => {
6849 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatBorrow));
6850 let table_idx = ty_idx.as_u32();
6851 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatBorrow).name();
6852 format!("{f}.bind(null, {table_idx})")
6853 }
6854
6855 InterfaceType::Future(ty_idx) => {
6856 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatFuture));
6857 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatFuture).name();
6858 let table_idx = ty_idx.as_u32();
6859 let table_ty = &component_types[*ty_idx];
6860 let component_idx = table_ty.instance.as_u32();
6861 let future_ty_idx = table_ty.ty;
6862 let future_ty = &component_types[future_ty_idx];
6863 let payload = future_ty.payload;
6864 let payload_ty_name_js = future_ty
6865 .payload
6866 .map(|iface_ty| format!("'{iface_ty:?}'"))
6867 .unwrap_or_else(|| "null".into());
6868
6869 let (
6871 payload_size32,
6872 payload_align32,
6873 payload_flat_count_js,
6874 payload_lift_fn_js,
6875 payload_lower_fn_js,
6876 is_borrowed,
6877 is_none_type,
6878 is_numeric_type,
6879 is_async_value,
6880 ) = match payload {
6881 None => (
6882 0,
6883 0,
6884 "0".into(),
6885 "() => {{ throw new Error('empty future payload'); }}".into(),
6886 "() => {{ throw new Error('empty future payload'); }}".into(),
6887 false,
6888 true,
6889 false,
6890 false,
6891 ),
6892 Some(payload_ty) => {
6893 let cabi = instantiator.types.canonical_abi(&payload_ty);
6894 (
6895 cabi.size32,
6896 cabi.align32,
6897 cabi.flat_count
6898 .map(|v| format!("{v}"))
6899 .unwrap_or_else(|| "null".into()),
6900 gen_flat_lift_fn_js_expr(instantiator, &payload_ty, extra_resource_map),
6901 gen_flat_lower_fn_js_expr(instantiator, &payload_ty, extra_resource_map),
6902 matches!(payload_ty, InterfaceType::Borrow(_)),
6903 false,
6904 matches!(
6905 payload_ty,
6906 InterfaceType::U8
6907 | InterfaceType::U16
6908 | InterfaceType::U32
6909 | InterfaceType::U64
6910 | InterfaceType::S8
6911 | InterfaceType::S16
6912 | InterfaceType::S32
6913 | InterfaceType::S64
6914 | InterfaceType::Float32
6915 | InterfaceType::Float64
6916 ),
6917 matches!(
6918 payload_ty,
6919 InterfaceType::Stream(_) | InterfaceType::Future(_)
6920 ),
6921 )
6922 }
6923 };
6924
6925 let mut future_nesting_level = 0;
6927 let mut payload_ty = future_ty.payload;
6928 while let Some(InterfaceType::Future(inner_ty)) = payload_ty {
6929 future_nesting_level += 1;
6930 payload_ty = component_types[component_types[inner_ty].ty].payload;
6931 }
6932
6933 format!(
6934 r#"{f}({{
6935 futureTableIdx: {table_idx},
6936 futureNestingLevel: {future_nesting_level},
6937 componentIdx: {component_idx},
6938 elemMeta: {{
6939 liftFn: {payload_lift_fn_js},
6940 lowerFn: {payload_lower_fn_js},
6941 payloadTypeName: {payload_ty_name_js},
6942 isNone: {is_none_type},
6943 isNumeric: {is_numeric_type},
6944 isBorrowed: {is_borrowed},
6945 isAsyncValue: {is_async_value},
6946 flatCount: {payload_flat_count_js},
6947 align32: {payload_align32},
6948 size32: {payload_size32},
6949 }},
6950 }})
6951 "#
6952 )
6953 }
6954
6955 InterfaceType::Stream(ty_idx) => {
6956 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatStream));
6957 let table_idx = ty_idx.as_u32();
6958 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatStream).name();
6959 let table_ty = &component_types[*ty_idx];
6960 let component_idx = table_ty.instance.as_u32();
6961 let stream_ty_idx = table_ty.ty;
6962 let stream_ty = &component_types[stream_ty_idx];
6963 let payload = stream_ty.payload;
6964 let payload_ty_name_js = stream_ty
6965 .payload
6966 .map(|iface_ty| format!("'{iface_ty:?}'"))
6967 .unwrap_or_else(|| "null".into());
6968
6969 let (
6972 payload_size32,
6973 payload_align32,
6974 payload_flat_count_js,
6975 payload_lift_fn_js,
6976 payload_lower_fn_js,
6977 is_borrowed,
6978 is_none_type,
6979 is_numeric_type,
6980 is_async_value,
6981 typed_array_js,
6982 ) = match payload {
6983 None => (
6984 0,
6985 0,
6986 "0".into(),
6987 "() => {{ throw new Error('empty stream payload'); }}".into(),
6988 "() => {{ throw new Error('empty stream payload'); }}".into(),
6989 false,
6990 true,
6991 false,
6992 false,
6993 "undefined",
6994 ),
6995 Some(payload_ty) => {
6996 let cabi = instantiator.types.canonical_abi(&payload_ty);
6997 (
6998 cabi.size32,
6999 cabi.align32,
7000 cabi.flat_count
7001 .map(|v| format!("{v}"))
7002 .unwrap_or_else(|| "null".into()),
7003 gen_flat_lift_fn_js_expr(instantiator, &payload_ty, extra_resource_map),
7004 gen_flat_lower_fn_js_expr(instantiator, &payload_ty, extra_resource_map),
7005 matches!(payload_ty, InterfaceType::Borrow(_)),
7006 false,
7007 matches!(
7008 payload_ty,
7009 InterfaceType::U8
7010 | InterfaceType::U16
7011 | InterfaceType::U32
7012 | InterfaceType::U64
7013 | InterfaceType::S8
7014 | InterfaceType::S16
7015 | InterfaceType::S32
7016 | InterfaceType::S64
7017 | InterfaceType::Float32
7018 | InterfaceType::Float64
7019 ),
7020 matches!(
7021 payload_ty,
7022 InterfaceType::Stream(_) | InterfaceType::Future(_)
7023 ),
7024 js_typed_array_ctor(&payload_ty).unwrap_or("undefined"),
7025 )
7026 }
7027 };
7028
7029 format!(
7030 r#"{f}({{
7031 streamTableIdx: {table_idx},
7032 componentIdx: {component_idx},
7033 elemMeta: {{
7034 liftFn: {payload_lift_fn_js},
7035 lowerFn: {payload_lower_fn_js},
7036 payloadTypeName: {payload_ty_name_js},
7037 isNone: {is_none_type},
7038 isNumeric: {is_numeric_type},
7039 isBorrowed: {is_borrowed},
7040 isAsyncValue: {is_async_value},
7041 typedArray: {typed_array_js},
7042 flatCount: {payload_flat_count_js},
7043 align32: {payload_align32},
7044 size32: {payload_size32},
7045 }},
7046 }})
7047 "#
7048 )
7049 }
7050
7051 InterfaceType::ErrorContext(ty_idx) => {
7052 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatErrorContext));
7053 let table_idx = ty_idx.as_u32();
7054 let lower_flat_err_ctx_fn =
7055 Intrinsic::Lower(LowerIntrinsic::LowerFlatErrorContext).name();
7056 format!("{lower_flat_err_ctx_fn}.bind(null, {table_idx})")
7057 }
7058
7059 InterfaceType::Map(ty_idx) => {
7060 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatMap));
7061 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatMap).name();
7062 let map_ty = &component_types[*ty_idx];
7063 let key_lower =
7064 gen_flat_lower_fn_js_expr(instantiator, &map_ty.key, extra_resource_map);
7065 let value_lower =
7066 gen_flat_lower_fn_js_expr(instantiator, &map_ty.value, extra_resource_map);
7067 let entry_size32 = map_ty.entry_abi.size32;
7068 let entry_align32 = map_ty.entry_abi.align32;
7069 let value_offset32 = map_ty.value_offset32;
7070 format!(
7071 "{f}({{
7072 keyLowerFn: {key_lower},
7073 valueLowerFn: {value_lower},
7074 entrySize32: {entry_size32},
7075 entryAlign32: {entry_align32},
7076 valueOffset32: {value_offset32},
7077 }})"
7078 )
7079 }
7080 }
7081}
7082
7083#[cfg(test)]
7084mod tests {
7085 use super::*;
7086
7087 fn compat_key(version_str: &str) -> Option<String> {
7089 semver_compat_key(version_str).map(|(key, _)| key)
7090 }
7091
7092 #[test]
7093 fn test_semver_compat_key() {
7094 assert_eq!(compat_key("1.0.0"), Some("1".into()));
7095 assert_eq!(compat_key("1.2.3"), Some("1".into()));
7096 assert_eq!(compat_key("2.0.0"), Some("2".into()));
7097 assert_eq!(compat_key("0.2.0"), Some("0.2".into()));
7098 assert_eq!(compat_key("0.2.10"), Some("0.2".into()));
7099 assert_eq!(compat_key("0.1.0"), Some("0.1".into()));
7100 assert_eq!(compat_key("0.0.1"), None);
7101 assert_eq!(compat_key("1.0.0-rc.1"), None);
7102 assert_eq!(compat_key("0.2.0-pre"), None);
7103 assert_eq!(compat_key("not-a-version"), None);
7104 }
7105
7106 #[test]
7107 fn test_semver_compat_key_returns_parsed_version() {
7108 let (key, ver) = semver_compat_key("1.2.3").unwrap();
7109 assert_eq!(key, "1");
7110 assert_eq!(ver, Version::new(1, 2, 3));
7111 }
7112
7113 #[test]
7114 fn test_map_import_exact_match() {
7115 let mut map = HashMap::new();
7116 map.insert("wasi:http/types@0.2.0".into(), "./http.js#types".into());
7117 let map = Some(map);
7118 assert_eq!(
7119 map_import(&map, "wasi:http/types@0.2.0"),
7120 ("./http.js".into(), Some("types".into()))
7121 );
7122 }
7123
7124 #[test]
7125 fn test_map_import_sans_version_match() {
7126 let mut map = HashMap::new();
7127 map.insert("wasi:http/types".into(), "./http.js".into());
7128 let map = Some(map);
7129 assert_eq!(
7130 map_import(&map, "wasi:http/types@0.2.10"),
7131 ("./http.js".into(), None)
7132 );
7133 }
7134
7135 #[test]
7136 fn test_map_import_wildcard_sans_version() {
7137 let mut map = HashMap::new();
7139 map.insert("wasi:http/*".into(), "./http.js#*".into());
7140 let map = Some(map);
7141 assert_eq!(
7142 map_import(&map, "wasi:http/types@0.2.10"),
7143 ("./http.js".into(), Some("types".into()))
7144 );
7145 }
7146
7147 #[test]
7148 fn test_map_import_semver_exact_key() {
7149 let mut map = HashMap::new();
7151 map.insert("wasi:http/types@0.2.0".into(), "./http.js".into());
7152 let map = Some(map);
7153 assert_eq!(
7154 map_import(&map, "wasi:http/types@0.2.10"),
7155 ("./http.js".into(), None)
7156 );
7157 }
7158
7159 #[test]
7160 fn test_map_import_semver_wildcard_key() {
7161 let mut map = HashMap::new();
7163 map.insert("wasi:http/*@0.2.1".into(), "./http.js#*".into());
7164 let map = Some(map);
7165 assert_eq!(
7166 map_import(&map, "wasi:http/types@0.2.10"),
7167 ("./http.js".into(), Some("types".into()))
7168 );
7169 }
7170
7171 #[test]
7172 fn test_map_import_semver_lower_import_version() {
7173 let mut map = HashMap::new();
7175 map.insert("wasi:http/types@0.2.10".into(), "./http.js".into());
7176 let map = Some(map);
7177 assert_eq!(
7178 map_import(&map, "wasi:http/types@0.2.1"),
7179 ("./http.js".into(), None)
7180 );
7181 }
7182
7183 #[test]
7184 fn test_map_import_semver_no_cross_minor() {
7185 let mut map = HashMap::new();
7187 map.insert("wasi:http/types@0.3.0".into(), "./http.js".into());
7188 let map = Some(map);
7189 assert_eq!(
7190 map_import(&map, "wasi:http/types@0.2.10"),
7191 ("wasi:http/types".into(), None)
7192 );
7193 }
7194
7195 #[test]
7196 fn test_map_import_semver_prefers_highest() {
7197 let mut map = HashMap::new();
7199 map.insert("wasi:http/types@0.2.1".into(), "./http-old.js".into());
7200 map.insert("wasi:http/types@0.2.5".into(), "./http-new.js".into());
7201 let map = Some(map);
7202 assert_eq!(
7203 map_import(&map, "wasi:http/types@0.2.10"),
7204 ("./http-new.js".into(), None)
7205 );
7206 }
7207
7208 #[test]
7209 fn test_map_import_no_match_prerelease() {
7210 let mut map = HashMap::new();
7211 map.insert("wasi:http/types@0.2.0-rc.1".into(), "./http.js".into());
7212 let map = Some(map);
7213 assert_eq!(
7214 map_import(&map, "wasi:http/types@0.2.0"),
7215 ("wasi:http/types".into(), None)
7216 );
7217 }
7218
7219 #[test]
7220 fn test_map_import_prerelease_versioned_wildcard_wins_over_unversioned_wildcard() {
7221 let mut map = HashMap::new();
7224 map.insert(
7225 "wasi:cli/*".into(),
7226 "@bytecodealliance/preview2-shim/cli#*".into(),
7227 );
7228 map.insert(
7229 "wasi:cli/*@0.3.0".into(),
7230 "@bytecodealliance/preview3-shim/cli#*".into(),
7231 );
7232 let map = Some(map);
7233 assert_eq!(
7234 map_import(&map, "wasi:cli/stdout@0.3.0"),
7235 (
7236 "@bytecodealliance/preview3-shim/cli".into(),
7237 Some("stdout".into())
7238 )
7239 );
7240 assert_eq!(
7242 map_import(&map, "wasi:cli/stdout@0.2.6"),
7243 (
7244 "@bytecodealliance/preview2-shim/cli".into(),
7245 Some("stdout".into())
7246 )
7247 );
7248 assert_eq!(
7250 map_import(&map, "wasi:cli/stdout"),
7251 (
7252 "@bytecodealliance/preview2-shim/cli".into(),
7253 Some("stdout".into())
7254 )
7255 );
7256 }
7257
7258 #[test]
7259 fn test_map_import_no_match_zero_zero() {
7260 let mut map = HashMap::new();
7261 map.insert("wasi:http/types@0.0.1".into(), "./http.js".into());
7262 let map = Some(map);
7263 assert_eq!(
7264 map_import(&map, "wasi:http/types@0.0.2"),
7265 ("wasi:http/types".into(), None)
7266 );
7267 }
7268
7269 #[test]
7270 fn test_map_import_semver_major_version() {
7271 let mut map = HashMap::new();
7273 map.insert("wasi:http/types@1.0.0".into(), "./http.js".into());
7274 let map = Some(map);
7275 assert_eq!(
7276 map_import(&map, "wasi:http/types@1.2.3"),
7277 ("./http.js".into(), None)
7278 );
7279 }
7280
7281 #[test]
7282 fn test_map_import_semver_no_cross_major() {
7283 let mut map = HashMap::new();
7285 map.insert("wasi:http/types@1.0.0".into(), "./http.js".into());
7286 let map = Some(map);
7287 assert_eq!(
7288 map_import(&map, "wasi:http/types@2.0.0"),
7289 ("wasi:http/types".into(), None)
7290 );
7291 }
7292
7293 #[test]
7294 fn test_map_import_no_map() {
7295 assert_eq!(
7297 map_import(&None, "wasi:http/types@0.2.0"),
7298 ("wasi:http/types".into(), None)
7299 );
7300 }
7301
7302 #[test]
7303 fn test_map_import_no_map_unversioned() {
7304 assert_eq!(
7306 map_import(&None, "wasi:http/types"),
7307 ("wasi:http/types".into(), None)
7308 );
7309 }
7310
7311 #[test]
7312 fn test_parse_mapping_with_hash() {
7313 assert_eq!(
7314 parse_mapping("./http.js#types"),
7315 ("./http.js".into(), Some("types".into()))
7316 );
7317 }
7318
7319 #[test]
7320 fn test_parse_mapping_without_hash() {
7321 assert_eq!(parse_mapping("./http.js"), ("./http.js".into(), None));
7322 }
7323
7324 #[test]
7325 fn test_parse_mapping_leading_hash() {
7326 assert_eq!(parse_mapping("#foo"), ("#foo".into(), None));
7328 }
7329
7330 #[test]
7331 fn test_parse_mapping_empty() {
7332 assert_eq!(parse_mapping(""), ("".into(), None));
7333 }
7334}