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