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