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_namespaced_exports: bool,
104 #[builder(default)]
107 pub multi_memory: bool,
108 #[builder(default)]
110 pub guest: bool,
111 pub async_mode: Option<AsyncMode>,
114 #[builder(default)]
116 pub strict: bool,
117 #[builder(default)]
119 pub asmjs: bool,
120 #[builder(default)]
128 pub supports_wasm_exnref: bool,
129}
130
131#[derive(Default, Clone, Debug)]
132#[non_exhaustive]
133pub enum AsyncMode {
134 #[default]
135 Sync,
136 JavaScriptPromiseIntegration {
137 imports: Vec<String>,
138 exports: Vec<String>,
139 },
140}
141
142#[derive(Default, Clone, Debug)]
143#[non_exhaustive]
144pub enum InstantiationMode {
145 #[default]
146 Async,
147 Sync,
148}
149
150enum CallType {
152 Standard,
154 AsyncStandard,
156 FirstArgIsThis,
158 AsyncFirstArgIsThis,
160 CalleeResourceDispatch,
162 AsyncCalleeResourceDispatch,
164}
165
166#[derive(Default, Clone, Debug)]
167#[non_exhaustive]
168pub enum BindingsMode {
169 Hybrid,
170 #[default]
171 Js,
172 Optimized,
173 DirectOptimized,
174}
175
176struct JsBindgen<'a> {
177 local_names: LocalNames,
178
179 esm_bindgen: EsmBindgen,
180
181 src: Source,
186
187 core_module_cnt: usize,
189
190 opts: &'a TranspileOpts,
192
193 all_intrinsics: BTreeSet<Intrinsic>,
195
196 all_core_exported_funcs: Vec<(String, bool)>,
202}
203
204struct JsFunctionBindgenArgs<'a> {
206 nparams: usize,
208 call_type: CallType,
210 iface_name: Option<&'a str>,
212 callee: &'a str,
214 opts: &'a CanonicalOptions,
216 func: &'a Function,
218 resource_map: &'a ResourceMap,
219 abi: AbiVariant,
221 requires_async_porcelain: bool,
223 is_async: bool,
225 wrap_async_future_result: bool,
227 for_import: bool,
230}
231
232impl<'a> ManagesIntrinsics for JsBindgen<'a> {
233 fn add_intrinsic(&mut self, intrinsic: Intrinsic) {
234 self.intrinsic(intrinsic);
235 }
236}
237
238#[derive(PartialEq, Eq, Clone)]
239#[non_exhaustive]
240pub enum ExportKind {
241 LiftedFunction,
243 Instance,
245}
246
247#[allow(clippy::too_many_arguments)]
248pub fn transpile_bindgen(
249 name: &str,
250 component: &ComponentTranslation,
251 modules: &PrimaryMap<StaticModuleIndex, core::Translation<'_>>,
252 types: &ComponentTypes,
253 resolve: &Resolve,
254 id: WorldId,
255 opts: TranspileOpts,
256 files: &mut Files,
257) -> (Vec<String>, Vec<(String, ExportKind)>) {
258 let (async_imports, async_exports) = match opts.async_mode.clone() {
259 None | Some(AsyncMode::Sync) => (Default::default(), Default::default()),
260 Some(AsyncMode::JavaScriptPromiseIntegration { imports, exports }) => {
261 (imports.into_iter().collect(), exports.into_iter().collect())
262 }
263 };
264
265 let mut bindgen = JsBindgen {
266 local_names: LocalNames::default(),
267 src: Source::default(),
268 esm_bindgen: EsmBindgen::default(),
269 core_module_cnt: 0,
270 opts: &opts,
271 all_intrinsics: BTreeSet::new(),
272 all_core_exported_funcs: Vec::new(),
273 };
274 bindgen.local_names.exclude_globals(
275 &Intrinsic::get_global_names()
276 .into_iter()
277 .collect::<Vec<_>>(),
278 );
279 bindgen.core_module_cnt = modules.len();
280
281 let mut stream_tables = BTreeMap::new();
283 for idx in 0..component.component.num_stream_tables {
284 let stream_table_idx = TypeStreamTableIndex::from_u32(idx as u32);
285 let stream_table_ty = &types[stream_table_idx];
286 stream_tables.insert(stream_table_idx, stream_table_ty.instance);
287 }
288
289 let mut future_tables = BTreeMap::new();
291 for idx in 0..component.component.num_future_tables {
292 let future_table_idx = TypeFutureTableIndex::from_u32(idx as u32);
293 let future_table_ty = &types[future_table_idx];
294 future_tables.insert(future_table_idx, future_table_ty.instance);
295 }
296
297 let mut err_ctx_tables = BTreeMap::new();
299 for idx in 0..component.component.num_error_context_tables {
300 let err_ctx_table_idx = TypeComponentLocalErrorContextTableIndex::from_u32(idx as u32);
301 let err_ctx_table_ty = &types[err_ctx_table_idx];
302 err_ctx_tables.insert(err_ctx_table_idx, err_ctx_table_ty.instance);
303 }
304
305 let mut instantiator = Instantiator {
308 src: Source::default(),
309 sizes: SizeAlign::default(),
310 bindgen: &mut bindgen,
311 modules,
312 instances: Default::default(),
313 error_context_component_initialized: (0..component
314 .component
315 .num_runtime_component_instances)
316 .map(|_| false)
317 .collect(),
318 error_context_component_table_initialized: (0..component
319 .component
320 .num_error_context_tables)
321 .map(|_| false)
322 .collect(),
323 resolve,
324 world: id,
325 translation: component,
326 component: &component.component,
327 types,
328 async_imports,
329 async_exports,
330 imports: Default::default(),
331 exports: Default::default(),
332 lowering_options: Default::default(),
333 used_instance_flags: Default::default(),
334 defined_resource_classes: Default::default(),
335 imports_resource_types: Default::default(),
336 imports_resource_index_types: Default::default(),
337 exports_resource_types: Default::default(),
338 exports_resource_index_types: Default::default(),
339 resource_exports: Default::default(),
340 resource_imports: Default::default(),
341 resources_initialized: BTreeMap::new(),
342 resource_tables_initialized: BTreeMap::new(),
343 stream_tables,
344 future_tables,
345 err_ctx_tables,
346 init_current_module: None,
347 };
348 instantiator.sizes.fill(resolve);
349 instantiator.initialize();
350 instantiator.instantiate();
351
352 instantiator.resource_definitions();
353 instantiator.instance_flags();
354
355 instantiator.bindgen.src.js(&instantiator.src.js);
356 instantiator.bindgen.src.js_init(&instantiator.src.js_init);
357
358 instantiator
359 .bindgen
360 .finish_component(name, files, &opts, source::Source::default());
361
362 let exports = instantiator
363 .bindgen
364 .esm_bindgen
365 .exports()
366 .iter()
367 .map(|(export_name, canon_export_name)| {
368 let expected_export_name =
369 if canon_export_name.contains(':') || canon_export_name.starts_with("[async]") {
370 canon_export_name.to_string()
371 } else {
372 canon_export_name.to_kebab_case()
373 };
374 let (export_idx, _extern_data) = instantiator
375 .component
376 .exports
377 .get(&expected_export_name, &NameMapNoIntern)
378 .unwrap_or_else(|| panic!("failed to find component export [{expected_export_name}] (original '{canon_export_name}')"));
379
380 let export_kind = match &instantiator.component.export_items[*export_idx] {
381 wasmtime_environ::component::Export::LiftedFunction { .. } => {
382 ExportKind::LiftedFunction
383 }
384 wasmtime_environ::component::Export::Instance { .. } => {
385 ExportKind::Instance
386 }
387 _ => panic!("unexpected export kind"),
388 };
389
390
391 (
392 export_name.to_string(),
393export_kind,
394 )
395 })
396 .collect();
397
398 (bindgen.esm_bindgen.import_specifiers(), exports)
399}
400
401impl JsBindgen<'_> {
402 fn finish_component(
403 &mut self,
404 name: &str,
405 files: &mut Files,
406 opts: &TranspileOpts,
407 intrinsic_definitions: source::Source,
408 ) {
409 let mut output = source::Source::default();
410 let mut compilation_promises = source::Source::default();
411 let mut core_exported_funcs = source::Source::default();
412
413 for (core_export_fn, is_async) in self.all_core_exported_funcs.iter() {
414 let local_name = self.local_names.get(core_export_fn);
415 if *is_async {
416 uwriteln!(
417 core_exported_funcs,
418 "{local_name} = WebAssembly.promising({core_export_fn});",
419 );
420 } else {
421 uwriteln!(core_exported_funcs, "{local_name} = {core_export_fn};",);
422 }
423 }
424
425 if matches!(self.opts.instantiation_mode, Some(InstantiationMode::Async)) {
427 uwriteln!(
428 compilation_promises,
429 "if (!getCoreModule) getCoreModule = (name) => {}(new URL(`./${{name}}`, import.meta.url));",
430 self.intrinsic(Intrinsic::FetchCompile)
431 );
432 }
433
434 let mut removed = BTreeSet::new();
436 for i in 0..self.core_module_cnt {
437 let local_name = format!("module{i}");
438 let mut name_idx = core_file_name(name, i as u32);
439 if self.opts.instantiation_mode.is_some() {
440 uwriteln!(
441 compilation_promises,
442 "const {local_name} = getCoreModule('{name_idx}');"
443 );
444 } else if files.get_size(&name_idx).unwrap() < self.opts.base64_cutoff {
445 assert!(removed.insert(i));
446 let data = files.remove(&name_idx).unwrap();
447 uwriteln!(
448 compilation_promises,
449 "const {local_name} = {}('{}');",
450 self.intrinsic(Intrinsic::Base64Compile),
451 general_purpose::STANDARD_NO_PAD.encode(&data),
452 );
453 } else {
454 if let Some(&replacement) = removed.iter().next() {
457 assert!(removed.remove(&replacement) && removed.insert(i));
458 let data = files.remove(&name_idx).unwrap();
459 name_idx = core_file_name(name, replacement as u32);
460 files.push(&name_idx, &data);
461 }
462 uwriteln!(
463 compilation_promises,
464 "const {local_name} = {}(new URL('./{name_idx}', import.meta.url));",
465 self.intrinsic(Intrinsic::FetchCompile)
466 );
467 }
468 }
469
470 uwriteln!(output, r#""use components";"#);
472
473 let render_args = RenderIntrinsicsArgs::builder()
474 .intrinsics(&mut self.all_intrinsics)
475 .instantiation_occurred(self.opts.instantiation_mode.is_some())
476 .determinism_profile(AsyncDeterminismProfile::default())
477 .transpile_opts(opts)
478 .build();
479 let js_intrinsics = render_intrinsics(render_args);
480
481 if let Some(instantiation) = &self.opts.instantiation_mode {
483 uwrite!(
484 output,
485 "\
486 export function instantiate(getCoreModule, imports, instantiateCore = {}) {{
487 {}
488 {}
489 {}
490 ",
491 match instantiation {
492 InstantiationMode::Async => "WebAssembly.instantiate",
493 InstantiationMode::Sync =>
494 "(module, importObject) => new WebAssembly.Instance(module, importObject)",
495 },
496 &js_intrinsics as &str,
497 &intrinsic_definitions as &str,
498 &compilation_promises as &str,
499 );
500 }
501
502 let imports_object = if self.opts.instantiation_mode.is_some() {
504 Some("imports")
505 } else {
506 None
507 };
508 self.esm_bindgen
509 .render_imports(&mut output, imports_object, &mut self.local_names);
510
511 if self.opts.instantiation_mode.is_some() {
513 uwrite!(&mut self.src.js, "{}", &core_exported_funcs as &str);
514 self.esm_bindgen.render_exports(
515 &mut self.src.js,
516 self.opts.instantiation_mode.is_some(),
517 &mut self.local_names,
518 opts,
519 );
520 uwrite!(
521 output,
522 "\
523 let gen = (function* _initGenerator () {{
524 {}\
525 {};
526 }})();
527 let promise, resolve, reject;
528 function runNext (value) {{
529 try {{
530 let done;
531 do {{
532 ({{ value, done }} = gen.next(value));
533 }} while (!(value instanceof Promise) && !done);
534 if (done) {{
535 if (resolve) return resolve(value);
536 else return value;
537 }}
538 if (!promise) promise = new Promise((_resolve, _reject) => (resolve = _resolve, reject = _reject));
539 value.then(nextVal => done ? resolve() : runNext(nextVal), reject);
540 }}
541 catch (e) {{
542 if (reject) reject(e);
543 else throw e;
544 }}
545 }}
546 const maybeSyncReturn = runNext(null);
547 return promise || maybeSyncReturn;
548 }};
549 ",
550 &self.src.js_init as &str,
551 &self.src.js as &str,
552 );
553 } else {
554 let (maybe_init_export, maybe_init) =
555 if self.opts.tla_compat && opts.instantiation_mode.is_none() {
556 uwriteln!(self.src.js_init, "_initialized = true;");
557 (
558 "\
559 let _initialized = false;
560 export ",
561 "",
562 )
563 } else {
564 (
565 "",
566 "
567 await $init;
568 ",
569 )
570 };
571
572 uwrite!(
573 output,
574 "\
575 {}
576 {}
577 {}
578 {maybe_init_export}const $init = (() => {{
579 let gen = (function* _initGenerator () {{
580 {}\
581 {}\
582 {}\
583 }})();
584 let promise, resolve, reject;
585 function runNext (value) {{
586 try {{
587 let done;
588 do {{
589 ({{ value, done }} = gen.next(value));
590 }} while (!(value instanceof Promise) && !done);
591 if (done) {{
592 if (resolve) resolve(value);
593 else return value;
594 }}
595 if (!promise) promise = new Promise((_resolve, _reject) => (resolve = _resolve, reject = _reject));
596 value.then(runNext, reject);
597 }}
598 catch (e) {{
599 if (reject) reject(e);
600 else throw e;
601 }}
602 }}
603 const maybeSyncReturn = runNext(null);
604 return promise || maybeSyncReturn;
605 }})();
606 {maybe_init}\
607 ",
608 &js_intrinsics as &str,
609 &intrinsic_definitions as &str,
610 &self.src.js as &str,
611 &compilation_promises as &str,
612 &self.src.js_init as &str,
613 &core_exported_funcs as &str,
614 );
615
616 self.esm_bindgen.render_exports(
617 &mut output,
618 self.opts.instantiation_mode.is_some(),
619 &mut self.local_names,
620 opts,
621 );
622 }
623
624 self.write_util_export(&mut output);
628
629 let mut bytes = output.as_bytes();
630 if bytes[0] == b'\n' {
632 bytes = &bytes[1..];
633 }
634 files.push(&format!("{name}.js"), bytes);
635 }
636
637 fn intrinsic(&mut self, intrinsic: Intrinsic) -> String {
638 self.all_intrinsics.insert(intrinsic);
639 intrinsic.name().to_string()
640 }
641
642 fn write_util_export(&mut self, output: &mut source::Source) {
644 let maybe_ext_future_class = if self.all_intrinsics.contains(&Intrinsic::AsyncFuture(
648 AsyncFutureIntrinsic::HostFutureClass,
649 )) {
650 r#"
651 Future: class Future {
652 #value;
653 #hidden = 0;
654 constructor(value) {
655 this.#value = value;
656 }
657 get then() {
658 if (this.#hidden !== 0) {
659 return undefined;
660 }
661 return (resolve) => {
662 if (this.#value instanceof Future) {
663 this.#value.resolveAsValue(resolve);
664 } else {
665 resolve(this.#value);
666 }
667 };
668 }
669 resolveAsValue(resolve) {
670 this.#hidden++;
671 try {
672 resolve(this);
673 } finally {
674 this.#hidden--;
675 }
676 }
677 },
678 "#
679 .to_string()
680 } else {
681 "".into()
682 };
683
684 uwriteln!(
685 output,
686 r#"
687 export const _util = {{
688 {maybe_ext_future_class}
689 }}
690 "#,
691 );
692 }
693}
694
695pub(crate) struct Instantiator<'a, 'b> {
699 src: Source,
700 bindgen: &'a mut JsBindgen<'b>,
701 modules: &'a PrimaryMap<StaticModuleIndex, core::Translation<'a>>,
702 instances: PrimaryMap<RuntimeInstanceIndex, StaticModuleIndex>,
703 types: &'a ComponentTypes,
704 resolve: &'a Resolve,
705 world: WorldId,
706 sizes: SizeAlign,
707 component: &'a Component,
708
709 error_context_component_initialized: PrimaryMap<RuntimeComponentInstanceIndex, bool>,
712 error_context_component_table_initialized:
713 PrimaryMap<TypeComponentLocalErrorContextTableIndex, bool>,
714
715 translation: &'a ComponentTranslation,
717
718 exports_resource_types: BTreeMap<TypeId, ResourceIndex>,
720 exports_resource_index_types: BTreeMap<ResourceIndex, TypeId>,
722
723 imports_resource_types: BTreeMap<TypeId, ResourceIndex>,
725 #[allow(unused)]
727 imports_resource_index_types: BTreeMap<ResourceIndex, TypeId>,
728
729 resources_initialized: BTreeMap<ResourceIndex, bool>,
730 resource_tables_initialized: BTreeMap<TypeResourceTableIndex, bool>,
731
732 exports: BTreeMap<String, WorldKey>,
733 imports: BTreeMap<String, WorldKey>,
734 used_instance_flags: RefCell<BTreeSet<RuntimeComponentInstanceIndex>>,
736 defined_resource_classes: BTreeSet<String>,
737 async_imports: HashSet<String>,
738 async_exports: HashSet<String>,
739 lowering_options:
740 PrimaryMap<LoweredIndex, (&'a CanonicalOptions, TrampolineIndex, TypeFuncIndex)>,
741
742 stream_tables: BTreeMap<TypeStreamTableIndex, RuntimeComponentInstanceIndex>,
744
745 future_tables: BTreeMap<TypeFutureTableIndex, RuntimeComponentInstanceIndex>,
747
748 err_ctx_tables:
750 BTreeMap<TypeComponentLocalErrorContextTableIndex, RuntimeComponentInstanceIndex>,
751
752 resource_exports: ResourceMap,
754 resource_imports: ResourceMap,
756
757 init_current_module: Option<RuntimeComponentInstanceIndex>,
763}
764
765impl<'a> ManagesIntrinsics for Instantiator<'a, '_> {
766 fn add_intrinsic(&mut self, intrinsic: Intrinsic) {
767 self.bindgen.intrinsic(intrinsic);
768 }
769}
770
771impl<'a> Instantiator<'a, '_> {
772 fn initialize(&mut self) {
773 for (key, _) in &self.resolve.worlds[self.world].imports {
775 let name = &self.resolve.name_world_key(key);
776 self.imports.insert(name.to_string(), key.clone());
777 }
778 for (key, _) in &self.resolve.worlds[self.world].exports {
779 let name = &self.resolve.name_world_key(key);
780 self.exports.insert(name.to_string(), key.clone());
781 }
782
783 for (key, item) in &self.resolve.worlds[self.world].imports {
786 let name = &self.resolve.name_world_key(key);
787 let Some((_, (_, import))) = self
788 .component
789 .import_types
790 .iter()
791 .find(|(_, (impt_name, _))| impt_name == name)
792 else {
793 match item {
794 WorldItem::Interface { .. } => {
795 unreachable!("unexpected interface in import types during initialization")
796 }
797 WorldItem::Function(_) => {
798 unreachable!("unexpected function in import types during initialization")
799 }
800 WorldItem::Type { id, .. } => {
801 assert!(!matches!(
802 self.resolve.types[*id].kind,
803 TypeDefKind::Resource
804 ))
805 }
806 }
807 continue;
808 };
809 match item {
810 WorldItem::Interface { id, .. } => {
811 let TypeDef::ComponentInstance(instance) = &import.ty else {
812 unreachable!("unexpectedly non-component instance import in interface")
813 };
814 let import_ty = &self.types[*instance];
815 let iface = &self.resolve.interfaces[*id];
816 for (ty_name, ty) in &iface.types {
817 match &import_ty.exports.get(ty_name) {
818 None => {}
819 Some(ComponentExtern {
820 ty: TypeDef::Resource(resource_table_idx),
821 ..
822 }) => {
823 let ty = crate::dealias(self.resolve, *ty);
824 let resource_table_ty = &self.types[*resource_table_idx];
825 let concrete_ty = resource_table_ty.unwrap_concrete_ty();
826 self.imports_resource_types.insert(ty, concrete_ty);
827 self.imports_resource_index_types.insert(concrete_ty, ty);
828 }
829 Some(ComponentExtern {
830 ty: TypeDef::Interface(_),
831 ..
832 }) => {}
833 Some(_) => unreachable!("unexpected type in interface"),
834 }
835 }
836 }
837 WorldItem::Function(_) => {}
838 WorldItem::Type { id, .. } => match import {
839 ComponentExtern {
840 ty: TypeDef::Resource(resource),
841 ..
842 } => {
843 let ty = crate::dealias(self.resolve, *id);
844 let resource_table_ty = &self.types[*resource];
845 let concrete_ty = resource_table_ty.unwrap_concrete_ty();
846 self.imports_resource_types.insert(ty, concrete_ty);
847 self.imports_resource_index_types.insert(concrete_ty, ty);
848 }
849 ComponentExtern {
850 ty: TypeDef::Interface(_),
851 ..
852 } => {}
853 _ => unreachable!("unexpected type in import world item"),
854 },
855 }
856 }
857 self.exports_resource_types = self.imports_resource_types.clone();
858 self.exports_resource_index_types = self.imports_resource_index_types.clone();
859
860 for (key, item) in &self.resolve.worlds[self.world].exports {
861 let name = &self.resolve.name_world_key(key);
862 let (_, (export_idx, _extern_data)) = self
863 .component
864 .exports
865 .raw_iter()
866 .find(|(expt_name, _)| ***expt_name == **name)
867 .unwrap();
868 let export = &self.component.export_items[*export_idx];
869 match item {
870 WorldItem::Interface { id, .. } => {
871 let iface = &self.resolve.interfaces[*id];
872 let Export::Instance { exports, .. } = &export else {
873 unreachable!("unexpectedly non export instance item")
874 };
875 for (ty_name, ty) in &iface.types {
876 let (export_idx, _exern_data) =
877 exports.get(ty_name, &NameMapNoIntern).unwrap();
878 match self.component.export_items[*export_idx] {
879 Export::Type(TypeDef::Resource(resource)) => {
880 let ty = crate::dealias(self.resolve, *ty);
881 let resource_table_ty = &self.types[resource];
882 let concrete_ty = resource_table_ty.unwrap_concrete_ty();
883 self.exports_resource_types.insert(ty, concrete_ty);
884 self.exports_resource_index_types.insert(concrete_ty, ty);
885 }
886 Export::Type(_) => {}
887 _ => unreachable!(
888 "unexpected type in component export items on iface [{iface_name}]",
889 iface_name = iface.name.as_deref().unwrap_or("<unknown>"),
890 ),
891 }
892 }
893 }
894 WorldItem::Function(_) => {}
895 WorldItem::Type { .. } => unreachable!("unexpected exported world item type"),
896 }
897 }
898 }
899
900 fn instantiate(&mut self) {
901 for (i, trampoline) in self.translation.trampolines.iter() {
903 let Trampoline::LowerImport {
904 index,
905 lower_ty,
906 options,
907 } = trampoline
908 else {
909 continue;
910 };
911
912 let options = self
913 .component
914 .options
915 .get(*options)
916 .expect("failed to find canon options");
917
918 let i = self.lowering_options.push((options, i, *lower_ty));
919 assert_eq!(i, *index);
920 }
921
922 if let Some(InstantiationMode::Async) = self.bindgen.opts.instantiation_mode {
923 if self.modules.len() > 1 {
926 self.src.js_init.push_str("Promise.all([");
927 for i in 0..self.modules.len() {
928 if i > 0 {
929 self.src.js_init.push_str(", ");
930 }
931 self.src.js_init.push_str(&format!("module{i}"));
932 }
933 uwriteln!(self.src.js_init, "]).catch(() => {{}});");
934 }
935 }
936
937 if !self.stream_tables.is_empty() {
943 let global_stream_table_map = self.bindgen.intrinsic(Intrinsic::AsyncStream(
944 AsyncStreamIntrinsic::GlobalStreamTableMap,
945 ));
946 let rep_table_class = Intrinsic::RepTableClass.name();
947 for (table_idx, component_idx) in self.stream_tables.iter() {
948 self.src.js.push_str(&format!(
949 "{global_stream_table_map}[{}] = {{ componentIdx: {}, table: new {rep_table_class}() }};\n",
950 table_idx.as_u32(),
951 component_idx.as_u32(),
952 ));
953 }
954 }
955
956 if !self.future_tables.is_empty() {
959 let global_future_table_map = self.bindgen.intrinsic(Intrinsic::AsyncFuture(
960 AsyncFutureIntrinsic::GlobalFutureTableMap,
961 ));
962 let rep_table_class = Intrinsic::RepTableClass.name();
963 for (table_idx, component_idx) in self.future_tables.iter() {
964 self.src.js.push_str(&format!(
965 "{global_future_table_map}[{}] = {{ componentIdx: {}, table: new {rep_table_class}() }};\n",
966 table_idx.as_u32(),
967 component_idx.as_u32(),
968 ));
969 }
970 }
971
972 if !self.err_ctx_tables.is_empty() {
975 let global_err_ctx_table_map = self
976 .bindgen
977 .intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::GlobalErrCtxTableMap));
978 let rep_table_class = Intrinsic::RepTableClass.name();
979 for (table_idx, component_idx) in self.err_ctx_tables.iter() {
980 self.src.js.push_str(&format!(
981 "{global_err_ctx_table_map}[{}] = {{ componentIdx: {}, table: new {rep_table_class}() }};\n",
982 table_idx.as_u32(),
983 component_idx.as_u32(),
984 ));
985 }
986 }
987
988 let mut lower_import_initializers = Vec::new();
996
997 for init in self.component.initializers.iter() {
999 match init {
1000 GlobalInitializer::InstantiateModule(_m, _maybe_idx) => {
1001 for lower_import_init in lower_import_initializers.drain(..) {
1003 self.instantiation_global_initializer(lower_import_init);
1004 }
1005 }
1006
1007 GlobalInitializer::LowerImport { .. } => {
1011 lower_import_initializers.push(init);
1012 continue;
1013 }
1014 _ => {}
1015 }
1016
1017 self.instantiation_global_initializer(init);
1018 }
1019
1020 for init in lower_import_initializers.drain(..) {
1022 self.instantiation_global_initializer(init);
1023 }
1024
1025 self.process_imports();
1027
1028 self.process_exports();
1030
1031 for (i, trampoline) in self
1034 .translation
1035 .trampolines
1036 .iter()
1037 .filter(|(_, t)| Instantiator::is_early_trampoline(t))
1038 {
1039 self.trampoline(i, trampoline);
1040 }
1041
1042 if self.bindgen.opts.instantiation_mode.is_some() {
1043 let js_init = mem::take(&mut self.src.js_init);
1044 self.src.js.push_str(&js_init);
1045 }
1046
1047 for (i, trampoline) in self
1050 .translation
1051 .trampolines
1052 .iter()
1053 .filter(|(_, t)| !Instantiator::is_early_trampoline(t))
1054 {
1055 self.trampoline(i, trampoline);
1056 }
1057 }
1058
1059 fn ensure_local_resource_class(&mut self, local_name: String) {
1060 if !self.defined_resource_classes.contains(&local_name) {
1061 uwriteln!(
1062 self.src.js,
1063 "\nclass {local_name} {{
1064 constructor () {{
1065 throw new Error('\"{local_name}\" resource does not define a constructor');
1066 }}
1067 }}"
1068 );
1069 self.defined_resource_classes.insert(local_name.to_string());
1070 }
1071 }
1072
1073 fn resource_definitions(&mut self) {
1074 for resource in 0..self.component.num_resources {
1077 let resource = ResourceIndex::from_u32(resource);
1078 let is_imported = self.component.defined_resource_index(resource).is_none();
1079 if is_imported {
1080 continue;
1081 }
1082 if let Some(local_name) = self.bindgen.local_names.try_get(resource) {
1083 self.ensure_local_resource_class(local_name.to_string());
1084 }
1085 }
1086
1087 }
1103
1104 fn ensure_error_context_local_table(
1112 &mut self,
1113 component_idx: RuntimeComponentInstanceIndex,
1114 err_ctx_tbl_idx: TypeComponentLocalErrorContextTableIndex,
1115 ) {
1116 if self.error_context_component_initialized[component_idx]
1117 && self.error_context_component_table_initialized[err_ctx_tbl_idx]
1118 {
1119 return;
1120 }
1121 let err_ctx_local_tables = self
1122 .bindgen
1123 .intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::ComponentLocalTable));
1124 let rep_table_class = Intrinsic::RepTableClass.name();
1125 let c = component_idx.as_u32();
1126 if !self.error_context_component_initialized[component_idx] {
1127 uwriteln!(self.src.js, "{err_ctx_local_tables}.set({c}, new Map());");
1128 self.error_context_component_initialized[component_idx] = true;
1129 }
1130 if !self.error_context_component_table_initialized[err_ctx_tbl_idx] {
1131 let t = err_ctx_tbl_idx.as_u32();
1132 uwriteln!(
1133 self.src.js,
1134 "{err_ctx_local_tables}.get({c}).set({t}, new {rep_table_class}({{ target: `component [{c}] local error ctx table [{t}]` }}));"
1135 );
1136 self.error_context_component_table_initialized[err_ctx_tbl_idx] = true;
1137 }
1138 }
1139
1140 fn ensure_resource_table(&mut self, resource_table_idx: TypeResourceTableIndex) {
1147 if self
1148 .resource_tables_initialized
1149 .contains_key(&resource_table_idx)
1150 {
1151 return;
1152 }
1153
1154 let resource_table_ty = &self.types[resource_table_idx];
1155 let resource_idx = resource_table_ty.unwrap_concrete_ty();
1156
1157 let (is_imported, maybe_dtor) =
1158 if let Some(resource_idx) = self.component.defined_resource_index(resource_idx) {
1159 let resource_def = self
1160 .component
1161 .initializers
1162 .iter()
1163 .find_map(|i| match i {
1164 GlobalInitializer::Resource(r) if r.index == resource_idx => Some(r),
1165 _ => None,
1166 })
1167 .unwrap();
1168
1169 if let Some(dtor) = &resource_def.dtor {
1170 (false, format!("\n{}(rep);", self.core_def(dtor)))
1171 } else {
1172 (false, "".into())
1173 }
1174 } else {
1175 (true, "".into())
1176 };
1177
1178 let handle_tables = self.bindgen.intrinsic(Intrinsic::HandleTables);
1179 let rsc_table_flag = self
1180 .bindgen
1181 .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableFlag));
1182 let rsc_table_remove = self
1183 .bindgen
1184 .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableRemove));
1185
1186 let rtid = resource_table_idx.as_u32();
1188 if is_imported {
1189 uwriteln!(
1191 self.src.js,
1192 r#"
1193 const handleTable{rtid} = [{rsc_table_flag}, 0];
1194 handleTable{rtid}._createdReps = new Set();
1195 "#,
1196 );
1197 if !self.resources_initialized.contains_key(&resource_idx) {
1198 let ridx = resource_idx.as_u32();
1199 uwriteln!(
1200 self.src.js,
1201 r#"
1202 const captureTable{ridx} = new Map();
1203 let captureCnt{ridx} = 0;
1204 "#
1205 );
1206 self.resources_initialized.insert(resource_idx, true);
1207 }
1208 } else {
1209 let finalization_registry_create = self
1211 .bindgen
1212 .intrinsic(Intrinsic::FinalizationRegistryCreate);
1213 uwriteln!(
1214 self.src.js,
1215 r#"
1216 const handleTable{rtid} = [{rsc_table_flag}, 0];
1217 handleTable{rtid}._createdReps = new Set();
1218 const finalizationRegistry{rtid} = {finalization_registry_create}((handle) => {{
1219 const {{ rep }} = {rsc_table_remove}(handleTable{rtid}, handle);{maybe_dtor}
1220 }});
1221 "#,
1222 );
1223 }
1224
1225 uwriteln!(self.src.js, "{handle_tables}[{rtid}] = handleTable{rtid};");
1227 self.resource_tables_initialized
1228 .insert(resource_table_idx, true);
1229 }
1230
1231 fn instance_flags(&mut self) {
1232 let mut instance_flag_defs = String::new();
1234 for used in self.used_instance_flags.borrow().iter() {
1235 let i = used.as_u32();
1236 uwriteln!(
1239 &mut instance_flag_defs,
1240 "const instanceFlags{i} = new WebAssembly.Global({{ value: \"i32\", mutable: true }}, 1);",
1241 );
1242 }
1243 self.src.js_init.prepend_str(&instance_flag_defs);
1244 }
1245
1246 fn is_early_trampoline(trampoline: &Trampoline) -> bool {
1251 matches!(
1252 trampoline,
1253 Trampoline::AsyncStartCall { .. }
1254 | Trampoline::BackpressureDec { .. }
1255 | Trampoline::BackpressureInc { .. }
1256 | Trampoline::EnterSyncCall
1257 | Trampoline::ErrorContextDebugMessage { .. }
1258 | Trampoline::ErrorContextDrop { .. }
1259 | Trampoline::ErrorContextNew { .. }
1260 | Trampoline::ErrorContextTransfer
1261 | Trampoline::ExitSyncCall
1262 | Trampoline::FutureCancelRead { .. }
1263 | Trampoline::FutureCancelWrite { .. }
1264 | Trampoline::FutureDropReadable { .. }
1265 | Trampoline::FutureDropWritable { .. }
1266 | Trampoline::FutureNew { .. }
1267 | Trampoline::FutureRead { .. }
1268 | Trampoline::FutureWrite { .. }
1269 | Trampoline::LowerImport { .. }
1270 | Trampoline::PrepareCall { .. }
1271 | Trampoline::ResourceDrop { .. }
1272 | Trampoline::ResourceNew { .. }
1273 | Trampoline::ResourceRep { .. }
1274 | Trampoline::ResourceTransferBorrow
1275 | Trampoline::ResourceTransferOwn
1276 | Trampoline::StreamCancelRead { .. }
1277 | Trampoline::StreamCancelWrite { .. }
1278 | Trampoline::StreamDropReadable { .. }
1279 | Trampoline::StreamDropWritable { .. }
1280 | Trampoline::StreamNew { .. }
1281 | Trampoline::StreamRead { .. }
1282 | Trampoline::StreamTransfer
1283 | Trampoline::StreamWrite { .. }
1284 | Trampoline::SubtaskCancel { .. }
1285 | Trampoline::SubtaskDrop { .. }
1286 | Trampoline::SyncStartCall { .. }
1287 | Trampoline::TaskCancel { .. }
1288 | Trampoline::TaskReturn { .. }
1289 | Trampoline::ThreadYield { .. }
1290 | Trampoline::ThreadYieldToSuspended { .. }
1291 | Trampoline::WaitableJoin { .. }
1292 | Trampoline::WaitableSetDrop { .. }
1293 | Trampoline::WaitableSetNew { .. }
1294 | Trampoline::WaitableSetPoll { .. }
1295 | Trampoline::WaitableSetWait { .. }
1296 )
1297 }
1298
1299 fn trampoline(&mut self, i: TrampolineIndex, trampoline: &'a Trampoline) {
1300 let i = i.as_u32();
1301 match trampoline {
1302 Trampoline::TaskCancel { instance } => {
1303 let task_cancel_fn = self
1304 .bindgen
1305 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::TaskCancel));
1306 uwriteln!(
1307 self.src.js,
1308 "const trampoline{i} = {task_cancel_fn}.bind(null, {instance_idx});\n",
1309 instance_idx = instance.as_u32(),
1310 );
1311 }
1312
1313 Trampoline::SubtaskCancel { instance, async_ } => {
1314 let task_cancel_fn = self
1315 .bindgen
1316 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::SubtaskCancel));
1317 uwriteln!(
1318 self.src.js,
1319 "const trampoline{i} = {task_cancel_fn}.bind(null, {instance_idx}, {async_});\n",
1320 instance_idx = instance.as_u32(),
1321 );
1322 }
1323
1324 Trampoline::SubtaskDrop { instance } => {
1325 let component_idx = instance.as_u32();
1326 let subtask_drop_fn = self
1327 .bindgen
1328 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::SubtaskDrop));
1329 uwriteln!(
1330 self.src.js,
1331 "const trampoline{i} = {subtask_drop_fn}.bind(
1332 null,
1333 {component_idx},
1334 );"
1335 );
1336 }
1337
1338 Trampoline::WaitableSetNew { instance } => {
1339 let waitable_set_new_fn = self
1340 .bindgen
1341 .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableSetNew));
1342 uwriteln!(
1343 self.src.js,
1344 "const trampoline{i} = {waitable_set_new_fn}.bind(null, {});\n",
1345 instance.as_u32(),
1346 );
1347 }
1348
1349 Trampoline::WaitableSetWait { instance, options } => {
1350 let options = self
1351 .component
1352 .options
1353 .get(*options)
1354 .expect("failed to find options");
1355 assert_eq!(
1356 instance.as_u32(),
1357 options.instance.as_u32(),
1358 "options index instance must match trampoline"
1359 );
1360
1361 let CanonicalOptions {
1362 instance,
1363 async_,
1364 data_model:
1365 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, .. }),
1366 ..
1367 } = options
1368 else {
1369 panic!("unexpected/missing memory data model during waitable-set.wait");
1370 };
1371
1372 let instance_idx = instance.as_u32();
1373 let memory_idx = memory
1374 .expect("missing memory idx for waitable-set.wait")
1375 .as_u32();
1376 let waitable_set_wait_fn = self
1377 .bindgen
1378 .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableSetWait));
1379
1380 uwriteln!(
1381 self.src.js,
1382 r#"
1383 const trampoline{i} = new WebAssembly.Suspending({waitable_set_wait_fn}.bind(null, {{
1384 componentIdx: {instance_idx},
1385 isAsync: {async_},
1386 memoryIdx: {memory_idx},
1387 getMemoryFn: () => memory{memory_idx},
1388 }}));
1389 "#,
1390 );
1391 }
1392
1393 Trampoline::WaitableSetPoll { options, .. } => {
1394 let CanonicalOptions {
1395 instance,
1396 async_,
1397 data_model:
1398 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, .. }),
1399 cancellable,
1400 ..
1401 } = self
1402 .component
1403 .options
1404 .get(*options)
1405 .expect("failed to find options")
1406 else {
1407 panic!("unexpected memory data model during waitable-set.poll");
1408 };
1409
1410 let instance_idx = instance.as_u32();
1411 let memory_idx = memory
1412 .expect("missing memory idx for waitable-set.poll")
1413 .as_u32();
1414 let waitable_set_poll_fn = self
1415 .bindgen
1416 .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableSetPoll));
1417
1418 uwriteln!(
1419 self.src.js,
1420 r#"
1421 const trampoline{i} = {waitable_set_poll_fn}.bind(
1422 null,
1423 {{
1424 componentIdx: {instance_idx},
1425 isAsync: {async_},
1426 isCancellable: {cancellable},
1427 memoryIdx: {memory_idx},
1428 getMemoryFn: () => memory{memory_idx},
1429 }}
1430 );
1431 "#,
1432 );
1433 }
1434
1435 Trampoline::WaitableSetDrop { instance } => {
1436 let waitable_set_drop_fn = self
1437 .bindgen
1438 .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableSetDrop));
1439 uwriteln!(
1440 self.src.js,
1441 "const trampoline{i} = {waitable_set_drop_fn}.bind(null, {instance_idx});\n",
1442 instance_idx = instance.as_u32(),
1443 );
1444 }
1445
1446 Trampoline::WaitableJoin { instance } => {
1447 let waitable_join_fn = self
1448 .bindgen
1449 .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableJoin));
1450 uwriteln!(
1451 self.src.js,
1452 "const trampoline{i} = {waitable_join_fn}.bind(null, {instance_idx});\n",
1453 instance_idx = instance.as_u32(),
1454 );
1455 }
1456
1457 Trampoline::StreamNew { ty, instance } => {
1458 let stream_new_fn = self
1459 .bindgen
1460 .intrinsic(Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamNew));
1461 let instance_idx = instance.as_u32();
1462 let stream_table_idx = ty.as_u32();
1463
1464 let table_ty = &self.types[*ty];
1466 let stream_ty_idx = table_ty.ty;
1467 let stream_ty = &self.types[stream_ty_idx];
1468
1469 let payload_ty_name_js = stream_ty
1474 .payload
1475 .map(|iface_ty| format!("'{iface_ty:?}'"))
1476 .unwrap_or_else(|| "null".into());
1477
1478 let (
1480 align_32_js,
1481 size_32_js,
1482 flat_count_js,
1483 lift_fn_js,
1484 lower_fn_js,
1485 is_none_js,
1486 is_numeric_type_js,
1487 is_borrow_js,
1488 is_async_value_js,
1489 typed_array_js,
1490 ) = match stream_ty.payload {
1491 None => (
1493 "0".into(),
1494 "0".into(),
1495 "0".into(),
1496 "null".into(),
1497 "null".into(),
1498 "true",
1499 "false".into(),
1500 "false".into(),
1501 "false".into(),
1502 "undefined",
1503 ),
1504 Some(ty) => (
1506 self.types.canonical_abi(&ty).align32.to_string(),
1507 self.types.canonical_abi(&ty).size32.to_string(),
1508 self.types
1509 .canonical_abi(&ty)
1510 .flat_count
1511 .map(|v| v.to_string())
1512 .unwrap_or_else(|| "null".into()),
1513 gen_flat_lift_fn_js_expr(self, &ty, &None),
1514 gen_flat_lower_fn_js_expr(self, &ty, &None),
1515 "false",
1516 format!(
1517 "{}",
1518 matches!(
1519 ty,
1520 InterfaceType::U8
1521 | InterfaceType::U16
1522 | InterfaceType::U32
1523 | InterfaceType::U64
1524 | InterfaceType::S8
1525 | InterfaceType::S16
1526 | InterfaceType::S32
1527 | InterfaceType::S64
1528 | InterfaceType::Float32
1529 | InterfaceType::Float64
1530 )
1531 ),
1532 format!("{}", matches!(ty, InterfaceType::Borrow(_))),
1533 format!(
1534 "{}",
1535 matches!(ty, InterfaceType::Stream(_) | InterfaceType::Future(_))
1536 ),
1537 js_typed_array_ctor(&ty).unwrap_or("undefined"),
1538 ),
1539 };
1540
1541 uwriteln!(
1542 self.src.js,
1543 "const trampoline{i} = {stream_new_fn}.bind(null, {{
1544 streamTableIdx: {stream_table_idx},
1545 callerComponentIdx: {instance_idx},
1546 elemMeta: {{
1547 liftFn: {lift_fn_js},
1548 lowerFn: {lower_fn_js},
1549 payloadTypeName: {payload_ty_name_js},
1550 isNone: {is_none_js},
1551 isNumeric: {is_numeric_type_js},
1552 isBorrowed: {is_borrow_js},
1553 isAsyncValue: {is_async_value_js},
1554 typedArray: {typed_array_js},
1555 flatCount: {flat_count_js},
1556 align32: {align_32_js},
1557 size32: {size_32_js},
1558 }},
1559 }});\n",
1560 );
1561 }
1562
1563 Trampoline::StreamRead {
1564 instance,
1565 ty,
1566 options,
1567 } => {
1568 let options = self
1569 .component
1570 .options
1571 .get(*options)
1572 .expect("failed to find options");
1573 assert_eq!(
1574 instance.as_u32(),
1575 options.instance.as_u32(),
1576 "options index instance must match trampoline"
1577 );
1578
1579 let CanonicalOptions {
1580 instance,
1581 string_encoding,
1582 async_,
1583 data_model:
1584 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1585 ..
1586 } = options
1587 else {
1588 unreachable!("missing/invalid data model for options during stream.read")
1589 };
1590 let memory_idx = memory.expect("missing memory idx for stream.read").as_u32();
1591 let (realloc_idx, get_realloc_fn_js) = match realloc {
1592 Some(v) => {
1593 let v = v.as_u32().to_string();
1594 (v.to_string(), format!("() => realloc{v}"))
1595 }
1596 None => ("undefined".into(), "undefined".into()),
1597 };
1598
1599 let component_instance_id = instance.as_u32();
1600 let string_encoding = string_encoding_js_literal(string_encoding);
1601 let stream_table_idx = ty.as_u32();
1602 let stream_read_fn = self
1603 .bindgen
1604 .intrinsic(Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamRead));
1605
1606 let register_global_memory_for_component_fn =
1611 Intrinsic::RegisterGlobalMemoryForComponent.name();
1612 uwriteln!(
1613 self.src.js_init,
1614 r#"{register_global_memory_for_component_fn}({{
1615 componentIdx: {component_instance_id},
1616 memoryIdx: {memory_idx},
1617 memory: memory{memory_idx},
1618 }});"#
1619 );
1620
1621 uwriteln!(
1622 self.src.js,
1623 r#"const trampoline{i} = new WebAssembly.Suspending({stream_read_fn}.bind(
1624 null,
1625 {{
1626 componentIdx: {component_instance_id},
1627 memoryIdx: {memory_idx},
1628 getMemoryFn: () => memory{memory_idx},
1629 reallocIdx: {realloc_idx},
1630 getReallocFn: {get_realloc_fn_js},
1631 stringEncoding: {string_encoding},
1632 isAsync: {async_},
1633 streamTableIdx: {stream_table_idx},
1634 }}
1635 ));
1636 "#,
1637 );
1638 }
1639
1640 Trampoline::StreamWrite {
1641 instance,
1642 ty,
1643 options,
1644 } => {
1645 let options = self
1646 .component
1647 .options
1648 .get(*options)
1649 .expect("failed to find options");
1650 assert_eq!(
1651 instance.as_u32(),
1652 options.instance.as_u32(),
1653 "options index instance must match trampoline"
1654 );
1655
1656 let CanonicalOptions {
1657 instance,
1658 string_encoding,
1659 async_,
1660 data_model:
1661 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1662 ..
1663 } = options
1664 else {
1665 unreachable!("unexpected memory data model during stream.write");
1666 };
1667 let component_instance_id = instance.as_u32();
1668 let memory_idx = memory
1669 .expect("missing memory idx for stream.write")
1670 .as_u32();
1671 let (realloc_idx, get_realloc_fn_js) = match realloc {
1672 Some(v) => {
1673 let v = v.as_u32().to_string();
1674 (v.to_string(), format!("() => realloc{v}"))
1675 }
1676 None => ("undefined".into(), "undefined".into()),
1677 };
1678
1679 let string_encoding = string_encoding_js_literal(string_encoding);
1680 let stream_table_idx = ty.as_u32();
1681 let stream_write_fn = self
1682 .bindgen
1683 .intrinsic(Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamWrite));
1684
1685 let register_global_memory_for_component_fn =
1689 Intrinsic::RegisterGlobalMemoryForComponent.name();
1690 uwriteln!(
1691 self.src.js_init,
1692 r#"{register_global_memory_for_component_fn}({{
1693 componentIdx: {component_instance_id},
1694 memoryIdx: {memory_idx},
1695 memory: memory{memory_idx},
1696 }});"#
1697 );
1698
1699 uwriteln!(
1700 self.src.js,
1701 r#"
1702 const trampoline{i} = new WebAssembly.Suspending({stream_write_fn}.bind(
1703 null,
1704 {{
1705 componentIdx: {component_instance_id},
1706 memoryIdx: {memory_idx},
1707 getMemoryFn: () => memory{memory_idx},
1708 reallocIdx: {realloc_idx},
1709 getReallocFn: {get_realloc_fn_js},
1710 stringEncoding: {string_encoding},
1711 isAsync: {async_},
1712 streamTableIdx: {stream_table_idx},
1713 }}
1714 ));
1715 "#,
1716 );
1717 }
1718
1719 Trampoline::StreamCancelRead {
1720 instance,
1721 ty,
1722 async_,
1723 }
1724 | Trampoline::StreamCancelWrite {
1725 instance,
1726 ty,
1727 async_,
1728 } => {
1729 let stream_cancel_fn = match trampoline {
1730 Trampoline::StreamCancelRead { .. } => self.bindgen.intrinsic(
1731 Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamCancelRead),
1732 ),
1733 Trampoline::StreamCancelWrite { .. } => self.bindgen.intrinsic(
1734 Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamCancelWrite),
1735 ),
1736 _ => unreachable!("unexpected trampoline"),
1737 };
1738
1739 let stream_table_idx = ty.as_u32();
1740 let component_idx = instance.as_u32();
1741 uwriteln!(
1742 self.src.js,
1743 r#"
1744 const trampoline{i} = new WebAssembly.Suspending({stream_cancel_fn}.bind(null, {{
1745 streamTableIdx: {stream_table_idx},
1746 isAsync: {async_},
1747 componentIdx: {component_idx},
1748 }}));
1749 "#,
1750 );
1751 }
1752
1753 Trampoline::StreamDropReadable { ty, instance }
1754 | Trampoline::StreamDropWritable { ty, instance } => {
1755 let intrinsic_fn = match trampoline {
1756 Trampoline::StreamDropReadable { .. } => self.bindgen.intrinsic(
1757 Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamDropReadable),
1758 ),
1759 Trampoline::StreamDropWritable { .. } => self.bindgen.intrinsic(
1760 Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamDropWritable),
1761 ),
1762 _ => unreachable!("unexpected trampoline"),
1763 };
1764 let stream_idx = ty.as_u32();
1765 let instance_idx = instance.as_u32();
1766 uwriteln!(
1767 self.src.js,
1768 "const trampoline{i} = {intrinsic_fn}.bind(null, {{
1769 streamTableIdx: {stream_idx},
1770 componentIdx: {instance_idx},
1771 }});\n",
1772 );
1773 }
1774
1775 Trampoline::StreamTransfer => {
1776 let stream_transfer_fn = self
1777 .bindgen
1778 .intrinsic(Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamTransfer));
1779 uwriteln!(self.src.js, "const trampoline{i} = {stream_transfer_fn};\n",);
1780 }
1781
1782 Trampoline::FutureNew { instance, ty } => {
1783 let future_new_fn = self
1784 .bindgen
1785 .intrinsic(Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureNew));
1786 let future_table_idx = ty.as_u32();
1787 let component_idx = instance.as_u32();
1788
1789 let future_table_ty = &self.types[*ty];
1791 let future_ty = &self.types[future_table_ty.ty];
1792 let (
1793 payload_size32,
1794 payload_align32,
1795 payload_flat_count_js,
1796 payload_lift_fn_js,
1797 payload_lower_fn_js,
1798 is_borrowed,
1799 is_none_type,
1800 is_numeric_type,
1801 is_async_value,
1802 ) = match future_ty.payload {
1803 None => (
1804 0,
1805 0,
1806 "0".into(),
1807 "() => {{ throw new Error('empty future payload'); }}".into(),
1808 "() => {{ throw new Error('empty future payload'); }}".into(),
1809 false,
1810 true,
1811 false,
1812 false,
1813 ),
1814 Some(payload_ty) => {
1815 let cabi = self.types.canonical_abi(&payload_ty);
1816 (
1817 cabi.size32,
1818 cabi.align32,
1819 cabi.flat_count
1820 .map(|v| format!("{v}"))
1821 .unwrap_or_else(|| "null".into()),
1822 gen_flat_lift_fn_js_expr(self, &payload_ty, &None),
1823 gen_flat_lower_fn_js_expr(self, &payload_ty, &None),
1824 matches!(payload_ty, InterfaceType::Borrow(_)),
1825 false,
1826 matches!(
1827 payload_ty,
1828 InterfaceType::U8
1829 | InterfaceType::U16
1830 | InterfaceType::U32
1831 | InterfaceType::U64
1832 | InterfaceType::S8
1833 | InterfaceType::S16
1834 | InterfaceType::S32
1835 | InterfaceType::S64
1836 | InterfaceType::Float32
1837 | InterfaceType::Float64
1838 ),
1839 matches!(
1840 payload_ty,
1841 InterfaceType::Stream(_) | InterfaceType::Future(_)
1842 ),
1843 )
1844 }
1845 };
1846 let payload_ty_name_js = future_ty
1847 .payload
1848 .map(|iface_ty| format!("'{iface_ty:?}'"))
1849 .unwrap_or_else(|| "null".into());
1850
1851 uwriteln!(
1852 self.src.js,
1853 r#"
1854 const trampoline{i} = {future_new_fn}.bind(null, {{
1855 componentIdx: {component_idx},
1856 futureTableIdx: {future_table_idx},
1857 elemMeta: {{
1858 liftFn: {payload_lift_fn_js},
1859 lowerFn: {payload_lower_fn_js},
1860 payloadTypeName: {payload_ty_name_js},
1861 isNone: {is_none_type},
1862 isNumeric: {is_numeric_type},
1863 isBorrowed: {is_borrowed},
1864 isAsyncValue: {is_async_value},
1865 flatCount: {payload_flat_count_js},
1866 align32: {payload_align32},
1867 size32: {payload_size32},
1868 }},
1869 }});
1870 "#,
1871 );
1872 }
1873
1874 Trampoline::FutureWrite {
1875 instance,
1876 ty,
1877 options,
1878 }
1879 | Trampoline::FutureRead {
1880 instance,
1881 ty,
1882 options,
1883 } => {
1884 let intrinsic_fn = match trampoline {
1885 Trampoline::FutureRead { .. } => self
1886 .bindgen
1887 .intrinsic(Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureRead)),
1888 Trampoline::FutureWrite { .. } => self
1889 .bindgen
1890 .intrinsic(Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureWrite)),
1891 _ => unreachable!("invalid trampoline"),
1892 };
1893
1894 let options = self
1895 .component
1896 .options
1897 .get(*options)
1898 .expect("failed to find options");
1899 let CanonicalOptions {
1900 async_,
1901 string_encoding,
1902 callback,
1903 post_return,
1904 data_model:
1905 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1906 ..
1907 } = options
1908 else {
1909 unreachable!("unexpected memory data model during future intrinsic");
1910 };
1911
1912 assert_eq!(
1913 *instance, options.instance,
1914 "component instances should match"
1915 );
1916 assert!(
1917 callback.is_none(),
1918 "callback should not be present for future intrinsic"
1919 );
1920 assert!(
1921 post_return.is_none(),
1922 "post_return should not be present for future intrinsic"
1923 );
1924
1925 let future_table_idx = ty.as_u32();
1926 let component_idx = instance.as_u32();
1927 let memory_idx = memory
1928 .expect("missing memory idx for future intrinsic")
1929 .as_u32();
1930 let (realloc_idx, get_realloc_fn_js) = match realloc {
1931 Some(idx) => (
1932 idx.as_u32().to_string(),
1933 format!("() => realloc{}", idx.as_u32()),
1934 ),
1935 None => ("undefined".into(), "undefined".to_string()),
1936 };
1937 let string_encoding = string_encoding_js_literal(string_encoding);
1938
1939 uwriteln!(
1940 self.src.js,
1941 r#"
1942 const trampoline{i} = new WebAssembly.Suspending({intrinsic_fn}.bind(
1943 null,
1944 {{
1945 componentIdx: {component_idx},
1946 memoryIdx: {memory_idx},
1947 getMemoryFn: () => memory{memory_idx},
1948 reallocIdx: {realloc_idx},
1949 getReallocFn: {get_realloc_fn_js},
1950 stringEncoding: {string_encoding},
1951 futureTableIdx: {future_table_idx},
1952 isAsync: {async_},
1953 }},
1954 ));
1955 "#,
1956 );
1957 }
1958
1959 Trampoline::FutureCancelRead {
1960 instance,
1961 ty,
1962 async_,
1963 }
1964 | Trampoline::FutureCancelWrite {
1965 instance,
1966 ty,
1967 async_,
1968 } => {
1969 let future_cancel_op_fn = match trampoline {
1970 Trampoline::FutureCancelRead { .. } => self.bindgen.intrinsic(
1971 Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureCancelRead),
1972 ),
1973 Trampoline::FutureCancelWrite { .. } => self.bindgen.intrinsic(
1974 Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureCancelWrite),
1975 ),
1976 _ => unreachable!(),
1977 };
1978
1979 let component_idx = instance.as_u32();
1980 let future_table_idx = ty.as_u32();
1981
1982 uwriteln!(
1983 self.src.js,
1984 r#"
1985 const trampoline{i} = new WebAssembly.Suspending({future_cancel_op_fn}.bind(
1986 null,
1987 {{
1988 futureTableIdx: {future_table_idx},
1989 componentIdx: {component_idx},
1990 isAsync: {async_},
1991 }},
1992 ));
1993 "#,
1994 );
1995 }
1996
1997 Trampoline::FutureDropReadable { instance, ty }
1998 | Trampoline::FutureDropWritable { instance, ty } => {
1999 let future_drop_op_fn = match trampoline {
2000 Trampoline::FutureDropReadable { .. } => self.bindgen.intrinsic(
2001 Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureDropReadable),
2002 ),
2003 Trampoline::FutureDropWritable { .. } => self.bindgen.intrinsic(
2004 Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureDropWritable),
2005 ),
2006 _ => unreachable!(),
2007 };
2008
2009 let component_idx = instance.as_u32();
2010 let future_table_idx = ty.as_u32();
2011
2012 uwriteln!(
2013 self.src.js,
2014 r#"
2015 const trampoline{i} = new WebAssembly.Suspending({future_drop_op_fn}.bind(
2016 null,
2017 {{
2018 futureTableIdx: {future_table_idx},
2019 componentIdx: {component_idx},
2020 }},
2021 ));
2022 "#
2023 );
2024 }
2025
2026 Trampoline::FutureTransfer => {
2027 let future_drop_writable_fn = self
2028 .bindgen
2029 .intrinsic(Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureTransfer));
2030 uwriteln!(
2031 self.src.js,
2032 "const trampoline{i} = {future_drop_writable_fn};"
2033 );
2034 }
2035
2036 Trampoline::ErrorContextNew { ty, options, .. } => {
2037 let CanonicalOptions {
2038 instance,
2039 string_encoding,
2040 data_model:
2041 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, .. }),
2042 ..
2043 } = self
2044 .component
2045 .options
2046 .get(*options)
2047 .expect("failed to find options")
2048 else {
2049 panic!("unexpected memory data model during error-context.new");
2050 };
2051
2052 self.ensure_error_context_local_table(*instance, *ty);
2053
2054 let local_err_tbl_idx = ty.as_u32();
2055 let component_idx = instance.as_u32();
2056
2057 let memory_idx = memory
2058 .expect("missing realloc fn idx for error-context.debug-message")
2059 .as_u32();
2060
2061 let decoder = match string_encoding {
2063 wasmtime_environ::component::StringEncoding::Utf8 => self
2064 .bindgen
2065 .intrinsic(Intrinsic::String(StringIntrinsic::GlobalTextDecoderUtf8)),
2066 wasmtime_environ::component::StringEncoding::Utf16 => self
2067 .bindgen
2068 .intrinsic(Intrinsic::String(StringIntrinsic::Utf16Decoder)),
2069 enc => panic!(
2070 "unsupported string encoding [{enc:?}] for error-context.debug-message"
2071 ),
2072 };
2073 uwriteln!(
2074 self.src.js,
2075 "function trampoline{i}InputStr(ptr, len) {{
2076 return {decoder}.decode(new DataView(memory{memory_idx}.buffer, ptr, len));
2077 }}"
2078 );
2079
2080 let err_ctx_new_fn = self
2081 .bindgen
2082 .intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::ErrorContextNew));
2083 uwriteln!(
2085 self.src.js,
2086 "const trampoline{i} = {err_ctx_new_fn}.bind(
2087 null,
2088 {{
2089 componentIdx: {component_idx},
2090 localTableIdx: {local_err_tbl_idx},
2091 readStrFn: trampoline{i}InputStr,
2092 }}
2093 );
2094 "
2095 );
2096 }
2097
2098 Trampoline::ErrorContextDebugMessage {
2099 instance, options, ..
2100 } => {
2101 let CanonicalOptions {
2102 async_,
2103 callback,
2104 post_return,
2105 string_encoding,
2106 data_model:
2107 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
2108 ..
2109 } = self
2110 .component
2111 .options
2112 .get(*options)
2113 .expect("failed to find options")
2114 else {
2115 panic!("unexpected memory data model during error-context.debug-message");
2116 };
2117
2118 let debug_message_fn = self
2119 .bindgen
2120 .intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::ErrorContextDebugMessage));
2121
2122 let realloc_fn_idx = realloc
2123 .expect("missing realloc fn idx for error-context.debug-message")
2124 .as_u32();
2125 let memory_idx = memory
2126 .expect("missing realloc fn idx for error-context.debug-message")
2127 .as_u32();
2128
2129 match string_encoding {
2131 wasmtime_environ::component::StringEncoding::Utf8 => {
2132 let encode_fn = self
2133 .bindgen
2134 .intrinsic(Intrinsic::String(StringIntrinsic::Utf8Encode));
2135 uwriteln!(
2136 self.src.js,
2137 "function trampoline{i}OutputStr(s, outputPtr) {{
2138 const memory = memory{memory_idx};
2139 const reallocFn = realloc{realloc_fn_idx};
2140 let {{ ptr, len }} = {encode_fn}(s, reallocFn, memory);
2141 new DataView(memory.buffer).setUint32(outputPtr, ptr, true)
2142 new DataView(memory.buffer).setUint32(outputPtr + 4, len, true)
2143 }}"
2144 );
2145 }
2146 wasmtime_environ::component::StringEncoding::Utf16 => {
2147 let encode_fn = self
2148 .bindgen
2149 .intrinsic(Intrinsic::String(StringIntrinsic::Utf16Encode));
2150 uwriteln!(
2151 self.src.js,
2152 "function trampoline{i}OutputStr(s, outputPtr) {{
2153 const memory = memory{memory_idx};
2154 const reallocFn = realloc{realloc_fn_idx};
2155 let ptr = {encode_fn}(s, reallocFn, memory);
2156 let len = s.length;
2157 new DataView(memory.buffer).setUint32(outputPtr, ptr, true)
2158 new DataView(memory.buffer).setUint32(outputPtr + 4, len, true)
2159 }}"
2160 );
2161 }
2162 enc => panic!(
2163 "unsupported string encoding [{enc:?}] for error-context.debug-message"
2164 ),
2165 };
2166
2167 let options_obj = format!(
2168 "{{callback:{callback}, postReturn: {post_return}, async: {async_}}}",
2169 callback = callback
2170 .map(|v| v.as_u32().to_string())
2171 .unwrap_or_else(|| "null".into()),
2172 post_return = post_return
2173 .map(|v| v.as_u32().to_string())
2174 .unwrap_or_else(|| "null".into()),
2175 );
2176
2177 let component_idx = instance.as_u32();
2178 uwriteln!(
2179 self.src.js,
2180 "const trampoline{i} = {debug_message_fn}.bind(
2181 null,
2182 {{
2183 componentIdx: {component_idx},
2184 options: {options_obj},
2185 writeStrFn: trampoline{i}OutputStr,
2186 }}
2187 );"
2188 );
2189 }
2190
2191 Trampoline::ErrorContextDrop { instance, ty } => {
2192 let drop_fn = self
2193 .bindgen
2194 .intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::ErrorContextDrop));
2195 let local_err_tbl_idx = ty.as_u32();
2196 let component_idx = instance.as_u32();
2197 uwriteln!(
2198 self.src.js,
2199 r#"
2200 const trampoline{i} = {drop_fn}.bind(
2201 null,
2202 {{ componentIdx: {component_idx}, localTableIdx: {local_err_tbl_idx} }},
2203 );
2204 "#
2205 );
2206 }
2207
2208 Trampoline::ErrorContextTransfer => {
2209 let transfer_fn = self
2210 .bindgen
2211 .intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::ErrorContextTransfer));
2212 uwriteln!(self.src.js, "const trampoline{i} = {transfer_fn};");
2213 }
2214
2215 Trampoline::PrepareCall { memory } => {
2217 let prepare_call_fn = self
2218 .bindgen
2219 .intrinsic(Intrinsic::Host(HostIntrinsic::PrepareCall));
2220 let (memory_idx_js, memory_fn_js) = memory
2221 .map(|v| {
2222 (
2223 v.as_u32().to_string(),
2224 format!("() => memory{}", v.as_u32()),
2225 )
2226 })
2227 .unwrap_or_else(|| ("null".into(), "() => null".into()));
2228 uwriteln!(
2229 self.src.js,
2230 "const trampoline{i} = {prepare_call_fn}.bind(null, {memory_idx_js}, {memory_fn_js});",
2231 )
2232 }
2233
2234 Trampoline::SyncStartCall { callback } => {
2235 let sync_start_call_fn = self
2236 .bindgen
2237 .intrinsic(Intrinsic::Host(HostIntrinsic::SyncStartCall));
2238 uwriteln!(
2239 self.src.js,
2240 "const trampoline{i} = {sync_start_call_fn}.bind(null, {});",
2241 callback
2242 .map(|v| v.as_u32().to_string())
2243 .unwrap_or_else(|| "null".into()),
2244 );
2245 }
2246
2247 Trampoline::AsyncStartCall {
2250 callback,
2251 post_return,
2252 } => {
2253 let async_start_call_fn = self
2254 .bindgen
2255 .intrinsic(Intrinsic::Host(HostIntrinsic::AsyncStartCall));
2256 let (callback_idx, callback_fn) = callback
2257 .map(|v| (v.as_u32().to_string(), format!("callback_{}", v.as_u32())))
2258 .unwrap_or_else(|| ("null".into(), "null".into()));
2259 let (post_return_idx, post_return_fn) = post_return
2260 .map(|v| (v.as_u32().to_string(), format!("postReturn{}", v.as_u32())))
2261 .unwrap_or_else(|| ("null".into(), "null".into()));
2262
2263 uwriteln!(
2264 self.src.js,
2265 "const trampoline{i} = {async_start_call_fn}.bind(
2266 null,
2267 {{
2268 postReturnIdx: {post_return_idx},
2269 getPostReturnFn: () => {post_return_fn},
2270 callbackIdx: {callback_idx},
2271 getCallbackFn: () => {callback_fn},
2272 }},
2273 );",
2274 );
2275 }
2276
2277 Trampoline::LowerImport {
2278 index: _,
2279 lower_ty,
2280 options,
2281 } => {
2282 let canon_opts = self
2283 .component
2284 .options
2285 .get(*options)
2286 .expect("failed to find options");
2287
2288 let component_idx = canon_opts.instance.as_u32();
2294 let is_async = canon_opts.async_;
2295
2296 let cancellable = canon_opts.cancellable;
2297
2298 let func_ty = self.types.index(*lower_ty);
2299
2300 let param_types = &self.types.index(func_ty.params).types;
2302 let param_lift_fns_js =
2303 gen_flat_lift_fn_list_js_expr(self, param_types.iter().as_slice(), &None);
2304
2305 let result_types = &self.types.index(func_ty.results).types;
2307 let result_lower_fns_js =
2308 gen_flat_lower_fn_list_js_expr(self, result_types.iter().as_slice(), &None);
2309 let result_flat_count = result_types.iter().try_fold(0usize, |count, ty| {
2310 self.types
2311 .canonical_abi(ty)
2312 .flat_count
2313 .map(|flat_count| count + usize::from(flat_count))
2314 });
2315
2316 let get_callback_fn_js = canon_opts
2317 .callback
2318 .map(|idx| format!("() => callback_{}", idx.as_u32()))
2319 .unwrap_or_else(|| "() => null".into());
2320 let get_post_return_fn_js = canon_opts
2321 .post_return
2322 .map(|idx| format!("() => postReturn{}", idx.as_u32()))
2323 .unwrap_or_else(|| "() => null".into());
2324
2325 let (memory_exprs, realloc_expr_js) =
2327 if let CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions {
2328 memory,
2329 realloc,
2330 }) = canon_opts.data_model
2331 {
2332 (
2333 memory.map(|idx| {
2334 (
2335 idx.as_u32().to_string(),
2336 format!("() => memory{}", idx.as_u32()),
2337 )
2338 }),
2339 realloc.map(|idx| format!("() => realloc{}", idx.as_u32())),
2340 )
2341 } else {
2342 (None, None)
2343 };
2344 let (memory_idx_js, memory_expr_js) =
2345 memory_exprs.unwrap_or_else(|| ("null".into(), "() => null".into()));
2346 let realloc_expr_js = realloc_expr_js.unwrap_or_else(|| "undefined".into());
2347 let string_encoding_js = string_encoding_js_literal(&canon_opts.string_encoding);
2348
2349 let func_ty_async = func_ty.async_;
2351 let max_direct_results = if is_async || func_ty_async {
2352 0
2353 } else {
2354 MAX_FLAT_RESULTS
2355 };
2356 let has_result_pointer = result_flat_count
2357 .map(|count| count > max_direct_results)
2358 .unwrap_or(true);
2359 let call = format!(
2360 r#"{lower_import_intrinsic}.bind(
2361 null,
2362 {{
2363 trampolineIdx: {i},
2364 componentIdx: {component_idx},
2365 isAsync: {is_async},
2366 isManualAsync: _trampoline{i}.manuallyAsync,
2367 paramLiftFns: {param_lift_fns_js},
2368 resultLowerFns: {result_lower_fns_js},
2369 hasResultPointer: {has_result_pointer},
2370 funcTypeIsAsync: {func_ty_async},
2371 getCallbackFn: {get_callback_fn_js},
2372 getPostReturnFn: {get_post_return_fn_js},
2373 isCancellable: {cancellable},
2374 memoryIdx: {memory_idx_js},
2375 stringEncoding: {string_encoding_js},
2376 getMemoryFn: {memory_expr_js},
2377 getReallocFn: {realloc_expr_js},
2378 importFn: _trampoline{i},
2379 }},
2380 )"#,
2381 lower_import_intrinsic = if is_async || func_ty_async {
2382 self.bindgen
2383 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::LowerImport))
2384 } else {
2385 self.bindgen.intrinsic(Intrinsic::AsyncTask(
2386 AsyncTaskIntrinsic::LowerImportBackwardsCompat,
2387 ))
2388 }
2389 );
2390
2391 if is_async || func_ty_async {
2394 uwriteln!(
2395 self.src.js,
2396 "let trampoline{i} = new WebAssembly.Suspending({call});"
2397 );
2398 } else {
2399 uwriteln!(
2402 self.src.js,
2403 "let trampoline{i} = _trampoline{i}.manuallyAsync ? new WebAssembly.Suspending({call}) : {call};"
2404 );
2405 }
2406 }
2407
2408 Trampoline::Transcoder {
2409 op,
2410 from,
2411 from64,
2412 to,
2413 to64,
2414 } => {
2415 if *from64 || *to64 {
2416 unimplemented!("memory 64 transcoder");
2417 }
2418 let from = from.as_u32();
2419 let to = to.as_u32();
2420 match op {
2421 Transcode::Copy(FixedEncoding::Utf8) => {
2422 uwriteln!(
2423 self.src.js,
2424 r#"
2425 function trampoline{i} (from_ptr, len, to_ptr) {{
2426 new Uint8Array(memory{to}.buffer, to_ptr, len).set(new Uint8Array(memory{from}.buffer, from_ptr, len));
2427 }}
2428 "#
2429 );
2430 }
2431 Transcode::Copy(FixedEncoding::Utf16) => unimplemented!("utf16 copier"),
2432 Transcode::Copy(FixedEncoding::Latin1) => unimplemented!("latin1 copier"),
2433 Transcode::Latin1ToUtf16 => unimplemented!("latin to utf16 transcoder"),
2434 Transcode::Latin1ToUtf8 => unimplemented!("latin to utf8 transcoder"),
2435 Transcode::Utf16ToCompactProbablyUtf16 => {
2436 unimplemented!("utf16 to compact wtf16 transcoder")
2437 }
2438 Transcode::Utf16ToCompactUtf16 => {
2439 unimplemented!("utf16 to compact utf16 transcoder")
2440 }
2441 Transcode::Utf16ToLatin1 => unimplemented!("utf16 to latin1 transcoder"),
2442 Transcode::Utf16ToUtf8 => {
2443 uwriteln!(
2444 self.src.js,
2445 r#"
2446 function trampoline{i} (src, src_len, dst, dst_len) {{
2447 const encoder = new TextEncoder();
2448 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));
2449 return [read, written];
2450 }}
2451 "#,
2452 );
2453 }
2454 Transcode::Utf8ToCompactUtf16 => {
2455 unimplemented!("utf8 to compact utf16 transcoder")
2456 }
2457 Transcode::Utf8ToLatin1 => unimplemented!("utf8 to latin1 transcoder"),
2458 Transcode::Utf8ToUtf16 => {
2459 uwriteln!(
2460 self.src.js,
2461 r#"
2462 function trampoline{i} (from_ptr, len, to_ptr) {{
2463 const decoder = new TextDecoder();
2464 const content = decoder.decode(new Uint8Array(memory{from}.buffer, from_ptr, len));
2465 const strlen = content.length
2466 const view = new Uint16Array(memory{to}.buffer, to_ptr, strlen * 2)
2467 for (var i = 0; i < strlen; i++) {{
2468 view[i] = content.charCodeAt(i);
2469 }}
2470 return strlen;
2471 }}
2472 "#,
2473 );
2474 }
2475 };
2476 }
2477
2478 Trampoline::ResourceNew {
2479 ty: resource_ty_idx,
2480 ..
2481 } => {
2482 self.ensure_resource_table(*resource_ty_idx);
2483 let rid = resource_ty_idx.as_u32();
2484 let rsc_table_create_own = self.bindgen.intrinsic(Intrinsic::Resource(
2485 ResourceIntrinsic::ResourceTableCreateOwn,
2486 ));
2487 uwriteln!(
2488 self.src.js,
2489 "const trampoline{i} = {rsc_table_create_own}.bind(null, handleTable{rid});"
2490 );
2491 }
2492
2493 Trampoline::ResourceRep {
2494 ty: resource_ty_idx,
2495 ..
2496 } => {
2497 self.ensure_resource_table(*resource_ty_idx);
2498 let rid = resource_ty_idx.as_u32();
2499 let rsc_flag = self
2500 .bindgen
2501 .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableFlag));
2502 uwriteln!(
2503 self.src.js,
2504 "function trampoline{i} (handle) {{
2505 return handleTable{rid}[(handle << 1) + 1] & ~{rsc_flag};
2506 }}"
2507 );
2508 }
2509
2510 Trampoline::ResourceDrop {
2511 ty: resource_table_ty_idx,
2512 ..
2513 } => {
2514 self.ensure_resource_table(*resource_table_ty_idx);
2515 let tid = resource_table_ty_idx.as_u32();
2516 let resource_table_ty = &self.types[*resource_table_ty_idx];
2517 let resource_ty = resource_table_ty.unwrap_concrete_ty();
2518 let rid = resource_ty.as_u32();
2519
2520 let dtor = if let Some(resource_idx) =
2522 self.component.defined_resource_index(resource_ty)
2523 {
2524 let resource_def = self
2525 .component
2526 .initializers
2527 .iter()
2528 .find_map(|i| match i {
2529 GlobalInitializer::Resource(r) if r.index == resource_idx => Some(r),
2530 _ => None,
2531 })
2532 .unwrap();
2533
2534 if let Some(dtor) = &resource_def.dtor {
2536 format!(
2537 "
2538 {}(handleEntry.rep);",
2539 self.core_def(dtor)
2540 )
2541 } else {
2542 "".into()
2543 }
2544 } else {
2545 let symbol_dispose = self.bindgen.intrinsic(Intrinsic::SymbolDispose);
2552 let symbol_cabi_dispose = self.bindgen.intrinsic(Intrinsic::SymbolCabiDispose);
2553
2554 if let Some(imported_resource_local_name) =
2556 self.bindgen.local_names.try_get(resource_ty)
2557 {
2558 format!(
2559 "
2560 const rsc = captureTable{rid}.get(handleEntry.rep);
2561 if (rsc) {{
2562 if (rsc[{symbol_dispose}]) rsc[{symbol_dispose}]();
2563 captureTable{rid}.delete(handleEntry.rep);
2564 }} else if ({imported_resource_local_name}[{symbol_cabi_dispose}]) {{
2565 {imported_resource_local_name}[{symbol_cabi_dispose}](handleEntry.rep);
2566 }}"
2567 )
2568 } else {
2569 format!(
2571 "throw new TypeError('unreachable trampoline for resource [{:?}]')",
2572 resource_ty
2573 )
2574 }
2575 };
2576
2577 let rsc_table_remove = self
2578 .bindgen
2579 .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableRemove));
2580 uwrite!(
2581 self.src.js,
2582 "function trampoline{i}(handle) {{
2583 const handleEntry = {rsc_table_remove}(handleTable{tid}, handle);
2584 if (handleEntry.own) {{
2585 {dtor}
2586 }}
2587 }}
2588 ",
2589 );
2590 }
2591
2592 Trampoline::ResourceTransferOwn => {
2593 let resource_transfer = self
2594 .bindgen
2595 .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTransferOwn));
2596 uwriteln!(self.src.js, "const trampoline{i} = {resource_transfer};");
2597 }
2598
2599 Trampoline::ResourceTransferBorrow => {
2600 let resource_transfer =
2601 self.bindgen
2602 .intrinsic(if self.bindgen.opts.valid_lifting_optimization {
2603 Intrinsic::Resource(
2604 ResourceIntrinsic::ResourceTransferBorrowValidLifting,
2605 )
2606 } else {
2607 Intrinsic::Resource(ResourceIntrinsic::ResourceTransferBorrow)
2608 });
2609 uwriteln!(self.src.js, "const trampoline{i} = {resource_transfer};");
2610 }
2611
2612 Trampoline::TaskReturn {
2613 results, options, ..
2614 } => {
2615 let canon_opts = self
2616 .component
2617 .options
2618 .get(*options)
2619 .expect("failed to find options");
2620 let CanonicalOptions {
2621 instance,
2622 async_,
2623 data_model:
2624 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
2625 callback,
2626 post_return,
2627 string_encoding,
2628 ..
2629 } = canon_opts
2630 else {
2631 unreachable!("unexpected memory data model during task.return");
2632 };
2633
2634 if realloc.is_some() && memory.is_none() {
2636 panic!("memory must be present if realloc is");
2637 }
2638 if *async_ && post_return.is_some() {
2639 panic!("async and post return must not be specified together");
2640 }
2641 if *async_ && callback.is_none() {
2642 panic!("callback must be specified for async");
2643 }
2644 if let Some(cb_idx) = callback {
2645 let cb_fn = &self.types[TypeFuncIndex::from_u32(cb_idx.as_u32())];
2646 match self.types[cb_fn.params].types[..] {
2647 [InterfaceType::S32, InterfaceType::S32, InterfaceType::S32] => {}
2648 _ => panic!("unexpected params for async callback fn"),
2649 }
2650 match self.types[cb_fn.results].types[..] {
2651 [InterfaceType::S32] => {}
2652 _ => panic!("unexpected results for async callback fn"),
2653 }
2654 }
2655
2656 let result_types = &self.types[*results].types;
2657
2658 let result_flat_param_total: usize = result_types
2661 .iter()
2662 .map(|t| {
2663 self.types
2664 .canonical_abi(t)
2665 .flat_count
2666 .map(usize::from)
2667 .unwrap_or(0)
2668 })
2669 .sum();
2670 let use_direct_params = result_flat_param_total < MAX_FLAT_PARAMS;
2671
2672 let mut lift_fns: Vec<String> = Vec::with_capacity(result_types.len());
2675 for result_ty in result_types {
2676 lift_fns.push(gen_flat_lift_fn_js_expr(self, result_ty, &None));
2677 }
2678 let lift_fns_js = format!("[{}]", lift_fns.join(","));
2679
2680 let mut lower_fns: Vec<String> = Vec::with_capacity(result_types.len());
2686 for result_ty in result_types {
2687 lower_fns.push(gen_flat_lower_fn_js_expr(self, result_ty, &None));
2688 }
2689 let lower_fns_js = format!("[{}]", lower_fns.join(","));
2690
2691 let get_memory_fn_js = memory
2692 .map(|idx| format!("() => memory{}", idx.as_u32()))
2693 .unwrap_or_else(|| "() => null".into());
2694 let memory_idx_js = memory
2695 .map(|idx| idx.as_u32().to_string())
2696 .unwrap_or_else(|| "null".into());
2697 let component_idx = instance.as_u32();
2698 let task_return_fn = self
2699 .bindgen
2700 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::TaskReturn));
2701 let callback_fn_idx = callback
2702 .map(|v| v.as_u32().to_string())
2703 .unwrap_or_else(|| "null".into());
2704 let string_encoding_js = string_encoding_js_literal(string_encoding);
2705
2706 uwriteln!(
2707 self.src.js,
2708 "const trampoline{i} = {task_return_fn}.bind(
2709 null,
2710 {{
2711 componentIdx: {component_idx},
2712 useDirectParams: {use_direct_params},
2713 getMemoryFn: {get_memory_fn_js},
2714 memoryIdx: {memory_idx_js},
2715 callbackFnIdx: {callback_fn_idx},
2716 liftFns: {lift_fns_js},
2717 lowerFns: {lower_fns_js},
2718 stringEncoding: {string_encoding_js},
2719 }},
2720 );",
2721 );
2722 }
2723
2724 Trampoline::BackpressureInc { instance } => {
2725 let backpressure_inc_fn = self
2726 .bindgen
2727 .intrinsic(Intrinsic::Component(ComponentIntrinsic::BackpressureInc));
2728 uwriteln!(
2729 self.src.js,
2730 "const trampoline{i} = {backpressure_inc_fn}.bind(null, {instance});\n",
2731 instance = instance.as_u32(),
2732 );
2733 }
2734
2735 Trampoline::BackpressureDec { instance } => {
2736 let backpressure_dec_fn = self
2737 .bindgen
2738 .intrinsic(Intrinsic::Component(ComponentIntrinsic::BackpressureDec));
2739 uwriteln!(
2740 self.src.js,
2741 "const trampoline{i} = {backpressure_dec_fn}.bind(null, {instance});\n",
2742 instance = instance.as_u32(),
2743 );
2744 }
2745
2746 Trampoline::ThreadYield {
2747 cancellable,
2748 instance,
2749 } => {
2750 let yield_fn = self
2751 .bindgen
2752 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::Yield));
2753 let component_instance_idx = instance.as_u32();
2754 uwriteln!(
2755 self.src.js,
2756 r#"
2757 const trampoline{i} = new WebAssembly.Suspending({yield_fn}.bind(null, {{
2758 isCancellable: {cancellable},
2759 componentIdx: {component_instance_idx},
2760 }}));
2761 "#,
2762 );
2763 }
2764 Trampoline::ThreadIndex => todo!("Trampoline::ThreadIndex"),
2765 Trampoline::ThreadNewIndirect { .. } => todo!("Trampoline::ThreadNewIndirect"),
2766 Trampoline::ThreadSuspend { .. } => todo!("Trampoline::ThreadSuspend"),
2767 Trampoline::ThreadSuspendTo { .. } => todo!("Trampoline::ThreadSuspendTo"),
2768 Trampoline::ThreadUnsuspend { .. } => todo!("Trampoline::ThreadUnsuspend"),
2769 Trampoline::ThreadYieldToSuspended { .. } => {
2770 todo!("Trampoline::ThreadYieldToSuspended")
2771 }
2772 Trampoline::ThreadSuspendToSuspended { .. } => {
2773 todo!("Trampoline::ThreadYieldToSuspended")
2774 }
2775
2776 Trampoline::Trap => {
2777 uwriteln!(
2778 self.src.js,
2779 "function trampoline{i}(rep) {{ throw new TypeError('Trap'); }}"
2780 );
2781 }
2782
2783 Trampoline::EnterSyncCall => {
2784 let enter_symmetric_sync_guest_call_fn = self.bindgen.intrinsic(
2785 Intrinsic::AsyncTask(AsyncTaskIntrinsic::EnterSymmetricSyncGuestCall),
2786 );
2787 uwriteln!(
2788 self.src.js,
2789 r#"
2790 const trampoline{i} = {enter_symmetric_sync_guest_call_fn};
2791 "#,
2792 );
2793 }
2794
2795 Trampoline::ExitSyncCall => {
2796 let exit_symmetric_sync_guest_call_fn = self.bindgen.intrinsic(
2797 Intrinsic::AsyncTask(AsyncTaskIntrinsic::ExitSymmetricSyncGuestCall),
2798 );
2799 uwriteln!(
2800 self.src.js,
2801 "const trampoline{i} = {exit_symmetric_sync_guest_call_fn};\n",
2802 );
2803 }
2804 }
2805 }
2806
2807 fn instantiation_global_initializer(&mut self, init: &GlobalInitializer) {
2808 match init {
2809 GlobalInitializer::ExtractCallback(ExtractCallback { index, def }) => {
2816 let callback_idx = index.as_u32();
2817 let core_def = self.core_def(def);
2818
2819 uwriteln!(self.src.js, "let callback_{callback_idx};",);
2820
2821 uwriteln!(
2830 self.src.js_init,
2831 r#"
2832 callback_{callback_idx} = WebAssembly.promising({core_def});
2833 callback_{callback_idx}.fnName = "{core_def}";
2834 "#
2835 );
2836 }
2837
2838 GlobalInitializer::InstantiateModule(m, instance) => {
2839 self.init_current_module = *instance;
2843
2844 match m {
2845 InstantiateModule::Static(idx, args) => {
2846 self.instantiate_static_module(*idx, args, *instance);
2847 }
2848 InstantiateModule::Import(..) => unimplemented!(),
2852 }
2853 }
2854
2855 GlobalInitializer::LowerImport { index, import } => {
2856 self.lower_import(*index, *import);
2857 }
2858
2859 GlobalInitializer::ExtractMemory(m) => {
2860 let def = self.core_export_var_name(&m.export);
2861 let idx = m.index.as_u32();
2862 uwriteln!(self.src.js, "let memory{idx};");
2863 uwriteln!(self.src.js_init, "memory{idx} = {def};");
2864 }
2865
2866 GlobalInitializer::ExtractRealloc(r) => {
2867 let def = self.core_def(&r.def);
2868 let idx = r.index.as_u32();
2869 uwriteln!(self.src.js, "let realloc{idx};");
2870 uwriteln!(self.src.js, "let realloc{idx}Async;");
2871 uwriteln!(self.src.js_init, "realloc{idx} = {def};",);
2872 uwriteln!(
2875 self.src.js_init,
2876 r#"
2877 try {{
2878 realloc{idx}Async = WebAssembly.promising({def});
2879 }} catch(err) {{
2880 realloc{idx}Async = {def};
2881 }}
2882 "#
2883 );
2884 }
2885
2886 GlobalInitializer::ExtractPostReturn(p) => {
2887 let def = self.core_def(&p.def);
2888 let idx = p.index.as_u32();
2889 uwriteln!(self.src.js, "let postReturn{idx};");
2890 uwriteln!(self.src.js, "let postReturn{idx}Async;");
2891 uwriteln!(self.src.js_init, "postReturn{idx} = {def};");
2892 uwriteln!(
2895 self.src.js_init,
2896 r#"
2897 try {{
2898 postReturn{idx}Async = WebAssembly.promising({def});
2899 }} catch(err) {{
2900 postReturn{idx}Async = {def};
2901 }}
2902 "#
2903 );
2904 }
2905
2906 GlobalInitializer::Resource(_) => {}
2907
2908 GlobalInitializer::ExtractTable(_) => {}
2909 }
2910 }
2911
2912 fn instantiate_static_module(
2913 &mut self,
2914 module_idx: StaticModuleIndex,
2915 args: &[CoreDef],
2916 instance: Option<RuntimeComponentInstanceIndex>,
2917 ) {
2918 let mut import_obj = BTreeMap::new();
2923 for (module, name, arg) in self.modules[module_idx].imports(args) {
2924 let def = self.augmented_import_def(&arg);
2925 let dst = import_obj.entry(module).or_insert(BTreeMap::new());
2926 let prev = dst.insert(name, def);
2927 assert!(
2928 prev.is_none(),
2929 "unsupported duplicate import of `{module}::{name}`"
2930 );
2931 assert!(prev.is_none());
2932 }
2933
2934 if self.bindgen.opts.asmjs {
2935 let component_instance_idx = instance
2936 .expect("missing runtime component index during static module instantiation")
2937 .as_u32();
2938
2939 self.add_intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::GetCurrentTask));
2940 self.add_intrinsic(Intrinsic::GetGlobalCurrentTaskMetaFn);
2941 let current_task_get_fn =
2942 Intrinsic::AsyncTask(AsyncTaskIntrinsic::GetCurrentTask).name();
2943 let get_global_current_task_meta_fn = Intrinsic::GetGlobalCurrentTaskMetaFn.name();
2944
2945 let dst = import_obj.entry("env").or_insert(BTreeMap::new());
2946 let prev = dst.insert(
2947 "setTempRet0",
2948 format!(
2949 "(x) => {{
2950 const {{ taskID }} = {get_global_current_task_meta_fn}({component_instance_idx});
2951
2952 const taskMeta = {current_task_get_fn}({component_instance_idx}, taskID);
2953 if (!taskMeta) {{ throw new Error('invalid/missing async task meta'); }}
2954
2955 const task = taskMeta.task;
2956 if (!task) {{ throw new Error('invalid/missing async task'); }}
2957
2958 task.tmpRetI64HighBits = x|0;
2959 }}"
2960 ),
2961 );
2962 assert!(
2963 prev.is_none(),
2964 "unsupported duplicate import of `env::setTempRet0`"
2965 );
2966 assert!(prev.is_none());
2967 }
2968
2969 let mut imports = String::new();
2971 if !import_obj.is_empty() {
2972 imports.push_str(", {\n");
2973 for (module, names) in import_obj {
2974 imports.push_str(&maybe_quote_id(module));
2975 imports.push_str(": {\n");
2976 for (name, val) in names {
2977 imports.push_str(&maybe_quote_id(name));
2978 uwriteln!(imports, ": {val},");
2979 }
2980 imports.push_str("},\n");
2981 }
2982 imports.push('}');
2983 }
2984
2985 let i = self.instances.push(module_idx);
2986 let iu32 = i.as_u32();
2987 let instantiate = self.bindgen.intrinsic(Intrinsic::InstantiateCore);
2988 uwriteln!(self.src.js, "let exports{iu32};");
2989
2990 match self.bindgen.opts.instantiation_mode {
2991 Some(InstantiationMode::Async) | None => {
2992 uwriteln!(
2993 self.src.js_init,
2994 "({{ exports: exports{iu32} }} = yield {instantiate}(yield module{}{imports}));",
2995 module_idx.as_u32(),
2996 )
2997 }
2998
2999 Some(InstantiationMode::Sync) => {
3000 uwriteln!(
3001 self.src.js_init,
3002 "({{ exports: exports{iu32} }} = {instantiate}(module{}{imports}));",
3003 module_idx.as_u32(),
3004 );
3005 }
3006 }
3007 }
3008
3009 fn create_resource_fn_map(
3017 &mut self,
3018 func: &Function,
3019 ty_func_idx: TypeFuncIndex,
3020 resource_map: &mut ResourceMap,
3021 ) {
3022 let params_ty = &self.types[self.types[ty_func_idx].params];
3024 for (p, iface_ty) in func.params.iter().zip(params_ty.types.iter()) {
3025 if let Type::Id(id) = p.ty {
3026 self.connect_resource_types(id, iface_ty, resource_map);
3027 }
3028 }
3029 let results_ty = &self.types[self.types[ty_func_idx].results];
3031 if let (Some(Type::Id(id)), Some(iface_ty)) = (func.result, results_ty.types.first()) {
3032 self.connect_resource_types(id, iface_ty, resource_map);
3033 }
3034 }
3035
3036 fn resource_name(
3037 resolve: &Resolve,
3038 local_names: &'a mut LocalNames,
3039 resource: TypeId,
3040 resource_map: &BTreeMap<TypeId, ResourceIndex>,
3041 ) -> &'a str {
3042 let resource = crate::dealias(resolve, resource);
3043 local_names
3044 .get_or_create(
3045 resource_map[&resource],
3046 &resolve.types[resource]
3047 .name
3048 .as_ref()
3049 .unwrap()
3050 .to_upper_camel_case(),
3051 )
3052 .0
3053 }
3054
3055 fn imported_resource_name(&mut self, import_index: ImportIndex, resource: TypeId) -> String {
3066 let resolve = self.resolve;
3067 let types = self.types;
3068 let component = self.component;
3069 let resource = crate::dealias(resolve, resource);
3070 let resource_wit_name = resolve.types[resource].name.as_ref().unwrap();
3071 if let (
3072 _,
3073 ComponentExtern {
3074 ty: TypeDef::ComponentInstance(inst),
3075 ..
3076 },
3077 ) = &component.import_types[import_index]
3078 && let Some(ComponentExtern {
3079 ty: TypeDef::Resource(rt_idx),
3080 ..
3081 }) = types[*inst].exports.get(resource_wit_name)
3082 {
3083 let rid = types[*rt_idx].unwrap_concrete_ty();
3084 return self
3085 .bindgen
3086 .local_names
3087 .get_or_create(rid, &resource_wit_name.to_upper_camel_case())
3088 .0
3089 .to_string();
3090 }
3091 Instantiator::resource_name(
3094 resolve,
3095 &mut self.bindgen.local_names,
3096 resource,
3097 &self.imports_resource_types,
3098 )
3099 .to_string()
3100 }
3101
3102 fn find_import_providing_resource(
3114 &self,
3115 resource_idx: ResourceIndex,
3116 ) -> Option<(&'a str, bool)> {
3117 let component = self.component;
3118 let types = self.types;
3119 for (_, (imp_name, extern_)) in component.import_types.iter() {
3120 match &extern_.ty {
3121 TypeDef::ComponentInstance(inst) => {
3122 for (_, export) in types[*inst].exports.iter() {
3123 if let TypeDef::Resource(rt) = &export.ty
3124 && types[*rt].unwrap_concrete_ty() == resource_idx
3125 {
3126 return Some((imp_name.as_str(), true));
3127 }
3128 }
3129 }
3130 TypeDef::Resource(rt) if types[*rt].unwrap_concrete_ty() == resource_idx => {
3131 return Some((imp_name.as_str(), false));
3132 }
3133 _ => {}
3134 }
3135 }
3136 None
3137 }
3138
3139 fn lower_import(&mut self, index: LoweredIndex, import: RuntimeImportIndex) {
3140 let (options, trampoline, func_ty) = self.lowering_options[index];
3141
3142 let (import_index, path) = &self.component.imports[import];
3144 let (import_name, _) = &self.component.import_types[*import_index];
3145 let world_key = &self.imports[import_name];
3146
3147 let (func, func_name, iface_name) =
3149 match &self.resolve.worlds[self.world].imports[world_key] {
3150 WorldItem::Function(func) => {
3151 assert_eq!(path.len(), 0);
3152 (func, import_name, None)
3153 }
3154 WorldItem::Interface { id, .. } => {
3155 assert_eq!(path.len(), 1);
3156 let iface = &self.resolve.interfaces[*id];
3157 let func = &iface.functions[&path[0]];
3158 (
3159 func,
3160 &path[0],
3161 Some(iface.name.as_deref().unwrap_or_else(|| import_name)),
3162 )
3163 }
3164 WorldItem::Type { .. } => unreachable!("unexpected imported world item type"),
3165 };
3166
3167 let is_async = is_async_fn(func, options);
3168
3169 if options.async_ {
3170 assert!(
3171 options.post_return.is_none(),
3172 "async function {func_name} (import {import_name}) can't have post return",
3173 );
3174 }
3175
3176 let requires_async_porcelain = requires_async_porcelain(
3178 FunctionIdentifier::Fn(func),
3179 import_name,
3180 &self.async_imports,
3181 );
3182
3183 let implements = self.resolve.implements_value(
3188 world_key,
3189 &self.resolve.worlds[self.world].imports[world_key],
3190 );
3191
3192 let (import_specifier, maybe_iface_member) = map_import_with_implements(
3194 &self.bindgen.opts.map,
3195 if iface_name.is_some() {
3196 import_name
3197 } else {
3198 match func.kind {
3199 FunctionKind::Method(_) => {
3200 let stripped = import_name.strip_prefix("[method]").unwrap();
3201 &stripped[0..stripped.find(".").unwrap()]
3202 }
3203 FunctionKind::AsyncMethod(_) => {
3204 let stripped = import_name.strip_prefix("[async method]").unwrap();
3205 &stripped[0..stripped.find(".").unwrap()]
3206 }
3207 FunctionKind::Static(_) => {
3208 let stripped = import_name.strip_prefix("[static]").unwrap();
3209 &stripped[0..stripped.find(".").unwrap()]
3210 }
3211 FunctionKind::AsyncStatic(_) => {
3212 let stripped = import_name.strip_prefix("[async static]").unwrap();
3213 &stripped[0..stripped.find(".").unwrap()]
3214 }
3215 FunctionKind::Constructor(_) => {
3216 import_name.strip_prefix("[constructor]").unwrap()
3217 }
3218 FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => import_name,
3219 }
3220 },
3221 implements.as_deref(),
3222 );
3223
3224 let mut import_resource_map = ResourceMap::new();
3226
3227 self.create_resource_fn_map(func, func_ty, &mut import_resource_map);
3228
3229 let (callee_name, call_type) = match func.kind {
3230 FunctionKind::Freestanding => (
3231 self.bindgen
3232 .local_names
3233 .get_or_create(
3234 format!(
3235 "import:{import}-{maybe_iface_member}-{func_name}",
3236 import = import_specifier,
3237 maybe_iface_member = maybe_iface_member.as_deref().unwrap_or(""),
3238 func_name = func.name
3239 ),
3240 &func.name,
3241 )
3242 .0
3243 .to_string(),
3244 CallType::Standard,
3245 ),
3246
3247 FunctionKind::AsyncFreestanding => (
3248 self.bindgen
3249 .local_names
3250 .get_or_create(
3251 format!(
3252 "import:async-{import}-{maybe_iface_member}-{func_name}",
3253 import = import_specifier,
3254 maybe_iface_member = maybe_iface_member.as_deref().unwrap_or(""),
3255 func_name = func.name
3256 ),
3257 &func.name,
3258 )
3259 .0
3260 .to_string(),
3261 CallType::AsyncStandard,
3262 ),
3263
3264 FunctionKind::Method(_) => (
3265 func.item_name().to_lower_camel_case(),
3266 CallType::CalleeResourceDispatch,
3267 ),
3268
3269 FunctionKind::AsyncMethod(_) => (
3270 func.item_name().to_lower_camel_case(),
3271 CallType::AsyncCalleeResourceDispatch,
3272 ),
3273
3274 FunctionKind::Static(resource_id) => (
3275 format!(
3276 "{}.{}",
3277 self.imported_resource_name(*import_index, resource_id),
3278 func.item_name().to_lower_camel_case()
3279 ),
3280 CallType::Standard,
3281 ),
3282
3283 FunctionKind::AsyncStatic(resource_id) => (
3284 format!(
3285 "{}.{}",
3286 self.imported_resource_name(*import_index, resource_id),
3287 func.item_name().to_lower_camel_case()
3288 ),
3289 CallType::AsyncStandard,
3290 ),
3291
3292 FunctionKind::Constructor(resource_id) => (
3293 format!(
3294 "new {}",
3295 self.imported_resource_name(*import_index, resource_id)
3296 ),
3297 CallType::Standard,
3298 ),
3299 };
3300
3301 let abi = if is_async {
3302 AbiVariant::GuestImportAsync
3303 } else {
3304 AbiVariant::GuestImport
3305 };
3306
3307 let nparams = self.resolve.wasm_signature(abi, func).params.len();
3308
3309 let trampoline_idx = trampoline.as_u32();
3311 match self.bindgen.opts.import_bindings {
3312 None | Some(BindingsMode::Js) | Some(BindingsMode::Hybrid) => {
3313 if is_async | requires_async_porcelain {
3315 uwrite!(
3320 self.src.js,
3321 "\nconst _trampoline{trampoline_idx} = async function"
3322 );
3323 } else {
3324 uwrite!(
3325 self.src.js,
3326 "\nconst _trampoline{trampoline_idx} = function"
3327 );
3328 }
3329
3330 let iface_name = if import_name.is_empty() {
3331 None
3332 } else {
3333 Some(import_name.to_string())
3334 };
3335
3336 self.bindgen(JsFunctionBindgenArgs {
3338 nparams,
3339 call_type,
3340 iface_name: iface_name.as_deref(),
3341 callee: &callee_name,
3342 opts: options,
3343 func,
3344 resource_map: &import_resource_map,
3345 abi,
3346 requires_async_porcelain,
3347 is_async,
3348 wrap_async_future_result: false,
3349 for_import: true,
3350 });
3351 uwriteln!(self.src.js, "");
3352
3353 uwriteln!(
3354 self.src.js,
3355 "_trampoline{trampoline_idx}.fnName = '{}#{callee_name}';",
3356 iface_name.unwrap_or_default(),
3357 );
3358
3359 if requires_async_porcelain {
3361 uwriteln!(
3362 self.src.js,
3363 "_trampoline{trampoline_idx}.manuallyAsync = true;"
3364 );
3365 }
3366 }
3367
3368 Some(BindingsMode::Optimized) | Some(BindingsMode::DirectOptimized) => {
3369 uwriteln!(self.src.js, "let trampoline{trampoline_idx};");
3370 }
3371 };
3372
3373 if !matches!(
3378 self.bindgen.opts.import_bindings,
3379 None | Some(BindingsMode::Js)
3380 ) {
3381 let (memory, realloc) =
3382 if let CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions {
3383 memory,
3384 realloc,
3385 }) = options.data_model
3386 {
3387 (
3388 memory.map(|idx| format!(" memory: memory{},", idx.as_u32())),
3389 realloc.map(|idx| format!(" realloc: realloc{},", idx.as_u32())),
3390 )
3391 } else {
3392 (None, None)
3393 };
3394 let memory = memory.unwrap_or_default();
3395 let realloc = realloc.unwrap_or_default();
3396
3397 let post_return = options
3398 .post_return
3399 .map(|idx| format!(" postReturn: postReturn{},", idx.as_u32()))
3400 .unwrap_or("".into());
3401 let string_encoding = match options.string_encoding {
3402 wasmtime_environ::component::StringEncoding::Utf8 => "",
3403 wasmtime_environ::component::StringEncoding::Utf16 => " stringEncoding: 'utf16',",
3404 wasmtime_environ::component::StringEncoding::CompactUtf16 => {
3405 " stringEncoding: 'compact-utf16',"
3406 }
3407 };
3408
3409 let callee_name = match func.kind {
3410 FunctionKind::Constructor(_) => callee_name[4..].to_string(),
3411
3412 FunctionKind::Static(_)
3413 | FunctionKind::AsyncStatic(_)
3414 | FunctionKind::Freestanding
3415 | FunctionKind::AsyncFreestanding => callee_name.to_string(),
3416
3417 FunctionKind::Method(resource_id) | FunctionKind::AsyncMethod(resource_id) => {
3418 format!(
3419 "{}.prototype.{callee_name}",
3420 self.imported_resource_name(*import_index, resource_id)
3421 )
3422 }
3423 };
3424
3425 self.resource_imports.extend(import_resource_map.clone());
3427
3428 let resource_tables = {
3429 let mut resource_table_ids: Vec<TypeResourceTableIndex> = Vec::new();
3430
3431 for (_, data) in import_resource_map {
3432 let ResourceTable {
3433 data: ResourceData::Host { tid, .. },
3434 ..
3435 } = &data
3436 else {
3437 unreachable!("unexpected non-host resource table");
3438 };
3439 resource_table_ids.push(*tid);
3440 }
3441
3442 if resource_table_ids.is_empty() {
3443 "".to_string()
3444 } else {
3445 format!(
3446 " resourceTables: [{}],",
3447 resource_table_ids
3448 .iter()
3449 .map(|x| format!("handleTable{}", x.as_u32()))
3450 .collect::<Vec<String>>()
3451 .join(", ")
3452 )
3453 }
3454 };
3455
3456 match self.bindgen.opts.import_bindings {
3458 Some(BindingsMode::Hybrid) => {
3459 let symbol_cabi_lower = self.bindgen.intrinsic(Intrinsic::SymbolCabiLower);
3460 uwriteln!(self.src.js_init, "if ({callee_name}[{symbol_cabi_lower}]) {{
3461 trampoline{} = {callee_name}[{symbol_cabi_lower}]({{{memory}{realloc}{post_return}{string_encoding}{resource_tables}}});
3462 }}", trampoline.as_u32());
3463 }
3464 Some(BindingsMode::Optimized) => {
3465 let symbol_cabi_lower = self.bindgen.intrinsic(Intrinsic::SymbolCabiLower);
3466 if !self.bindgen.opts.valid_lifting_optimization {
3467 uwriteln!(self.src.js_init, "if (!{callee_name}[{symbol_cabi_lower}]) {{
3468 throw new TypeError('import for \"{import_name}\" does not define a Symbol.for(\"cabiLower\") optimized binding');
3469 }}");
3470 }
3471 uwriteln!(
3472 self.src.js_init,
3473 "trampoline{} = {callee_name}[{symbol_cabi_lower}]({{{memory}{realloc}{post_return}{string_encoding}{resource_tables}}});",
3474 trampoline.as_u32()
3475 );
3476 }
3477 Some(BindingsMode::DirectOptimized) => {
3478 uwriteln!(
3479 self.src.js_init,
3480 "trampoline{} = {callee_name}({{{memory}{realloc}{post_return}{string_encoding}}});",
3481 trampoline.as_u32()
3482 );
3483 }
3484 None | Some(BindingsMode::Js) => unreachable!("invalid bindings mode"),
3485 };
3486 }
3487
3488 let (import_name, binding_name) = match func.kind {
3490 FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
3491 (func_name.to_lower_camel_case(), callee_name)
3492 }
3493
3494 FunctionKind::Method(tid)
3495 | FunctionKind::AsyncMethod(tid)
3496 | FunctionKind::Static(tid)
3497 | FunctionKind::AsyncStatic(tid)
3498 | FunctionKind::Constructor(tid) => {
3499 let ty = &self.resolve.types[tid];
3500 let class_name = ty.name.as_ref().unwrap().to_upper_camel_case();
3501 let resource_name = self.imported_resource_name(*import_index, tid);
3502 (class_name, resource_name)
3503 }
3504 };
3505
3506 self.ensure_import(
3507 import_specifier,
3508 iface_name,
3509 maybe_iface_member.as_deref(),
3510 if iface_name.is_some() {
3511 Some(import_name.to_string())
3512 } else {
3513 None
3514 },
3515 binding_name,
3516 );
3517 }
3518
3519 fn ensure_import(
3530 &mut self,
3531 import_specifier: String,
3532 iface_name: Option<&str>,
3533 iface_member: Option<&str>,
3534 import_binding: Option<String>,
3535 local_name: String,
3536 ) {
3537 if import_specifier.starts_with("webidl:") {
3538 self.bindgen
3539 .intrinsic(Intrinsic::WebIdl(WebIdlIntrinsic::GlobalThisIdlProxy));
3540 }
3541
3542 let mut import_path = Vec::with_capacity(2);
3544 import_path.push(import_specifier);
3545 if let Some(_iface_name) = iface_name {
3546 if let Some(iface_member) = iface_member {
3549 import_path.push(iface_member.to_lower_camel_case());
3550 }
3551 import_path.push(import_binding.clone().unwrap());
3552 } else if let Some(iface_member) = iface_member {
3553 import_path.push(iface_member.into());
3554 } else if let Some(import_binding) = &import_binding {
3555 import_path.push(import_binding.into());
3556 }
3557
3558 self.bindgen
3560 .esm_bindgen
3561 .add_import_binding(&import_path, local_name);
3562 }
3563
3564 fn connect_p3_resources(
3573 &mut self,
3574 id: &TypeId,
3575 maybe_elem_ty: &Option<Type>,
3576 iface_ty: &InterfaceType,
3577 resource_map: &mut ResourceMap,
3578 ) {
3579 let remote_resource = match iface_ty {
3580 InterfaceType::Future(table_idx) => {
3581 let future_table_ty = &self.types[*table_idx];
3582 let future_ty = &self.types[future_table_ty.ty];
3583
3584 let mut future_nesting_level = 0;
3586 let mut payload_ty = future_ty.payload;
3587 while let Some(InterfaceType::Future(inner_ty)) = payload_ty {
3588 future_nesting_level += 1;
3589 payload_ty = self.types[self.types[inner_ty].ty].payload;
3590 }
3591
3592 ResourceTable {
3593 imported: true,
3594 data: ResourceData::Guest {
3595 resource_name: "Future".into(),
3596 prefix: Some(format!("${}", table_idx.as_u32())),
3597 extra: Some(ResourceExtraData::Future {
3598 table_idx: *table_idx,
3599 nesting_level: future_nesting_level,
3600 elem_ty: maybe_elem_ty.map(|ty| {
3601 let table_ty = &self.types[*table_idx];
3602 let future_ty_idx = table_ty.ty;
3603 let future_ty = &self.types[future_ty_idx];
3604 let iface_ty = future_ty.payload.expect(
3605 "missing future payload despite elem type being present",
3606 );
3607 let abi = self.types.canonical_abi(&iface_ty);
3608 PayloadTypeMetadata {
3609 ty,
3610 iface_ty,
3611
3612 lift_js_expr: gen_flat_lift_fn_js_expr(
3619 self,
3620 &iface_ty,
3621 &Some(resource_map),
3622 ),
3623 lower_js_expr: gen_flat_lower_fn_js_expr(
3624 self,
3625 &iface_ty,
3626 &Some(resource_map),
3627 ),
3628 size32: abi.size32,
3629 align32: abi.align32,
3630 flat_count: abi.flat_count,
3631 }
3632 }),
3633 }),
3634 },
3635 }
3636 }
3637 InterfaceType::Stream(table_idx) => ResourceTable {
3638 imported: true,
3639 data: ResourceData::Guest {
3640 resource_name: "Stream".into(),
3641 prefix: Some(format!("${}", table_idx.as_u32())),
3642 extra: Some(ResourceExtraData::Stream {
3643 table_idx: *table_idx,
3644 elem_ty: maybe_elem_ty.map(|ty| {
3645 let table_ty = &self.types[*table_idx];
3646 let stream_ty_idx = table_ty.ty;
3647 let stream_ty = &self.types[stream_ty_idx];
3648 let iface_ty = stream_ty
3649 .payload
3650 .expect("missing payload despite elem type being present");
3651 let abi = self.types.canonical_abi(&iface_ty);
3652 PayloadTypeMetadata {
3653 ty,
3654 iface_ty,
3655 lift_js_expr: gen_flat_lift_fn_js_expr(
3656 self,
3657 &iface_ty,
3658 &Some(resource_map),
3659 ),
3660 lower_js_expr: gen_flat_lower_fn_js_expr(
3661 self,
3662 &iface_ty,
3663 &Some(resource_map),
3664 ),
3665 size32: abi.size32,
3666 align32: abi.align32,
3667 flat_count: abi.flat_count,
3668 }
3669 }),
3670 }),
3671 },
3672 },
3673 InterfaceType::ErrorContext(table_idx) => ResourceTable {
3674 imported: true,
3675 data: ResourceData::Guest {
3676 resource_name: "ErrorContext".into(),
3677 prefix: Some(format!("${}", table_idx.as_u32())),
3678 extra: Some(ResourceExtraData::ErrorContext {
3679 table_idx: *table_idx,
3680 }),
3681 },
3682 },
3683 _ => unreachable!("unexpected interface type [{iface_ty:?}] with no type"),
3684 };
3685
3686 resource_map.insert(*id, remote_resource);
3687 }
3688
3689 fn connect_host_resource(
3698 &mut self,
3699 t: TypeId,
3700 resource_table_ty_idx: TypeResourceTableIndex,
3701 resource_map: &mut ResourceMap,
3702 ) {
3703 self.ensure_resource_table(resource_table_ty_idx);
3704
3705 let resource_table_ty = &self.types[resource_table_ty_idx];
3707 let resource_idx = resource_table_ty.unwrap_concrete_ty();
3708 let imported = self
3709 .component
3710 .defined_resource_index(resource_idx)
3711 .is_none();
3712
3713 let resource_id = crate::dealias(self.resolve, t);
3715 let ty = &self.resolve.types[resource_id];
3716
3717 let mut dtor_str = None;
3720 if let Some(resource_idx) = self.component.defined_resource_index(resource_idx) {
3721 assert!(!imported);
3722 let resource_def = self
3723 .component
3724 .initializers
3725 .iter()
3726 .find_map(|i| match i {
3727 GlobalInitializer::Resource(r) if r.index == resource_idx => Some(r),
3728 _ => None,
3729 })
3730 .unwrap();
3731
3732 if let Some(dtor) = &resource_def.dtor {
3733 dtor_str = Some(self.core_def(dtor));
3734 }
3735 }
3736
3737 let resource_name = ty.name.as_ref().unwrap().to_upper_camel_case();
3739
3740 let local_name = if imported {
3741 let imported_resource_entry = self.find_import_providing_resource(resource_idx);
3744
3745 let (world_key, iface_name) = match imported_resource_entry {
3746 Some((imp_name, _is_from_instance @ true)) => {
3749 let key = self.imports[imp_name].clone();
3750 let iface_name = match &key {
3751 WorldKey::Name(name) => Some(name.clone()),
3752 WorldKey::Interface(_) => {
3753 match &self.resolve.worlds[self.world].imports[&key] {
3754 WorldItem::Interface { id, .. } => {
3755 self.resolve.interfaces[*id].name.clone()
3756 }
3757 _ => None,
3758 }
3759 }
3760 };
3761 (key, iface_name)
3762 }
3763 Some((imp_name, _is_from_instance @ false)) => {
3765 (self.imports[imp_name].clone(), None)
3766 }
3767 None => match ty.owner {
3771 wit_parser::TypeOwner::World(world) => (
3772 self.resolve.worlds[world]
3773 .imports
3774 .iter()
3775 .find(
3776 |&(_, item)| matches!(item, WorldItem::Type { id, .. } if *id == t),
3777 )
3778 .unwrap()
3779 .0
3780 .clone(),
3781 None,
3782 ),
3783 wit_parser::TypeOwner::Interface(iface) => {
3784 let key = self.resolve.worlds[self.world]
3785 .imports
3786 .iter()
3787 .find(|&(_, item)| match item {
3788 WorldItem::Interface { id, .. } => *id == iface,
3789 _ => false,
3790 })
3791 .map(|(key, _)| key)
3792 .unwrap_or_else(|| {
3793 panic!(
3794 "unable to find world import for interface [{}]",
3795 self.resolve.interfaces[iface]
3796 .name
3797 .as_deref()
3798 .unwrap_or("<unnamed>")
3799 )
3800 });
3801 (
3802 key.clone(),
3803 match key {
3804 WorldKey::Name(name) => Some(name.clone()),
3805 WorldKey::Interface(_) => {
3806 self.resolve.interfaces[iface].name.clone()
3807 }
3808 },
3809 )
3810 }
3811 wit_parser::TypeOwner::None => unimplemented!(),
3812 },
3813 };
3814 let iface_name = iface_name.as_deref();
3815
3816 let import_name = self.resolve.name_world_key(&world_key);
3817 let implements = self.resolve.worlds[self.world]
3818 .imports
3819 .get(&world_key)
3820 .and_then(|item| self.resolve.implements_value(&world_key, item));
3821 let (local_name, _) = self
3822 .bindgen
3823 .local_names
3824 .get_or_create(resource_idx, &resource_name);
3825
3826 let local_name_str = local_name.to_string();
3827
3828 let (import_specifier, maybe_iface_member) = map_import_with_implements(
3832 &self.bindgen.opts.map,
3833 &import_name,
3834 implements.as_deref(),
3835 );
3836
3837 self.ensure_import(
3839 import_specifier,
3840 iface_name,
3841 maybe_iface_member.as_deref(),
3842 iface_name.map(|_| resource_name),
3843 local_name_str.to_string(),
3844 );
3845 local_name_str
3846 } else {
3847 let (local_name, _) = self
3848 .bindgen
3849 .local_names
3850 .get_or_create(resource_idx, &resource_name);
3851 local_name.to_string()
3852 };
3853
3854 let entry = ResourceTable {
3856 imported,
3857 data: ResourceData::Host {
3858 tid: resource_table_ty_idx,
3859 rid: resource_idx,
3860 local_name,
3861 dtor_name: dtor_str,
3862 },
3863 };
3864
3865 if let Some(existing) = resource_map.get(&resource_id) {
3868 if *existing != entry {
3874 assert!(
3875 imported && existing.imported,
3876 "conflicting resource tables for non-imported resource"
3877 );
3878 }
3879 return;
3880 }
3881
3882 resource_map.insert(resource_id, entry);
3884 }
3885
3886 fn connect_resource_types(
3899 &mut self,
3900 id: TypeId,
3901 iface_ty: &InterfaceType,
3902 resource_map: &mut ResourceMap,
3903 ) {
3904 let kind = &self.resolve.types[id].kind;
3905 match (kind, iface_ty) {
3906 (TypeDefKind::Flags(_), InterfaceType::Flags(_))
3908 | (TypeDefKind::Enum(_), InterfaceType::Enum(_)) => {}
3909
3910 (TypeDefKind::Record(t1), InterfaceType::Record(t2)) => {
3912 let t2 = &self.types[*t2];
3913 for (f1, f2) in t1.fields.iter().zip(t2.fields.iter()) {
3914 if let Type::Id(id) = f1.ty {
3915 self.connect_resource_types(id, &f2.ty, resource_map);
3916 }
3917 }
3918 }
3919
3920 (
3922 TypeDefKind::Handle(Handle::Own(t1) | Handle::Borrow(t1)),
3923 InterfaceType::Own(t2) | InterfaceType::Borrow(t2),
3924 ) => {
3925 self.connect_host_resource(*t1, *t2, resource_map);
3926 }
3927
3928 (TypeDefKind::Tuple(t1), InterfaceType::Tuple(t2)) => {
3930 let t2 = &self.types[*t2];
3931 for (f1, f2) in t1.types.iter().zip(t2.types.iter()) {
3932 if let Type::Id(id) = f1 {
3933 self.connect_resource_types(*id, f2, resource_map);
3934 }
3935 }
3936 }
3937
3938 (TypeDefKind::Variant(t1), InterfaceType::Variant(t2)) => {
3940 let t2 = &self.types[*t2];
3941 for (f1, f2) in t1.cases.iter().zip(t2.cases.iter()) {
3942 if let Some(Type::Id(id)) = &f1.ty {
3943 self.connect_resource_types(*id, f2.1.as_ref().unwrap(), resource_map);
3944 }
3945 }
3946 }
3947
3948 (TypeDefKind::Option(t1), InterfaceType::Option(t2)) => {
3950 let t2 = &self.types[*t2];
3951 if let Type::Id(id) = t1 {
3952 self.connect_resource_types(*id, &t2.ty, resource_map);
3953 }
3954 }
3955
3956 (TypeDefKind::Result(t1), InterfaceType::Result(t2)) => {
3958 let t2 = &self.types[*t2];
3959 if let Some(Type::Id(id)) = &t1.ok {
3960 self.connect_resource_types(*id, &t2.ok.unwrap(), resource_map);
3961 }
3962 if let Some(Type::Id(id)) = &t1.err {
3963 self.connect_resource_types(*id, &t2.err.unwrap(), resource_map);
3964 }
3965 }
3966
3967 (TypeDefKind::List(t1), InterfaceType::List(t2)) => {
3969 let t2 = &self.types[*t2];
3970 if let Type::Id(id) = t1 {
3971 self.connect_resource_types(*id, &t2.element, resource_map);
3972 }
3973 }
3974
3975 (TypeDefKind::Map(key, value), InterfaceType::Map(map)) => {
3977 let map = &self.types[*map];
3978 if let Type::Id(id) = key {
3979 self.connect_resource_types(*id, &map.key, resource_map);
3980 }
3981 if let Type::Id(id) = value {
3982 self.connect_resource_types(*id, &map.value, resource_map);
3983 }
3984 }
3985
3986 (TypeDefKind::FixedLengthList(t1, _len), InterfaceType::FixedLengthList(t2)) => {
3988 let t2 = &self.types[*t2];
3989 if let Type::Id(id) = t1 {
3990 self.connect_resource_types(*id, &t2.element, resource_map);
3991 }
3992 }
3993
3994 (TypeDefKind::Type(ty), _) => {
3996 if let Type::Id(id) = ty {
3997 self.connect_resource_types(*id, iface_ty, resource_map);
3998 }
3999 }
4000
4001 (TypeDefKind::Future(maybe_elem_ty), container_iface_ty)
4003 | (TypeDefKind::Stream(maybe_elem_ty), container_iface_ty) => {
4004 match maybe_elem_ty {
4005 None => {
4008 self.connect_p3_resources(&id, maybe_elem_ty, iface_ty, resource_map);
4009 }
4010 Some(elem_ty @ Type::Id(elem_ty_id)) => {
4012 let maybe_elem_iface_ty = match container_iface_ty {
4017 InterfaceType::Future(future_table_ty_idx) => {
4018 let future_table_ty = &self.types[*future_table_ty_idx];
4019 let future = &self.types[future_table_ty.ty];
4020 future.payload
4021 }
4022 InterfaceType::Stream(stream_table_ty_idx) => {
4023 let stream_table_ty = &self.types[*stream_table_ty_idx];
4024 let stream = &self.types[stream_table_ty.ty];
4025 stream.payload
4026 }
4027 _ => unreachable!("unexpected iface type"),
4028 };
4029 if let Some(elem_iface_ty) = maybe_elem_iface_ty {
4030 self.connect_resource_types(*elem_ty_id, &elem_iface_ty, resource_map);
4038 }
4039
4040 self.connect_p3_resources(&id, &Some(*elem_ty), iface_ty, resource_map);
4041 }
4042 Some(_) => {
4044 self.connect_p3_resources(&id, maybe_elem_ty, iface_ty, resource_map);
4045 }
4046 }
4047 }
4048
4049 (
4051 TypeDefKind::Result(Result_ { ok, err }),
4052 tk2 @ (InterfaceType::Future(_) | InterfaceType::Stream(_)),
4053 ) => {
4054 if let Some(Type::Id(ok_t)) = ok {
4055 self.connect_resource_types(*ok_t, tk2, resource_map)
4056 }
4057 if let Some(Type::Id(err_t)) = err {
4058 self.connect_resource_types(*err_t, tk2, resource_map)
4059 }
4060 }
4061
4062 (
4064 TypeDefKind::Option(ty),
4065 tk2 @ (InterfaceType::Future(_) | InterfaceType::Stream(_)),
4066 ) => {
4067 if let Type::Id(some_t) = ty {
4068 self.connect_resource_types(*some_t, tk2, resource_map)
4069 }
4070 }
4071
4072 (
4074 TypeDefKind::Handle(Handle::Own(t1) | Handle::Borrow(t1)),
4075 tk2 @ (InterfaceType::Future(_) | InterfaceType::Stream(_)),
4076 ) => self.connect_resource_types(*t1, tk2, resource_map),
4077
4078 (TypeDefKind::Resource, InterfaceType::Future(_) | InterfaceType::Stream(_)) => {}
4079
4080 (
4082 TypeDefKind::Variant(variant),
4083 tk2 @ (InterfaceType::Future(_) | InterfaceType::Stream(_)),
4084 ) => {
4085 for f1 in variant.cases.iter() {
4086 if let Some(Type::Id(id)) = &f1.ty {
4087 self.connect_resource_types(*id, tk2, resource_map);
4088 }
4089 }
4090 }
4091
4092 (
4094 TypeDefKind::Record(record),
4095 tk2 @ (InterfaceType::Future(_) | InterfaceType::Stream(_)),
4096 ) => {
4097 for f1 in record.fields.iter() {
4098 if let Type::Id(id) = f1.ty {
4099 self.connect_resource_types(id, tk2, resource_map);
4100 }
4101 }
4102 }
4103
4104 (
4107 TypeDefKind::Enum(_) | TypeDefKind::Flags(_),
4108 InterfaceType::Future(_) | InterfaceType::Stream(_),
4109 ) => {}
4110
4111 (TypeDefKind::Resource, tk2) => {
4112 unreachable!(
4113 "resource types do not need to be connected (in this case, to [{tk2:?}])"
4114 )
4115 }
4116
4117 (TypeDefKind::Unknown, tk2) => {
4118 unreachable!("unknown types cannot be connected (in this case to [{tk2:?}])")
4119 }
4120
4121 (tk1, tk2) => unreachable!("invalid typedef kind combination [{tk1:?}] [{tk2:?}]",),
4122 }
4123 }
4124
4125 fn bindgen(&mut self, args: JsFunctionBindgenArgs) {
4126 let JsFunctionBindgenArgs {
4127 nparams,
4128 call_type,
4129 iface_name,
4130 callee,
4131 opts,
4132 func,
4133 resource_map,
4134 abi,
4135 requires_async_porcelain,
4136 is_async,
4137 wrap_async_future_result,
4138 for_import,
4139 } = args;
4140
4141 let (memory, realloc) =
4142 if let CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions {
4143 memory,
4144 realloc,
4145 }) = opts.data_model
4146 {
4147 (
4148 memory.map(|idx| format!("memory{}", idx.as_u32())),
4149 realloc.map(|idx| {
4150 format!(
4151 "realloc{}{}",
4152 idx.as_u32(),
4153 if is_async {
4154 "Async"
4155 } else {
4156 Default::default()
4157 }
4158 )
4159 }),
4160 )
4161 } else {
4162 (None, None)
4163 };
4164
4165 let post_return = opts.post_return.map(|idx| {
4166 format!(
4167 "postReturn{}{}",
4168 idx.as_u32(),
4169 if is_async {
4170 "Async"
4171 } else {
4172 Default::default()
4173 }
4174 )
4175 });
4176
4177 let tracing_prefix = format!(
4178 "[iface=\"{}\", function=\"{}\"]",
4179 iface_name.unwrap_or("<no iface>"),
4180 func.name
4181 );
4182
4183 self.src.js("(");
4187 let mut params = Vec::new();
4188 let mut first = true;
4189 for i in 0..nparams {
4190 if i == 0
4191 && matches!(
4192 call_type,
4193 CallType::FirstArgIsThis | CallType::AsyncFirstArgIsThis
4194 )
4195 {
4196 params.push("this".into());
4197 continue;
4198 }
4199 if !first {
4200 self.src.js(", ");
4201 } else {
4202 first = false;
4203 }
4204 let param = format!("arg{i}");
4205 self.src.js(¶m);
4206 params.push(param);
4207 }
4208 uwriteln!(self.src.js, ") {{");
4209 if wrap_async_future_result {
4210 let future_value = self.bindgen.intrinsic(Intrinsic::AsyncFuture(
4211 AsyncFutureIntrinsic::FutureValueClass,
4212 ));
4213 uwriteln!(
4214 self.src.js,
4215 "return new {future_value}(() => (async () => {{"
4216 );
4217 }
4218
4219 if self.bindgen.opts.tracing {
4221 let event_fields = func
4222 .params
4223 .iter()
4224 .enumerate()
4225 .map(|(i, p)| format!("{}=${{arguments[{i}]}}", p.name))
4226 .collect::<Vec<String>>();
4227 uwriteln!(
4228 self.src.js,
4229 "console.error(`{tracing_prefix} call {}`);",
4230 event_fields.join(", ")
4231 );
4232 }
4233
4234 if self.bindgen.opts.tla_compat
4236 && matches!(abi, AbiVariant::GuestExport)
4237 && self.bindgen.opts.instantiation_mode.is_none()
4238 {
4239 let throw_uninitialized = self.bindgen.intrinsic(Intrinsic::ThrowUninitialized);
4240 uwrite!(
4241 self.src.js,
4242 "\
4243 if (!_initialized) {throw_uninitialized}();
4244 "
4245 );
4246 }
4247
4248 let mut f = FunctionBindgen {
4250 resource_map,
4251 clear_resource_borrows: false,
4252 intrinsics: &mut self.bindgen.all_intrinsics,
4253 valid_lifting_optimization: self.bindgen.opts.valid_lifting_optimization,
4254 sizes: &self.sizes,
4255 err: if get_thrown_type(self.resolve, func.result).is_some() {
4256 match abi {
4257 AbiVariant::GuestExport
4258 | AbiVariant::GuestExportAsync
4259 | AbiVariant::GuestExportAsyncStackful => ErrHandling::ThrowResultErr,
4260 AbiVariant::GuestImport | AbiVariant::GuestImportAsync => {
4261 ErrHandling::ResultCatchHandler
4262 }
4263 }
4264 } else {
4265 ErrHandling::None
4266 },
4267 block_storage: Vec::new(),
4268 blocks: Vec::new(),
4269 callee,
4270 callee_resource_dynamic: matches!(
4271 call_type,
4272 CallType::CalleeResourceDispatch | CallType::AsyncCalleeResourceDispatch
4273 ),
4274 memory: memory.as_ref(),
4275 realloc: realloc.as_ref(),
4276 tmp: 0,
4277 params,
4278 post_return: post_return.as_ref(),
4279 tracing_prefix: &tracing_prefix,
4280 tracing_enabled: self.bindgen.opts.tracing,
4281 encoding: match opts.string_encoding {
4282 wasmtime_environ::component::StringEncoding::Utf8 => StringEncoding::UTF8,
4283 wasmtime_environ::component::StringEncoding::Utf16 => StringEncoding::UTF16,
4284 wasmtime_environ::component::StringEncoding::CompactUtf16 => {
4285 StringEncoding::CompactUTF16
4286 }
4287 },
4288 src: source::Source::default(),
4289 resolve: self.resolve,
4290 requires_async_porcelain,
4291 is_async,
4292 wrap_async_future_result,
4293 iface_name,
4294 asmjs: self.bindgen.opts.asmjs,
4295 component_state: Some(FunctionBindgenComponentState {
4296 component_idx: opts.instance,
4297 realloc_fn_idx: if let CanonicalOptionsDataModel::LinearMemory(
4298 LinearMemoryOptions { realloc, .. },
4299 ) = opts.data_model
4300 {
4301 realloc
4302 } else {
4303 None
4304 },
4305 memory_idx: opts.memory(),
4306 callback_fn_idx: opts.callback,
4307 }),
4308 for_import: Some(for_import),
4309 };
4310
4311 abi::call(
4314 self.resolve,
4315 abi,
4316 match abi {
4317 AbiVariant::GuestImport | AbiVariant::GuestImportAsync => {
4318 LiftLower::LiftArgsLowerResults
4319 }
4320 AbiVariant::GuestExport
4321 | AbiVariant::GuestExportAsync
4322 | AbiVariant::GuestExportAsyncStackful => LiftLower::LowerArgsLiftResults,
4323 },
4324 func,
4325 &mut f,
4326 is_async,
4327 );
4328
4329 self.src.js(&f.src);
4331 if wrap_async_future_result {
4332 self.src.js("})());");
4333 }
4334
4335 self.src.js("}");
4337 }
4338
4339 fn augmented_import_def(&self, def: &core::AugmentedImport<'_>) -> String {
4340 match def {
4341 core::AugmentedImport::CoreDef(def) => self.core_def(def),
4342 core::AugmentedImport::Memory { mem, op } => {
4343 let mem = self.core_def(mem);
4344 match op {
4345 core::AugmentedOp::I32Load => {
4346 format!(
4347 "(ptr, off) => new DataView({mem}.buffer).getInt32(ptr + off, true)"
4348 )
4349 }
4350 core::AugmentedOp::I32Load8U => {
4351 format!(
4352 "(ptr, off) => new DataView({mem}.buffer).getUint8(ptr + off, true)"
4353 )
4354 }
4355 core::AugmentedOp::I32Load8S => {
4356 format!("(ptr, off) => new DataView({mem}.buffer).getInt8(ptr + off, true)")
4357 }
4358 core::AugmentedOp::I32Load16U => {
4359 format!(
4360 "(ptr, off) => new DataView({mem}.buffer).getUint16(ptr + off, true)"
4361 )
4362 }
4363 core::AugmentedOp::I32Load16S => {
4364 format!(
4365 "(ptr, off) => new DataView({mem}.buffer).getInt16(ptr + off, true)"
4366 )
4367 }
4368 core::AugmentedOp::I64Load => {
4369 format!(
4370 "(ptr, off) => new DataView({mem}.buffer).getBigInt64(ptr + off, true)"
4371 )
4372 }
4373 core::AugmentedOp::F32Load => {
4374 format!(
4375 "(ptr, off) => new DataView({mem}.buffer).getFloat32(ptr + off, true)"
4376 )
4377 }
4378 core::AugmentedOp::F64Load => {
4379 format!(
4380 "(ptr, off) => new DataView({mem}.buffer).getFloat64(ptr + off, true)"
4381 )
4382 }
4383 core::AugmentedOp::I32Store8 => {
4384 format!(
4385 "(ptr, val, offset) => {{
4386 new DataView({mem}.buffer).setInt8(ptr + offset, val, true);
4387 }}"
4388 )
4389 }
4390 core::AugmentedOp::I32Store16 => {
4391 format!(
4392 "(ptr, val, offset) => {{
4393 new DataView({mem}.buffer).setInt16(ptr + offset, val, true);
4394 }}"
4395 )
4396 }
4397 core::AugmentedOp::I32Store => {
4398 format!(
4399 "(ptr, val, offset) => {{
4400 new DataView({mem}.buffer).setInt32(ptr + offset, val, true);
4401 }}"
4402 )
4403 }
4404 core::AugmentedOp::I64Store => {
4405 format!(
4406 "(ptr, val, offset) => {{
4407 new DataView({mem}.buffer).setBigInt64(ptr + offset, val, true);
4408 }}"
4409 )
4410 }
4411 core::AugmentedOp::F32Store => {
4412 format!(
4413 "(ptr, val, offset) => {{
4414 new DataView({mem}.buffer).setFloat32(ptr + offset, val, true);
4415 }}"
4416 )
4417 }
4418 core::AugmentedOp::F64Store => {
4419 format!(
4420 "(ptr, val, offset) => {{
4421 new DataView({mem}.buffer).setFloat64(ptr + offset, val, true);
4422 }}"
4423 )
4424 }
4425 core::AugmentedOp::MemorySize => {
4426 format!("ptr => {mem}.buffer.byteLength / 65536")
4427 }
4428 }
4429 }
4430 }
4431 }
4432
4433 fn core_def(&self, def: &CoreDef) -> String {
4434 match def {
4435 CoreDef::Export(e) => self.core_export_var_name(e),
4436 CoreDef::TaskMayBlock => AsyncTaskIntrinsic::CurrentTaskMayBlock.name().into(),
4437 CoreDef::Trampoline(i) => format!("trampoline{}", i.as_u32()),
4438 CoreDef::InstanceFlags(i) => {
4439 self.used_instance_flags.borrow_mut().insert(*i);
4441 format!("instanceFlags{}", i.as_u32())
4442 }
4443 CoreDef::UnsafeIntrinsic(ui) => match ui {
4444 wasmtime_environ::component::UnsafeIntrinsic::ContextGetI32_0 => {
4445 let context_get_fn =
4446 Intrinsic::AsyncTask(AsyncTaskIntrinsic::ContextGet).name();
4447 format!(
4448 "{context_get_fn}.bind(null, {{ componentIdx: {}, slot: 0 }})",
4449 self.init_current_module
4450 .expect("missing current module")
4451 .as_u32(),
4452 )
4453 }
4454 wasmtime_environ::component::UnsafeIntrinsic::ContextSetI32_0 => {
4455 let context_set_fn =
4456 Intrinsic::AsyncTask(AsyncTaskIntrinsic::ContextSet).name();
4457 format!(
4458 "{context_set_fn}.bind(null, {{ componentIdx: {}, slot: 0 }})",
4459 self.init_current_module
4460 .expect("missing current module")
4461 .as_u32(),
4462 )
4463 }
4464 wasmtime_environ::component::UnsafeIntrinsic::ContextGetI32_1 => {
4465 let context_get_fn =
4466 Intrinsic::AsyncTask(AsyncTaskIntrinsic::ContextGet).name();
4467 format!(
4468 "{context_get_fn}.bind(null, {{ componentIdx: {}, slot: 1 }})",
4469 self.init_current_module
4470 .expect("missing current module")
4471 .as_u32(),
4472 )
4473 }
4474 wasmtime_environ::component::UnsafeIntrinsic::ContextSetI32_1 => {
4475 let context_set_fn =
4476 Intrinsic::AsyncTask(AsyncTaskIntrinsic::ContextSet).name();
4477 format!(
4478 "{context_set_fn}.bind(null, {{ componentIdx: {}, slot: 1 }})",
4479 self.init_current_module
4480 .expect("missing current module")
4481 .as_u32(),
4482 )
4483 }
4484
4485 ui => {
4487 let idx = ui.index();
4488 format!("unsafeIntrinsic{idx}")
4489 }
4490 },
4491 }
4492 }
4493
4494 fn core_export_var_name<T>(&self, export: &CoreExport<T>) -> String
4495 where
4496 T: Into<EntityIndex> + Copy,
4497 {
4498 let name = match &export.item {
4499 ExportItem::Index(idx) => {
4500 let module_idx = self
4501 .instances
4502 .get(export.instance)
4503 .expect("unexpectedly missing export instance");
4504 let module = &self
4505 .modules
4506 .get(*module_idx)
4507 .expect("unexpectedly missing module by idx");
4508 let idx = (*idx).into();
4509 module
4510 .exports()
4511 .iter()
4512 .find_map(|(name, i)| if *i == idx { Some(name) } else { None })
4513 .unwrap()
4514 .to_string()
4515 }
4516 ExportItem::Name(s) => s.to_string(),
4517 };
4518 let i = export.instance.as_u32() as usize;
4519 let quoted = maybe_quote_member(&name);
4520 format!("exports{i}{quoted}")
4521 }
4522
4523 fn process_imports(&mut self) {
4525 let mut import_resource_map = ResourceMap::new();
4526 for (_import_name, (import_idx, _import_path)) in self.component.imports.iter() {
4527 let (import_name, import_type_def) = &self.component.import_types[*import_idx];
4528 let import_world_key = &self
4529 .imports
4530 .get(import_name)
4531 .expect("missing import mapping");
4532 let import_world_item = &self
4533 .resolve
4534 .worlds
4535 .get(self.world)
4536 .expect("missing world")
4537 .imports
4538 .get(*import_world_key)
4539 .expect("missing import in world for import");
4540
4541 match import_world_item {
4543 WorldItem::Interface { id: iface_id, .. } => {
4544 let iface = &self.resolve.interfaces[*iface_id];
4545
4546 for (fn_name, iface_fn) in iface.functions.iter() {
4549 match import_type_def {
4550 ComponentExtern {
4551 ty: TypeDef::ComponentInstance(instance_ty),
4552 ..
4553 } => {
4554 if let Some(ComponentExtern {
4555 ty: TypeDef::ComponentFunc(type_func_index),
4556 ..
4557 }) = &self.types[*instance_ty].exports.get(fn_name)
4558 {
4559 self.create_resource_fn_map(
4560 iface_fn,
4561 *type_func_index,
4562 &mut import_resource_map,
4563 );
4564 }
4565 }
4566 ComponentExtern {
4567 ty: TypeDef::ComponentFunc(type_func_idx),
4568 ..
4569 } => {
4570 self.create_resource_fn_map(
4571 iface_fn,
4572 *type_func_idx,
4573 &mut import_resource_map,
4574 );
4575 }
4576 _ => {}
4577 }
4578 }
4579 }
4580
4581 WorldItem::Function(func) => {
4583 let TypeDef::ComponentFunc(func_ty_idx) = &import_type_def.ty else {
4584 unreachable!("invalid fn export");
4585 };
4586 self.create_resource_fn_map(func, *func_ty_idx, &mut import_resource_map);
4587 }
4588 WorldItem::Type { .. } => {}
4590 }
4591 }
4592
4593 self.resource_imports.extend(import_resource_map);
4594 }
4595
4596 fn process_exports(&mut self) {
4598 self.resource_exports.extend(self.resource_imports.clone());
4600
4601 for (export_name, (export_idx, _extern_data)) in self.component.exports.raw_iter() {
4603 let export_name = export_name.as_ref().to_string();
4604 let export = &self.component.export_items[*export_idx];
4605 let world_key = &self.exports[&export_name];
4606 let item = &self.resolve.worlds[self.world].exports[world_key];
4607 let mut export_resource_map = ResourceMap::new();
4608
4609 match export {
4610 Export::LiftedFunction {
4611 func: def,
4612 options,
4613 ty: func_ty,
4614 } => {
4615 let func = match item {
4616 WorldItem::Function(f) => f,
4617 WorldItem::Interface { .. } | WorldItem::Type { .. } => {
4618 unreachable!("unexpectedly non-function lifted function export")
4619 }
4620 };
4621
4622 self.create_resource_fn_map(func, *func_ty, &mut export_resource_map);
4623
4624 let local_name = String::from(match func.kind {
4625 FunctionKind::Constructor(resource_id)
4627 | FunctionKind::Method(resource_id)
4628 | FunctionKind::AsyncMethod(resource_id)
4629 | FunctionKind::Static(resource_id)
4630 | FunctionKind::AsyncStatic(resource_id) => Instantiator::resource_name(
4631 self.resolve,
4632 &mut self.bindgen.local_names,
4633 resource_id,
4634 &self.exports_resource_types,
4635 ),
4636 FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
4638 self.bindgen.local_names.create_once(&export_name)
4639 }
4640 });
4641
4642 let options = self
4643 .component
4644 .options
4645 .get(*options)
4646 .expect("failed to find options");
4647
4648 self.export_bindgen(
4649 &local_name,
4650 def,
4651 options,
4652 func,
4653 func_ty,
4654 &export_name,
4655 &export_resource_map,
4656 );
4657
4658 let js_binding_name = match func.kind {
4659 FunctionKind::Constructor(ty)
4661 | FunctionKind::Method(ty)
4662 | FunctionKind::AsyncMethod(ty)
4663 | FunctionKind::Static(ty)
4664 | FunctionKind::AsyncStatic(ty) => self.resolve.types[ty]
4665 .name
4666 .as_ref()
4667 .unwrap()
4668 .to_upper_camel_case(),
4669 FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
4671 export_name.to_lower_camel_case()
4672 }
4673 };
4674
4675 self.bindgen.esm_bindgen.add_export_binding(
4677 None,
4678 local_name,
4679 js_binding_name,
4680 func,
4681 );
4682 }
4683
4684 Export::Instance { exports, .. } => {
4685 let iface_id = match item {
4686 WorldItem::Interface { id, .. } => *id,
4687 WorldItem::Function(_) | WorldItem::Type { .. } => {
4688 unreachable!("unexpectedly non-interface export instance")
4689 }
4690 };
4691
4692 for (func_name, (export_idx, _extern_data)) in exports.raw_iter() {
4694 let func_name = func_name.as_ref().to_string();
4695 let export = &self.component.export_items[*export_idx];
4696
4697 let (def, options, func_ty) = match export {
4699 Export::LiftedFunction { func, options, ty } => (func, options, ty),
4700 Export::Type(_) => continue, _ => unreachable!("unexpected non-lifted function export"),
4702 };
4703
4704 let func = &self.resolve.interfaces[iface_id].functions[&func_name];
4705
4706 self.create_resource_fn_map(func, *func_ty, &mut export_resource_map);
4707
4708 let local_name = String::from(match func.kind {
4709 FunctionKind::Constructor(resource_id)
4711 | FunctionKind::Method(resource_id)
4712 | FunctionKind::AsyncMethod(resource_id)
4713 | FunctionKind::Static(resource_id)
4714 | FunctionKind::AsyncStatic(resource_id) => {
4715 Instantiator::resource_name(
4716 self.resolve,
4717 &mut self.bindgen.local_names,
4718 resource_id,
4719 &self.exports_resource_types,
4720 )
4721 }
4722 FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
4724 self.bindgen.local_names.create_once(&func_name)
4725 }
4726 });
4727
4728 let options = self
4729 .component
4730 .options
4731 .get(*options)
4732 .expect("failed to find options");
4733
4734 self.export_bindgen(
4735 &local_name,
4736 def,
4737 options,
4738 func,
4739 func_ty,
4740 &export_name,
4741 &export_resource_map,
4742 );
4743
4744 let export_binding_name = match func.kind {
4746 FunctionKind::Constructor(ty)
4748 | FunctionKind::Method(ty)
4749 | FunctionKind::AsyncMethod(ty)
4750 | FunctionKind::Static(ty)
4751 | FunctionKind::AsyncStatic(ty) => self.resolve.types[ty]
4752 .name
4753 .as_ref()
4754 .unwrap()
4755 .to_upper_camel_case(),
4756 FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
4758 func_name.to_lower_camel_case()
4759 }
4760 };
4761
4762 self.bindgen.esm_bindgen.add_export_binding(
4764 Some(&export_name),
4765 local_name,
4766 export_binding_name,
4767 func,
4768 );
4769 }
4770 }
4771
4772 Export::Type(_) => {}
4774
4775 Export::ModuleStatic { .. } | Export::ModuleImport { .. } => unimplemented!(),
4777 }
4778
4779 self.resource_exports.extend(export_resource_map);
4781 }
4782
4783 self.bindgen.esm_bindgen.populate_export_aliases();
4784 }
4785
4786 #[allow(clippy::too_many_arguments)]
4787 fn export_bindgen(
4788 &mut self,
4789 local_name: &str,
4790 def: &CoreDef,
4791 options: &CanonicalOptions,
4792 func: &Function,
4793 _func_ty_idx: &TypeFuncIndex,
4794 export_name: &String,
4795 export_resource_map: &ResourceMap,
4796 ) {
4797 let requires_async_porcelain = requires_async_porcelain(
4799 FunctionIdentifier::Fn(func),
4800 export_name,
4801 &self.async_exports,
4802 );
4803 if options.async_ {
4805 assert!(
4806 options.post_return.is_none(),
4807 "async function {local_name} (export {export_name}) can't have post return"
4808 );
4809 }
4810
4811 let is_async = is_async_fn(func, options);
4812
4813 let wrap_async_future_result = (requires_async_porcelain || is_async)
4814 && matches!(
4815 func.result.as_ref(),
4816 Some(Type::Id(id))
4817 if matches!(
4818 self.resolve.types[crate::dealias(self.resolve, *id)].kind,
4819 TypeDefKind::Future(_)
4820 )
4821 );
4822
4823 let maybe_async = if (requires_async_porcelain || is_async) && !wrap_async_future_result {
4824 "async "
4825 } else {
4826 ""
4827 };
4828 let wrapped_function_target = wrap_async_future_result.then(|| match func.kind {
4829 FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => local_name.to_string(),
4830 FunctionKind::Method(_) | FunctionKind::AsyncMethod(_) => format!(
4831 "{local_name}.prototype.{}",
4832 func.item_name().to_lower_camel_case()
4833 ),
4834 FunctionKind::Static(_) | FunctionKind::AsyncStatic(_) => {
4835 format!("{local_name}.{}", func.item_name().to_lower_camel_case())
4836 }
4837 FunctionKind::Constructor(_) => {
4838 unreachable!("constructors cannot return futures")
4839 }
4840 });
4841
4842 let core_export_fn = self.core_def(def);
4844 let callee = match self
4845 .bindgen
4846 .local_names
4847 .get_or_create(&core_export_fn, &core_export_fn)
4848 {
4849 (local_name, true) => local_name.to_string(),
4850 (local_name, false) => {
4851 let local_name = local_name.to_string();
4852 uwriteln!(self.src.js, "let {local_name};");
4853 self.bindgen
4854 .all_core_exported_funcs
4855 .push((core_export_fn.clone(), is_async | requires_async_porcelain));
4859 local_name
4860 }
4861 };
4862
4863 let iface_name = if export_name.is_empty() {
4864 None
4865 } else {
4866 Some(export_name)
4867 };
4868
4869 match func.kind {
4871 FunctionKind::Freestanding => {
4872 uwrite!(self.src.js, "\n{maybe_async}function {local_name}")
4873 }
4874 FunctionKind::Method(_) => {
4875 self.ensure_local_resource_class(local_name.to_string());
4876 let method_name = func.item_name().to_lower_camel_case();
4877
4878 uwrite!(
4879 self.src.js,
4880 "\n{local_name}.prototype.{method_name} = {maybe_async}function {}",
4881 if !is_js_reserved_word(&method_name) {
4882 method_name.to_string()
4883 } else {
4884 format!("${method_name}")
4885 }
4886 );
4887 }
4888 FunctionKind::Static(_) => {
4889 self.ensure_local_resource_class(local_name.to_string());
4890 let method_name = func.item_name().to_lower_camel_case();
4891 uwrite!(
4892 self.src.js,
4893 "\n{local_name}.{method_name} = function {}",
4894 if !is_js_reserved_word(&method_name) {
4895 method_name.to_string()
4896 } else {
4897 format!("${method_name}")
4898 }
4899 );
4900 }
4901 FunctionKind::Constructor(_) => {
4902 if self.defined_resource_classes.contains(local_name) {
4903 panic!(
4904 "Internal error: Resource constructor must be defined before other methods and statics"
4905 );
4906 }
4907 uwrite!(
4908 self.src.js,
4909 "
4910 class {local_name} {{
4911 constructor"
4912 );
4913 self.defined_resource_classes.insert(local_name.to_string());
4914 }
4915 FunctionKind::AsyncFreestanding => {
4916 uwrite!(self.src.js, "\n{maybe_async}function {local_name}")
4917 }
4918 FunctionKind::AsyncMethod(_) => {
4919 self.ensure_local_resource_class(local_name.to_string());
4920 let method_name = func.item_name().to_lower_camel_case();
4921 let fn_name = if !is_js_reserved_word(&method_name) {
4922 method_name.to_string()
4923 } else {
4924 format!("${method_name}")
4925 };
4926 uwrite!(
4927 self.src.js,
4928 "\n{local_name}.prototype.{method_name} = {maybe_async}function {fn_name}",
4929 );
4930 }
4931 FunctionKind::AsyncStatic(_) => {
4932 self.ensure_local_resource_class(local_name.to_string());
4933 let method_name = func.item_name().to_lower_camel_case();
4934 let fn_name = if !is_js_reserved_word(&method_name) {
4935 method_name.to_string()
4936 } else {
4937 format!("${method_name}")
4938 };
4939 uwrite!(
4940 self.src.js,
4941 "\n{local_name}.{method_name} = {maybe_async}function {fn_name}",
4942 );
4943 }
4944 };
4945
4946 self.bindgen(JsFunctionBindgenArgs {
4948 nparams: func.params.len(),
4949 call_type: match func.kind {
4950 FunctionKind::Method(_) => CallType::FirstArgIsThis,
4951 FunctionKind::AsyncMethod(_) => CallType::AsyncFirstArgIsThis,
4952 FunctionKind::Freestanding
4953 | FunctionKind::Static(_)
4954 | FunctionKind::Constructor(_) => CallType::Standard,
4955 FunctionKind::AsyncFreestanding | FunctionKind::AsyncStatic(_) => {
4956 CallType::AsyncStandard
4957 }
4958 },
4959 iface_name: iface_name.map(|v| v.as_str()),
4960 callee: &callee,
4961 opts: options,
4962 func,
4963 resource_map: export_resource_map,
4964 abi: AbiVariant::GuestExport,
4965 requires_async_porcelain,
4966 is_async,
4967 wrap_async_future_result,
4968 for_import: false,
4969 });
4970 if let Some(target) = wrapped_function_target {
4971 let async_fn_ctor = self.bindgen.intrinsic(Intrinsic::AsyncFunctionCtor);
4972 uwriteln!(
4973 self.src.js,
4974 "\nObject.setPrototypeOf({target}, {async_fn_ctor}.prototype);"
4975 );
4976 }
4977
4978 match func.kind {
4980 FunctionKind::AsyncFreestanding | FunctionKind::Freestanding => self.src.js("\n"),
4981 FunctionKind::AsyncMethod(_)
4982 | FunctionKind::AsyncStatic(_)
4983 | FunctionKind::Method(_)
4984 | FunctionKind::Static(_) => self.src.js(";\n"),
4985 FunctionKind::Constructor(_) => self.src.js("\n}\n"),
4986 }
4987 }
4988}
4989
4990#[derive(Default)]
4991pub struct Source {
4992 pub js: source::Source,
4993 pub js_init: source::Source,
4994}
4995
4996impl Source {
4997 pub fn js(&mut self, s: &str) {
4998 self.js.push_str(s);
4999 }
5000 pub fn js_init(&mut self, s: &str) {
5001 self.js_init.push_str(s);
5002 }
5003}
5004
5005fn semver_compat_key(version_str: &str) -> Option<(String, Version)> {
5016 let version = Version::parse(version_str).ok()?;
5017 if !version.pre.is_empty() {
5018 None
5019 } else if version.major != 0 {
5020 Some((format!("{}", version.major), version))
5021 } else if version.minor != 0 {
5022 Some((format!("0.{}", version.minor), version))
5023 } else {
5024 None
5025 }
5026}
5027
5028fn parse_mapping(mapping: &str) -> (String, Option<String>) {
5029 if mapping.len() > 1
5030 && let Some(hash_idx) = mapping[1..].find('#')
5031 {
5032 return (
5033 mapping[0..hash_idx + 1].to_string(),
5034 Some(mapping[hash_idx + 2..].into()),
5035 );
5036 }
5037 (mapping.into(), None)
5038}
5039
5040fn resolve_wildcard_mapping(key: &str, mapping: &str, impt: &str) -> Option<String> {
5041 let idx = key.find('*')?;
5042 let lhs = &key[..idx];
5043 let rhs = &key[idx + 1..];
5044
5045 if !impt.starts_with(lhs) || !impt.ends_with(rhs) {
5046 return None;
5047 }
5048
5049 let matched_len = impt.len() - lhs.len() - rhs.len();
5050 let matched = &impt[lhs.len()..lhs.len() + matched_len];
5051 Some(mapping.replace('*', matched))
5052}
5053
5054fn map_import_with_implements(
5059 map: &Option<HashMap<String, String>>,
5060 impt: &str,
5061 implements: Option<&str>,
5062) -> (String, Option<String>) {
5063 let (specifier, iface_member) = map_import(map, impt);
5064 if specifier == impt
5065 && iface_member.is_none()
5066 && let Some(target) = implements
5067 {
5068 let (mapped, member) = map_import(map, target);
5069 let target_sans_version = match target.find('@') {
5071 Some(version_idx) => &target[0..version_idx],
5072 None => target,
5073 };
5074 if mapped != target_sans_version || member.is_some() {
5075 return (mapped, member);
5076 }
5077 }
5078 (specifier, iface_member)
5079}
5080
5081fn map_import(map: &Option<HashMap<String, String>>, impt: &str) -> (String, Option<String>) {
5082 let impt_sans_version = match impt.find('@') {
5083 Some(version_idx) => &impt[0..version_idx],
5084 None => impt,
5085 };
5086 if let Some(map) = map.as_ref() {
5087 if let Some(mapping) = map.get(impt) {
5089 return parse_mapping(mapping);
5090 }
5091
5092 if let Some(mapping) = map.get(impt_sans_version) {
5094 return parse_mapping(mapping);
5095 }
5096
5097 for (key, mapping) in map {
5099 if !key.contains('@') {
5100 continue;
5101 }
5102 if let Some(mapping) = resolve_wildcard_mapping(key, mapping, impt) {
5103 return parse_mapping(&mapping);
5104 }
5105 }
5106
5107 for (key, mapping) in map {
5109 if key.contains('@') {
5110 continue;
5111 }
5112 if let Some(mapping) = resolve_wildcard_mapping(key, mapping, impt_sans_version) {
5113 return parse_mapping(&mapping);
5114 }
5115 }
5116
5117 if let Some(at) = impt.find('@') {
5120 let impt_ver_str = &impt[at + 1..];
5121 if let Some((impt_compat, _)) = semver_compat_key(impt_ver_str) {
5122 let mut best_match: Option<(String, Version)> = None;
5123
5124 for (key, mapping) in map {
5125 let key_at = match key.find('@') {
5126 Some(at) => at,
5127 None => continue,
5128 };
5129 let key_base = &key[..key_at];
5130 let key_ver_str = &key[key_at + 1..];
5131
5132 let (key_compat, key_ver) = match semver_compat_key(key_ver_str) {
5133 Some(k) => k,
5134 None => continue,
5135 };
5136 if impt_compat != key_compat {
5137 continue;
5138 }
5139
5140 let resolved = if let Some(mapping) =
5141 resolve_wildcard_mapping(key_base, mapping, impt_sans_version)
5142 {
5143 Some(mapping)
5144 } else if key_base == impt_sans_version {
5145 Some(mapping.clone())
5146 } else {
5147 None
5148 };
5149
5150 if let Some(resolved_mapping) = resolved {
5151 match &best_match {
5152 Some((_, prev_ver)) if key_ver <= *prev_ver => {}
5153 _ => {
5154 best_match = Some((resolved_mapping, key_ver));
5155 }
5156 }
5157 }
5158 }
5159
5160 if let Some((mapping, _)) = best_match {
5161 return parse_mapping(&mapping);
5162 }
5163 }
5164 }
5165 }
5166 (impt_sans_version.to_string(), None)
5167}
5168
5169pub fn parse_world_key(name: &str) -> Option<(&str, &str, &str)> {
5170 let registry_idx = name.find(':')?;
5171 let ns = &name[0..registry_idx];
5172 match name.rfind('/') {
5173 Some(sep_idx) => {
5174 let end = if let Some(version_idx) = name.rfind('@') {
5175 version_idx
5176 } else {
5177 name.len()
5178 };
5179 Some((
5180 ns,
5181 &name[registry_idx + 1..sep_idx],
5182 &name[sep_idx + 1..end],
5183 ))
5184 }
5185 None => Some((ns, &name[registry_idx + 1..], "")),
5187 }
5188}
5189
5190fn core_file_name(name: &str, idx: u32) -> String {
5191 let i_str = if idx == 0 {
5192 String::from("")
5193 } else {
5194 (idx + 1).to_string()
5195 };
5196 format!("{name}.core{i_str}.wasm")
5197}
5198
5199fn string_encoding_js_literal(val: &wasmtime_environ::component::StringEncoding) -> &'static str {
5201 match val {
5202 wasmtime_environ::component::StringEncoding::Utf8 => "'utf8'",
5203 wasmtime_environ::component::StringEncoding::Utf16 => "'utf16'",
5204 wasmtime_environ::component::StringEncoding::CompactUtf16 => "'compact-utf16'",
5205 }
5206}
5207
5208pub fn gen_flat_lift_fn_list_js_expr(
5217 instantiator: &mut Instantiator,
5218 types: &[InterfaceType],
5219 extra_resource_map: &Option<&mut ResourceMap>,
5220) -> String {
5221 let mut lift_fns: Vec<String> = Vec::with_capacity(types.len());
5222 for ty in types.iter() {
5223 lift_fns.push(gen_flat_lift_fn_js_expr(
5224 instantiator,
5225 ty,
5226 extra_resource_map,
5227 ));
5228 }
5229 format!("[{}]", lift_fns.join(","))
5230}
5231
5232fn flat_count_js_expr(flat_count: &Option<u8>) -> String {
5233 flat_count
5234 .map(|count| count.to_string())
5235 .unwrap_or_else(|| "null".into())
5236}
5237
5238pub fn gen_flat_lift_fn_js_expr(
5259 instantiator: &mut Instantiator,
5260 ty: &InterfaceType,
5261 extra_resource_map: &Option<&mut ResourceMap>,
5262) -> String {
5263 let component_types = instantiator.types;
5264
5265 match ty {
5266 InterfaceType::Bool => {
5267 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatBool));
5268 Intrinsic::Lift(LiftIntrinsic::LiftFlatBool).name().into()
5269 }
5270
5271 InterfaceType::S8 => {
5272 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS8));
5273 Intrinsic::Lift(LiftIntrinsic::LiftFlatS8).name().into()
5274 }
5275
5276 InterfaceType::U8 => {
5277 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU8));
5278 Intrinsic::Lift(LiftIntrinsic::LiftFlatU8).name().into()
5279 }
5280
5281 InterfaceType::S16 => {
5282 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS16));
5283 Intrinsic::Lift(LiftIntrinsic::LiftFlatS16).name().into()
5284 }
5285
5286 InterfaceType::U16 => {
5287 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU16));
5288 Intrinsic::Lift(LiftIntrinsic::LiftFlatU16).name().into()
5289 }
5290
5291 InterfaceType::S32 => {
5292 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS32));
5293 Intrinsic::Lift(LiftIntrinsic::LiftFlatS32).name().into()
5294 }
5295
5296 InterfaceType::U32 => {
5297 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU32));
5298 Intrinsic::Lift(LiftIntrinsic::LiftFlatU32).name().into()
5299 }
5300
5301 InterfaceType::S64 => {
5302 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS64));
5303 Intrinsic::Lift(LiftIntrinsic::LiftFlatS64).name().into()
5304 }
5305
5306 InterfaceType::U64 => {
5307 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU64));
5308 Intrinsic::Lift(LiftIntrinsic::LiftFlatU64).name().into()
5309 }
5310
5311 InterfaceType::Float32 => {
5312 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFloat32));
5313 Intrinsic::Lift(LiftIntrinsic::LiftFlatFloat32)
5314 .name()
5315 .into()
5316 }
5317
5318 InterfaceType::Float64 => {
5319 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFloat64));
5320 Intrinsic::Lift(LiftIntrinsic::LiftFlatFloat64)
5321 .name()
5322 .into()
5323 }
5324
5325 InterfaceType::Char => {
5326 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatChar));
5327 Intrinsic::Lift(LiftIntrinsic::LiftFlatChar).name().into()
5328 }
5329
5330 InterfaceType::String => {
5331 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatStringAny));
5332 Intrinsic::Lift(LiftIntrinsic::LiftFlatStringAny)
5333 .name()
5334 .into()
5335 }
5336
5337 InterfaceType::Record(ty_idx) => {
5338 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatRecord));
5339 let lift_fn = Intrinsic::Lift(LiftIntrinsic::LiftFlatRecord).name();
5340 let record_ty = &component_types[*ty_idx];
5341 let size32 = record_ty.abi.size32;
5342 let align32 = record_ty.abi.align32;
5343 let mut keys_and_lifts_expr = String::from("[");
5344 for f in &record_ty.fields {
5348 let field_abi = component_types.canonical_abi(&f.ty);
5349 let field_size32 = field_abi.size32;
5350 let field_align32 = field_abi.align32;
5351 keys_and_lifts_expr.push_str(&format!(
5352 "['{}', {}, {}, {}],",
5353 f.name.to_lower_camel_case(),
5354 gen_flat_lift_fn_js_expr(instantiator, &f.ty, extra_resource_map),
5355 field_size32,
5356 field_align32,
5357 ));
5358 }
5359 keys_and_lifts_expr.push(']');
5360 format!(
5361 "{lift_fn}({{ fieldMetas: {keys_and_lifts_expr}, size32: {size32}, align32: {align32} }})"
5362 )
5363 }
5364
5365 InterfaceType::Variant(ty_idx) => {
5366 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatVariant));
5367 let lift_fn = Intrinsic::Lift(LiftIntrinsic::LiftFlatVariant).name();
5368 let variant_ty = &component_types[*ty_idx];
5369 let variant_flat_count = flat_count_js_expr(&variant_ty.abi.flat_count);
5370 let variant_size32 = variant_ty.abi.size32;
5371 let variant_align32 = variant_ty.abi.align32;
5372 let variant_payload_offset32 = variant_ty.info.payload_offset32;
5373
5374 let mut lift_metas_expr = String::from("[");
5375 for (name, maybe_ty) in &variant_ty.cases {
5376 let (lift_fn_js, case_size32, case_align32, case_flat_count) = match maybe_ty {
5377 Some(ty) => {
5378 let cabi_info = component_types.canonical_abi(ty);
5379 (
5380 gen_flat_lift_fn_js_expr(instantiator, ty, extra_resource_map),
5381 cabi_info.size32.to_string(),
5382 cabi_info.align32.to_string(),
5383 cabi_info
5384 .flat_count(MAX_FLAT_PARAMS)
5385 .map(|v| v.to_string())
5386 .unwrap_or_else(|| "null".into()),
5387 )
5388 }
5389 None => ("null".into(), "0".into(), "0".into(), "0".into()),
5390 };
5391
5392 lift_metas_expr.push_str(&format!(
5393 "['{name}', {lift_fn_js}, {case_size32}, {case_align32}, {case_flat_count}],",
5394 ));
5395 }
5396 lift_metas_expr.push(']');
5397
5398 format!(
5399 "{lift_fn}({{
5400 caseMetas: {lift_metas_expr},
5401 variantSize32: {variant_size32},
5402 variantAlign32: {variant_align32},
5403 variantPayloadOffset32: {variant_payload_offset32},
5404 variantFlatCount: {variant_flat_count},
5405 }} )"
5406 )
5407 }
5408
5409 InterfaceType::List(ty_idx) => {
5410 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatList));
5411 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatList).name();
5412 let list_ty = &component_types[*ty_idx];
5413 let lift_fn_expr =
5414 gen_flat_lift_fn_js_expr(instantiator, &list_ty.element, extra_resource_map);
5415 let elem_cabi = component_types.canonical_abi(&list_ty.element);
5416 let elem_align32 = elem_cabi.align32;
5417 let elem_size32 = elem_cabi.size32;
5418 let typed_array = js_typed_array_ctor(&list_ty.element).unwrap_or("undefined");
5419 format!(
5420 "{f}({{
5421 elemLiftFn: {lift_fn_expr},
5422 elemAlign32: {elem_align32},
5423 elemSize32: {elem_size32},
5424 typedArray: {typed_array},
5425 }})"
5426 )
5427 }
5428
5429 InterfaceType::FixedLengthList(ty_idx) => {
5430 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatList));
5431 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatList).name();
5432 let list_ty = &component_types[*ty_idx];
5433 let list_size32 = list_ty.abi.size32;
5434 let list_align32 = list_ty.abi.align32;
5435 let lift_fn_expr =
5436 gen_flat_lift_fn_js_expr(instantiator, &list_ty.element, extra_resource_map);
5437 let list_len = list_ty.size;
5438 let elem_cabi = component_types.canonical_abi(&list_ty.element);
5439 let elem_align32 = elem_cabi.align32;
5440 let elem_size32 = elem_cabi.size32;
5441 format!(
5442 "{f}({{
5443 elemLiftFn: {lift_fn_expr},
5444 elemAlign32: {elem_align32},
5445 elemSize32: {elem_size32},
5446 listSize32: {list_size32},
5447 listAlign32: {list_align32},
5448 knownLen: {list_len},
5449 }})"
5450 )
5451 }
5452
5453 InterfaceType::Tuple(ty_idx) => {
5454 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatTuple));
5455 let tuple_ty = &component_types[*ty_idx];
5456 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatTuple).name();
5457 let size_u32 = tuple_ty.abi.size32;
5458 let align_u32 = tuple_ty.abi.align32;
5459
5460 let mut elem_lifts_expr = String::from("[");
5461 for ty in &tuple_ty.types {
5462 let lift_fn_js = gen_flat_lift_fn_js_expr(instantiator, ty, extra_resource_map);
5463 let elem_abi = component_types.canonical_abi(ty);
5464 let elem_size32 = elem_abi.size32;
5465 let elem_align32 = elem_abi.align32;
5466 elem_lifts_expr
5467 .push_str(&format!("[{lift_fn_js}, {elem_size32}, {elem_align32}],"));
5468 }
5469 elem_lifts_expr.push(']');
5470
5471 format!(
5472 "{f}({{ elemLiftFns: {elem_lifts_expr}, size32: {size_u32}, align32: {align_u32} }})"
5473 )
5474 }
5475
5476 InterfaceType::Flags(ty_idx) => {
5477 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFlags));
5478 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatFlags).name();
5479 let flags_ty = &component_types[*ty_idx];
5480 let size_u32 = flags_ty.abi.size32;
5481 let align_u32 = flags_ty.abi.align32;
5482 let names_expr = format!(
5483 "[{}]",
5484 flags_ty
5485 .names
5486 .iter()
5487 .map(|s| format!("'{}'", s.to_lower_camel_case()))
5488 .collect::<Vec<_>>()
5489 .join(",")
5490 );
5491 let num_flags = flags_ty.names.len();
5492 let elem_size = if num_flags <= 8 {
5493 1
5494 } else if num_flags <= 16 {
5495 2
5496 } else {
5497 4
5498 };
5499
5500 format!(
5501 "{f}({{ names: {names_expr}, size32: {size_u32}, align32: {align_u32}, intSizeBytes: {elem_size} }})"
5502 )
5503 }
5504
5505 InterfaceType::Enum(ty_idx) => {
5506 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatEnum));
5507 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatEnum).name();
5508 let enum_ty = &component_types[*ty_idx];
5509 let enum_size32 = enum_ty.abi.size32;
5510 let enum_align32 = enum_ty.abi.align32;
5511 let enum_payload_offset32 = enum_ty.info.payload_offset32;
5512 let enum_flat_count = flat_count_js_expr(&enum_ty.abi.flat_count);
5513
5514 let mut elem_lifts_expr = String::from("[");
5515 for name in &enum_ty.names {
5516 elem_lifts_expr.push_str(&format!(
5517 "['{name}', null, {enum_size32}, {enum_align32}, {enum_payload_offset32}],"
5518 ));
5519 }
5520 elem_lifts_expr.push(']');
5521
5522 format!(
5523 r#"
5524 {f}({{
5525 caseMetas: {elem_lifts_expr},
5526 variantSize32: {enum_size32},
5527 variantAlign32: {enum_align32},
5528 variantPayloadOffset32: {enum_payload_offset32},
5529 variantFlatCount: {enum_flat_count},
5530 }})
5531 "#
5532 )
5533 }
5534
5535 InterfaceType::Option(ty_idx) => {
5536 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatOption));
5537 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatOption).name();
5538 let option_ty = &component_types[*ty_idx];
5539 let option_payload_offset32 = option_ty.info.payload_offset32;
5540 let option_align32 = option_ty.abi.align32;
5541 let option_size32 = option_ty.abi.size32;
5542 let option_flat_count = flat_count_js_expr(&option_ty.abi.flat_count);
5543
5544 let some_ty_abi = component_types.canonical_abi(&option_ty.ty);
5545 let some_ty_flat_count = flat_count_js_expr(&some_ty_abi.flat_count);
5546 let some_ty_size32 = some_ty_abi.size32;
5547 let some_ty_align32 = some_ty_abi.align32;
5548 let some_ty_lift_fn_js =
5549 gen_flat_lift_fn_js_expr(instantiator, &option_ty.ty, extra_resource_map);
5550
5551 format!(
5552 r#"
5553 {f}({{
5554 caseMetas: [
5555 ['none', null, 0, 0, 0 ],
5556 ['some', {some_ty_lift_fn_js}, {some_ty_size32}, {some_ty_align32}, {some_ty_flat_count} ],
5557 ],
5558 variantSize32: {option_size32},
5559 variantAlign32: {option_align32},
5560 variantPayloadOffset32: {option_payload_offset32},
5561 variantFlatCount: {option_flat_count},
5562 }})
5563 "#
5564 )
5565 }
5566
5567 InterfaceType::Result(ty_idx) => {
5568 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatResult));
5569 let lift_fn = Intrinsic::Lift(LiftIntrinsic::LiftFlatResult).name();
5570 let result_ty = &component_types[*ty_idx];
5571 let result_size32 = result_ty.abi.size32;
5572 let result_align32 = result_ty.abi.align32;
5573 let result_payload_offset32 = result_ty.info.payload_offset32;
5574 let result_flat_count = flat_count_js_expr(&result_ty.abi.flat_count);
5575
5576 let mut cases_and_lifts_expr = String::from("[");
5577 if let Some(ok_ty) = result_ty.ok {
5578 let ok_ty_abi = component_types.canonical_abi(&ok_ty);
5579 let ok_ty_size32 = ok_ty_abi.size32;
5580 let ok_ty_align32 = ok_ty_abi.align32;
5581 let ok_flat_count = flat_count_js_expr(&ok_ty_abi.flat_count);
5582 let ok_ty_lift_fn =
5583 gen_flat_lift_fn_js_expr(instantiator, &ok_ty, extra_resource_map);
5584 cases_and_lifts_expr.push_str(&format!(
5585 "['ok', {ok_ty_lift_fn}, {ok_ty_size32}, {ok_ty_align32}, {ok_flat_count}],",
5586 ))
5587 } else {
5588 cases_and_lifts_expr.push_str("['ok', null, 0, 0, 0],");
5589 }
5590
5591 if let Some(err_ty) = &result_ty.err {
5592 let err_ty_abi = component_types.canonical_abi(err_ty);
5593 let err_ty_size32 = err_ty_abi.size32;
5594 let err_ty_align32 = err_ty_abi.align32;
5595 let err_ty_flat_count = flat_count_js_expr(&err_ty_abi.flat_count);
5596 let err_ty_lift_fn =
5597 gen_flat_lift_fn_js_expr(instantiator, err_ty, extra_resource_map);
5598 cases_and_lifts_expr.push_str(&format!(
5599 "['err', {err_ty_lift_fn}, {err_ty_size32}, {err_ty_align32}, {err_ty_flat_count}],",
5600 ))
5601 } else {
5602 cases_and_lifts_expr.push_str("['err', null, 0, 0, 0],");
5603 }
5604 cases_and_lifts_expr.push(']');
5605
5606 format!(
5607 r#"
5608 {lift_fn}({{
5609 caseMetas: {cases_and_lifts_expr},
5610 variantSize32: {result_size32},
5611 variantAlign32: {result_align32},
5612 variantPayloadOffset32: {result_payload_offset32},
5613 variantFlatCount: {result_flat_count},
5614 }})
5615 "#
5616 )
5617 }
5618
5619 InterfaceType::Own(ty_idx) => {
5620 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatOwn));
5621 instantiator.add_intrinsic(Intrinsic::JsHelper(JsHelperIntrinsic::EmptyFunc));
5622 instantiator.add_intrinsic(Intrinsic::SymbolResourceHandle);
5623 instantiator.add_intrinsic(Intrinsic::SymbolResourceRep);
5624 instantiator.add_intrinsic(Intrinsic::SymbolDispose);
5625 instantiator.add_intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableRemove));
5626 instantiator.add_intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableFlag));
5627 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatOwn).name();
5628 let table_ty = &component_types[*ty_idx];
5629 let component_idx = table_ty.unwrap_concrete_instance().as_u32();
5630 let resource_idx = table_ty.unwrap_concrete_ty();
5631
5632 match instantiator.exports_resource_index_types.get(&resource_idx) {
5634 None => format!(
5636 r#"{f}({{
5637 componentIdx: {component_idx},
5638 className: null,
5639 createResourceFn: () => {{ throw new Error('invalid/missing resource type data'); }},
5640 }})
5641 "#,
5642 ),
5643
5644 Some(resource_typedef) => {
5647 let (resource_class_name, create_resource_fn_js) = match (
5649 instantiator.resource_exports.get(resource_typedef),
5650 extra_resource_map
5651 .as_ref()
5652 .and_then(|v| v.get(resource_typedef)),
5653 ) {
5654 (None, None) => (
5656 "null".into(),
5657 "() => {{ throw new Error('missing resource information'); }}".into(),
5658 ),
5659
5660 (Some(ResourceTable { imported, data }), _)
5662 | (_, Some(ResourceTable { imported, data })) => match data {
5663 ResourceData::Guest { .. } => {
5664 unimplemented!(
5665 "owned resources created by guests should must have host-side data"
5666 )
5667 }
5668 ResourceData::Host {
5669 tid,
5670 rid,
5671 local_name,
5672 dtor_name,
5673 } => {
5674 let empty_func = JsHelperIntrinsic::EmptyFunc.name();
5675 let symbol_resource_handle = Intrinsic::SymbolResourceHandle.name();
5676 let symbol_dispose = Intrinsic::SymbolDispose.name();
5677 let rsc_table_remove =
5678 ResourceIntrinsic::ResourceTableRemove.name();
5679 let tid = tid.as_u32();
5680 let rsc_flag = ResourceIntrinsic::ResourceTableFlag.name();
5681
5682 let create_resource_fn_js = if *imported {
5684 let symbol_resource_rep = Intrinsic::SymbolResourceRep.name();
5685 let rid = rid.as_u32();
5686 format!(
5687 r#"
5688 (handle) => {{
5689 const rep = handleTable{tid}[(handle << 1) + 1] & ~{rsc_flag};
5690 let resourceObj = captureTable{rid}.get(rep);
5691 if (!resourceObj) {{
5692 resourceObj = Object.create({local_name}.prototype);
5693 Object.defineProperty(resourceObj, {symbol_resource_handle}, {{ writable: true, value: handle }});
5694 Object.defineProperty(resourceObj, {symbol_resource_rep}, {{ writable: true, value: rep }});
5695 }} else {{
5696 captureTable{rid}.delete(rep);
5697 }}
5698 {rsc_table_remove}(handleTable{tid}, handle);
5699 return resourceObj;
5700 }}
5701 "#
5702 )
5703 } else {
5704 let dtor_setup_js = dtor_name
5705 .as_ref()
5706 .map(|dtor|
5707 format!(
5708 r#"
5709 Object.defineProperty(
5710 resourceObj,
5711 {symbol_dispose},
5712 {{
5713 writable: true,
5714 value: function() {{
5715 finalizationRegistry{tid}.unregister(resourceObj);
5716 {rsc_table_remove}(handleTable{tid}, handle);
5717 resourceObj[{symbol_dispose}] = {empty_func};
5718 resourceObj[{symbol_resource_handle}] = undefined;
5719 {dtor}(handleTable{tid}[(handle << 1) + 1] & ~{rsc_flag});
5720 }}
5721 }}
5722 );
5723 "#
5724 )
5725 ).unwrap_or_default();
5726
5727 format!(
5728 r#"
5729 (handle) => {{
5730 const resourceObj = Object.create({local_name}.prototype);
5731 Object.defineProperty(resourceObj, {symbol_resource_handle}, {{
5732 writable: true,
5733 value: handle,
5734 }});
5735 finalizationRegistry{tid}.register(resourceObj, handle, resourceObj);
5736 {dtor_setup_js}
5737 return resourceObj;
5738 }}
5739 "#
5740 )
5741 };
5742
5743 (local_name.to_string(), create_resource_fn_js)
5744 }
5745 },
5746 };
5747
5748 format!(
5749 r#"{f}({{
5750 componentIdx: {component_idx},
5751 className: {resource_class_name},
5752 createResourceFn: {create_resource_fn_js},
5753 }})
5754 "#,
5755 )
5756 }
5757 }
5758 }
5759
5760 InterfaceType::Borrow(ty_idx) => {
5761 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatBorrow));
5762 let table_idx = ty_idx.as_u32();
5763 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatBorrow).name();
5764 format!("{f}.bind(null, {table_idx})")
5765 }
5766
5767 InterfaceType::Future(ty_idx) => {
5768 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFuture));
5769 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatFuture).name();
5770 let table_idx = ty_idx.as_u32();
5771 let table_ty = &component_types[*ty_idx];
5772 let component_idx = table_ty.instance.as_u32();
5773 format!("{f}({{ futureTableIdx: {table_idx}, componentIdx: {component_idx} }})")
5774 }
5775
5776 InterfaceType::Stream(ty_idx) => {
5777 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatStream));
5778 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatStream).name();
5779 let table_idx = ty_idx.as_u32();
5780 let table_ty = &component_types[*ty_idx];
5781 let component_idx = table_ty.instance.as_u32();
5782 format!("{f}({{ streamTableIdx: {table_idx}, componentIdx: {component_idx} }})")
5783 }
5784
5785 InterfaceType::ErrorContext(ty_idx) => {
5786 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatErrorContext));
5787 let table_idx = ty_idx.as_u32();
5788 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatErrorContext).name();
5789 format!("{f}.bind(null, {table_idx})")
5790 }
5791
5792 InterfaceType::Map(ty_idx) => {
5793 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatMap));
5794 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatMap).name();
5795 let map_ty = &component_types[*ty_idx];
5796 let key_lift = gen_flat_lift_fn_js_expr(instantiator, &map_ty.key, extra_resource_map);
5797 let value_lift =
5798 gen_flat_lift_fn_js_expr(instantiator, &map_ty.value, extra_resource_map);
5799 let entry_size32 = map_ty.entry_abi.size32;
5800 let entry_align32 = map_ty.entry_abi.align32;
5801 let value_offset32 = map_ty.value_offset32;
5802 format!(
5803 "{f}({{
5804 keyLiftFn: {key_lift},
5805 valueLiftFn: {value_lift},
5806 entrySize32: {entry_size32},
5807 entryAlign32: {entry_align32},
5808 valueOffset32: {value_offset32},
5809 }})"
5810 )
5811 }
5812 }
5813}
5814
5815fn js_typed_array_ctor(ty: &InterfaceType) -> Option<&'static str> {
5816 match ty {
5817 InterfaceType::U8 => Some("Uint8Array"),
5818 InterfaceType::S8 => Some("Int8Array"),
5819 InterfaceType::U16 => Some("Uint16Array"),
5820 InterfaceType::S16 => Some("Int16Array"),
5821 InterfaceType::U32 => Some("Uint32Array"),
5822 InterfaceType::S32 => Some("Int32Array"),
5823 InterfaceType::U64 => Some("BigUint64Array"),
5824 InterfaceType::S64 => Some("BigInt64Array"),
5825 InterfaceType::Float32 => Some("Float32Array"),
5826 InterfaceType::Float64 => Some("Float64Array"),
5827 _ => None,
5828 }
5829}
5830
5831pub fn gen_flat_lower_fn_list_js_expr(
5840 instantiator: &mut Instantiator,
5841 types: &[InterfaceType],
5842 extra_import_map: &Option<&mut ResourceMap>,
5843) -> String {
5844 let mut lower_fns: Vec<String> = Vec::with_capacity(types.len());
5845 for ty in types.iter() {
5846 lower_fns.push(gen_flat_lower_fn_js_expr(
5847 instantiator,
5848 ty,
5849 extra_import_map,
5850 ));
5851 }
5852 format!("[{}]", lower_fns.join(","))
5853}
5854
5855pub fn gen_flat_lower_fn_js_expr(
5876 instantiator: &mut Instantiator,
5877 ty: &InterfaceType,
5878 extra_resource_map: &Option<&mut ResourceMap>,
5879) -> String {
5880 let component_types = instantiator.types;
5881 match ty {
5882 InterfaceType::Bool => {
5883 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatBool));
5884 Intrinsic::Lower(LowerIntrinsic::LowerFlatBool)
5885 .name()
5886 .into()
5887 }
5888
5889 InterfaceType::S8 => {
5890 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatS8));
5891 Intrinsic::Lower(LowerIntrinsic::LowerFlatS8).name().into()
5892 }
5893
5894 InterfaceType::U8 => {
5895 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatU8));
5896 Intrinsic::Lower(LowerIntrinsic::LowerFlatU8).name().into()
5897 }
5898
5899 InterfaceType::S16 => {
5900 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatS16));
5901 Intrinsic::Lower(LowerIntrinsic::LowerFlatS16).name().into()
5902 }
5903
5904 InterfaceType::U16 => {
5905 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatU16));
5906 Intrinsic::Lower(LowerIntrinsic::LowerFlatU16).name().into()
5907 }
5908
5909 InterfaceType::S32 => {
5910 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatS32));
5911 Intrinsic::Lower(LowerIntrinsic::LowerFlatS32).name().into()
5912 }
5913
5914 InterfaceType::U32 => {
5915 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatU32));
5916 Intrinsic::Lower(LowerIntrinsic::LowerFlatU32).name().into()
5917 }
5918
5919 InterfaceType::S64 => {
5920 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatS64));
5921 Intrinsic::Lower(LowerIntrinsic::LowerFlatS64).name().into()
5922 }
5923
5924 InterfaceType::U64 => {
5925 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatU64));
5926 Intrinsic::Lower(LowerIntrinsic::LowerFlatU64).name().into()
5927 }
5928
5929 InterfaceType::Float32 => {
5930 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatFloat32));
5931 Intrinsic::Lower(LowerIntrinsic::LowerFlatFloat32)
5932 .name()
5933 .into()
5934 }
5935
5936 InterfaceType::Float64 => {
5937 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatFloat64));
5938 Intrinsic::Lower(LowerIntrinsic::LowerFlatFloat64)
5939 .name()
5940 .into()
5941 }
5942
5943 InterfaceType::Char => {
5944 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatChar));
5945 Intrinsic::Lower(LowerIntrinsic::LowerFlatChar)
5946 .name()
5947 .into()
5948 }
5949
5950 InterfaceType::String => {
5951 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatStringAny));
5952 Intrinsic::Lower(LowerIntrinsic::LowerFlatStringAny)
5953 .name()
5954 .into()
5955 }
5956
5957 InterfaceType::Record(ty_idx) => {
5958 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatRecord));
5959 let lower_fn = Intrinsic::Lower(LowerIntrinsic::LowerFlatRecord).name();
5960 let record_ty = &component_types[*ty_idx];
5961 let size32 = record_ty.abi.size32;
5962 let align32 = record_ty.abi.align32;
5963 let mut keys_and_lowers_expr = String::from("[");
5964 for f in &record_ty.fields {
5965 let field_abi = component_types.canonical_abi(&f.ty);
5969 let field_size32 = field_abi.size32;
5970 let field_align32 = field_abi.align32;
5971 keys_and_lowers_expr.push_str(&format!(
5972 "['{}', {}, {}, {} ],",
5973 f.name.to_lower_camel_case(),
5974 gen_flat_lower_fn_js_expr(instantiator, &f.ty, &None),
5975 field_size32,
5976 field_align32,
5977 ));
5978 }
5979 keys_and_lowers_expr.push(']');
5980 format!(
5981 "{lower_fn}({{ fieldMetas: {keys_and_lowers_expr}, size32: {size32}, align32: {align32} }})"
5982 )
5983 }
5984
5985 InterfaceType::Variant(ty_idx) => {
5986 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatVariant));
5987 let lower_fn = Intrinsic::Lower(LowerIntrinsic::LowerFlatVariant).name();
5988 let variant_ty = &component_types[*ty_idx];
5989 let variant_flat_count = flat_count_js_expr(&variant_ty.abi.flat_count);
5990 let size32 = variant_ty.abi.size32;
5991 let align32 = variant_ty.abi.align32;
5992 let payload_offset32 = variant_ty.info.payload_offset32;
5993
5994 let mut lower_metas_expr = String::from("[");
5995 for (name, maybe_ty) in variant_ty.cases.iter() {
5996 let (case_size32, case_align32, case_flat_count) = if let Some(iface_ty) = maybe_ty
5997 {
5998 let cabi_info = component_types.canonical_abi(iface_ty);
5999 (
6000 cabi_info.size32.to_string(),
6001 cabi_info.align32.to_string(),
6002 cabi_info
6003 .flat_count(MAX_FLAT_PARAMS)
6004 .map(|v| v.to_string())
6005 .unwrap_or_else(|| "null".into()),
6006 )
6007 } else {
6008 ("0".into(), "0".into(), "0".into())
6009 };
6010
6011 lower_metas_expr.push_str(&format!(
6012 "[ '{name}', {}, {case_size32}, {case_align32}, {case_flat_count} ],",
6013 maybe_ty
6014 .map(|ty| gen_flat_lower_fn_js_expr(instantiator, &ty, &None))
6015 .unwrap_or_else(|| "null".into()),
6016 ));
6017 }
6018 lower_metas_expr.push(']');
6019
6020 format!(
6021 "{lower_fn}({{
6022 caseMetas: {lower_metas_expr},
6023 variantSize32: {size32},
6024 variantAlign32: {align32},
6025 variantPayloadOffset32: {payload_offset32},
6026 variantFlatCount: {variant_flat_count},
6027 }} )"
6028 )
6029 }
6030
6031 InterfaceType::List(ty_idx) => {
6032 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatList));
6033 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatList).name();
6034 let list_ty = &component_types[*ty_idx];
6035 let elem_ty_lower_expr =
6036 gen_flat_lower_fn_js_expr(instantiator, &list_ty.element, extra_resource_map);
6037 let elem_cabi = component_types.canonical_abi(&list_ty.element);
6038 let elem_align32 = elem_cabi.align32;
6039 let elem_size32 = elem_cabi.size32;
6040
6041 format!(
6042 "{f}({{
6043 elemLowerFn: {elem_ty_lower_expr},
6044 elemSize32: {elem_size32},
6045 elemAlign32: {elem_align32},
6046 }})"
6047 )
6048 }
6049
6050 InterfaceType::FixedLengthList(ty_idx) => {
6051 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatList));
6052 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatList).name();
6053 let list_ty = &component_types[*ty_idx];
6054 let elem_ty_lower_expr =
6055 gen_flat_lower_fn_js_expr(instantiator, &list_ty.element, extra_resource_map);
6056 let list_len = list_ty.size;
6057 let list_align32 = list_ty.abi.size32;
6058 let list_size32 = list_ty.abi.size32;
6059 let elem_cabi = component_types.canonical_abi(&list_ty.element);
6060 let elem_align32 = elem_cabi.align32;
6061 let elem_size32 = elem_cabi.size32;
6062
6063 format!(
6064 r#"{f}({{
6065 elemLowerFn: {elem_ty_lower_expr},
6066 elemAlign32: {elem_align32},
6067 elemSize32: {elem_size32},
6068 align32: {list_align32},
6069 size32: {list_size32},
6070 knownLen: {list_len},
6071 }})"#
6072 )
6073 }
6074
6075 InterfaceType::Tuple(ty_idx) => {
6076 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatTuple));
6077 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatTuple).name();
6078 let tuple_ty = &component_types[*ty_idx];
6079 let size_u32 = tuple_ty.abi.size32;
6080 let align_u32 = tuple_ty.abi.align32;
6081
6082 let mut elem_lowers_expr = String::from("[");
6083 for ty in &tuple_ty.types {
6084 let lower_fn_js = gen_flat_lower_fn_js_expr(instantiator, ty, extra_resource_map);
6085 let elem_abi = component_types.canonical_abi(ty);
6086 let elem_size32 = elem_abi.size32;
6087 let elem_align32 = elem_abi.align32;
6088 elem_lowers_expr
6089 .push_str(&format!("[{lower_fn_js}, {elem_size32}, {elem_align32}],"));
6090 }
6091 elem_lowers_expr.push(']');
6092
6093 format!(
6094 "{f}({{ elemLowerMetas: {elem_lowers_expr}, size32: {size_u32}, align32: {align_u32} }})"
6095 )
6096 }
6097
6098 InterfaceType::Flags(ty_idx) => {
6099 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatFlags));
6100 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatFlags).name();
6101 let flags_ty = &component_types[*ty_idx];
6102 let size32 = flags_ty.abi.size32;
6103 let align32 = flags_ty.abi.align32;
6104 let names_list_js = format!(
6105 "[{}]",
6106 flags_ty
6107 .names
6108 .iter()
6109 .map(|s| format!("'{}'", s.to_lower_camel_case()))
6110 .collect::<Vec<_>>()
6111 .join(",")
6112 );
6113 let num_flags = flags_ty.names.len();
6114 let elem_size = if num_flags <= 8 {
6115 1
6116 } else if num_flags <= 16 {
6117 2
6118 } else {
6119 4
6120 };
6121
6122 format!(
6123 "{f}({{ names: {names_list_js}, size32: {size32}, align32: {align32}, intSizeBytes: {elem_size} }})"
6124 )
6125 }
6126
6127 InterfaceType::Enum(ty_idx) => {
6128 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatEnum));
6129 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatEnum).name();
6130 let enum_ty = &component_types[*ty_idx];
6131 let enum_size32 = enum_ty.abi.size32;
6132 let enum_align32 = enum_ty.abi.align32;
6133 let enum_flat_count = flat_count_js_expr(&enum_ty.abi.flat_count);
6134 let enum_payload_offset32 = enum_ty.info.payload_offset32;
6135
6136 let mut elem_lowers_expr = String::from("[");
6137 for name in &enum_ty.names {
6138 elem_lowers_expr.push_str(&format!(
6139 "['{name}', null, {enum_size32}, {enum_align32}, {enum_payload_offset32}],"
6140 ));
6141 }
6142 elem_lowers_expr.push(']');
6143
6144 format!(
6145 r#"
6146 {f}({{
6147 caseMetas: {elem_lowers_expr},
6148 variantSize32: {enum_size32},
6149 variantAlign32: {enum_align32},
6150 variantPayloadOffset32: {enum_payload_offset32},
6151 variantFlatCount: {enum_flat_count},
6152 }})
6153 "#
6154 )
6155 }
6156
6157 InterfaceType::Option(ty_idx) => {
6158 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatOption));
6159 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatOption).name();
6160 let option_ty = &component_types[*ty_idx];
6161 let option_size32 = option_ty.abi.size32;
6162 let option_align32 = option_ty.abi.align32;
6163 let option_payload_offset32 = option_ty.info.payload_offset32;
6164 let option_flat_count = flat_count_js_expr(&option_ty.abi.flat_count);
6165
6166 let some_ty_abi = component_types.canonical_abi(&option_ty.ty);
6167 let some_ty_flat_count = flat_count_js_expr(&some_ty_abi.flat_count);
6168 let some_ty_size32 = some_ty_abi.size32;
6169 let some_ty_align32 = some_ty_abi.align32;
6170 let some_ty_lower_fn_js =
6171 gen_flat_lower_fn_js_expr(instantiator, &option_ty.ty, extra_resource_map);
6172
6173 format!(
6174 r#"
6175 {f}({{
6176 caseMetas: [
6177 [ 'none', null, 0, 0, 0 ],
6178 [ 'some', {some_ty_lower_fn_js}, {some_ty_size32}, {some_ty_align32}, {some_ty_flat_count}],
6179 ],
6180 variantSize32: {option_size32},
6181 variantAlign32: {option_align32},
6182 variantPayloadOffset32: {option_payload_offset32},
6183 variantFlatCount: {option_flat_count},
6184 }})
6185 "#
6186 )
6187 }
6188
6189 InterfaceType::Result(ty_idx) => {
6190 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatResult));
6191 let lower_fn = Intrinsic::Lower(LowerIntrinsic::LowerFlatResult).name();
6192 let result_ty = &component_types[*ty_idx];
6193 let result_size32 = result_ty.abi.size32;
6194 let result_align32 = result_ty.abi.align32;
6195 let result_payload_offset32 = result_ty.info.payload_offset32;
6196 let result_flat_count = flat_count_js_expr(&result_ty.abi.flat_count);
6197
6198 let ok_lower_fn_js = result_ty
6199 .ok
6200 .map(|ty| gen_flat_lower_fn_js_expr(instantiator, &ty, extra_resource_map))
6201 .unwrap_or_else(|| "null".into());
6202 let err_lower_fn_js = result_ty
6203 .err
6204 .map(|ty| gen_flat_lower_fn_js_expr(instantiator, &ty, extra_resource_map))
6205 .unwrap_or_else(|| "null".into());
6206
6207 format!(
6208 r#"
6209 {lower_fn}({{
6210 caseMetas: [
6211 [ 'ok', {ok_lower_fn_js}, {result_size32}, {result_align32}, {result_payload_offset32} ],
6212 [ 'err', {err_lower_fn_js}, {result_size32}, {result_align32}, {result_payload_offset32} ],
6213 ],
6214 variantSize32: {result_size32},
6215 variantAlign32: {result_align32},
6216 variantPayloadOffset32: {result_payload_offset32},
6217 variantFlatCount: {result_flat_count},
6218 }})
6219 "#
6220 )
6221 }
6222
6223 InterfaceType::Own(ty_idx) => {
6224 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatOwn));
6225 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatOwn).name();
6226 let resource_table_ty = &component_types[*ty_idx];
6227 let component_idx = resource_table_ty.unwrap_concrete_instance().as_u32();
6228 let resource_idx = resource_table_ty.unwrap_concrete_ty();
6229
6230 let (_, ResourceTable { imported, data }) = match (
6234 instantiator.imports_resource_index_types.get(&resource_idx),
6235 instantiator.exports_resource_index_types.get(&resource_idx),
6236 ) {
6237 (Some(import_ty_id), _) => {
6238 let ty = crate::dealias(instantiator.resolve, *import_ty_id);
6239 let maybe_resource_table =
6240 instantiator.resource_imports.get(&ty).or(extra_resource_map
6241 .as_ref()
6242 .and_then(|m| m.get(import_ty_id)));
6243 (
6244 ty,
6245 maybe_resource_table.expect("missing imported resource table information"),
6246 )
6247 }
6248 (_, Some(export_ty_id)) => {
6249 let ty = crate::dealias(instantiator.resolve, *export_ty_id);
6250 let maybe_resource_table =
6251 instantiator.resource_exports.get(&ty).or(extra_resource_map
6252 .as_ref()
6253 .and_then(|m| m.get(export_ty_id)));
6254 (
6255 ty,
6256 maybe_resource_table.expect("missing exported resource table information"),
6257 )
6258 }
6259
6260 (None, None) => {
6262 return format!(
6263 "{f}({{
6264 componentIdx: {component_idx},
6265 lowerFn: () => {{ throw new Error('missing/invalid resource metadata'); }}
6266 }})"
6267 );
6268 }
6269 };
6270
6271 let lower_fn_js = match data {
6273 ResourceData::Host {
6275 tid,
6276 rid,
6277 local_name,
6278 ..
6279 } => {
6280 let tid = tid.as_u32();
6281 let rid = rid.as_u32();
6282 let symbol_resource_rep =
6283 instantiator.bindgen.intrinsic(Intrinsic::SymbolResourceRep);
6284 let symbol_resource_handle = instantiator
6285 .bindgen
6286 .intrinsic(Intrinsic::SymbolResourceHandle);
6287 let symbol_dispose = instantiator.bindgen.intrinsic(Intrinsic::SymbolDispose);
6288
6289 if *imported {
6290 let create_own_fn = instantiator.bindgen.intrinsic(Intrinsic::Resource(
6293 ResourceIntrinsic::ResourceTableCreateOwn,
6294 ));
6295 format!(
6296 r#"
6297 function lowerImportedOwnedHost_{local_name}(obj) {{
6298 if (!(obj instanceof {local_name})) {{
6299 throw new TypeError('Resource error: Not a valid \"{local_name}\" resource.');
6300 }}
6301 let handle = obj[{symbol_resource_handle}];
6302 if (!handle) {{
6303 const rep = obj[{symbol_resource_rep}] || ++captureCnt{rid};
6304 captureTable{rid}.set(rep, obj);
6305 handle = {create_own_fn}(handleTable{tid}, rep);
6306 }}
6307 return handle;
6308 }}
6309 "#
6310 )
6311 } else {
6312 let empty_func = instantiator
6318 .bindgen
6319 .intrinsic(Intrinsic::JsHelper(JsHelperIntrinsic::EmptyFunc));
6320 format!(
6321 r#"
6322 function lowerExportedOwnedHost_{local_name}(obj) {{
6323 let handle = obj[{symbol_resource_handle}];
6324 if (!handle) {{
6325 throw new TypeError('Resource error: Not a valid \"{local_name}\" resource.');
6326 }}
6327 finalizationRegistry{tid}.unregister(obj);
6328 obj[{symbol_dispose}] = {empty_func};
6329 obj[{symbol_resource_handle}] = undefined;
6330 return handle;
6331 }}
6332 "#
6333 )
6334 }
6335 }
6336
6337 ResourceData::Guest {
6339 resource_name,
6340 prefix,
6341 extra,
6342 } => {
6343 assert!(
6344 extra.is_none(),
6345 "plain resource handles do not carry extra data"
6346 );
6347
6348 let upper_camel = resource_name.to_upper_camel_case();
6349 let lower_camel = resource_name.to_lower_camel_case();
6350 let prefix = prefix.as_deref().unwrap_or("");
6351
6352 if *imported {
6353 let symbol_resource_handle = instantiator
6356 .bindgen
6357 .intrinsic(Intrinsic::SymbolResourceHandle);
6358 format!(
6359 r#"
6360 function lowerImportedOwnedGuest_{upper_camel}(obj) {{
6361 const handle = obj[{symbol_resource_handle}];
6362 finalizationRegistry_import${prefix}{lower_camel}.unregister(obj);
6363 return handle;
6364 }}
6365 "#
6366 )
6367 } else {
6368 let symbol_resource_handle = instantiator
6372 .bindgen
6373 .intrinsic(Intrinsic::SymbolResourceHandle);
6374 format!(
6375 r#"
6376 function lowerExportedOwnedGuest_{upper_camel}(obj) {{
6377 if (!(obj instanceof {upper_camel})) {{
6378 throw new TypeError('Resource error: Not a valid \"{upper_camel}\" resource.');
6379 }}
6380 let handle = obj[{symbol_resource_handle}];
6381 if (handle === undefined) {{
6382 const localRep = repCnt++;
6383 repTable.set(localRep, {{ rep: obj, own: true }});
6384 handle = $resource_{prefix}new${lower_camel}(localRep);
6385 obj[{symbol_resource_handle}] = handle;
6386 finalizationRegistry_export${prefix}{lower_camel}.register(obj, handle, obj);
6387 }}
6388 return handle;
6389 }}
6390 "#
6391 )
6392 }
6393 }
6394 };
6395
6396 format!(
6397 "{f}({{
6398 componentIdx: {component_idx},
6399 lowerFn: {lower_fn_js},
6400 }})"
6401 )
6402 }
6403
6404 InterfaceType::Borrow(ty_idx) => {
6405 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatBorrow));
6406 let table_idx = ty_idx.as_u32();
6407 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatBorrow).name();
6408 format!("{f}.bind(null, {table_idx})")
6409 }
6410
6411 InterfaceType::Future(ty_idx) => {
6412 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatFuture));
6413 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatFuture).name();
6414 let table_idx = ty_idx.as_u32();
6415 let table_ty = &component_types[*ty_idx];
6416 let component_idx = table_ty.instance.as_u32();
6417 let future_ty_idx = table_ty.ty;
6418 let future_ty = &component_types[future_ty_idx];
6419 let payload = future_ty.payload;
6420 let payload_ty_name_js = future_ty
6421 .payload
6422 .map(|iface_ty| format!("'{iface_ty:?}'"))
6423 .unwrap_or_else(|| "null".into());
6424
6425 let (
6427 payload_size32,
6428 payload_align32,
6429 payload_flat_count_js,
6430 payload_lift_fn_js,
6431 payload_lower_fn_js,
6432 is_borrowed,
6433 is_none_type,
6434 is_numeric_type,
6435 is_async_value,
6436 ) = match payload {
6437 None => (
6438 0,
6439 0,
6440 "0".into(),
6441 "() => {{ throw new Error('empty future payload'); }}".into(),
6442 "() => {{ throw new Error('empty future payload'); }}".into(),
6443 false,
6444 true,
6445 false,
6446 false,
6447 ),
6448 Some(payload_ty) => {
6449 let cabi = instantiator.types.canonical_abi(&payload_ty);
6450 (
6451 cabi.size32,
6452 cabi.align32,
6453 cabi.flat_count
6454 .map(|v| format!("{v}"))
6455 .unwrap_or_else(|| "null".into()),
6456 gen_flat_lift_fn_js_expr(instantiator, &payload_ty, extra_resource_map),
6457 gen_flat_lower_fn_js_expr(instantiator, &payload_ty, extra_resource_map),
6458 matches!(payload_ty, InterfaceType::Borrow(_)),
6459 false,
6460 matches!(
6461 payload_ty,
6462 InterfaceType::U8
6463 | InterfaceType::U16
6464 | InterfaceType::U32
6465 | InterfaceType::U64
6466 | InterfaceType::S8
6467 | InterfaceType::S16
6468 | InterfaceType::S32
6469 | InterfaceType::S64
6470 | InterfaceType::Float32
6471 | InterfaceType::Float64
6472 ),
6473 matches!(
6474 payload_ty,
6475 InterfaceType::Stream(_) | InterfaceType::Future(_)
6476 ),
6477 )
6478 }
6479 };
6480
6481 let mut future_nesting_level = 0;
6483 let mut payload_ty = future_ty.payload;
6484 while let Some(InterfaceType::Future(inner_ty)) = payload_ty {
6485 future_nesting_level += 1;
6486 payload_ty = component_types[component_types[inner_ty].ty].payload;
6487 }
6488
6489 format!(
6490 r#"{f}({{
6491 futureTableIdx: {table_idx},
6492 futureNestingLevel: {future_nesting_level},
6493 componentIdx: {component_idx},
6494 elemMeta: {{
6495 liftFn: {payload_lift_fn_js},
6496 lowerFn: {payload_lower_fn_js},
6497 payloadTypeName: {payload_ty_name_js},
6498 isNone: {is_none_type},
6499 isNumeric: {is_numeric_type},
6500 isBorrowed: {is_borrowed},
6501 isAsyncValue: {is_async_value},
6502 flatCount: {payload_flat_count_js},
6503 align32: {payload_align32},
6504 size32: {payload_size32},
6505 }},
6506 }})
6507 "#
6508 )
6509 }
6510
6511 InterfaceType::Stream(ty_idx) => {
6512 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatStream));
6513 let table_idx = ty_idx.as_u32();
6514 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatStream).name();
6515 let table_ty = &component_types[*ty_idx];
6516 let component_idx = table_ty.instance.as_u32();
6517 let stream_ty_idx = table_ty.ty;
6518 let stream_ty = &component_types[stream_ty_idx];
6519 let payload = stream_ty.payload;
6520 let payload_ty_name_js = stream_ty
6521 .payload
6522 .map(|iface_ty| format!("'{iface_ty:?}'"))
6523 .unwrap_or_else(|| "null".into());
6524
6525 let (
6528 payload_size32,
6529 payload_align32,
6530 payload_flat_count_js,
6531 payload_lift_fn_js,
6532 payload_lower_fn_js,
6533 is_borrowed,
6534 is_none_type,
6535 is_numeric_type,
6536 is_async_value,
6537 typed_array_js,
6538 ) = match payload {
6539 None => (
6540 0,
6541 0,
6542 "0".into(),
6543 "() => {{ throw new Error('empty stream payload'); }}".into(),
6544 "() => {{ throw new Error('empty stream payload'); }}".into(),
6545 false,
6546 true,
6547 false,
6548 false,
6549 "undefined",
6550 ),
6551 Some(payload_ty) => {
6552 let cabi = instantiator.types.canonical_abi(&payload_ty);
6553 (
6554 cabi.size32,
6555 cabi.align32,
6556 cabi.flat_count
6557 .map(|v| format!("{v}"))
6558 .unwrap_or_else(|| "null".into()),
6559 gen_flat_lift_fn_js_expr(instantiator, &payload_ty, extra_resource_map),
6560 gen_flat_lower_fn_js_expr(instantiator, &payload_ty, extra_resource_map),
6561 matches!(payload_ty, InterfaceType::Borrow(_)),
6562 false,
6563 matches!(
6564 payload_ty,
6565 InterfaceType::U8
6566 | InterfaceType::U16
6567 | InterfaceType::U32
6568 | InterfaceType::U64
6569 | InterfaceType::S8
6570 | InterfaceType::S16
6571 | InterfaceType::S32
6572 | InterfaceType::S64
6573 | InterfaceType::Float32
6574 | InterfaceType::Float64
6575 ),
6576 matches!(
6577 payload_ty,
6578 InterfaceType::Stream(_) | InterfaceType::Future(_)
6579 ),
6580 js_typed_array_ctor(&payload_ty).unwrap_or("undefined"),
6581 )
6582 }
6583 };
6584
6585 format!(
6586 r#"{f}({{
6587 streamTableIdx: {table_idx},
6588 componentIdx: {component_idx},
6589 elemMeta: {{
6590 liftFn: {payload_lift_fn_js},
6591 lowerFn: {payload_lower_fn_js},
6592 payloadTypeName: {payload_ty_name_js},
6593 isNone: {is_none_type},
6594 isNumeric: {is_numeric_type},
6595 isBorrowed: {is_borrowed},
6596 isAsyncValue: {is_async_value},
6597 typedArray: {typed_array_js},
6598 flatCount: {payload_flat_count_js},
6599 align32: {payload_align32},
6600 size32: {payload_size32},
6601 }},
6602 }})
6603 "#
6604 )
6605 }
6606
6607 InterfaceType::ErrorContext(ty_idx) => {
6608 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatErrorContext));
6609 let table_idx = ty_idx.as_u32();
6610 let lower_flat_err_ctx_fn =
6611 Intrinsic::Lower(LowerIntrinsic::LowerFlatErrorContext).name();
6612 format!("{lower_flat_err_ctx_fn}.bind(null, {table_idx})")
6613 }
6614
6615 InterfaceType::Map(ty_idx) => {
6616 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatMap));
6617 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatMap).name();
6618 let map_ty = &component_types[*ty_idx];
6619 let key_lower =
6620 gen_flat_lower_fn_js_expr(instantiator, &map_ty.key, extra_resource_map);
6621 let value_lower =
6622 gen_flat_lower_fn_js_expr(instantiator, &map_ty.value, extra_resource_map);
6623 let entry_size32 = map_ty.entry_abi.size32;
6624 let entry_align32 = map_ty.entry_abi.align32;
6625 let value_offset32 = map_ty.value_offset32;
6626 format!(
6627 "{f}({{
6628 keyLowerFn: {key_lower},
6629 valueLowerFn: {value_lower},
6630 entrySize32: {entry_size32},
6631 entryAlign32: {entry_align32},
6632 valueOffset32: {value_offset32},
6633 }})"
6634 )
6635 }
6636 }
6637}
6638
6639#[cfg(test)]
6640mod tests {
6641 use super::*;
6642
6643 fn compat_key(version_str: &str) -> Option<String> {
6645 semver_compat_key(version_str).map(|(key, _)| key)
6646 }
6647
6648 #[test]
6649 fn test_semver_compat_key() {
6650 assert_eq!(compat_key("1.0.0"), Some("1".into()));
6651 assert_eq!(compat_key("1.2.3"), Some("1".into()));
6652 assert_eq!(compat_key("2.0.0"), Some("2".into()));
6653 assert_eq!(compat_key("0.2.0"), Some("0.2".into()));
6654 assert_eq!(compat_key("0.2.10"), Some("0.2".into()));
6655 assert_eq!(compat_key("0.1.0"), Some("0.1".into()));
6656 assert_eq!(compat_key("0.0.1"), None);
6657 assert_eq!(compat_key("1.0.0-rc.1"), None);
6658 assert_eq!(compat_key("0.2.0-pre"), None);
6659 assert_eq!(compat_key("not-a-version"), None);
6660 }
6661
6662 #[test]
6663 fn test_semver_compat_key_returns_parsed_version() {
6664 let (key, ver) = semver_compat_key("1.2.3").unwrap();
6665 assert_eq!(key, "1");
6666 assert_eq!(ver, Version::new(1, 2, 3));
6667 }
6668
6669 #[test]
6670 fn test_map_import_exact_match() {
6671 let mut map = HashMap::new();
6672 map.insert("wasi:http/types@0.2.0".into(), "./http.js#types".into());
6673 let map = Some(map);
6674 assert_eq!(
6675 map_import(&map, "wasi:http/types@0.2.0"),
6676 ("./http.js".into(), Some("types".into()))
6677 );
6678 }
6679
6680 #[test]
6681 fn test_map_import_sans_version_match() {
6682 let mut map = HashMap::new();
6683 map.insert("wasi:http/types".into(), "./http.js".into());
6684 let map = Some(map);
6685 assert_eq!(
6686 map_import(&map, "wasi:http/types@0.2.10"),
6687 ("./http.js".into(), None)
6688 );
6689 }
6690
6691 #[test]
6692 fn test_map_import_wildcard_sans_version() {
6693 let mut map = HashMap::new();
6695 map.insert("wasi:http/*".into(), "./http.js#*".into());
6696 let map = Some(map);
6697 assert_eq!(
6698 map_import(&map, "wasi:http/types@0.2.10"),
6699 ("./http.js".into(), Some("types".into()))
6700 );
6701 }
6702
6703 #[test]
6704 fn test_map_import_semver_exact_key() {
6705 let mut map = HashMap::new();
6707 map.insert("wasi:http/types@0.2.0".into(), "./http.js".into());
6708 let map = Some(map);
6709 assert_eq!(
6710 map_import(&map, "wasi:http/types@0.2.10"),
6711 ("./http.js".into(), None)
6712 );
6713 }
6714
6715 #[test]
6716 fn test_map_import_semver_wildcard_key() {
6717 let mut map = HashMap::new();
6719 map.insert("wasi:http/*@0.2.1".into(), "./http.js#*".into());
6720 let map = Some(map);
6721 assert_eq!(
6722 map_import(&map, "wasi:http/types@0.2.10"),
6723 ("./http.js".into(), Some("types".into()))
6724 );
6725 }
6726
6727 #[test]
6728 fn test_map_import_semver_lower_import_version() {
6729 let mut map = HashMap::new();
6731 map.insert("wasi:http/types@0.2.10".into(), "./http.js".into());
6732 let map = Some(map);
6733 assert_eq!(
6734 map_import(&map, "wasi:http/types@0.2.1"),
6735 ("./http.js".into(), None)
6736 );
6737 }
6738
6739 #[test]
6740 fn test_map_import_semver_no_cross_minor() {
6741 let mut map = HashMap::new();
6743 map.insert("wasi:http/types@0.3.0".into(), "./http.js".into());
6744 let map = Some(map);
6745 assert_eq!(
6746 map_import(&map, "wasi:http/types@0.2.10"),
6747 ("wasi:http/types".into(), None)
6748 );
6749 }
6750
6751 #[test]
6752 fn test_map_import_semver_prefers_highest() {
6753 let mut map = HashMap::new();
6755 map.insert("wasi:http/types@0.2.1".into(), "./http-old.js".into());
6756 map.insert("wasi:http/types@0.2.5".into(), "./http-new.js".into());
6757 let map = Some(map);
6758 assert_eq!(
6759 map_import(&map, "wasi:http/types@0.2.10"),
6760 ("./http-new.js".into(), None)
6761 );
6762 }
6763
6764 #[test]
6765 fn test_map_import_no_match_prerelease() {
6766 let mut map = HashMap::new();
6767 map.insert("wasi:http/types@0.2.0-rc.1".into(), "./http.js".into());
6768 let map = Some(map);
6769 assert_eq!(
6770 map_import(&map, "wasi:http/types@0.2.0"),
6771 ("wasi:http/types".into(), None)
6772 );
6773 }
6774
6775 #[test]
6776 fn test_map_import_prerelease_versioned_wildcard_wins_over_unversioned_wildcard() {
6777 let mut map = HashMap::new();
6780 map.insert(
6781 "wasi:cli/*".into(),
6782 "@bytecodealliance/preview2-shim/cli#*".into(),
6783 );
6784 map.insert(
6785 "wasi:cli/*@0.3.0".into(),
6786 "@bytecodealliance/preview3-shim/cli#*".into(),
6787 );
6788 let map = Some(map);
6789 assert_eq!(
6790 map_import(&map, "wasi:cli/stdout@0.3.0"),
6791 (
6792 "@bytecodealliance/preview3-shim/cli".into(),
6793 Some("stdout".into())
6794 )
6795 );
6796 assert_eq!(
6798 map_import(&map, "wasi:cli/stdout@0.2.6"),
6799 (
6800 "@bytecodealliance/preview2-shim/cli".into(),
6801 Some("stdout".into())
6802 )
6803 );
6804 assert_eq!(
6806 map_import(&map, "wasi:cli/stdout"),
6807 (
6808 "@bytecodealliance/preview2-shim/cli".into(),
6809 Some("stdout".into())
6810 )
6811 );
6812 }
6813
6814 #[test]
6815 fn test_map_import_no_match_zero_zero() {
6816 let mut map = HashMap::new();
6817 map.insert("wasi:http/types@0.0.1".into(), "./http.js".into());
6818 let map = Some(map);
6819 assert_eq!(
6820 map_import(&map, "wasi:http/types@0.0.2"),
6821 ("wasi:http/types".into(), None)
6822 );
6823 }
6824
6825 #[test]
6826 fn test_map_import_semver_major_version() {
6827 let mut map = HashMap::new();
6829 map.insert("wasi:http/types@1.0.0".into(), "./http.js".into());
6830 let map = Some(map);
6831 assert_eq!(
6832 map_import(&map, "wasi:http/types@1.2.3"),
6833 ("./http.js".into(), None)
6834 );
6835 }
6836
6837 #[test]
6838 fn test_map_import_semver_no_cross_major() {
6839 let mut map = HashMap::new();
6841 map.insert("wasi:http/types@1.0.0".into(), "./http.js".into());
6842 let map = Some(map);
6843 assert_eq!(
6844 map_import(&map, "wasi:http/types@2.0.0"),
6845 ("wasi:http/types".into(), None)
6846 );
6847 }
6848
6849 #[test]
6850 fn test_map_import_no_map() {
6851 assert_eq!(
6853 map_import(&None, "wasi:http/types@0.2.0"),
6854 ("wasi:http/types".into(), None)
6855 );
6856 }
6857
6858 #[test]
6859 fn test_map_import_no_map_unversioned() {
6860 assert_eq!(
6862 map_import(&None, "wasi:http/types"),
6863 ("wasi:http/types".into(), None)
6864 );
6865 }
6866
6867 #[test]
6868 fn test_parse_mapping_with_hash() {
6869 assert_eq!(
6870 parse_mapping("./http.js#types"),
6871 ("./http.js".into(), Some("types".into()))
6872 );
6873 }
6874
6875 #[test]
6876 fn test_parse_mapping_without_hash() {
6877 assert_eq!(parse_mapping("./http.js"), ("./http.js".into(), None));
6878 }
6879
6880 #[test]
6881 fn test_parse_mapping_leading_hash() {
6882 assert_eq!(parse_mapping("#foo"), ("#foo".into(), None));
6884 }
6885
6886 #[test]
6887 fn test_parse_mapping_empty() {
6888 assert_eq!(parse_mapping(""), ("".into(), None));
6889 }
6890}