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