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