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