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_ASYNC_FLAT_PARAMS: usize = 4;
63
64#[derive(Debug, Default, Clone)]
65pub struct TranspileOpts {
66 pub name: String,
67 pub no_typescript: bool,
70 pub instantiation: Option<InstantiationMode>,
73 pub import_bindings: Option<BindingsMode>,
76 pub map: Option<HashMap<String, String>>,
79 pub no_nodejs_compat: bool,
81 pub base64_cutoff: usize,
84 pub tla_compat: bool,
87 pub valid_lifting_optimization: bool,
90 pub tracing: bool,
92 pub no_namespaced_exports: bool,
95 pub multi_memory: bool,
98 pub guest: bool,
100 pub async_mode: Option<AsyncMode>,
103 pub strict: 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 jco";"#);
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 => ("null".into(), "() => null".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 => ("null".into(), "() => null".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 => ("null".into(), "() => null".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(|| "() => null".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_ASYNC_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, _) => match m {
2700 InstantiateModule::Static(idx, args) => {
2701 self.instantiate_static_module(*idx, args);
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(&mut self, module_idx: StaticModuleIndex, args: &[CoreDef]) {
2767 let mut import_obj = BTreeMap::new();
2772 for (module, name, arg) in self.modules[module_idx].imports(args) {
2773 let def = self.augmented_import_def(&arg);
2774 let dst = import_obj.entry(module).or_insert(BTreeMap::new());
2775 let prev = dst.insert(name, def);
2776 assert!(
2777 prev.is_none(),
2778 "unsupported duplicate import of `{module}::{name}`"
2779 );
2780 assert!(prev.is_none());
2781 }
2782
2783 let mut imports = String::new();
2785 if !import_obj.is_empty() {
2786 imports.push_str(", {\n");
2787 for (module, names) in import_obj {
2788 imports.push_str(&maybe_quote_id(module));
2789 imports.push_str(": {\n");
2790 for (name, val) in names {
2791 imports.push_str(&maybe_quote_id(name));
2792 uwriteln!(imports, ": {val},");
2793 }
2794 imports.push_str("},\n");
2795 }
2796 imports.push('}');
2797 }
2798
2799 let i = self.instances.push(module_idx);
2800 let iu32 = i.as_u32();
2801 let instantiate = self.bindgen.intrinsic(Intrinsic::InstantiateCore);
2802 uwriteln!(self.src.js, "let exports{iu32};");
2803
2804 match self.bindgen.opts.instantiation {
2805 Some(InstantiationMode::Async) | None => {
2806 uwriteln!(
2807 self.src.js_init,
2808 "({{ exports: exports{iu32} }} = yield {instantiate}(yield module{}{imports}));",
2809 module_idx.as_u32(),
2810 )
2811 }
2812
2813 Some(InstantiationMode::Sync) => {
2814 uwriteln!(
2815 self.src.js_init,
2816 "({{ exports: exports{iu32} }} = {instantiate}(module{}{imports}));",
2817 module_idx.as_u32(),
2818 );
2819 }
2820 }
2821 }
2822
2823 fn create_resource_fn_map(
2831 &mut self,
2832 func: &Function,
2833 ty_func_idx: TypeFuncIndex,
2834 resource_map: &mut ResourceMap,
2835 ) {
2836 let params_ty = &self.types[self.types[ty_func_idx].params];
2838 for (p, iface_ty) in func.params.iter().zip(params_ty.types.iter()) {
2839 if let Type::Id(id) = p.ty {
2840 self.connect_resource_types(id, iface_ty, resource_map);
2841 }
2842 }
2843 let results_ty = &self.types[self.types[ty_func_idx].results];
2845 if let (Some(Type::Id(id)), Some(iface_ty)) = (func.result, results_ty.types.first()) {
2846 self.connect_resource_types(id, iface_ty, resource_map);
2847 }
2848 }
2849
2850 fn resource_name(
2851 resolve: &Resolve,
2852 local_names: &'a mut LocalNames,
2853 resource: TypeId,
2854 resource_map: &BTreeMap<TypeId, ResourceIndex>,
2855 ) -> &'a str {
2856 let resource = crate::dealias(resolve, resource);
2857 local_names
2858 .get_or_create(
2859 resource_map[&resource],
2860 &resolve.types[resource]
2861 .name
2862 .as_ref()
2863 .unwrap()
2864 .to_upper_camel_case(),
2865 )
2866 .0
2867 }
2868
2869 fn lower_import(&mut self, index: LoweredIndex, import: RuntimeImportIndex) {
2870 let (options, trampoline, func_ty) = self.lowering_options[index];
2871
2872 let (import_index, path) = &self.component.imports[import];
2874 let (import_name, _) = &self.component.import_types[*import_index];
2875 let world_key = &self.imports[import_name];
2876
2877 let (func, func_name, iface_name) =
2879 match &self.resolve.worlds[self.world].imports[world_key] {
2880 WorldItem::Function(func) => {
2881 assert_eq!(path.len(), 0);
2882 (func, import_name, None)
2883 }
2884 WorldItem::Interface { id, .. } => {
2885 assert_eq!(path.len(), 1);
2886 let iface = &self.resolve.interfaces[*id];
2887 let func = &iface.functions[&path[0]];
2888 (
2889 func,
2890 &path[0],
2891 Some(iface.name.as_deref().unwrap_or_else(|| import_name)),
2892 )
2893 }
2894 WorldItem::Type { .. } => unreachable!("unexpected imported world item type"),
2895 };
2896
2897 let is_async = is_async_fn(func, options);
2898
2899 if options.async_ {
2900 assert!(
2901 options.post_return.is_none(),
2902 "async function {func_name} (import {import_name}) can't have post return",
2903 );
2904 }
2905
2906 let requires_async_porcelain = requires_async_porcelain(
2908 FunctionIdentifier::Fn(func),
2909 import_name,
2910 &self.async_imports,
2911 );
2912
2913 let (import_specifier, maybe_iface_member) = map_import(
2915 &self.bindgen.opts.map,
2916 if iface_name.is_some() {
2917 import_name
2918 } else {
2919 match func.kind {
2920 FunctionKind::Method(_) => {
2921 let stripped = import_name.strip_prefix("[method]").unwrap();
2922 &stripped[0..stripped.find(".").unwrap()]
2923 }
2924 FunctionKind::AsyncMethod(_) => {
2925 let stripped = import_name.strip_prefix("[async method]").unwrap();
2926 &stripped[0..stripped.find(".").unwrap()]
2927 }
2928 FunctionKind::Static(_) => {
2929 let stripped = import_name.strip_prefix("[static]").unwrap();
2930 &stripped[0..stripped.find(".").unwrap()]
2931 }
2932 FunctionKind::AsyncStatic(_) => {
2933 let stripped = import_name.strip_prefix("[async static]").unwrap();
2934 &stripped[0..stripped.find(".").unwrap()]
2935 }
2936 FunctionKind::Constructor(_) => {
2937 import_name.strip_prefix("[constructor]").unwrap()
2938 }
2939 FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => import_name,
2940 }
2941 },
2942 );
2943
2944 let mut import_resource_map = ResourceMap::new();
2946
2947 self.create_resource_fn_map(func, func_ty, &mut import_resource_map);
2948
2949 let (callee_name, call_type) = match func.kind {
2950 FunctionKind::Freestanding => (
2951 self.bindgen
2952 .local_names
2953 .get_or_create(
2954 format!(
2955 "import:{import}-{maybe_iface_member}-{func_name}",
2956 import = import_specifier,
2957 maybe_iface_member = maybe_iface_member.as_deref().unwrap_or(""),
2958 func_name = &func.name
2959 ),
2960 &func.name,
2961 )
2962 .0
2963 .to_string(),
2964 CallType::Standard,
2965 ),
2966
2967 FunctionKind::AsyncFreestanding => (
2968 self.bindgen
2969 .local_names
2970 .get_or_create(
2971 format!(
2972 "import:async-{import}-{maybe_iface_member}-{func_name}",
2973 import = import_specifier,
2974 maybe_iface_member = maybe_iface_member.as_deref().unwrap_or(""),
2975 func_name = &func.name
2976 ),
2977 &func.name,
2978 )
2979 .0
2980 .to_string(),
2981 CallType::AsyncStandard,
2982 ),
2983
2984 FunctionKind::Method(_) => (
2985 func.item_name().to_lower_camel_case(),
2986 CallType::CalleeResourceDispatch,
2987 ),
2988
2989 FunctionKind::AsyncMethod(_) => (
2990 func.item_name().to_lower_camel_case(),
2991 CallType::AsyncCalleeResourceDispatch,
2992 ),
2993
2994 FunctionKind::Static(resource_id) => (
2995 format!(
2996 "{}.{}",
2997 Instantiator::resource_name(
2998 self.resolve,
2999 &mut self.bindgen.local_names,
3000 resource_id,
3001 &self.imports_resource_types
3002 ),
3003 func.item_name().to_lower_camel_case()
3004 ),
3005 CallType::Standard,
3006 ),
3007
3008 FunctionKind::AsyncStatic(resource_id) => (
3009 format!(
3010 "{}.{}",
3011 Instantiator::resource_name(
3012 self.resolve,
3013 &mut self.bindgen.local_names,
3014 resource_id,
3015 &self.imports_resource_types
3016 ),
3017 func.item_name().to_lower_camel_case()
3018 ),
3019 CallType::AsyncStandard,
3020 ),
3021
3022 FunctionKind::Constructor(resource_id) => (
3023 format!(
3024 "new {}",
3025 Instantiator::resource_name(
3026 self.resolve,
3027 &mut self.bindgen.local_names,
3028 resource_id,
3029 &self.imports_resource_types
3030 )
3031 ),
3032 CallType::Standard,
3033 ),
3034 };
3035
3036 let nparams = self
3037 .resolve
3038 .wasm_signature(AbiVariant::GuestImport, func)
3039 .params
3040 .len();
3041
3042 let trampoline_idx = trampoline.as_u32();
3044 match self.bindgen.opts.import_bindings {
3045 None | Some(BindingsMode::Js) | Some(BindingsMode::Hybrid) => {
3046 if is_async | requires_async_porcelain {
3048 uwrite!(
3053 self.src.js,
3054 "\nconst _trampoline{trampoline_idx} = async function"
3055 );
3056 } else {
3057 uwrite!(
3058 self.src.js,
3059 "\nconst _trampoline{trampoline_idx} = function"
3060 );
3061 }
3062
3063 let iface_name = if import_name.is_empty() {
3064 None
3065 } else {
3066 Some(import_name.to_string())
3067 };
3068
3069 self.bindgen(JsFunctionBindgenArgs {
3071 nparams,
3072 call_type,
3073 iface_name: iface_name.as_deref(),
3074 callee: &callee_name,
3075 opts: options,
3076 func,
3077 resource_map: &import_resource_map,
3078 abi: AbiVariant::GuestImport,
3079 requires_async_porcelain,
3080 is_async,
3081 });
3082 uwriteln!(self.src.js, "");
3083
3084 uwriteln!(
3085 self.src.js,
3086 "_trampoline{trampoline_idx}.fnName = '{}#{callee_name}';",
3087 iface_name.unwrap_or_default(),
3088 );
3089
3090 if requires_async_porcelain {
3092 uwriteln!(
3093 self.src.js,
3094 "_trampoline{trampoline_idx}.manuallyAsync = true;"
3095 );
3096 }
3097 }
3098
3099 Some(BindingsMode::Optimized) | Some(BindingsMode::DirectOptimized) => {
3100 uwriteln!(self.src.js, "let trampoline{trampoline_idx};");
3101 }
3102 };
3103
3104 if !matches!(
3109 self.bindgen.opts.import_bindings,
3110 None | Some(BindingsMode::Js)
3111 ) {
3112 let (memory, realloc) =
3113 if let CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions {
3114 memory,
3115 realloc,
3116 }) = options.data_model
3117 {
3118 (
3119 memory.map(|idx| format!(" memory: memory{},", idx.as_u32())),
3120 realloc.map(|idx| format!(" realloc: realloc{},", idx.as_u32())),
3121 )
3122 } else {
3123 (None, None)
3124 };
3125 let memory = memory.unwrap_or_default();
3126 let realloc = realloc.unwrap_or_default();
3127
3128 let post_return = options
3129 .post_return
3130 .map(|idx| format!(" postReturn: postReturn{},", idx.as_u32()))
3131 .unwrap_or("".into());
3132 let string_encoding = match options.string_encoding {
3133 wasmtime_environ::component::StringEncoding::Utf8 => "",
3134 wasmtime_environ::component::StringEncoding::Utf16 => " stringEncoding: 'utf16',",
3135 wasmtime_environ::component::StringEncoding::CompactUtf16 => {
3136 " stringEncoding: 'compact-utf16',"
3137 }
3138 };
3139
3140 let callee_name = match func.kind {
3141 FunctionKind::Constructor(_) => callee_name[4..].to_string(),
3142
3143 FunctionKind::Static(_)
3144 | FunctionKind::AsyncStatic(_)
3145 | FunctionKind::Freestanding
3146 | FunctionKind::AsyncFreestanding => callee_name.to_string(),
3147
3148 FunctionKind::Method(resource_id) | FunctionKind::AsyncMethod(resource_id) => {
3149 format!(
3150 "{}.prototype.{callee_name}",
3151 Instantiator::resource_name(
3152 self.resolve,
3153 &mut self.bindgen.local_names,
3154 resource_id,
3155 &self.imports_resource_types
3156 )
3157 )
3158 }
3159 };
3160
3161 self.resource_imports.extend(import_resource_map.clone());
3163
3164 let resource_tables = {
3165 let mut resource_table_ids: Vec<TypeResourceTableIndex> = Vec::new();
3166
3167 for (_, data) in import_resource_map {
3168 let ResourceTable {
3169 data: ResourceData::Host { tid, .. },
3170 ..
3171 } = &data
3172 else {
3173 unreachable!("unexpected non-host resource table");
3174 };
3175 resource_table_ids.push(*tid);
3176 }
3177
3178 if resource_table_ids.is_empty() {
3179 "".to_string()
3180 } else {
3181 format!(
3182 " resourceTables: [{}],",
3183 resource_table_ids
3184 .iter()
3185 .map(|x| format!("handleTable{}", x.as_u32()))
3186 .collect::<Vec<String>>()
3187 .join(", ")
3188 )
3189 }
3190 };
3191
3192 match self.bindgen.opts.import_bindings {
3194 Some(BindingsMode::Hybrid) => {
3195 let symbol_cabi_lower = self.bindgen.intrinsic(Intrinsic::SymbolCabiLower);
3196 uwriteln!(self.src.js_init, "if ({callee_name}[{symbol_cabi_lower}]) {{
3197 trampoline{} = {callee_name}[{symbol_cabi_lower}]({{{memory}{realloc}{post_return}{string_encoding}{resource_tables}}});
3198 }}", trampoline.as_u32());
3199 }
3200 Some(BindingsMode::Optimized) => {
3201 let symbol_cabi_lower = self.bindgen.intrinsic(Intrinsic::SymbolCabiLower);
3202 if !self.bindgen.opts.valid_lifting_optimization {
3203 uwriteln!(self.src.js_init, "if (!{callee_name}[{symbol_cabi_lower}]) {{
3204 throw new TypeError('import for \"{import_name}\" does not define a Symbol.for(\"cabiLower\") optimized binding');
3205 }}");
3206 }
3207 uwriteln!(
3208 self.src.js_init,
3209 "trampoline{} = {callee_name}[{symbol_cabi_lower}]({{{memory}{realloc}{post_return}{string_encoding}{resource_tables}}});",
3210 trampoline.as_u32()
3211 );
3212 }
3213 Some(BindingsMode::DirectOptimized) => {
3214 uwriteln!(
3215 self.src.js_init,
3216 "trampoline{} = {callee_name}({{{memory}{realloc}{post_return}{string_encoding}}});",
3217 trampoline.as_u32()
3218 );
3219 }
3220 None | Some(BindingsMode::Js) => unreachable!("invalid bindings mode"),
3221 };
3222 }
3223
3224 let (import_name, binding_name) = match func.kind {
3226 FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
3227 (func_name.to_lower_camel_case(), callee_name)
3228 }
3229
3230 FunctionKind::Method(tid)
3231 | FunctionKind::AsyncMethod(tid)
3232 | FunctionKind::Static(tid)
3233 | FunctionKind::AsyncStatic(tid)
3234 | FunctionKind::Constructor(tid) => {
3235 let ty = &self.resolve.types[tid];
3236 let class_name = ty.name.as_ref().unwrap().to_upper_camel_case();
3237 let resource_name = Instantiator::resource_name(
3238 self.resolve,
3239 &mut self.bindgen.local_names,
3240 tid,
3241 &self.imports_resource_types,
3242 )
3243 .to_string();
3244 (class_name, resource_name)
3245 }
3246 };
3247
3248 self.ensure_import(
3249 import_specifier,
3250 iface_name,
3251 maybe_iface_member.as_deref(),
3252 if iface_name.is_some() {
3253 Some(import_name.to_string())
3254 } else {
3255 None
3256 },
3257 binding_name,
3258 );
3259 }
3260
3261 fn ensure_import(
3272 &mut self,
3273 import_specifier: String,
3274 iface_name: Option<&str>,
3275 iface_member: Option<&str>,
3276 import_binding: Option<String>,
3277 local_name: String,
3278 ) {
3279 if import_specifier.starts_with("webidl:") {
3280 self.bindgen
3281 .intrinsic(Intrinsic::WebIdl(WebIdlIntrinsic::GlobalThisIdlProxy));
3282 }
3283
3284 let mut import_path = Vec::with_capacity(2);
3286 import_path.push(import_specifier);
3287 if let Some(_iface_name) = iface_name {
3288 if let Some(iface_member) = iface_member {
3291 import_path.push(iface_member.to_lower_camel_case());
3292 }
3293 import_path.push(import_binding.clone().unwrap());
3294 } else if let Some(iface_member) = iface_member {
3295 import_path.push(iface_member.into());
3296 } else if let Some(import_binding) = &import_binding {
3297 import_path.push(import_binding.into());
3298 }
3299
3300 self.bindgen
3302 .esm_bindgen
3303 .add_import_binding(&import_path, local_name);
3304 }
3305
3306 fn connect_p3_resources(
3315 &mut self,
3316 id: &TypeId,
3317 maybe_elem_ty: &Option<Type>,
3318 iface_ty: &InterfaceType,
3319 resource_map: &mut ResourceMap,
3320 ) {
3321 let remote_resource = match iface_ty {
3322 InterfaceType::Future(table_idx) => ResourceTable {
3323 imported: true,
3324 data: ResourceData::Guest {
3325 resource_name: "Future".into(),
3326 prefix: Some(format!("${}", table_idx.as_u32())),
3327 extra: Some(ResourceExtraData::Future {
3328 table_idx: *table_idx,
3329 elem_ty: maybe_elem_ty.map(|ty| {
3330 let table_ty = &self.types[*table_idx];
3331 let future_ty_idx = table_ty.ty;
3332 let future_ty = &self.types[future_ty_idx];
3333 let iface_ty = future_ty
3334 .payload
3335 .expect("missing future payload despite elem type being present");
3336 let abi = self.types.canonical_abi(&iface_ty);
3337 PayloadTypeMetadata {
3338 ty,
3339 iface_ty,
3340
3341 lift_js_expr: gen_flat_lift_fn_js_expr(
3348 self,
3349 &iface_ty,
3350 &Some(resource_map),
3351 ),
3352 lower_js_expr: gen_flat_lower_fn_js_expr(
3353 self,
3354 &iface_ty,
3355 &Some(resource_map),
3356 ),
3357 size32: abi.size32,
3358 align32: abi.align32,
3359 flat_count: abi.flat_count,
3360 }
3361 }),
3362 }),
3363 },
3364 },
3365 InterfaceType::Stream(table_idx) => ResourceTable {
3366 imported: true,
3367 data: ResourceData::Guest {
3368 resource_name: "Stream".into(),
3369 prefix: Some(format!("${}", table_idx.as_u32())),
3370 extra: Some(ResourceExtraData::Stream {
3371 table_idx: *table_idx,
3372 elem_ty: maybe_elem_ty.map(|ty| {
3373 let table_ty = &self.types[*table_idx];
3374 let stream_ty_idx = table_ty.ty;
3375 let stream_ty = &self.types[stream_ty_idx];
3376 let iface_ty = stream_ty
3377 .payload
3378 .expect("missing payload despite elem type being present");
3379 let abi = self.types.canonical_abi(&iface_ty);
3380 PayloadTypeMetadata {
3381 ty,
3382 iface_ty,
3383 lift_js_expr: gen_flat_lift_fn_js_expr(
3384 self,
3385 &iface_ty,
3386 &Some(resource_map),
3387 ),
3388 lower_js_expr: gen_flat_lower_fn_js_expr(
3389 self,
3390 &iface_ty,
3391 &Some(resource_map),
3392 ),
3393 size32: abi.size32,
3394 align32: abi.align32,
3395 flat_count: abi.flat_count,
3396 }
3397 }),
3398 }),
3399 },
3400 },
3401 InterfaceType::ErrorContext(table_idx) => ResourceTable {
3402 imported: true,
3403 data: ResourceData::Guest {
3404 resource_name: "ErrorContext".into(),
3405 prefix: Some(format!("${}", table_idx.as_u32())),
3406 extra: Some(ResourceExtraData::ErrorContext {
3407 table_idx: *table_idx,
3408 }),
3409 },
3410 },
3411 _ => unreachable!("unexpected interface type [{iface_ty:?}] with no type"),
3412 };
3413
3414 resource_map.insert(*id, remote_resource);
3415 }
3416
3417 fn connect_host_resource(
3426 &mut self,
3427 t: TypeId,
3428 resource_table_ty_idx: TypeResourceTableIndex,
3429 resource_map: &mut ResourceMap,
3430 ) {
3431 self.ensure_resource_table(resource_table_ty_idx);
3432
3433 let resource_table_ty = &self.types[resource_table_ty_idx];
3435 let resource_idx = resource_table_ty.unwrap_concrete_ty();
3436 let imported = self
3437 .component
3438 .defined_resource_index(resource_idx)
3439 .is_none();
3440
3441 let resource_id = crate::dealias(self.resolve, t);
3443 let ty = &self.resolve.types[resource_id];
3444
3445 let mut dtor_str = None;
3448 if let Some(resource_idx) = self.component.defined_resource_index(resource_idx) {
3449 assert!(!imported);
3450 let resource_def = self
3451 .component
3452 .initializers
3453 .iter()
3454 .find_map(|i| match i {
3455 GlobalInitializer::Resource(r) if r.index == resource_idx => Some(r),
3456 _ => None,
3457 })
3458 .unwrap();
3459
3460 if let Some(dtor) = &resource_def.dtor {
3461 dtor_str = Some(self.core_def(dtor));
3462 }
3463 }
3464
3465 let resource_name = ty.name.as_ref().unwrap().to_upper_camel_case();
3467
3468 let local_name = if imported {
3469 let (world_key, iface_name) = match ty.owner {
3470 wit_parser::TypeOwner::World(world) => (
3471 self.resolve.worlds[world]
3472 .imports
3473 .iter()
3474 .find(|&(_, item)| matches!(*item, WorldItem::Type { id, .. } if id == t))
3475 .unwrap()
3476 .0
3477 .clone(),
3478 None,
3479 ),
3480 wit_parser::TypeOwner::Interface(iface) => {
3481 match &self.resolve.interfaces[iface].name {
3482 Some(name) => (WorldKey::Interface(iface), Some(name.as_str())),
3483 None => {
3484 let key = self.resolve.worlds[self.world]
3485 .imports
3486 .iter()
3487 .find(|&(_, item)| match item {
3488 WorldItem::Interface { id, .. } => *id == iface,
3489 _ => false,
3490 })
3491 .unwrap()
3492 .0;
3493 (
3494 key.clone(),
3495 match key {
3496 WorldKey::Name(name) => Some(name.as_str()),
3497 WorldKey::Interface(_) => None,
3498 },
3499 )
3500 }
3501 }
3502 }
3503 wit_parser::TypeOwner::None => unimplemented!(),
3504 };
3505
3506 let import_name = self.resolve.name_world_key(&world_key);
3507 let (local_name, _) = self
3508 .bindgen
3509 .local_names
3510 .get_or_create(resource_idx, &resource_name);
3511
3512 let local_name_str = local_name.to_string();
3513
3514 let (import_specifier, maybe_iface_member) =
3516 map_import(&self.bindgen.opts.map, &import_name);
3517
3518 self.ensure_import(
3520 import_specifier,
3521 iface_name,
3522 maybe_iface_member.as_deref(),
3523 iface_name.map(|_| resource_name),
3524 local_name_str.to_string(),
3525 );
3526 local_name_str
3527 } else {
3528 let (local_name, _) = self
3529 .bindgen
3530 .local_names
3531 .get_or_create(resource_idx, &resource_name);
3532 local_name.to_string()
3533 };
3534
3535 let entry = ResourceTable {
3537 imported,
3538 data: ResourceData::Host {
3539 tid: resource_table_ty_idx,
3540 rid: resource_idx,
3541 local_name,
3542 dtor_name: dtor_str,
3543 },
3544 };
3545
3546 if let Some(existing) = resource_map.get(&resource_id) {
3549 assert_eq!(*existing, entry);
3550 return;
3551 }
3552
3553 resource_map.insert(resource_id, entry);
3555 }
3556
3557 fn connect_resource_types(
3570 &mut self,
3571 id: TypeId,
3572 iface_ty: &InterfaceType,
3573 resource_map: &mut ResourceMap,
3574 ) {
3575 let kind = &self.resolve.types[id].kind;
3576 match (kind, iface_ty) {
3577 (TypeDefKind::Flags(_), InterfaceType::Flags(_))
3579 | (TypeDefKind::Enum(_), InterfaceType::Enum(_)) => {}
3580
3581 (TypeDefKind::Record(t1), InterfaceType::Record(t2)) => {
3583 let t2 = &self.types[*t2];
3584 for (f1, f2) in t1.fields.iter().zip(t2.fields.iter()) {
3585 if let Type::Id(id) = f1.ty {
3586 self.connect_resource_types(id, &f2.ty, resource_map);
3587 }
3588 }
3589 }
3590
3591 (
3593 TypeDefKind::Handle(Handle::Own(t1) | Handle::Borrow(t1)),
3594 InterfaceType::Own(t2) | InterfaceType::Borrow(t2),
3595 ) => {
3596 self.connect_host_resource(*t1, *t2, resource_map);
3597 }
3598
3599 (TypeDefKind::Tuple(t1), InterfaceType::Tuple(t2)) => {
3601 let t2 = &self.types[*t2];
3602 for (f1, f2) in t1.types.iter().zip(t2.types.iter()) {
3603 if let Type::Id(id) = f1 {
3604 self.connect_resource_types(*id, f2, resource_map);
3605 }
3606 }
3607 }
3608
3609 (TypeDefKind::Variant(t1), InterfaceType::Variant(t2)) => {
3611 let t2 = &self.types[*t2];
3612 for (f1, f2) in t1.cases.iter().zip(t2.cases.iter()) {
3613 if let Some(Type::Id(id)) = &f1.ty {
3614 self.connect_resource_types(*id, f2.1.as_ref().unwrap(), resource_map);
3615 }
3616 }
3617 }
3618
3619 (TypeDefKind::Option(t1), InterfaceType::Option(t2)) => {
3621 let t2 = &self.types[*t2];
3622 if let Type::Id(id) = t1 {
3623 self.connect_resource_types(*id, &t2.ty, resource_map);
3624 }
3625 }
3626
3627 (TypeDefKind::Result(t1), InterfaceType::Result(t2)) => {
3629 let t2 = &self.types[*t2];
3630 if let Some(Type::Id(id)) = &t1.ok {
3631 self.connect_resource_types(*id, &t2.ok.unwrap(), resource_map);
3632 }
3633 if let Some(Type::Id(id)) = &t1.err {
3634 self.connect_resource_types(*id, &t2.err.unwrap(), resource_map);
3635 }
3636 }
3637
3638 (TypeDefKind::List(t1), InterfaceType::List(t2)) => {
3640 let t2 = &self.types[*t2];
3641 if let Type::Id(id) = t1 {
3642 self.connect_resource_types(*id, &t2.element, resource_map);
3643 }
3644 }
3645
3646 (TypeDefKind::FixedLengthList(t1, _len), InterfaceType::FixedLengthList(t2)) => {
3648 let t2 = &self.types[*t2];
3649 if let Type::Id(id) = t1 {
3650 self.connect_resource_types(*id, &t2.element, resource_map);
3651 }
3652 }
3653
3654 (TypeDefKind::Type(ty), _) => {
3656 if let Type::Id(id) = ty {
3657 self.connect_resource_types(*id, iface_ty, resource_map);
3658 }
3659 }
3660
3661 (TypeDefKind::Future(maybe_elem_ty), container_iface_ty)
3663 | (TypeDefKind::Stream(maybe_elem_ty), container_iface_ty) => {
3664 match maybe_elem_ty {
3665 None => {
3668 self.connect_p3_resources(&id, maybe_elem_ty, iface_ty, resource_map);
3669 }
3670 Some(elem_ty @ Type::Id(elem_ty_id)) => {
3672 let maybe_elem_iface_ty = match container_iface_ty {
3677 InterfaceType::Future(future_table_ty_idx) => {
3678 let future_table_ty = &self.types[*future_table_ty_idx];
3679 let future = &self.types[future_table_ty.ty];
3680 future.payload
3681 }
3682 InterfaceType::Stream(stream_table_ty_idx) => {
3683 let stream_table_ty = &self.types[*stream_table_ty_idx];
3684 let stream = &self.types[stream_table_ty.ty];
3685 stream.payload
3686 }
3687 _ => unreachable!("unexpected iface type"),
3688 };
3689 if let Some(elem_iface_ty) = maybe_elem_iface_ty {
3690 self.connect_resource_types(*elem_ty_id, &elem_iface_ty, resource_map);
3698 }
3699
3700 self.connect_p3_resources(&id, &Some(*elem_ty), iface_ty, resource_map);
3701 }
3702 Some(_) => {
3704 self.connect_p3_resources(&id, maybe_elem_ty, iface_ty, resource_map);
3705 }
3706 }
3707 }
3708
3709 (
3711 TypeDefKind::Result(Result_ { ok, err }),
3712 tk2 @ (InterfaceType::Future(_) | InterfaceType::Stream(_)),
3713 ) => {
3714 if let Some(Type::Id(ok_t)) = ok {
3715 self.connect_resource_types(*ok_t, tk2, resource_map)
3716 }
3717 if let Some(Type::Id(err_t)) = err {
3718 self.connect_resource_types(*err_t, tk2, resource_map)
3719 }
3720 }
3721
3722 (
3724 TypeDefKind::Option(ty),
3725 tk2 @ (InterfaceType::Future(_) | InterfaceType::Stream(_)),
3726 ) => {
3727 if let Type::Id(some_t) = ty {
3728 self.connect_resource_types(*some_t, tk2, resource_map)
3729 }
3730 }
3731
3732 (
3734 TypeDefKind::Handle(Handle::Own(t1) | Handle::Borrow(t1)),
3735 tk2 @ (InterfaceType::Future(_) | InterfaceType::Stream(_)),
3736 ) => self.connect_resource_types(*t1, tk2, resource_map),
3737
3738 (TypeDefKind::Resource, InterfaceType::Future(_) | InterfaceType::Stream(_)) => {}
3739
3740 (
3742 TypeDefKind::Variant(variant),
3743 tk2 @ (InterfaceType::Future(_) | InterfaceType::Stream(_)),
3744 ) => {
3745 for f1 in variant.cases.iter() {
3746 if let Some(Type::Id(id)) = &f1.ty {
3747 self.connect_resource_types(*id, tk2, resource_map);
3748 }
3749 }
3750 }
3751
3752 (
3754 TypeDefKind::Record(record),
3755 tk2 @ (InterfaceType::Future(_) | InterfaceType::Stream(_)),
3756 ) => {
3757 for f1 in record.fields.iter() {
3758 if let Type::Id(id) = f1.ty {
3759 self.connect_resource_types(id, tk2, resource_map);
3760 }
3761 }
3762 }
3763
3764 (
3767 TypeDefKind::Enum(_) | TypeDefKind::Flags(_),
3768 InterfaceType::Future(_) | InterfaceType::Stream(_),
3769 ) => {}
3770
3771 (TypeDefKind::Resource, tk2) => {
3772 unreachable!(
3773 "resource types do not need to be connected (in this case, to [{tk2:?}])"
3774 )
3775 }
3776
3777 (TypeDefKind::Unknown, tk2) => {
3778 unreachable!("unknown types cannot be connected (in this case to [{tk2:?}])")
3779 }
3780
3781 (tk1, tk2) => unreachable!("invalid typedef kind combination [{tk1:?}] [{tk2:?}]",),
3782 }
3783 }
3784
3785 fn bindgen(&mut self, args: JsFunctionBindgenArgs) {
3786 let JsFunctionBindgenArgs {
3787 nparams,
3788 call_type,
3789 iface_name,
3790 callee,
3791 opts,
3792 func,
3793 resource_map,
3794 abi,
3795 requires_async_porcelain,
3796 is_async,
3797 } = args;
3798
3799 let (memory, realloc) =
3800 if let CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions {
3801 memory,
3802 realloc,
3803 }) = opts.data_model
3804 {
3805 (
3806 memory.map(|idx| format!("memory{}", idx.as_u32())),
3807 realloc.map(|idx| {
3808 format!(
3809 "realloc{}{}",
3810 idx.as_u32(),
3811 if is_async {
3812 "Async"
3813 } else {
3814 Default::default()
3815 }
3816 )
3817 }),
3818 )
3819 } else {
3820 (None, None)
3821 };
3822
3823 let post_return = opts.post_return.map(|idx| {
3824 format!(
3825 "postReturn{}{}",
3826 idx.as_u32(),
3827 if is_async {
3828 "Async"
3829 } else {
3830 Default::default()
3831 }
3832 )
3833 });
3834
3835 let tracing_prefix = format!(
3836 "[iface=\"{}\", function=\"{}\"]",
3837 iface_name.unwrap_or("<no iface>"),
3838 func.name
3839 );
3840
3841 self.src.js("(");
3845 let mut params = Vec::new();
3846 let mut first = true;
3847 for i in 0..nparams {
3848 if i == 0
3849 && matches!(
3850 call_type,
3851 CallType::FirstArgIsThis | CallType::AsyncFirstArgIsThis
3852 )
3853 {
3854 params.push("this".into());
3855 continue;
3856 }
3857 if !first {
3858 self.src.js(", ");
3859 } else {
3860 first = false;
3861 }
3862 let param = format!("arg{i}");
3863 self.src.js(¶m);
3864 params.push(param);
3865 }
3866 uwriteln!(self.src.js, ") {{");
3867
3868 if self.bindgen.opts.tracing {
3870 let event_fields = func
3871 .params
3872 .iter()
3873 .enumerate()
3874 .map(|(i, p)| format!("{}=${{arguments[{i}]}}", p.name))
3875 .collect::<Vec<String>>();
3876 uwriteln!(
3877 self.src.js,
3878 "console.error(`{tracing_prefix} call {}`);",
3879 event_fields.join(", ")
3880 );
3881 }
3882
3883 if self.bindgen.opts.tla_compat
3885 && matches!(abi, AbiVariant::GuestExport)
3886 && self.bindgen.opts.instantiation.is_none()
3887 {
3888 let throw_uninitialized = self.bindgen.intrinsic(Intrinsic::ThrowUninitialized);
3889 uwrite!(
3890 self.src.js,
3891 "\
3892 if (!_initialized) {throw_uninitialized}();
3893 "
3894 );
3895 }
3896
3897 let mut f = FunctionBindgen {
3899 resource_map,
3900 clear_resource_borrows: false,
3901 intrinsics: &mut self.bindgen.all_intrinsics,
3902 valid_lifting_optimization: self.bindgen.opts.valid_lifting_optimization,
3903 sizes: &self.sizes,
3904 err: if get_thrown_type(self.resolve, func.result).is_some() {
3905 match abi {
3906 AbiVariant::GuestExport
3907 | AbiVariant::GuestExportAsync
3908 | AbiVariant::GuestExportAsyncStackful => ErrHandling::ThrowResultErr,
3909 AbiVariant::GuestImport | AbiVariant::GuestImportAsync => {
3910 ErrHandling::ResultCatchHandler
3911 }
3912 }
3913 } else {
3914 ErrHandling::None
3915 },
3916 block_storage: Vec::new(),
3917 blocks: Vec::new(),
3918 callee,
3919 callee_resource_dynamic: matches!(call_type, CallType::CalleeResourceDispatch),
3920 memory: memory.as_ref(),
3921 realloc: realloc.as_ref(),
3922 tmp: 0,
3923 params,
3924 post_return: post_return.as_ref(),
3925 tracing_prefix: &tracing_prefix,
3926 tracing_enabled: self.bindgen.opts.tracing,
3927 encoding: match opts.string_encoding {
3928 wasmtime_environ::component::StringEncoding::Utf8 => StringEncoding::UTF8,
3929 wasmtime_environ::component::StringEncoding::Utf16 => StringEncoding::UTF16,
3930 wasmtime_environ::component::StringEncoding::CompactUtf16 => {
3931 StringEncoding::CompactUTF16
3932 }
3933 },
3934 src: source::Source::default(),
3935 resolve: self.resolve,
3936 requires_async_porcelain,
3937 is_async,
3938 canon_opts: opts,
3939 iface_name,
3940 };
3941
3942 abi::call(
3945 self.resolve,
3946 abi,
3947 match abi {
3948 AbiVariant::GuestImport | AbiVariant::GuestImportAsync => {
3949 LiftLower::LiftArgsLowerResults
3950 }
3951 AbiVariant::GuestExport
3952 | AbiVariant::GuestExportAsync
3953 | AbiVariant::GuestExportAsyncStackful => LiftLower::LowerArgsLiftResults,
3954 },
3955 func,
3956 &mut f,
3957 is_async,
3958 );
3959
3960 self.src.js(&f.src);
3962
3963 self.src.js("}");
3965 }
3966
3967 fn augmented_import_def(&self, def: &core::AugmentedImport<'_>) -> String {
3968 match def {
3969 core::AugmentedImport::CoreDef(def) => self.core_def(def),
3970 core::AugmentedImport::Memory { mem, op } => {
3971 let mem = self.core_def(mem);
3972 match op {
3973 core::AugmentedOp::I32Load => {
3974 format!(
3975 "(ptr, off) => new DataView({mem}.buffer).getInt32(ptr + off, true)"
3976 )
3977 }
3978 core::AugmentedOp::I32Load8U => {
3979 format!(
3980 "(ptr, off) => new DataView({mem}.buffer).getUint8(ptr + off, true)"
3981 )
3982 }
3983 core::AugmentedOp::I32Load8S => {
3984 format!("(ptr, off) => new DataView({mem}.buffer).getInt8(ptr + off, true)")
3985 }
3986 core::AugmentedOp::I32Load16U => {
3987 format!(
3988 "(ptr, off) => new DataView({mem}.buffer).getUint16(ptr + off, true)"
3989 )
3990 }
3991 core::AugmentedOp::I32Load16S => {
3992 format!(
3993 "(ptr, off) => new DataView({mem}.buffer).getInt16(ptr + off, true)"
3994 )
3995 }
3996 core::AugmentedOp::I64Load => {
3997 format!(
3998 "(ptr, off) => new DataView({mem}.buffer).getBigInt64(ptr + off, true)"
3999 )
4000 }
4001 core::AugmentedOp::F32Load => {
4002 format!(
4003 "(ptr, off) => new DataView({mem}.buffer).getFloat32(ptr + off, true)"
4004 )
4005 }
4006 core::AugmentedOp::F64Load => {
4007 format!(
4008 "(ptr, off) => new DataView({mem}.buffer).getFloat64(ptr + off, true)"
4009 )
4010 }
4011 core::AugmentedOp::I32Store8 => {
4012 format!(
4013 "(ptr, val, offset) => {{
4014 new DataView({mem}.buffer).setInt8(ptr + offset, val, true);
4015 }}"
4016 )
4017 }
4018 core::AugmentedOp::I32Store16 => {
4019 format!(
4020 "(ptr, val, offset) => {{
4021 new DataView({mem}.buffer).setInt16(ptr + offset, val, true);
4022 }}"
4023 )
4024 }
4025 core::AugmentedOp::I32Store => {
4026 format!(
4027 "(ptr, val, offset) => {{
4028 new DataView({mem}.buffer).setInt32(ptr + offset, val, true);
4029 }}"
4030 )
4031 }
4032 core::AugmentedOp::I64Store => {
4033 format!(
4034 "(ptr, val, offset) => {{
4035 new DataView({mem}.buffer).setBigInt64(ptr + offset, val, true);
4036 }}"
4037 )
4038 }
4039 core::AugmentedOp::F32Store => {
4040 format!(
4041 "(ptr, val, offset) => {{
4042 new DataView({mem}.buffer).setFloat32(ptr + offset, val, true);
4043 }}"
4044 )
4045 }
4046 core::AugmentedOp::F64Store => {
4047 format!(
4048 "(ptr, val, offset) => {{
4049 new DataView({mem}.buffer).setFloat64(ptr + offset, val, true);
4050 }}"
4051 )
4052 }
4053 core::AugmentedOp::MemorySize => {
4054 format!("ptr => {mem}.buffer.byteLength / 65536")
4055 }
4056 }
4057 }
4058 }
4059 }
4060
4061 fn core_def(&self, def: &CoreDef) -> String {
4062 match def {
4063 CoreDef::Export(e) => self.core_export_var_name(e),
4064 CoreDef::TaskMayBlock => AsyncTaskIntrinsic::CurrentTaskMayBlock.name().into(),
4065 CoreDef::Trampoline(i) => format!("trampoline{}", i.as_u32()),
4066 CoreDef::InstanceFlags(i) => {
4067 self.used_instance_flags.borrow_mut().insert(*i);
4069 format!("instanceFlags{}", i.as_u32())
4070 }
4071 CoreDef::UnsafeIntrinsic(ui) => {
4072 let idx = ui.index();
4073 format!("unsafeIntrinsic{idx}")
4074 }
4075 }
4076 }
4077
4078 fn core_export_var_name<T>(&self, export: &CoreExport<T>) -> String
4079 where
4080 T: Into<EntityIndex> + Copy,
4081 {
4082 let name = match &export.item {
4083 ExportItem::Index(idx) => {
4084 let module_idx = self
4085 .instances
4086 .get(export.instance)
4087 .expect("unexpectedly missing export instance");
4088 let module = &self
4089 .modules
4090 .get(*module_idx)
4091 .expect("unexpectedly missing module by idx");
4092 let idx = (*idx).into();
4093 module
4094 .exports()
4095 .iter()
4096 .find_map(|(name, i)| if *i == idx { Some(name) } else { None })
4097 .unwrap()
4098 .to_string()
4099 }
4100 ExportItem::Name(s) => s.to_string(),
4101 };
4102 let i = export.instance.as_u32() as usize;
4103 let quoted = maybe_quote_member(&name);
4104 format!("exports{i}{quoted}")
4105 }
4106
4107 fn process_imports(&mut self) {
4109 let mut import_resource_map = ResourceMap::new();
4110 for (_import_name, (import_idx, _import_path)) in self.component.imports.iter() {
4111 let (import_name, import_type_def) = &self.component.import_types[*import_idx];
4112 let import_world_key = &self
4113 .imports
4114 .get(import_name)
4115 .expect("missing import mapping");
4116 let import_world_item = &self
4117 .resolve
4118 .worlds
4119 .get(self.world)
4120 .expect("missing world")
4121 .imports
4122 .get(*import_world_key)
4123 .expect("missing import in world for import");
4124
4125 match import_world_item {
4127 WorldItem::Interface { id: iface_id, .. } => {
4128 let iface = &self.resolve.interfaces[*iface_id];
4129
4130 for (fn_name, iface_fn) in iface.functions.iter() {
4133 match import_type_def {
4134 TypeDef::ComponentInstance(instance_ty) => {
4135 if let Some(TypeDef::ComponentFunc(type_func_index)) =
4136 &self.types[*instance_ty].exports.get(fn_name)
4137 {
4138 self.create_resource_fn_map(
4139 iface_fn,
4140 *type_func_index,
4141 &mut import_resource_map,
4142 );
4143 }
4144 }
4145 TypeDef::ComponentFunc(type_func_idx) => {
4146 self.create_resource_fn_map(
4147 iface_fn,
4148 *type_func_idx,
4149 &mut import_resource_map,
4150 );
4151 }
4152 _ => {}
4153 }
4154 }
4155 }
4156
4157 WorldItem::Function(func) => {
4159 let TypeDef::ComponentFunc(func_ty_idx) = import_type_def else {
4161 unreachable!("invalid fn export");
4162 };
4163 self.create_resource_fn_map(func, *func_ty_idx, &mut import_resource_map);
4164 }
4165 WorldItem::Type { .. } => {}
4167 }
4168 }
4169
4170 self.resource_imports.extend(import_resource_map);
4171 }
4172
4173 fn process_exports(&mut self) {
4175 self.resource_exports.extend(self.resource_imports.clone());
4177
4178 for (export_name, export_idx) in self.component.exports.raw_iter() {
4180 let export = &self.component.export_items[*export_idx];
4181 let world_key = &self.exports[export_name];
4182 let item = &self.resolve.worlds[self.world].exports[world_key];
4183 let mut export_resource_map = ResourceMap::new();
4184
4185 match export {
4186 Export::LiftedFunction {
4187 func: def,
4188 options,
4189 ty: func_ty,
4190 } => {
4191 let func = match item {
4192 WorldItem::Function(f) => f,
4193 WorldItem::Interface { .. } | WorldItem::Type { .. } => {
4194 unreachable!("unexpectedly non-function lifted function export")
4195 }
4196 };
4197
4198 self.create_resource_fn_map(func, *func_ty, &mut export_resource_map);
4199
4200 let local_name = String::from(match func.kind {
4201 FunctionKind::Constructor(resource_id)
4203 | FunctionKind::Method(resource_id)
4204 | FunctionKind::AsyncMethod(resource_id)
4205 | FunctionKind::Static(resource_id)
4206 | FunctionKind::AsyncStatic(resource_id) => Instantiator::resource_name(
4207 self.resolve,
4208 &mut self.bindgen.local_names,
4209 resource_id,
4210 &self.exports_resource_types,
4211 ),
4212 FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
4214 self.bindgen.local_names.create_once(export_name)
4215 }
4216 });
4217
4218 let options = self
4219 .component
4220 .options
4221 .get(*options)
4222 .expect("failed to find options");
4223
4224 self.export_bindgen(
4225 &local_name,
4226 def,
4227 options,
4228 func,
4229 func_ty,
4230 export_name,
4231 &export_resource_map,
4232 );
4233
4234 let js_binding_name = match func.kind {
4235 FunctionKind::Constructor(ty)
4237 | FunctionKind::Method(ty)
4238 | FunctionKind::AsyncMethod(ty)
4239 | FunctionKind::Static(ty)
4240 | FunctionKind::AsyncStatic(ty) => self.resolve.types[ty]
4241 .name
4242 .as_ref()
4243 .unwrap()
4244 .to_upper_camel_case(),
4245 FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
4247 export_name.to_lower_camel_case()
4248 }
4249 };
4250
4251 self.bindgen.esm_bindgen.add_export_binding(
4253 None,
4254 local_name,
4255 js_binding_name,
4256 func,
4257 );
4258 }
4259
4260 Export::Instance { exports, .. } => {
4261 let iface_id = match item {
4262 WorldItem::Interface { id, .. } => *id,
4263 WorldItem::Function(_) | WorldItem::Type { .. } => {
4264 unreachable!("unexpectedly non-interface export instance")
4265 }
4266 };
4267
4268 for (func_name, export_idx) in exports.raw_iter() {
4270 let export = &self.component.export_items[*export_idx];
4271
4272 let (def, options, func_ty) = match export {
4274 Export::LiftedFunction { func, options, ty } => (func, options, ty),
4275 Export::Type(_) => continue, _ => unreachable!("unexpected non-lifted function export"),
4277 };
4278
4279 let func = &self.resolve.interfaces[iface_id].functions[func_name];
4280
4281 self.create_resource_fn_map(func, *func_ty, &mut export_resource_map);
4282
4283 let local_name = String::from(match func.kind {
4284 FunctionKind::Constructor(resource_id)
4286 | FunctionKind::Method(resource_id)
4287 | FunctionKind::AsyncMethod(resource_id)
4288 | FunctionKind::Static(resource_id)
4289 | FunctionKind::AsyncStatic(resource_id) => {
4290 Instantiator::resource_name(
4291 self.resolve,
4292 &mut self.bindgen.local_names,
4293 resource_id,
4294 &self.exports_resource_types,
4295 )
4296 }
4297 FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
4299 self.bindgen.local_names.create_once(func_name)
4300 }
4301 });
4302
4303 let options = self
4304 .component
4305 .options
4306 .get(*options)
4307 .expect("failed to find options");
4308
4309 self.export_bindgen(
4310 &local_name,
4311 def,
4312 options,
4313 func,
4314 func_ty,
4315 export_name,
4316 &export_resource_map,
4317 );
4318
4319 let export_binding_name = match func.kind {
4321 FunctionKind::Constructor(ty)
4323 | FunctionKind::Method(ty)
4324 | FunctionKind::AsyncMethod(ty)
4325 | FunctionKind::Static(ty)
4326 | FunctionKind::AsyncStatic(ty) => self.resolve.types[ty]
4327 .name
4328 .as_ref()
4329 .unwrap()
4330 .to_upper_camel_case(),
4331 FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
4333 func_name.to_lower_camel_case()
4334 }
4335 };
4336
4337 self.bindgen.esm_bindgen.add_export_binding(
4339 Some(export_name),
4340 local_name,
4341 export_binding_name,
4342 func,
4343 );
4344 }
4345 }
4346
4347 Export::Type(_) => {}
4349
4350 Export::ModuleStatic { .. } | Export::ModuleImport { .. } => unimplemented!(),
4352 }
4353
4354 self.resource_exports.extend(export_resource_map);
4356 }
4357
4358 self.bindgen.esm_bindgen.populate_export_aliases();
4359 }
4360
4361 #[allow(clippy::too_many_arguments)]
4362 fn export_bindgen(
4363 &mut self,
4364 local_name: &str,
4365 def: &CoreDef,
4366 options: &CanonicalOptions,
4367 func: &Function,
4368 _func_ty_idx: &TypeFuncIndex,
4369 export_name: &String,
4370 export_resource_map: &ResourceMap,
4371 ) {
4372 let requires_async_porcelain = requires_async_porcelain(
4374 FunctionIdentifier::Fn(func),
4375 export_name,
4376 &self.async_exports,
4377 );
4378 if options.async_ {
4380 assert!(
4381 options.post_return.is_none(),
4382 "async function {local_name} (export {export_name}) can't have post return"
4383 );
4384 }
4385
4386 let is_async = is_async_fn(func, options);
4387
4388 let maybe_async = if requires_async_porcelain || is_async {
4389 "async "
4390 } else {
4391 ""
4392 };
4393
4394 let core_export_fn = self.core_def(def);
4396 let callee = match self
4397 .bindgen
4398 .local_names
4399 .get_or_create(&core_export_fn, &core_export_fn)
4400 {
4401 (local_name, true) => local_name.to_string(),
4402 (local_name, false) => {
4403 let local_name = local_name.to_string();
4404 uwriteln!(self.src.js, "let {local_name};");
4405 self.bindgen
4406 .all_core_exported_funcs
4407 .push((core_export_fn.clone(), is_async | requires_async_porcelain));
4411 local_name
4412 }
4413 };
4414
4415 let iface_name = if export_name.is_empty() {
4416 None
4417 } else {
4418 Some(export_name)
4419 };
4420
4421 match func.kind {
4423 FunctionKind::Freestanding => {
4424 uwrite!(self.src.js, "\n{maybe_async}function {local_name}")
4425 }
4426 FunctionKind::Method(_) => {
4427 self.ensure_local_resource_class(local_name.to_string());
4428 let method_name = func.item_name().to_lower_camel_case();
4429
4430 uwrite!(
4431 self.src.js,
4432 "\n{local_name}.prototype.{method_name} = {maybe_async}function {}",
4433 if !is_js_reserved_word(&method_name) {
4434 method_name.to_string()
4435 } else {
4436 format!("${method_name}")
4437 }
4438 );
4439 }
4440 FunctionKind::Static(_) => {
4441 self.ensure_local_resource_class(local_name.to_string());
4442 let method_name = func.item_name().to_lower_camel_case();
4443 uwrite!(
4444 self.src.js,
4445 "\n{local_name}.{method_name} = function {}",
4446 if !is_js_reserved_word(&method_name) {
4447 method_name.to_string()
4448 } else {
4449 format!("${method_name}")
4450 }
4451 );
4452 }
4453 FunctionKind::Constructor(_) => {
4454 if self.defined_resource_classes.contains(local_name) {
4455 panic!(
4456 "Internal error: Resource constructor must be defined before other methods and statics"
4457 );
4458 }
4459 uwrite!(
4460 self.src.js,
4461 "
4462 class {local_name} {{
4463 constructor"
4464 );
4465 self.defined_resource_classes.insert(local_name.to_string());
4466 }
4467 FunctionKind::AsyncFreestanding => {
4468 uwrite!(self.src.js, "\nasync function {local_name}")
4469 }
4470 FunctionKind::AsyncMethod(_) => {
4471 self.ensure_local_resource_class(local_name.to_string());
4472 let method_name = func.item_name().to_lower_camel_case();
4473 let fn_name = if !is_js_reserved_word(&method_name) {
4474 method_name.to_string()
4475 } else {
4476 format!("${method_name}")
4477 };
4478 uwrite!(
4479 self.src.js,
4480 "\n{local_name}.prototype.{method_name} = async function {fn_name}",
4481 );
4482 }
4483 FunctionKind::AsyncStatic(_) => {
4484 self.ensure_local_resource_class(local_name.to_string());
4485 let method_name = func.item_name().to_lower_camel_case();
4486 let fn_name = if !is_js_reserved_word(&method_name) {
4487 method_name.to_string()
4488 } else {
4489 format!("${method_name}")
4490 };
4491 uwrite!(
4492 self.src.js,
4493 "\n{local_name}.{method_name} = async function {fn_name}",
4494 );
4495 }
4496 };
4497
4498 self.bindgen(JsFunctionBindgenArgs {
4500 nparams: func.params.len(),
4501 call_type: match func.kind {
4502 FunctionKind::Method(_) => CallType::FirstArgIsThis,
4503 FunctionKind::AsyncMethod(_) => CallType::AsyncFirstArgIsThis,
4504 FunctionKind::Freestanding
4505 | FunctionKind::Static(_)
4506 | FunctionKind::Constructor(_) => CallType::Standard,
4507 FunctionKind::AsyncFreestanding | FunctionKind::AsyncStatic(_) => {
4508 CallType::AsyncStandard
4509 }
4510 },
4511 iface_name: iface_name.map(|v| v.as_str()),
4512 callee: &callee,
4513 opts: options,
4514 func,
4515 resource_map: export_resource_map,
4516 abi: AbiVariant::GuestExport,
4517 requires_async_porcelain,
4518 is_async,
4519 });
4520
4521 match func.kind {
4523 FunctionKind::AsyncFreestanding | FunctionKind::Freestanding => self.src.js("\n"),
4524 FunctionKind::AsyncMethod(_)
4525 | FunctionKind::AsyncStatic(_)
4526 | FunctionKind::Method(_)
4527 | FunctionKind::Static(_) => self.src.js(";\n"),
4528 FunctionKind::Constructor(_) => self.src.js("\n}\n"),
4529 }
4530 }
4531}
4532
4533#[derive(Default)]
4534pub struct Source {
4535 pub js: source::Source,
4536 pub js_init: source::Source,
4537}
4538
4539impl Source {
4540 pub fn js(&mut self, s: &str) {
4541 self.js.push_str(s);
4542 }
4543 pub fn js_init(&mut self, s: &str) {
4544 self.js_init.push_str(s);
4545 }
4546}
4547
4548fn semver_compat_key(version_str: &str) -> Option<(String, Version)> {
4559 let version = Version::parse(version_str).ok()?;
4560 if !version.pre.is_empty() {
4561 None
4562 } else if version.major != 0 {
4563 Some((format!("{}", version.major), version))
4564 } else if version.minor != 0 {
4565 Some((format!("0.{}", version.minor), version))
4566 } else {
4567 None
4568 }
4569}
4570
4571fn parse_mapping(mapping: &str) -> (String, Option<String>) {
4572 if mapping.len() > 1
4573 && let Some(hash_idx) = mapping[1..].find('#')
4574 {
4575 return (
4576 mapping[0..hash_idx + 1].to_string(),
4577 Some(mapping[hash_idx + 2..].into()),
4578 );
4579 }
4580 (mapping.into(), None)
4581}
4582
4583fn map_import(map: &Option<HashMap<String, String>>, impt: &str) -> (String, Option<String>) {
4584 let impt_sans_version = match impt.find('@') {
4585 Some(version_idx) => &impt[0..version_idx],
4586 None => impt,
4587 };
4588 if let Some(map) = map.as_ref() {
4589 if let Some(mapping) = map.get(impt) {
4591 return parse_mapping(mapping);
4592 }
4593 if let Some(mapping) = map.get(impt_sans_version) {
4595 return parse_mapping(mapping);
4596 }
4597 for (key, mapping) in map {
4599 if let Some(wildcard_idx) = key.find('*') {
4600 let lhs = &key[0..wildcard_idx];
4601 let rhs = &key[wildcard_idx + 1..];
4602 if impt_sans_version.starts_with(lhs) && impt_sans_version.ends_with(rhs) {
4603 let matched = &impt_sans_version[wildcard_idx
4604 ..wildcard_idx + impt_sans_version.len() - lhs.len() - rhs.len()];
4605 let mapping = mapping.replace('*', matched);
4606 return parse_mapping(&mapping);
4607 }
4608 if impt.starts_with(lhs) && impt.ends_with(rhs) {
4609 let matched =
4610 &impt[wildcard_idx..wildcard_idx + impt.len() - lhs.len() - rhs.len()];
4611 let mapping = mapping.replace('*', matched);
4612 return parse_mapping(&mapping);
4613 }
4614 }
4615 }
4616 if let Some(at) = impt.find('@') {
4620 let impt_ver_str = &impt[at + 1..];
4621 if let Some((impt_compat, _)) = semver_compat_key(impt_ver_str) {
4622 let mut best_match: Option<(String, Version)> = None;
4623
4624 for (key, mapping) in map {
4625 let key_at = match key.find('@') {
4627 Some(at) => at,
4628 None => continue,
4629 };
4630 let key_base = &key[..key_at];
4631 let key_ver_str = &key[key_at + 1..];
4632
4633 let (key_compat, key_ver) = match semver_compat_key(key_ver_str) {
4635 Some(k) => k,
4636 None => continue,
4637 };
4638 if impt_compat != key_compat {
4639 continue;
4640 }
4641
4642 let resolved = if let Some(wildcard_idx) = key_base.find('*') {
4645 let lhs = &key_base[..wildcard_idx];
4646 let rhs = &key_base[wildcard_idx + 1..];
4647 if impt_sans_version.starts_with(lhs) && impt_sans_version.ends_with(rhs) {
4648 let matched = &impt_sans_version[wildcard_idx
4649 ..wildcard_idx + impt_sans_version.len() - lhs.len() - rhs.len()];
4650 Some(mapping.replace('*', matched))
4651 } else {
4652 None
4653 }
4654 } else if key_base == impt_sans_version {
4655 Some(mapping.clone())
4656 } else {
4657 None
4658 };
4659
4660 if let Some(resolved_mapping) = resolved {
4661 match &best_match {
4663 Some((_, prev_ver)) if key_ver <= *prev_ver => {}
4664 _ => {
4665 best_match = Some((resolved_mapping, key_ver));
4666 }
4667 }
4668 }
4669 }
4670
4671 if let Some((mapping, _)) = best_match {
4672 return parse_mapping(&mapping);
4673 }
4674 }
4675 }
4676 }
4677 (impt_sans_version.to_string(), None)
4678}
4679
4680pub fn parse_world_key(name: &str) -> Option<(&str, &str, &str)> {
4681 let registry_idx = name.find(':')?;
4682 let ns = &name[0..registry_idx];
4683 match name.rfind('/') {
4684 Some(sep_idx) => {
4685 let end = if let Some(version_idx) = name.rfind('@') {
4686 version_idx
4687 } else {
4688 name.len()
4689 };
4690 Some((
4691 ns,
4692 &name[registry_idx + 1..sep_idx],
4693 &name[sep_idx + 1..end],
4694 ))
4695 }
4696 None => Some((ns, &name[registry_idx + 1..], "")),
4698 }
4699}
4700
4701fn core_file_name(name: &str, idx: u32) -> String {
4702 let i_str = if idx == 0 {
4703 String::from("")
4704 } else {
4705 (idx + 1).to_string()
4706 };
4707 format!("{name}.core{i_str}.wasm")
4708}
4709
4710fn string_encoding_js_literal(val: &wasmtime_environ::component::StringEncoding) -> &'static str {
4712 match val {
4713 wasmtime_environ::component::StringEncoding::Utf8 => "'utf8'",
4714 wasmtime_environ::component::StringEncoding::Utf16 => "'utf16'",
4715 wasmtime_environ::component::StringEncoding::CompactUtf16 => "'compact-utf16'",
4716 }
4717}
4718
4719pub fn gen_flat_lift_fn_list_js_expr(
4728 instantiator: &mut Instantiator,
4729 types: &[InterfaceType],
4730 extra_resource_map: &Option<&mut ResourceMap>,
4731) -> String {
4732 let mut lift_fns: Vec<String> = Vec::with_capacity(types.len());
4733 for ty in types.iter() {
4734 lift_fns.push(gen_flat_lift_fn_js_expr(
4735 instantiator,
4736 ty,
4737 extra_resource_map,
4738 ));
4739 }
4740 format!("[{}]", lift_fns.join(","))
4741}
4742
4743pub fn gen_flat_lift_fn_js_expr(
4764 instantiator: &mut Instantiator,
4765 ty: &InterfaceType,
4766 extra_resource_map: &Option<&mut ResourceMap>,
4767) -> String {
4768 let component_types = instantiator.types;
4769
4770 match ty {
4771 InterfaceType::Bool => {
4772 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatBool));
4773 Intrinsic::Lift(LiftIntrinsic::LiftFlatBool).name().into()
4774 }
4775
4776 InterfaceType::S8 => {
4777 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS8));
4778 Intrinsic::Lift(LiftIntrinsic::LiftFlatS8).name().into()
4779 }
4780
4781 InterfaceType::U8 => {
4782 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU8));
4783 Intrinsic::Lift(LiftIntrinsic::LiftFlatU8).name().into()
4784 }
4785
4786 InterfaceType::S16 => {
4787 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS16));
4788 Intrinsic::Lift(LiftIntrinsic::LiftFlatS16).name().into()
4789 }
4790
4791 InterfaceType::U16 => {
4792 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU16));
4793 Intrinsic::Lift(LiftIntrinsic::LiftFlatU16).name().into()
4794 }
4795
4796 InterfaceType::S32 => {
4797 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS32));
4798 Intrinsic::Lift(LiftIntrinsic::LiftFlatS32).name().into()
4799 }
4800
4801 InterfaceType::U32 => {
4802 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU32));
4803 Intrinsic::Lift(LiftIntrinsic::LiftFlatU32).name().into()
4804 }
4805
4806 InterfaceType::S64 => {
4807 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS64));
4808 Intrinsic::Lift(LiftIntrinsic::LiftFlatS64).name().into()
4809 }
4810
4811 InterfaceType::U64 => {
4812 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU64));
4813 Intrinsic::Lift(LiftIntrinsic::LiftFlatU64).name().into()
4814 }
4815
4816 InterfaceType::Float32 => {
4817 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFloat32));
4818 Intrinsic::Lift(LiftIntrinsic::LiftFlatFloat32)
4819 .name()
4820 .into()
4821 }
4822
4823 InterfaceType::Float64 => {
4824 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFloat64));
4825 Intrinsic::Lift(LiftIntrinsic::LiftFlatFloat64)
4826 .name()
4827 .into()
4828 }
4829
4830 InterfaceType::Char => {
4831 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatChar));
4832 Intrinsic::Lift(LiftIntrinsic::LiftFlatChar).name().into()
4833 }
4834
4835 InterfaceType::String => {
4836 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatStringAny));
4837 Intrinsic::Lift(LiftIntrinsic::LiftFlatStringAny)
4838 .name()
4839 .into()
4840 }
4841
4842 InterfaceType::Record(ty_idx) => {
4843 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatRecord));
4844 let lift_fn = Intrinsic::Lift(LiftIntrinsic::LiftFlatRecord).name();
4845 let record_ty = &component_types[*ty_idx];
4846 let mut keys_and_lifts_expr = String::from("[");
4847 for f in &record_ty.fields {
4851 keys_and_lifts_expr.push_str(&format!(
4852 "['{}', {}, {}, {}],",
4853 f.name.to_lower_camel_case(),
4854 gen_flat_lift_fn_js_expr(instantiator, &f.ty, extra_resource_map),
4855 component_types.canonical_abi(ty).size32,
4856 component_types.canonical_abi(ty).align32,
4857 ));
4858 }
4859 keys_and_lifts_expr.push(']');
4860 format!("{lift_fn}({keys_and_lifts_expr})")
4861 }
4862
4863 InterfaceType::Variant(ty_idx) => {
4864 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatVariant));
4865 let lift_fn = Intrinsic::Lift(LiftIntrinsic::LiftFlatVariant).name();
4866 let variant_ty = &component_types[*ty_idx];
4867 let mut cases_and_lifts_expr = String::from("[");
4868 for (name, maybe_ty) in &variant_ty.cases {
4869 let lift_args = match maybe_ty {
4870 None => format!("['{}', null, 0, 0, 0],", name),
4871 Some(ty) => {
4872 format!(
4873 "['{name}', {}, {}, {}, {}],",
4874 gen_flat_lift_fn_js_expr(instantiator, ty, extra_resource_map),
4875 variant_ty.abi.size32,
4876 variant_ty.abi.align32,
4877 variant_ty.info.payload_offset32,
4878 )
4879 }
4880 };
4881 cases_and_lifts_expr.push_str(&lift_args);
4882 }
4883 cases_and_lifts_expr.push(']');
4884 format!("{lift_fn}({cases_and_lifts_expr})")
4885 }
4886
4887 InterfaceType::List(ty_idx) => {
4888 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatList));
4889 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatList).name();
4890 let list_ty = &component_types[*ty_idx];
4891 let lift_fn_expr =
4892 gen_flat_lift_fn_js_expr(instantiator, &list_ty.element, extra_resource_map);
4893 let elem_cabi = component_types.canonical_abi(&list_ty.element);
4894 let elem_align32 = elem_cabi.align32;
4895 let elem_size32 = elem_cabi.size32;
4896 format!(
4897 "{f}({{
4898 elemLiftFn: {lift_fn_expr},
4899 elemAlign32: {elem_align32},
4900 elemSize32: {elem_size32},
4901 }})"
4902 )
4903 }
4904
4905 InterfaceType::FixedLengthList(ty_idx) => {
4906 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatList));
4907 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatList).name();
4908 let list_ty = &component_types[*ty_idx];
4909 let list_size32 = list_ty.abi.size32;
4910 let list_align32 = list_ty.abi.align32;
4911 let lift_fn_expr =
4912 gen_flat_lift_fn_js_expr(instantiator, &list_ty.element, extra_resource_map);
4913 let list_len = list_ty.size;
4914 let elem_cabi = component_types.canonical_abi(&list_ty.element);
4915 let elem_align32 = elem_cabi.align32;
4916 let elem_size32 = elem_cabi.size32;
4917 format!(
4918 "{f}({{
4919 elemLiftFn: {lift_fn_expr},
4920 elemAlign32: {elem_align32},
4921 elemSize32: {elem_size32},
4922 listSize32: {list_size32},
4923 listAlign32: {list_align32},
4924 knownLen: {list_len},
4925 }})"
4926 )
4927 }
4928
4929 InterfaceType::Tuple(ty_idx) => {
4930 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatTuple));
4931 let tuple_ty = &component_types[*ty_idx];
4932 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatTuple).name();
4933 let size_u32 = tuple_ty.abi.size32;
4934 let align_u32 = tuple_ty.abi.align32;
4935
4936 let mut elem_lifts_expr = String::from("[");
4937 for ty in &tuple_ty.types {
4938 let lift_fn_js = gen_flat_lift_fn_js_expr(instantiator, ty, extra_resource_map);
4939 elem_lifts_expr.push_str(&format!("[{lift_fn_js}, {size_u32}, {align_u32}],"));
4940 }
4941 elem_lifts_expr.push(']');
4942
4943 format!("{f}({elem_lifts_expr})")
4944 }
4945
4946 InterfaceType::Flags(ty_idx) => {
4947 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFlags));
4948 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatFlags).name();
4949 let flags_ty = &component_types[*ty_idx];
4950 let size_u32 = flags_ty.abi.size32;
4951 let align_u32 = flags_ty.abi.align32;
4952 let names_expr = format!(
4953 "[{}]",
4954 flags_ty
4955 .names
4956 .iter()
4957 .map(|s| format!("'{s}'"))
4958 .collect::<Vec<_>>()
4959 .join(",")
4960 );
4961 let num_flags = flags_ty.names.len();
4962 let elem_size = if num_flags <= 8 {
4963 1
4964 } else if num_flags <= 16 {
4965 2
4966 } else {
4967 4
4968 };
4969
4970 format!(
4971 "{f}({{ names: {names_expr}, size32: {size_u32}, align32: {align_u32}, intSizeBytes: {elem_size} }})"
4972 )
4973 }
4974
4975 InterfaceType::Enum(ty_idx) => {
4976 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatEnum));
4977 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatEnum).name();
4978 let enum_ty = &component_types[*ty_idx];
4979 let size_32 = enum_ty.abi.size32;
4980 let align_32 = enum_ty.abi.align32;
4981 let payload_offset_32 = enum_ty.info.payload_offset32;
4982
4983 let mut elem_lifts_expr = String::from("[");
4984 for name in &enum_ty.names {
4985 elem_lifts_expr.push_str(&format!(
4986 "['{name}', null, {size_32}, {align_32}, {payload_offset_32}],"
4987 ));
4988 }
4989 elem_lifts_expr.push(']');
4990
4991 format!("{f}({elem_lifts_expr})")
4992 }
4993
4994 InterfaceType::Option(ty_idx) => {
4995 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatOption));
4996 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatOption).name();
4997 let option_ty = &component_types[*ty_idx];
4998 let payload_offset_32 = option_ty.info.payload_offset32;
4999 let align_32 = option_ty.abi.align32;
5000 let size_32 = option_ty.abi.size32;
5001 let lift_fn_js =
5002 gen_flat_lift_fn_js_expr(instantiator, &option_ty.ty, extra_resource_map);
5003 format!(
5005 "{f}([
5006 ['none', null, {size_32}, {align_32}, {payload_offset_32} ],
5007 ['some', {lift_fn_js}, {size_32}, {align_32}, {payload_offset_32} ],
5008 ])"
5009 )
5010 }
5011
5012 InterfaceType::Result(ty_idx) => {
5013 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatResult));
5014 let lift_fn = Intrinsic::Lift(LiftIntrinsic::LiftFlatResult).name();
5015 let result_ty = &component_types[*ty_idx];
5016 let mut cases_and_lifts_expr = String::from("[");
5017
5018 if let Some(ok_ty) = result_ty.ok {
5019 cases_and_lifts_expr.push_str(&format!(
5020 "['ok', {}, {}, {}, {}],",
5021 gen_flat_lift_fn_js_expr(instantiator, &ok_ty, extra_resource_map),
5022 result_ty.abi.size32,
5023 result_ty.abi.align32,
5024 result_ty.info.payload_offset32,
5025 ))
5026 } else {
5027 cases_and_lifts_expr.push_str("['ok', null, 0, 0, 0],");
5028 }
5029
5030 if let Some(err_ty) = &result_ty.err {
5031 cases_and_lifts_expr.push_str(&format!(
5032 "['err', {}, {}, {}, {}],",
5033 gen_flat_lift_fn_js_expr(instantiator, err_ty, extra_resource_map),
5034 result_ty.abi.size32,
5035 result_ty.abi.align32,
5036 result_ty.info.payload_offset32,
5037 ))
5038 } else {
5039 cases_and_lifts_expr.push_str("['err', null, 0, 0, 0],");
5040 }
5041
5042 cases_and_lifts_expr.push(']');
5043 format!("{lift_fn}({cases_and_lifts_expr})")
5044 }
5045
5046 InterfaceType::Own(ty_idx) => {
5047 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatOwn));
5048 instantiator.add_intrinsic(Intrinsic::JsHelper(JsHelperIntrinsic::EmptyFunc));
5049 instantiator.add_intrinsic(Intrinsic::SymbolResourceHandle);
5050 instantiator.add_intrinsic(Intrinsic::SymbolDispose);
5051 instantiator.add_intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableRemove));
5052 instantiator.add_intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableFlag));
5053 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatOwn).name();
5054 let table_ty = &component_types[*ty_idx];
5055 let component_idx = table_ty.unwrap_concrete_instance().as_u32();
5056 let resource_idx = table_ty.unwrap_concrete_ty();
5057
5058 match instantiator.exports_resource_index_types.get(&resource_idx) {
5060 None => format!(
5062 r#"{f}({{
5063 componentIdx: {component_idx},
5064 className: null,
5065 createResourceFn: () => {{ throw new Error('invalid/missing resource type data'); }},
5066 }})
5067 "#,
5068 ),
5069
5070 Some(resource_typedef) => {
5073 let (resource_class_name, create_resource_fn_js) = match (
5075 instantiator.resource_exports.get(resource_typedef),
5076 extra_resource_map
5077 .as_ref()
5078 .and_then(|v| v.get(resource_typedef)),
5079 ) {
5080 (None, None) => (
5082 "null".into(),
5083 "() => {{ throw new Error('missing resource information'); }}".into(),
5084 ),
5085
5086 (Some(ResourceTable { data, .. }), _)
5088 | (_, Some(ResourceTable { data, .. })) => match data {
5089 ResourceData::Guest { .. } => {
5090 unimplemented!(
5091 "owned resources created by guests should must have host-side data"
5092 )
5093 }
5094 ResourceData::Host {
5095 tid,
5096 local_name,
5097 dtor_name,
5098 ..
5099 } => {
5100 let empty_func = JsHelperIntrinsic::EmptyFunc.name();
5101 let symbol_resource_handle = Intrinsic::SymbolResourceHandle.name();
5102 let symbol_dispose = Intrinsic::SymbolDispose.name();
5103 let rsc_table_remove =
5104 ResourceIntrinsic::ResourceTableRemove.name();
5105 let tid = tid.as_u32();
5106 let rsc_flag = ResourceIntrinsic::ResourceTableFlag.name();
5107
5108 let dtor_setup_js = dtor_name
5109 .as_ref()
5110 .map(|dtor|
5111 format!(
5112 r#"
5113 Object.defineProperty(
5114 resourceObj,
5115 {symbol_dispose},
5116 {{
5117 writable: true,
5118 value: function() {{
5119 finalizationRegistry{tid}.unregister(resourceObj);
5120 {rsc_table_remove}(handleTable{tid}, handle);
5121 resourceObj[{symbol_dispose}] = {empty_func};
5122 resourceObj[{symbol_resource_handle}] = undefined;
5123 {dtor}(handleTable{tid}[(handle << 1) + 1] & ~{rsc_flag});
5124 }}
5125 }}
5126 );
5127 "#
5128 )
5129 ).unwrap_or_default();
5130
5131 let create_resource_fn_js = format!(
5132 r#"
5133 (handle) => {{
5134 const resourceObj = Object.create({local_name}.prototype);
5135 Object.defineProperty(resourceObj, {symbol_resource_handle}, {{
5136 writable: true,
5137 value: handle,
5138 }});
5139 finalizationRegistry{tid}.register(resourceObj, handle, resourceObj);
5140 {dtor_setup_js}
5141 return resourceObj;
5142 }}
5143 "#
5144 );
5145
5146 (local_name.to_string(), create_resource_fn_js)
5147 }
5148 },
5149 };
5150
5151 format!(
5152 r#"{f}({{
5153 componentIdx: {component_idx},
5154 className: {resource_class_name},
5155 createResourceFn: {create_resource_fn_js},
5156 }})
5157 "#,
5158 )
5159 }
5160 }
5161 }
5162
5163 InterfaceType::Borrow(ty_idx) => {
5164 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatBorrow));
5165 let table_idx = ty_idx.as_u32();
5166 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatBorrow).name();
5167 format!("{f}.bind(null, {table_idx})")
5168 }
5169
5170 InterfaceType::Future(ty_idx) => {
5171 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFuture));
5172 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatFuture).name();
5173 let table_idx = ty_idx.as_u32();
5174 let table_ty = &component_types[*ty_idx];
5175 let component_idx = table_ty.instance.as_u32();
5176 format!("{f}({{ futureTableIdx: {table_idx}, componentIdx: {component_idx} }})")
5177 }
5178
5179 InterfaceType::Stream(ty_idx) => {
5180 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatStream));
5181 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatStream).name();
5182 let table_idx = ty_idx.as_u32();
5183 let table_ty = &component_types[*ty_idx];
5184 let component_idx = table_ty.instance.as_u32();
5185 format!("{f}({{ streamTableIdx: {table_idx}, componentIdx: {component_idx} }})")
5186 }
5187
5188 InterfaceType::ErrorContext(ty_idx) => {
5189 instantiator.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatErrorContext));
5190 let table_idx = ty_idx.as_u32();
5191 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatErrorContext).name();
5192 format!("{f}.bind(null, {table_idx})")
5193 }
5194 }
5195}
5196
5197pub fn gen_flat_lower_fn_list_js_expr(
5206 instantiator: &mut Instantiator,
5207 types: &[InterfaceType],
5208 extra_import_map: &Option<&mut ResourceMap>,
5209) -> String {
5210 let mut lower_fns: Vec<String> = Vec::with_capacity(types.len());
5211 for ty in types.iter() {
5212 lower_fns.push(gen_flat_lower_fn_js_expr(
5213 instantiator,
5214 ty,
5215 extra_import_map,
5216 ));
5217 }
5218 format!("[{}]", lower_fns.join(","))
5219}
5220
5221pub fn gen_flat_lower_fn_js_expr(
5242 instantiator: &mut Instantiator,
5243 ty: &InterfaceType,
5244 extra_resource_map: &Option<&mut ResourceMap>,
5245) -> String {
5246 let component_types = instantiator.types;
5247 match ty {
5248 InterfaceType::Bool => {
5249 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatBool));
5250 Intrinsic::Lower(LowerIntrinsic::LowerFlatBool)
5251 .name()
5252 .into()
5253 }
5254
5255 InterfaceType::S8 => {
5256 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatS8));
5257 Intrinsic::Lower(LowerIntrinsic::LowerFlatS8).name().into()
5258 }
5259
5260 InterfaceType::U8 => {
5261 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatU8));
5262 Intrinsic::Lower(LowerIntrinsic::LowerFlatU8).name().into()
5263 }
5264
5265 InterfaceType::S16 => {
5266 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatS16));
5267 Intrinsic::Lower(LowerIntrinsic::LowerFlatS16).name().into()
5268 }
5269
5270 InterfaceType::U16 => {
5271 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatU16));
5272 Intrinsic::Lower(LowerIntrinsic::LowerFlatU16).name().into()
5273 }
5274
5275 InterfaceType::S32 => {
5276 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatS32));
5277 Intrinsic::Lower(LowerIntrinsic::LowerFlatS32).name().into()
5278 }
5279
5280 InterfaceType::U32 => {
5281 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatU32));
5282 Intrinsic::Lower(LowerIntrinsic::LowerFlatU32).name().into()
5283 }
5284
5285 InterfaceType::S64 => {
5286 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatS64));
5287 Intrinsic::Lower(LowerIntrinsic::LowerFlatS64).name().into()
5288 }
5289
5290 InterfaceType::U64 => {
5291 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatU64));
5292 Intrinsic::Lower(LowerIntrinsic::LowerFlatU64).name().into()
5293 }
5294
5295 InterfaceType::Float32 => {
5296 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatFloat32));
5297 Intrinsic::Lower(LowerIntrinsic::LowerFlatFloat32)
5298 .name()
5299 .into()
5300 }
5301
5302 InterfaceType::Float64 => {
5303 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatFloat64));
5304 Intrinsic::Lower(LowerIntrinsic::LowerFlatFloat64)
5305 .name()
5306 .into()
5307 }
5308
5309 InterfaceType::Char => {
5310 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatChar));
5311 Intrinsic::Lower(LowerIntrinsic::LowerFlatChar)
5312 .name()
5313 .into()
5314 }
5315
5316 InterfaceType::String => {
5317 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatStringAny));
5318 Intrinsic::Lower(LowerIntrinsic::LowerFlatStringAny)
5319 .name()
5320 .into()
5321 }
5322
5323 InterfaceType::Record(ty_idx) => {
5324 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatRecord));
5325 let lower_fn = Intrinsic::Lower(LowerIntrinsic::LowerFlatRecord).name();
5326 let record_ty = &component_types[*ty_idx];
5327 let mut keys_and_lowers_expr = String::from("[");
5328 for f in &record_ty.fields {
5329 keys_and_lowers_expr.push_str(&format!(
5333 "['{}', {}, {}, {} ],",
5334 f.name.to_lower_camel_case(),
5335 gen_flat_lower_fn_js_expr(instantiator, &f.ty, &None),
5336 component_types.canonical_abi(ty).size32,
5337 component_types.canonical_abi(ty).align32,
5338 ));
5339 }
5340 keys_and_lowers_expr.push(']');
5341 format!("{lower_fn}({keys_and_lowers_expr})")
5342 }
5343
5344 InterfaceType::Variant(ty_idx) => {
5345 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatVariant));
5346 let lower_fn = Intrinsic::Lower(LowerIntrinsic::LowerFlatVariant).name();
5347 let variant_ty = &component_types[*ty_idx];
5348 let size32 = variant_ty.abi.size32;
5349 let align32 = variant_ty.abi.align32;
5350 let payload_offset32 = variant_ty.info.payload_offset32;
5351
5352 let mut lower_metas_expr = String::from("[");
5353 for (name, maybe_ty) in variant_ty.cases.iter() {
5354 lower_metas_expr.push_str(&format!(
5355 "[ '{name}', {}, {size32}, {align32}, {payload_offset32} ],",
5356 maybe_ty
5357 .map(|ty| gen_flat_lower_fn_js_expr(instantiator, &ty, &None))
5358 .unwrap_or_else(|| "null".into()),
5359 ));
5360 }
5361 lower_metas_expr.push(']');
5362
5363 format!("{lower_fn}({lower_metas_expr})")
5364 }
5365
5366 InterfaceType::List(ty_idx) => {
5367 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatList));
5368 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatList).name();
5369 let list_ty = &component_types[*ty_idx];
5370 let elem_ty_lower_expr =
5371 gen_flat_lower_fn_js_expr(instantiator, &list_ty.element, extra_resource_map);
5372 let elem_cabi = component_types.canonical_abi(&list_ty.element);
5373 let elem_align32 = elem_cabi.align32;
5374 let elem_size32 = elem_cabi.size32;
5375
5376 format!(
5377 "{f}({{
5378 elemLowerFn: {elem_ty_lower_expr},
5379 elemSize32: {elem_size32},
5380 elemAlign32: {elem_align32},
5381 }})"
5382 )
5383 }
5384
5385 InterfaceType::FixedLengthList(ty_idx) => {
5386 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatList));
5387 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatList).name();
5388 let list_ty = &component_types[*ty_idx];
5389 let elem_ty_lower_expr =
5390 gen_flat_lower_fn_js_expr(instantiator, &list_ty.element, extra_resource_map);
5391 let list_len = list_ty.size;
5392 let list_align32 = list_ty.abi.size32;
5393 let list_size32 = list_ty.abi.size32;
5394 let elem_cabi = component_types.canonical_abi(&list_ty.element);
5395 let elem_align32 = elem_cabi.align32;
5396 let elem_size32 = elem_cabi.size32;
5397
5398 format!(
5399 r#"{f}({{
5400 elemLowerFn: {elem_ty_lower_expr},
5401 elemAlign32: {elem_align32},
5402 elemSize32: {elem_size32},
5403 align32: {list_align32},
5404 size32: {list_size32},
5405 knownLen: {list_len},
5406 }})"#
5407 )
5408 }
5409
5410 InterfaceType::Tuple(ty_idx) => {
5411 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatTuple));
5412 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatTuple).name();
5413 let tuple_ty = &component_types[*ty_idx];
5414 let size_u32 = tuple_ty.abi.size32;
5415 let align_u32 = tuple_ty.abi.align32;
5416
5417 let mut elem_lowers_expr = String::from("[");
5418 for ty in &tuple_ty.types {
5419 let lower_fn_js = gen_flat_lower_fn_js_expr(instantiator, ty, extra_resource_map);
5420 elem_lowers_expr.push_str(&format!("[{lower_fn_js}, {size_u32}, {align_u32}],"));
5421 }
5422 elem_lowers_expr.push(']');
5423
5424 format!("{f}({elem_lowers_expr})")
5425 }
5426
5427 InterfaceType::Flags(ty_idx) => {
5428 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatFlags));
5429 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatFlags).name();
5430 let flags_ty = &component_types[*ty_idx];
5431 let size32 = flags_ty.abi.size32;
5432 let align32 = flags_ty.abi.align32;
5433 let names_list_js = format!(
5434 "[{}]",
5435 flags_ty
5436 .names
5437 .iter()
5438 .map(|s| format!("'{s}'"))
5439 .collect::<Vec<_>>()
5440 .join(",")
5441 );
5442 let num_flags = flags_ty.names.len();
5443 let elem_size = if num_flags <= 8 {
5444 1
5445 } else if num_flags <= 16 {
5446 2
5447 } else {
5448 4
5449 };
5450
5451 format!(
5452 "{f}({{ names: {names_list_js}, size32: {size32}, align32: {align32}, intSizeBytes: {elem_size} }})"
5453 )
5454 }
5455
5456 InterfaceType::Enum(ty_idx) => {
5457 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatEnum));
5458 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatEnum).name();
5459 let enum_ty = &component_types[*ty_idx];
5460 let size32 = enum_ty.abi.size32;
5461 let align32 = enum_ty.abi.align32;
5462 let payload_offset32 = enum_ty.info.payload_offset32;
5463
5464 let mut elem_lowers_expr = String::from("[");
5465 for name in &enum_ty.names {
5466 elem_lowers_expr.push_str(&format!(
5467 "['{name}', null, {size32}, {align32}, {payload_offset32}],"
5468 ));
5469 }
5470 elem_lowers_expr.push(']');
5471
5472 format!("{f}({elem_lowers_expr})")
5473 }
5474
5475 InterfaceType::Option(ty_idx) => {
5476 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatOption));
5477 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatOption).name();
5478 let option_ty = &component_types[*ty_idx];
5479 let size32 = option_ty.abi.size32;
5480 let align32 = option_ty.abi.align32;
5481 let payload_offset32 = option_ty.info.payload_offset32;
5482 let lower_fn_js =
5483 gen_flat_lower_fn_js_expr(instantiator, &option_ty.ty, extra_resource_map);
5484
5485 format!(
5486 r#"{f}([
5487 [ 'none', null, {size32}, {align32}, {payload_offset32} ],
5488 [ 'some', {lower_fn_js}, {size32}, {align32}, {payload_offset32} ],
5489 ])
5490 "#
5491 )
5492 }
5493
5494 InterfaceType::Result(ty_idx) => {
5495 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatResult));
5496 let lower_fn = Intrinsic::Lower(LowerIntrinsic::LowerFlatResult).name();
5497 let result_ty = &component_types[*ty_idx];
5498 let size32 = result_ty.abi.size32;
5499 let align32 = result_ty.abi.align32;
5500 let payload_offset32 = result_ty.info.payload_offset32;
5501 let ok_lower_fn_js = result_ty
5502 .ok
5503 .map(|ty| gen_flat_lower_fn_js_expr(instantiator, &ty, extra_resource_map))
5504 .unwrap_or_else(|| "null".into());
5505 let err_lower_fn_js = result_ty
5506 .err
5507 .map(|ty| gen_flat_lower_fn_js_expr(instantiator, &ty, extra_resource_map))
5508 .unwrap_or_else(|| "null".into());
5509
5510 format!(
5511 r#"{lower_fn}([
5512 [ 'ok', {ok_lower_fn_js}, {size32}, {align32}, {payload_offset32} ],
5513 [ 'err', {err_lower_fn_js}, {size32}, {align32}, {payload_offset32} ],
5514 ])
5515 "#
5516 )
5517 }
5518
5519 InterfaceType::Own(ty_idx) => {
5520 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatOwn));
5521 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatOwn).name();
5522 let resource_table_ty = &component_types[*ty_idx];
5523 let component_idx = resource_table_ty.unwrap_concrete_instance().as_u32();
5524 let resource_idx = resource_table_ty.unwrap_concrete_ty();
5525
5526 let (_, ResourceTable { imported, data }) = match (
5530 instantiator.imports_resource_index_types.get(&resource_idx),
5531 instantiator.exports_resource_index_types.get(&resource_idx),
5532 ) {
5533 (Some(import_ty_id), _) => {
5534 let ty = crate::dealias(instantiator.resolve, *import_ty_id);
5535 let maybe_resource_table =
5536 instantiator.resource_imports.get(&ty).or(extra_resource_map
5537 .as_ref()
5538 .and_then(|m| m.get(import_ty_id)));
5539 (
5540 ty,
5541 maybe_resource_table.expect("missing imported resource table information"),
5542 )
5543 }
5544 (_, Some(export_ty_id)) => {
5545 let ty = crate::dealias(instantiator.resolve, *export_ty_id);
5546 let maybe_resource_table =
5547 instantiator.resource_exports.get(&ty).or(extra_resource_map
5548 .as_ref()
5549 .and_then(|m| m.get(export_ty_id)));
5550 (
5551 ty,
5552 maybe_resource_table.expect("missing exported resource table information"),
5553 )
5554 }
5555
5556 (None, None) => {
5558 return format!(
5559 "{f}({{
5560 componentIdx: {component_idx},
5561 lowerFn: () => {{ throw new Error('missing/invalid resource metadata'); }}
5562 }})"
5563 );
5564 }
5565 };
5566
5567 let lower_fn_js = match data {
5569 ResourceData::Host {
5571 tid,
5572 rid,
5573 local_name,
5574 ..
5575 } => {
5576 let tid = tid.as_u32();
5577 let rid = rid.as_u32();
5578 let symbol_resource_rep =
5579 instantiator.bindgen.intrinsic(Intrinsic::SymbolResourceRep);
5580 let symbol_resource_handle = instantiator
5581 .bindgen
5582 .intrinsic(Intrinsic::SymbolResourceHandle);
5583 let symbol_dispose = instantiator.bindgen.intrinsic(Intrinsic::SymbolDispose);
5584
5585 if *imported {
5586 let create_own_fn = instantiator.bindgen.intrinsic(Intrinsic::Resource(
5589 ResourceIntrinsic::ResourceTableCreateOwn,
5590 ));
5591 format!(
5592 r#"
5593 function lowerImportedOwnedHost_{local_name}(obj) {{
5594 if (!(obj instanceof {local_name})) {{
5595 throw new TypeError('Resource error: Not a valid \"{local_name}\" resource.');
5596 }}
5597 let handle = obj[{symbol_resource_handle}];
5598 if (!handle) {{
5599 const rep = obj[{symbol_resource_rep}] || ++captureCnt{rid};
5600 captureTable{rid}.set(rep, obj);
5601 handle = {create_own_fn}(handleTable{tid}, rep);
5602 }}
5603 return handle;
5604 }}
5605 "#
5606 )
5607 } else {
5608 let empty_func = instantiator
5614 .bindgen
5615 .intrinsic(Intrinsic::JsHelper(JsHelperIntrinsic::EmptyFunc));
5616 format!(
5617 r#"
5618 function lowerExportedOwnedHost_{local_name}(obj) {{
5619 let handle = obj[{symbol_resource_handle}];
5620 if (!handle) {{
5621 throw new TypeError('Resource error: Not a valid \"{local_name}\" resource.');
5622 }}
5623 finalizationRegistry{tid}.unregister(obj);
5624 obj[{symbol_dispose}] = {empty_func};
5625 obj[{symbol_resource_handle}] = undefined;
5626 return handle;
5627 }}
5628 "#
5629 )
5630 }
5631 }
5632
5633 ResourceData::Guest {
5635 resource_name,
5636 prefix,
5637 extra,
5638 } => {
5639 assert!(
5640 extra.is_none(),
5641 "plain resource handles do not carry extra data"
5642 );
5643
5644 let upper_camel = resource_name.to_upper_camel_case();
5645 let lower_camel = resource_name.to_lower_camel_case();
5646 let prefix = prefix.as_deref().unwrap_or("");
5647
5648 if *imported {
5649 let symbol_resource_handle = instantiator
5652 .bindgen
5653 .intrinsic(Intrinsic::SymbolResourceHandle);
5654 format!(
5655 r#"
5656 function lowerImportedOwnedGuest_{upper_camel}(obj) {{
5657 const handle = obj[{symbol_resource_handle}];
5658 finalizationRegistry_import${prefix}{lower_camel}.unregister(obj);
5659 return handle;
5660 }}
5661 "#
5662 )
5663 } else {
5664 let symbol_resource_handle = instantiator
5668 .bindgen
5669 .intrinsic(Intrinsic::SymbolResourceHandle);
5670 format!(
5671 r#"
5672 function lowerExportedOwnedGuest_{upper_camel}(obj) {{
5673 if (!(obj instanceof {upper_camel})) {{
5674 throw new TypeError('Resource error: Not a valid \"{upper_camel}\" resource.');
5675 }}
5676 let handle = obj[{symbol_resource_handle}];
5677 if (handle === undefined) {{
5678 const localRep = repCnt++;
5679 repTable.set(localRep, {{ rep: obj, own: true }});
5680 handle = $resource_{prefix}new${lower_camel}(localRep);
5681 obj[{symbol_resource_handle}] = handle;
5682 finalizationRegistry_export${prefix}{lower_camel}.register(obj, handle, obj);
5683 }}
5684 return handle;
5685 }}
5686 "#
5687 )
5688 }
5689 }
5690 };
5691
5692 format!(
5693 "{f}({{
5694 componentIdx: {component_idx},
5695 lowerFn: {lower_fn_js},
5696 }})"
5697 )
5698 }
5699
5700 InterfaceType::Borrow(ty_idx) => {
5701 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatBorrow));
5702 let table_idx = ty_idx.as_u32();
5703 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatBorrow).name();
5704 format!("{f}.bind(null, {table_idx})")
5705 }
5706
5707 InterfaceType::Future(ty_idx) => {
5708 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatFuture));
5709 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatFuture).name();
5710 let table_idx = ty_idx.as_u32();
5711 let table_ty = &component_types[*ty_idx];
5712 let component_idx = table_ty.instance.as_u32();
5713 let future_ty_idx = table_ty.ty;
5714 let future_ty = &component_types[future_ty_idx];
5715 let payload = future_ty.payload;
5716 let payload_ty_name_js = future_ty
5717 .payload
5718 .map(|iface_ty| format!("'{iface_ty:?}'"))
5719 .unwrap_or_else(|| "null".into());
5720
5721 let (
5723 payload_size32,
5724 payload_align32,
5725 payload_flat_count_js,
5726 payload_lift_fn_js,
5727 payload_lower_fn_js,
5728 is_borrowed,
5729 is_none_type,
5730 is_numeric_type,
5731 is_async_value,
5732 ) = match payload {
5733 None => (
5734 0,
5735 0,
5736 "0".into(),
5737 "() => {{ throw new Error('empty future payload'); }}".into(),
5738 "() => {{ throw new Error('empty future payload'); }}".into(),
5739 false,
5740 true,
5741 false,
5742 false,
5743 ),
5744 Some(payload_ty) => {
5745 let cabi = instantiator.types.canonical_abi(&payload_ty);
5746 (
5747 cabi.size32,
5748 cabi.align32,
5749 cabi.flat_count
5750 .map(|v| format!("{v}"))
5751 .unwrap_or_else(|| "null".into()),
5752 gen_flat_lift_fn_js_expr(instantiator, &payload_ty, extra_resource_map),
5753 gen_flat_lower_fn_js_expr(instantiator, &payload_ty, extra_resource_map),
5754 matches!(payload_ty, InterfaceType::Borrow(_)),
5755 false,
5756 matches!(
5757 payload_ty,
5758 InterfaceType::U8
5759 | InterfaceType::U16
5760 | InterfaceType::U32
5761 | InterfaceType::U64
5762 | InterfaceType::S8
5763 | InterfaceType::S16
5764 | InterfaceType::S32
5765 | InterfaceType::S64
5766 | InterfaceType::Float32
5767 | InterfaceType::Float64
5768 ),
5769 matches!(
5770 payload_ty,
5771 InterfaceType::Stream(_) | InterfaceType::Future(_)
5772 ),
5773 )
5774 }
5775 };
5776
5777 format!(
5778 r#"{f}.bind(null, {{
5779 futureTableIdx: {table_idx},
5780 componentIdx: {component_idx},
5781 elemMeta: {{
5782 liftFn: {payload_lift_fn_js},
5783 lowerFn: {payload_lower_fn_js},
5784 payloadTypeName: {payload_ty_name_js},
5785 isNone: {is_none_type},
5786 isNumeric: {is_numeric_type},
5787 isBorrowed: {is_borrowed},
5788 isAsyncValue: {is_async_value},
5789 flatCount: {payload_flat_count_js},
5790 align32: {payload_align32},
5791 size32: {payload_size32},
5792 }},
5793 }})
5794 "#
5795 )
5796 }
5797
5798 InterfaceType::Stream(ty_idx) => {
5799 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatStream));
5800 let table_idx = ty_idx.as_u32();
5801 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatStream).name();
5802 let table_ty = &component_types[*ty_idx];
5803 let component_idx = table_ty.instance.as_u32();
5804 let stream_ty_idx = table_ty.ty;
5805 let stream_ty = &component_types[stream_ty_idx];
5806 let payload = stream_ty.payload;
5807 let payload_ty_name_js = stream_ty
5808 .payload
5809 .map(|iface_ty| format!("'{iface_ty:?}'"))
5810 .unwrap_or_else(|| "null".into());
5811
5812 let (
5815 payload_size32,
5816 payload_align32,
5817 payload_flat_count_js,
5818 payload_lift_fn_js,
5819 payload_lower_fn_js,
5820 is_borrowed,
5821 is_none_type,
5822 is_numeric_type,
5823 is_async_value,
5824 ) = match payload {
5825 None => (
5826 0,
5827 0,
5828 "0".into(),
5829 "() => {{ throw new Error('empty stream payload'); }}".into(),
5830 "() => {{ throw new Error('empty stream payload'); }}".into(),
5831 false,
5832 true,
5833 false,
5834 false,
5835 ),
5836 Some(payload_ty) => {
5837 let cabi = instantiator.types.canonical_abi(&payload_ty);
5838 (
5839 cabi.size32,
5840 cabi.align32,
5841 cabi.flat_count
5842 .map(|v| format!("{v}"))
5843 .unwrap_or_else(|| "null".into()),
5844 gen_flat_lift_fn_js_expr(instantiator, &payload_ty, extra_resource_map),
5845 gen_flat_lower_fn_js_expr(instantiator, &payload_ty, extra_resource_map),
5846 matches!(payload_ty, InterfaceType::Borrow(_)),
5847 false,
5848 matches!(
5849 payload_ty,
5850 InterfaceType::U8
5851 | InterfaceType::U16
5852 | InterfaceType::U32
5853 | InterfaceType::U64
5854 | InterfaceType::S8
5855 | InterfaceType::S16
5856 | InterfaceType::S32
5857 | InterfaceType::S64
5858 | InterfaceType::Float32
5859 | InterfaceType::Float64
5860 ),
5861 matches!(
5862 payload_ty,
5863 InterfaceType::Stream(_) | InterfaceType::Future(_)
5864 ),
5865 )
5866 }
5867 };
5868
5869 format!(
5870 r#"{f}({{
5871 streamTableIdx: {table_idx},
5872 componentIdx: {component_idx},
5873 elemMeta: {{
5874 liftFn: {payload_lift_fn_js},
5875 lowerFn: {payload_lower_fn_js},
5876 payloadTypeName: {payload_ty_name_js},
5877 isNone: {is_none_type},
5878 isNumeric: {is_numeric_type},
5879 isBorrowed: {is_borrowed},
5880 isAsyncValue: {is_async_value},
5881 flatCount: {payload_flat_count_js},
5882 align32: {payload_align32},
5883 size32: {payload_size32},
5884 }},
5885 }})
5886 "#
5887 )
5888 }
5889
5890 InterfaceType::ErrorContext(ty_idx) => {
5891 instantiator.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatErrorContext));
5892 let table_idx = ty_idx.as_u32();
5893 let lower_flat_err_ctx_fn =
5894 Intrinsic::Lower(LowerIntrinsic::LowerFlatErrorContext).name();
5895 format!("{lower_flat_err_ctx_fn}.bind(null, {table_idx})")
5896 }
5897 }
5898}
5899
5900#[cfg(test)]
5901mod tests {
5902 use super::*;
5903
5904 fn compat_key(version_str: &str) -> Option<String> {
5906 semver_compat_key(version_str).map(|(key, _)| key)
5907 }
5908
5909 #[test]
5910 fn test_semver_compat_key() {
5911 assert_eq!(compat_key("1.0.0"), Some("1".into()));
5912 assert_eq!(compat_key("1.2.3"), Some("1".into()));
5913 assert_eq!(compat_key("2.0.0"), Some("2".into()));
5914 assert_eq!(compat_key("0.2.0"), Some("0.2".into()));
5915 assert_eq!(compat_key("0.2.10"), Some("0.2".into()));
5916 assert_eq!(compat_key("0.1.0"), Some("0.1".into()));
5917 assert_eq!(compat_key("0.0.1"), None);
5918 assert_eq!(compat_key("1.0.0-rc.1"), None);
5919 assert_eq!(compat_key("0.2.0-pre"), None);
5920 assert_eq!(compat_key("not-a-version"), None);
5921 }
5922
5923 #[test]
5924 fn test_semver_compat_key_returns_parsed_version() {
5925 let (key, ver) = semver_compat_key("1.2.3").unwrap();
5926 assert_eq!(key, "1");
5927 assert_eq!(ver, Version::new(1, 2, 3));
5928 }
5929
5930 #[test]
5931 fn test_map_import_exact_match() {
5932 let mut map = HashMap::new();
5933 map.insert("wasi:http/types@0.2.0".into(), "./http.js#types".into());
5934 let map = Some(map);
5935 assert_eq!(
5936 map_import(&map, "wasi:http/types@0.2.0"),
5937 ("./http.js".into(), Some("types".into()))
5938 );
5939 }
5940
5941 #[test]
5942 fn test_map_import_sans_version_match() {
5943 let mut map = HashMap::new();
5944 map.insert("wasi:http/types".into(), "./http.js".into());
5945 let map = Some(map);
5946 assert_eq!(
5947 map_import(&map, "wasi:http/types@0.2.10"),
5948 ("./http.js".into(), None)
5949 );
5950 }
5951
5952 #[test]
5953 fn test_map_import_wildcard_sans_version() {
5954 let mut map = HashMap::new();
5956 map.insert("wasi:http/*".into(), "./http.js#*".into());
5957 let map = Some(map);
5958 assert_eq!(
5959 map_import(&map, "wasi:http/types@0.2.10"),
5960 ("./http.js".into(), Some("types".into()))
5961 );
5962 }
5963
5964 #[test]
5965 fn test_map_import_semver_exact_key() {
5966 let mut map = HashMap::new();
5968 map.insert("wasi:http/types@0.2.0".into(), "./http.js".into());
5969 let map = Some(map);
5970 assert_eq!(
5971 map_import(&map, "wasi:http/types@0.2.10"),
5972 ("./http.js".into(), None)
5973 );
5974 }
5975
5976 #[test]
5977 fn test_map_import_semver_wildcard_key() {
5978 let mut map = HashMap::new();
5980 map.insert("wasi:http/*@0.2.1".into(), "./http.js#*".into());
5981 let map = Some(map);
5982 assert_eq!(
5983 map_import(&map, "wasi:http/types@0.2.10"),
5984 ("./http.js".into(), Some("types".into()))
5985 );
5986 }
5987
5988 #[test]
5989 fn test_map_import_semver_lower_import_version() {
5990 let mut map = HashMap::new();
5992 map.insert("wasi:http/types@0.2.10".into(), "./http.js".into());
5993 let map = Some(map);
5994 assert_eq!(
5995 map_import(&map, "wasi:http/types@0.2.1"),
5996 ("./http.js".into(), None)
5997 );
5998 }
5999
6000 #[test]
6001 fn test_map_import_semver_no_cross_minor() {
6002 let mut map = HashMap::new();
6004 map.insert("wasi:http/types@0.3.0".into(), "./http.js".into());
6005 let map = Some(map);
6006 assert_eq!(
6007 map_import(&map, "wasi:http/types@0.2.10"),
6008 ("wasi:http/types".into(), None)
6009 );
6010 }
6011
6012 #[test]
6013 fn test_map_import_semver_prefers_highest() {
6014 let mut map = HashMap::new();
6016 map.insert("wasi:http/types@0.2.1".into(), "./http-old.js".into());
6017 map.insert("wasi:http/types@0.2.5".into(), "./http-new.js".into());
6018 let map = Some(map);
6019 assert_eq!(
6020 map_import(&map, "wasi:http/types@0.2.10"),
6021 ("./http-new.js".into(), None)
6022 );
6023 }
6024
6025 #[test]
6026 fn test_map_import_no_match_prerelease() {
6027 let mut map = HashMap::new();
6028 map.insert("wasi:http/types@0.2.0-rc.1".into(), "./http.js".into());
6029 let map = Some(map);
6030 assert_eq!(
6031 map_import(&map, "wasi:http/types@0.2.0"),
6032 ("wasi:http/types".into(), None)
6033 );
6034 }
6035
6036 #[test]
6037 fn test_map_import_no_match_zero_zero() {
6038 let mut map = HashMap::new();
6039 map.insert("wasi:http/types@0.0.1".into(), "./http.js".into());
6040 let map = Some(map);
6041 assert_eq!(
6042 map_import(&map, "wasi:http/types@0.0.2"),
6043 ("wasi:http/types".into(), None)
6044 );
6045 }
6046
6047 #[test]
6048 fn test_map_import_semver_major_version() {
6049 let mut map = HashMap::new();
6051 map.insert("wasi:http/types@1.0.0".into(), "./http.js".into());
6052 let map = Some(map);
6053 assert_eq!(
6054 map_import(&map, "wasi:http/types@1.2.3"),
6055 ("./http.js".into(), None)
6056 );
6057 }
6058
6059 #[test]
6060 fn test_map_import_semver_no_cross_major() {
6061 let mut map = HashMap::new();
6063 map.insert("wasi:http/types@1.0.0".into(), "./http.js".into());
6064 let map = Some(map);
6065 assert_eq!(
6066 map_import(&map, "wasi:http/types@2.0.0"),
6067 ("wasi:http/types".into(), None)
6068 );
6069 }
6070
6071 #[test]
6072 fn test_map_import_no_map() {
6073 assert_eq!(
6075 map_import(&None, "wasi:http/types@0.2.0"),
6076 ("wasi:http/types".into(), None)
6077 );
6078 }
6079
6080 #[test]
6081 fn test_map_import_no_map_unversioned() {
6082 assert_eq!(
6084 map_import(&None, "wasi:http/types"),
6085 ("wasi:http/types".into(), None)
6086 );
6087 }
6088
6089 #[test]
6090 fn test_parse_mapping_with_hash() {
6091 assert_eq!(
6092 parse_mapping("./http.js#types"),
6093 ("./http.js".into(), Some("types".into()))
6094 );
6095 }
6096
6097 #[test]
6098 fn test_parse_mapping_without_hash() {
6099 assert_eq!(parse_mapping("./http.js"), ("./http.js".into(), None));
6100 }
6101
6102 #[test]
6103 fn test_parse_mapping_leading_hash() {
6104 assert_eq!(parse_mapping("#foo"), ("#foo".into(), None));
6106 }
6107
6108 #[test]
6109 fn test_parse_mapping_empty() {
6110 assert_eq!(parse_mapping(""), ("".into(), None));
6111 }
6112}