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