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