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