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