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