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