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, TypeResourceTableIndex, TypeStreamTableIndex,
17};
18use wasmtime_environ::component::{
19 ExportIndex, ExtractCallback, NameMap, NameMapNoIntern, Transcode,
20 TypeComponentLocalErrorContextTableIndex,
21};
22use wasmtime_environ::{EntityIndex, PrimaryMap};
23use wit_bindgen_core::abi::{self, LiftLower};
24use wit_component::StringEncoding;
25use wit_parser::abi::AbiVariant;
26use wit_parser::{
27 Function, FunctionKind, Handle, Resolve, Result_, SizeAlign, Type, TypeDefKind, TypeId,
28 WorldId, WorldItem, WorldKey,
29};
30
31use crate::esm_bindgen::EsmBindgen;
32use crate::files::Files;
33use crate::function_bindgen::{
34 ErrHandling, FunctionBindgen, ResourceData, ResourceExtraData, ResourceMap, ResourceTable,
35};
36use crate::intrinsics::component::ComponentIntrinsic;
37use crate::intrinsics::js_helper::JsHelperIntrinsic;
38use crate::intrinsics::lift::LiftIntrinsic;
39use crate::intrinsics::lower::LowerIntrinsic;
40use crate::intrinsics::p3::async_future::AsyncFutureIntrinsic;
41use crate::intrinsics::p3::async_stream::AsyncStreamIntrinsic;
42use crate::intrinsics::p3::async_task::AsyncTaskIntrinsic;
43use crate::intrinsics::p3::error_context::ErrCtxIntrinsic;
44use crate::intrinsics::p3::host::HostIntrinsic;
45use crate::intrinsics::p3::waitable::WaitableIntrinsic;
46use crate::intrinsics::resource::ResourceIntrinsic;
47use crate::intrinsics::string::StringIntrinsic;
48use crate::intrinsics::webidl::WebIdlIntrinsic;
49use crate::intrinsics::{
50 AsyncDeterminismProfile, Intrinsic, RenderIntrinsicsArgs, render_intrinsics,
51};
52use crate::names::{LocalNames, is_js_reserved_word, maybe_quote_id, maybe_quote_member};
53use crate::{
54 FunctionIdentifier, ManagesIntrinsics, core, get_thrown_type, is_async_fn,
55 requires_async_porcelain, source, uwrite, uwriteln,
56};
57
58const MAX_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 err_ctx_tables = BTreeMap::new();
248 for idx in 0..component.component.num_error_context_tables {
249 let err_ctx_table_idx = TypeComponentLocalErrorContextTableIndex::from_u32(idx as u32);
250 let err_ctx_table_ty = &types[err_ctx_table_idx];
251 err_ctx_tables.insert(err_ctx_table_idx, err_ctx_table_ty.instance);
252 }
253
254 let mut instantiator = Instantiator {
257 src: Source::default(),
258 sizes: SizeAlign::default(),
259 bindgen: &mut bindgen,
260 modules,
261 instances: Default::default(),
262 error_context_component_initialized: (0..component
263 .component
264 .num_runtime_component_instances)
265 .map(|_| false)
266 .collect(),
267 error_context_component_table_initialized: (0..component
268 .component
269 .num_error_context_tables)
270 .map(|_| false)
271 .collect(),
272 resolve,
273 world: id,
274 translation: component,
275 component: &component.component,
276 types,
277 async_imports,
278 async_exports,
279 imports: Default::default(),
280 exports: Default::default(),
281 lowering_options: Default::default(),
282 used_instance_flags: Default::default(),
283 defined_resource_classes: Default::default(),
284 imports_resource_types: Default::default(),
285 imports_resource_index_types: Default::default(),
286 exports_resource_types: Default::default(),
287 exports_resource_index_types: Default::default(),
288 resource_exports: Default::default(),
289 resource_imports: Default::default(),
290 resources_initialized: BTreeMap::new(),
291 resource_tables_initialized: BTreeMap::new(),
292 stream_tables,
293 err_ctx_tables,
294 };
295 instantiator.sizes.fill(resolve);
296 instantiator.initialize();
297 instantiator.instantiate();
298
299 let mut intrinsic_definitions = source::Source::default();
300
301 instantiator.resource_definitions(&mut intrinsic_definitions);
302 instantiator.instance_flags();
303
304 instantiator.bindgen.src.js(&instantiator.src.js);
305 instantiator.bindgen.src.js_init(&instantiator.src.js_init);
306
307 instantiator
308 .bindgen
309 .finish_component(name, files, &opts, intrinsic_definitions);
310
311 let exports = instantiator
312 .bindgen
313 .esm_bindgen
314 .exports()
315 .iter()
316 .map(|(export_name, canon_export_name)| {
317 let expected_export_name =
318 if canon_export_name.contains(':') || canon_export_name.starts_with("[async]") {
319 canon_export_name.to_string()
320 } else {
321 canon_export_name.to_kebab_case()
322 };
323 let export = instantiator
324 .component
325 .exports
326 .get(&expected_export_name, &NameMapNoIntern)
327 .unwrap_or_else(|| panic!("failed to find component export [{expected_export_name}] (original '{canon_export_name}')"));
328 (
329 export_name.to_string(),
330 instantiator.component.export_items[*export].clone(),
331 )
332 })
333 .collect();
334
335 (bindgen.esm_bindgen.import_specifiers(), exports)
336}
337
338impl JsBindgen<'_> {
339 fn finish_component(
340 &mut self,
341 name: &str,
342 files: &mut Files,
343 opts: &TranspileOpts,
344 intrinsic_definitions: source::Source,
345 ) {
346 let mut output = source::Source::default();
347 let mut compilation_promises = source::Source::default();
348 let mut core_exported_funcs = source::Source::default();
349
350 for (core_export_fn, is_async) in self.all_core_exported_funcs.iter() {
351 let local_name = self.local_names.get(core_export_fn);
352 if *is_async {
353 uwriteln!(
354 core_exported_funcs,
355 "{local_name} = WebAssembly.promising({core_export_fn});",
356 );
357 } else {
358 uwriteln!(core_exported_funcs, "{local_name} = {core_export_fn};",);
361 }
362 }
363
364 if matches!(self.opts.instantiation, Some(InstantiationMode::Async)) {
366 uwriteln!(
367 compilation_promises,
368 "if (!getCoreModule) getCoreModule = (name) => {}(new URL(`./${{name}}`, import.meta.url));",
369 self.intrinsic(Intrinsic::FetchCompile)
370 );
371 }
372
373 let mut removed = BTreeSet::new();
375 for i in 0..self.core_module_cnt {
376 let local_name = format!("module{i}");
377 let mut name_idx = core_file_name(name, i as u32);
378 if self.opts.instantiation.is_some() {
379 uwriteln!(
380 compilation_promises,
381 "const {local_name} = getCoreModule('{name_idx}');"
382 );
383 } else if files.get_size(&name_idx).unwrap() < self.opts.base64_cutoff {
384 assert!(removed.insert(i));
385 let data = files.remove(&name_idx).unwrap();
386 uwriteln!(
387 compilation_promises,
388 "const {local_name} = {}('{}');",
389 self.intrinsic(Intrinsic::Base64Compile),
390 general_purpose::STANDARD_NO_PAD.encode(&data),
391 );
392 } else {
393 if let Some(&replacement) = removed.iter().next() {
396 assert!(removed.remove(&replacement) && removed.insert(i));
397 let data = files.remove(&name_idx).unwrap();
398 name_idx = core_file_name(name, replacement as u32);
399 files.push(&name_idx, &data);
400 }
401 uwriteln!(
402 compilation_promises,
403 "const {local_name} = {}(new URL('./{name_idx}', import.meta.url));",
404 self.intrinsic(Intrinsic::FetchCompile)
405 );
406 }
407 }
408
409 uwriteln!(output, r#""use jco";"#);
411
412 let js_intrinsics = render_intrinsics(RenderIntrinsicsArgs {
413 intrinsics: &mut self.all_intrinsics,
414 no_nodejs_compat: self.opts.no_nodejs_compat,
415 instantiation: self.opts.instantiation.is_some(),
416 determinism: AsyncDeterminismProfile::default(),
417 });
418
419 if let Some(instantiation) = &self.opts.instantiation {
420 uwrite!(
421 output,
422 "\
423 export function instantiate(getCoreModule, imports, instantiateCore = {}) {{
424 {}
425 {}
426 {}
427 ",
428 match instantiation {
429 InstantiationMode::Async => "WebAssembly.instantiate",
430 InstantiationMode::Sync =>
431 "(module, importObject) => new WebAssembly.Instance(module, importObject)",
432 },
433 &js_intrinsics as &str,
434 &intrinsic_definitions as &str,
435 &compilation_promises as &str,
436 );
437 }
438
439 let imports_object = if self.opts.instantiation.is_some() {
441 Some("imports")
442 } else {
443 None
444 };
445 self.esm_bindgen
446 .render_imports(&mut output, imports_object, &mut self.local_names);
447
448 if self.opts.instantiation.is_some() {
450 uwrite!(&mut self.src.js, "{}", &core_exported_funcs as &str);
451 self.esm_bindgen.render_exports(
452 &mut self.src.js,
453 self.opts.instantiation.is_some(),
454 &mut self.local_names,
455 opts,
456 );
457 uwrite!(
458 output,
459 "\
460 let gen = (function* _initGenerator () {{
461 {}\
462 {};
463 }})();
464 let promise, resolve, reject;
465 function runNext (value) {{
466 try {{
467 let done;
468 do {{
469 ({{ value, done }} = gen.next(value));
470 }} while (!(value instanceof Promise) && !done);
471 if (done) {{
472 if (resolve) return resolve(value);
473 else return value;
474 }}
475 if (!promise) promise = new Promise((_resolve, _reject) => (resolve = _resolve, reject = _reject));
476 value.then(nextVal => done ? resolve() : runNext(nextVal), reject);
477 }}
478 catch (e) {{
479 if (reject) reject(e);
480 else throw e;
481 }}
482 }}
483 const maybeSyncReturn = runNext(null);
484 return promise || maybeSyncReturn;
485 }};
486 ",
487 &self.src.js_init as &str,
488 &self.src.js as &str,
489 );
490 } else {
491 let (maybe_init_export, maybe_init) =
492 if self.opts.tla_compat && opts.instantiation.is_none() {
493 uwriteln!(self.src.js_init, "_initialized = true;");
494 (
495 "\
496 let _initialized = false;
497 export ",
498 "",
499 )
500 } else {
501 (
502 "",
503 "
504 await $init;
505 ",
506 )
507 };
508
509 uwrite!(
510 output,
511 "\
512 {}
513 {}
514 {}
515 {maybe_init_export}const $init = (() => {{
516 let gen = (function* _initGenerator () {{
517 {}\
518 {}\
519 {}\
520 }})();
521 let promise, resolve, reject;
522 function runNext (value) {{
523 try {{
524 let done;
525 do {{
526 ({{ value, done }} = gen.next(value));
527 }} while (!(value instanceof Promise) && !done);
528 if (done) {{
529 if (resolve) resolve(value);
530 else return value;
531 }}
532 if (!promise) promise = new Promise((_resolve, _reject) => (resolve = _resolve, reject = _reject));
533 value.then(runNext, reject);
534 }}
535 catch (e) {{
536 if (reject) reject(e);
537 else throw e;
538 }}
539 }}
540 const maybeSyncReturn = runNext(null);
541 return promise || maybeSyncReturn;
542 }})();
543 {maybe_init}\
544 ",
545 &js_intrinsics as &str,
546 &intrinsic_definitions as &str,
547 &self.src.js as &str,
548 &compilation_promises as &str,
549 &self.src.js_init as &str,
550 &core_exported_funcs as &str,
551 );
552
553 self.esm_bindgen.render_exports(
554 &mut output,
555 self.opts.instantiation.is_some(),
556 &mut self.local_names,
557 opts,
558 );
559 }
560
561 let mut bytes = output.as_bytes();
562 if bytes[0] == b'\n' {
564 bytes = &bytes[1..];
565 }
566 files.push(&format!("{name}.js"), bytes);
567 }
568
569 fn intrinsic(&mut self, intrinsic: Intrinsic) -> String {
570 self.all_intrinsics.insert(intrinsic);
571 intrinsic.name().to_string()
572 }
573}
574
575pub(crate) struct Instantiator<'a, 'b> {
579 src: Source,
580 bindgen: &'a mut JsBindgen<'b>,
581 modules: &'a PrimaryMap<StaticModuleIndex, core::Translation<'a>>,
582 instances: PrimaryMap<RuntimeInstanceIndex, StaticModuleIndex>,
583 types: &'a ComponentTypes,
584 resolve: &'a Resolve,
585 world: WorldId,
586 sizes: SizeAlign,
587 component: &'a Component,
588
589 error_context_component_initialized: PrimaryMap<RuntimeComponentInstanceIndex, bool>,
592 error_context_component_table_initialized:
593 PrimaryMap<TypeComponentLocalErrorContextTableIndex, bool>,
594
595 translation: &'a ComponentTranslation,
597
598 exports_resource_types: BTreeMap<TypeId, ResourceIndex>,
600 exports_resource_index_types: BTreeMap<ResourceIndex, TypeId>,
602
603 imports_resource_types: BTreeMap<TypeId, ResourceIndex>,
605 #[allow(unused)]
607 imports_resource_index_types: BTreeMap<ResourceIndex, TypeId>,
608
609 resources_initialized: BTreeMap<ResourceIndex, bool>,
610 resource_tables_initialized: BTreeMap<TypeResourceTableIndex, bool>,
611
612 exports: BTreeMap<String, WorldKey>,
613 imports: BTreeMap<String, WorldKey>,
614 used_instance_flags: RefCell<BTreeSet<RuntimeComponentInstanceIndex>>,
616 defined_resource_classes: BTreeSet<String>,
617 async_imports: HashSet<String>,
618 async_exports: HashSet<String>,
619 lowering_options:
620 PrimaryMap<LoweredIndex, (&'a CanonicalOptions, TrampolineIndex, TypeFuncIndex)>,
621
622 stream_tables: BTreeMap<TypeStreamTableIndex, RuntimeComponentInstanceIndex>,
624
625 err_ctx_tables:
627 BTreeMap<TypeComponentLocalErrorContextTableIndex, RuntimeComponentInstanceIndex>,
628
629 resource_exports: ResourceMap,
631 resource_imports: ResourceMap,
633}
634
635impl<'a> ManagesIntrinsics for Instantiator<'a, '_> {
636 fn add_intrinsic(&mut self, intrinsic: Intrinsic) {
637 self.bindgen.intrinsic(intrinsic);
638 }
639}
640
641impl<'a> Instantiator<'a, '_> {
642 fn initialize(&mut self) {
643 for (key, _) in &self.resolve.worlds[self.world].imports {
645 let name = &self.resolve.name_world_key(key);
646 self.imports.insert(name.to_string(), key.clone());
647 }
648 for (key, _) in &self.resolve.worlds[self.world].exports {
649 let name = &self.resolve.name_world_key(key);
650 self.exports.insert(name.to_string(), key.clone());
651 }
652
653 for (key, item) in &self.resolve.worlds[self.world].imports {
656 let name = &self.resolve.name_world_key(key);
657 let Some((_, (_, import))) = self
658 .component
659 .import_types
660 .iter()
661 .find(|(_, (impt_name, _))| impt_name == name)
662 else {
663 match item {
664 WorldItem::Interface { .. } => {
665 unreachable!("unexpected interface in import types during initialization")
666 }
667 WorldItem::Function(_) => {
668 unreachable!("unexpected function in import types during initialization")
669 }
670 WorldItem::Type { id, .. } => {
671 assert!(!matches!(
672 self.resolve.types[*id].kind,
673 TypeDefKind::Resource
674 ))
675 }
676 }
677 continue;
678 };
679 match item {
680 WorldItem::Interface { id, .. } => {
681 let TypeDef::ComponentInstance(instance) = import else {
682 unreachable!("unexpectedly non-component instance import in interface")
683 };
684 let import_ty = &self.types[*instance];
685 let iface = &self.resolve.interfaces[*id];
686 for (ty_name, ty) in &iface.types {
687 match &import_ty.exports.get(ty_name) {
688 Some(TypeDef::Resource(resource_table_idx)) => {
689 let ty = crate::dealias(self.resolve, *ty);
690 let resource_table_ty = &self.types[*resource_table_idx];
691 self.imports_resource_types
692 .insert(ty, resource_table_ty.unwrap_concrete_ty());
693 }
694 Some(TypeDef::Interface(_)) | None => {}
695 Some(_) => unreachable!("unexpected type in interface"),
696 }
697 }
698 }
699 WorldItem::Function(_) => {}
700 WorldItem::Type { id, .. } => match import {
701 TypeDef::Resource(resource) => {
702 let ty = crate::dealias(self.resolve, *id);
703 let resource_table_ty = &self.types[*resource];
704 self.imports_resource_types
705 .insert(ty, resource_table_ty.unwrap_concrete_ty());
706 }
707 TypeDef::Interface(_) => {}
708 _ => unreachable!("unexpected type in import world item"),
709 },
710 }
711 }
712 self.exports_resource_types = self.imports_resource_types.clone();
713
714 for (key, item) in &self.resolve.worlds[self.world].exports {
715 let name = &self.resolve.name_world_key(key);
716 let (_, export_idx) = self
717 .component
718 .exports
719 .raw_iter()
720 .find(|(expt_name, _)| *expt_name == name)
721 .unwrap();
722 let export = &self.component.export_items[*export_idx];
723 match item {
724 WorldItem::Interface { id, .. } => {
725 let iface = &self.resolve.interfaces[*id];
726 let Export::Instance { exports, .. } = &export else {
727 unreachable!("unexpectedly non export instance item")
728 };
729 for (ty_name, ty) in &iface.types {
730 match self.component.export_items
731 [*exports.get(ty_name, &NameMapNoIntern).unwrap()]
732 {
733 Export::Type(TypeDef::Resource(resource)) => {
734 let ty = crate::dealias(self.resolve, *ty);
735 let resource_table_ty = &self.types[resource];
736 let concrete_ty = resource_table_ty.unwrap_concrete_ty();
737 self.exports_resource_types.insert(ty, concrete_ty);
738 self.exports_resource_index_types.insert(concrete_ty, ty);
739 }
740 Export::Type(_) => {}
741 _ => unreachable!(
742 "unexpected type in component export items on iface [{iface_name}]",
743 iface_name = iface.name.as_deref().unwrap_or("<unknown>"),
744 ),
745 }
746 }
747 }
748 WorldItem::Function(_) => {}
749 WorldItem::Type { .. } => unreachable!("unexpected exported world item type"),
750 }
751 }
752 }
753
754 fn instantiate(&mut self) {
755 for (i, trampoline) in self.translation.trampolines.iter() {
757 let Trampoline::LowerImport {
758 index,
759 lower_ty,
760 options,
761 } = trampoline
762 else {
763 continue;
764 };
765
766 let options = self
767 .component
768 .options
769 .get(*options)
770 .expect("failed to find canon options");
771
772 let i = self.lowering_options.push((options, i, *lower_ty));
773 assert_eq!(i, *index);
774 }
775
776 if let Some(InstantiationMode::Async) = self.bindgen.opts.instantiation {
777 if self.modules.len() > 1 {
780 self.src.js_init.push_str("Promise.all([");
781 for i in 0..self.modules.len() {
782 if i > 0 {
783 self.src.js_init.push_str(", ");
784 }
785 self.src.js_init.push_str(&format!("module{i}"));
786 }
787 uwriteln!(self.src.js_init, "]).catch(() => {{}});");
788 }
789 }
790
791 let global_stream_table_map =
793 Intrinsic::AsyncStream(AsyncStreamIntrinsic::GlobalStreamTableMap).name();
794 let rep_table_class = Intrinsic::RepTableClass.name();
795 for (table_idx, component_idx) in self.stream_tables.iter() {
796 self.src.js.push_str(&format!(
797 "{global_stream_table_map}[{}] = {{ componentIdx: {}, table: new {rep_table_class}() }};\n",
798 table_idx.as_u32(),
799 component_idx.as_u32(),
800 ));
801 }
802
803 let global_err_ctx_table_map =
805 Intrinsic::ErrCtx(ErrCtxIntrinsic::GlobalErrCtxTableMap).name();
806 let rep_table_class = Intrinsic::RepTableClass.name();
807 for (table_idx, component_idx) in self.err_ctx_tables.iter() {
808 self.src.js.push_str(&format!(
809 "{global_err_ctx_table_map}[{}] = {{ componentIdx: {}, table: new {rep_table_class}() }};\n",
810 table_idx.as_u32(),
811 component_idx.as_u32(),
812 ));
813 }
814
815 let mut lower_import_initializers = Vec::new();
823
824 for init in self.component.initializers.iter() {
826 match init {
827 GlobalInitializer::InstantiateModule(_m, _maybe_idx) => {
828 for lower_import_init in lower_import_initializers.drain(..) {
830 self.instantiation_global_initializer(lower_import_init);
831 }
832 }
833
834 GlobalInitializer::LowerImport { .. } => {
838 lower_import_initializers.push(init);
839 continue;
840 }
841 _ => {}
842 }
843
844 self.instantiation_global_initializer(init);
845 }
846
847 for init in lower_import_initializers.drain(..) {
849 self.instantiation_global_initializer(init);
850 }
851
852 self.exports(&self.component.exports);
857
858 for (i, trampoline) in self
861 .translation
862 .trampolines
863 .iter()
864 .filter(|(_, t)| Instantiator::is_early_trampoline(t))
865 {
866 self.trampoline(i, trampoline);
867 }
868
869 if self.bindgen.opts.instantiation.is_some() {
870 let js_init = mem::take(&mut self.src.js_init);
871 self.src.js.push_str(&js_init);
872 }
873
874 for (i, trampoline) in self
877 .translation
878 .trampolines
879 .iter()
880 .filter(|(_, t)| !Instantiator::is_early_trampoline(t))
881 {
882 self.trampoline(i, trampoline);
883 }
884 }
885
886 fn ensure_local_resource_class(&mut self, local_name: String) {
887 if !self.defined_resource_classes.contains(&local_name) {
888 uwriteln!(
889 self.src.js,
890 "\nclass {local_name} {{
891 constructor () {{
892 throw new Error('\"{local_name}\" resource does not define a constructor');
893 }}
894 }}"
895 );
896 self.defined_resource_classes.insert(local_name.to_string());
897 }
898 }
899
900 fn resource_definitions(&mut self, definitions: &mut source::Source) {
901 for resource in 0..self.component.num_resources {
904 let resource = ResourceIndex::from_u32(resource);
905 let is_imported = self.component.defined_resource_index(resource).is_none();
906 if is_imported {
907 continue;
908 }
909 if let Some(local_name) = self.bindgen.local_names.try_get(resource) {
910 self.ensure_local_resource_class(local_name.to_string());
911 }
912 }
913
914 if self.bindgen.all_intrinsics.contains(&Intrinsic::Resource(
916 ResourceIntrinsic::ResourceTransferBorrow,
917 )) || self.bindgen.all_intrinsics.contains(&Intrinsic::Resource(
918 ResourceIntrinsic::ResourceTransferBorrowValidLifting,
919 )) {
920 let defined_resource_tables = Intrinsic::DefinedResourceTables.name();
921 uwrite!(definitions, "const {defined_resource_tables} = [");
922 for tidx in 0..self.component.num_resources {
924 let tid = TypeResourceTableIndex::from_u32(tidx);
925 let resource_table_ty = &self.types[tid];
926 let rid = resource_table_ty.unwrap_concrete_ty();
927 if let Some(defined_index) = self.component.defined_resource_index(rid) {
928 let instance_idx = resource_table_ty.unwrap_concrete_instance();
929 if instance_idx == self.component.defined_resource_instances[defined_index] {
930 uwrite!(definitions, "true,");
931 }
932 } else {
933 uwrite!(definitions, ",");
934 };
935 }
936 uwrite!(definitions, "];\n");
937 }
938 }
939
940 fn ensure_error_context_local_table(
948 &mut self,
949 component_idx: RuntimeComponentInstanceIndex,
950 err_ctx_tbl_idx: TypeComponentLocalErrorContextTableIndex,
951 ) {
952 if self.error_context_component_initialized[component_idx]
953 && self.error_context_component_table_initialized[err_ctx_tbl_idx]
954 {
955 return;
956 }
957 let err_ctx_local_tables = self
958 .bindgen
959 .intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::ComponentLocalTable));
960 let rep_table_class = Intrinsic::RepTableClass.name();
961 let c = component_idx.as_u32();
962 if !self.error_context_component_initialized[component_idx] {
963 uwriteln!(self.src.js, "{err_ctx_local_tables}.set({c}, new Map());");
964 self.error_context_component_initialized[component_idx] = true;
965 }
966 if !self.error_context_component_table_initialized[err_ctx_tbl_idx] {
967 let t = err_ctx_tbl_idx.as_u32();
968 uwriteln!(
969 self.src.js,
970 "{err_ctx_local_tables}.get({c}).set({t}, new {rep_table_class}({{ target: `component [{c}] local error ctx table [{t}]` }}));"
971 );
972 self.error_context_component_table_initialized[err_ctx_tbl_idx] = true;
973 }
974 }
975
976 fn ensure_resource_table(&mut self, resource_table_idx: TypeResourceTableIndex) {
983 if self
984 .resource_tables_initialized
985 .contains_key(&resource_table_idx)
986 {
987 return;
988 }
989
990 let resource_table_ty = &self.types[resource_table_idx];
991 let resource_idx = resource_table_ty.unwrap_concrete_ty();
992
993 let (is_imported, maybe_dtor) =
994 if let Some(resource_idx) = self.component.defined_resource_index(resource_idx) {
995 let resource_def = self
996 .component
997 .initializers
998 .iter()
999 .find_map(|i| match i {
1000 GlobalInitializer::Resource(r) if r.index == resource_idx => Some(r),
1001 _ => None,
1002 })
1003 .unwrap();
1004
1005 if let Some(dtor) = &resource_def.dtor {
1006 (false, format!("\n{}(rep);", self.core_def(dtor)))
1007 } else {
1008 (false, "".into())
1009 }
1010 } else {
1011 (true, "".into())
1012 };
1013
1014 let handle_tables = self.bindgen.intrinsic(Intrinsic::HandleTables);
1015 let rsc_table_flag = self
1016 .bindgen
1017 .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableFlag));
1018 let rsc_table_remove = self
1019 .bindgen
1020 .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableRemove));
1021
1022 let rtid = resource_table_idx.as_u32();
1023 if is_imported {
1024 uwriteln!(
1025 self.src.js,
1026 "const handleTable{rtid} = [{rsc_table_flag}, 0];",
1027 );
1028 if !self.resources_initialized.contains_key(&resource_idx) {
1029 let ridx = resource_idx.as_u32();
1030 uwriteln!(
1031 self.src.js,
1032 "const captureTable{ridx} = new Map();
1033 let captureCnt{ridx} = 0;"
1034 );
1035 self.resources_initialized.insert(resource_idx, true);
1036 }
1037 } else {
1038 let finalization_registry_create = self
1039 .bindgen
1040 .intrinsic(Intrinsic::FinalizationRegistryCreate);
1041 uwriteln!(
1042 self.src.js,
1043 "const handleTable{rtid} = [{rsc_table_flag}, 0];
1044 const finalizationRegistry{rtid} = {finalization_registry_create}((handle) => {{
1045 const {{ rep }} = {rsc_table_remove}(handleTable{rtid}, handle);{maybe_dtor}
1046 }});
1047 ",
1048 );
1049 }
1050 uwriteln!(self.src.js, "{handle_tables}[{rtid}] = handleTable{rtid};");
1051 self.resource_tables_initialized
1052 .insert(resource_table_idx, true);
1053 }
1054
1055 fn instance_flags(&mut self) {
1056 let mut instance_flag_defs = String::new();
1058 for used in self.used_instance_flags.borrow().iter() {
1059 let i = used.as_u32();
1060 uwriteln!(
1061 &mut instance_flag_defs,
1062 "const instanceFlags{i} = new WebAssembly.Global({{ value: \"i32\", mutable: true }}, {});",
1063 wasmtime_environ::component::FLAG_MAY_LEAVE
1064 );
1065 }
1066 self.src.js_init.prepend_str(&instance_flag_defs);
1067 }
1068
1069 fn is_early_trampoline(trampoline: &Trampoline) -> bool {
1074 matches!(
1075 trampoline,
1076 Trampoline::AsyncStartCall { .. }
1077 | Trampoline::BackpressureDec { .. }
1078 | Trampoline::BackpressureInc { .. }
1079 | Trampoline::ContextGet { .. }
1080 | Trampoline::ContextSet { .. }
1081 | Trampoline::EnterSyncCall
1082 | Trampoline::ErrorContextDebugMessage { .. }
1083 | Trampoline::ErrorContextDrop { .. }
1084 | Trampoline::ErrorContextNew { .. }
1085 | Trampoline::ErrorContextTransfer
1086 | Trampoline::ExitSyncCall
1087 | Trampoline::FutureCancelRead { .. }
1088 | Trampoline::FutureCancelWrite { .. }
1089 | Trampoline::FutureDropReadable { .. }
1090 | Trampoline::FutureDropWritable { .. }
1091 | Trampoline::FutureRead { .. }
1092 | Trampoline::FutureWrite { .. }
1093 | Trampoline::LowerImport { .. }
1094 | Trampoline::PrepareCall { .. }
1095 | Trampoline::ResourceDrop { .. }
1096 | Trampoline::ResourceNew { .. }
1097 | Trampoline::ResourceRep { .. }
1098 | Trampoline::ResourceTransferBorrow
1099 | Trampoline::ResourceTransferOwn
1100 | Trampoline::StreamCancelRead { .. }
1101 | Trampoline::StreamCancelWrite { .. }
1102 | Trampoline::StreamDropReadable { .. }
1103 | Trampoline::StreamDropWritable { .. }
1104 | Trampoline::StreamNew { .. }
1105 | Trampoline::StreamRead { .. }
1106 | Trampoline::StreamTransfer
1107 | Trampoline::StreamWrite { .. }
1108 | Trampoline::SubtaskCancel { .. }
1109 | Trampoline::SubtaskDrop { .. }
1110 | Trampoline::SyncStartCall { .. }
1111 | Trampoline::TaskCancel { .. }
1112 | Trampoline::TaskReturn { .. }
1113 | Trampoline::WaitableJoin { .. }
1114 | Trampoline::WaitableSetDrop { .. }
1115 | Trampoline::WaitableSetNew { .. }
1116 | Trampoline::WaitableSetPoll { .. }
1117 | Trampoline::WaitableSetWait { .. }
1118 )
1119 }
1120
1121 fn trampoline(&mut self, i: TrampolineIndex, trampoline: &'a Trampoline) {
1122 let i = i.as_u32();
1123 match trampoline {
1124 Trampoline::TaskCancel { instance } => {
1125 let task_cancel_fn = self
1126 .bindgen
1127 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::TaskCancel));
1128 uwriteln!(
1129 self.src.js,
1130 "const trampoline{i} = {task_cancel_fn}.bind(null, {instance_idx});\n",
1131 instance_idx = instance.as_u32(),
1132 );
1133 }
1134
1135 Trampoline::SubtaskCancel { instance, async_ } => {
1136 let task_cancel_fn = self
1137 .bindgen
1138 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::SubtaskCancel));
1139 uwriteln!(
1140 self.src.js,
1141 "const trampoline{i} = {task_cancel_fn}.bind(null, {instance_idx}, {async_});\n",
1142 instance_idx = instance.as_u32(),
1143 );
1144 }
1145
1146 Trampoline::SubtaskDrop { instance } => {
1147 let component_idx = instance.as_u32();
1148 let subtask_drop_fn = self
1149 .bindgen
1150 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::SubtaskDrop));
1151 uwriteln!(
1152 self.src.js,
1153 "const trampoline{i} = {subtask_drop_fn}.bind(
1154 null,
1155 {component_idx},
1156 );"
1157 );
1158 }
1159
1160 Trampoline::WaitableSetNew { instance } => {
1161 let waitable_set_new_fn = self
1162 .bindgen
1163 .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableSetNew));
1164 uwriteln!(
1165 self.src.js,
1166 "const trampoline{i} = {waitable_set_new_fn}.bind(null, {});\n",
1167 instance.as_u32(),
1168 );
1169 }
1170
1171 Trampoline::WaitableSetWait { instance, options } => {
1172 let options = self
1173 .component
1174 .options
1175 .get(*options)
1176 .expect("failed to find options");
1177 assert_eq!(
1178 instance.as_u32(),
1179 options.instance.as_u32(),
1180 "options index instance must match trampoline"
1181 );
1182
1183 let CanonicalOptions {
1184 instance,
1185 async_,
1186 data_model:
1187 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, .. }),
1188 ..
1189 } = options
1190 else {
1191 panic!("unexpected/missing memory data model during waitable-set.wait");
1192 };
1193
1194 let instance_idx = instance.as_u32();
1195 let memory_idx = memory
1196 .expect("missing memory idx for waitable-set.wait")
1197 .as_u32();
1198 let waitable_set_wait_fn = self
1199 .bindgen
1200 .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableSetWait));
1201
1202 uwriteln!(
1203 self.src.js,
1204 r#"
1205 const trampoline{i} = new WebAssembly.Suspending({waitable_set_wait_fn}.bind(null, {{
1206 componentIdx: {instance_idx},
1207 isAsync: {async_},
1208 memoryIdx: {memory_idx},
1209 getMemoryFn: () => memory{memory_idx},
1210 }}));
1211 "#,
1212 );
1213 }
1214
1215 Trampoline::WaitableSetPoll { options, .. } => {
1216 let CanonicalOptions {
1217 instance,
1218 async_,
1219 data_model:
1220 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, .. }),
1221 cancellable,
1222 ..
1223 } = self
1224 .component
1225 .options
1226 .get(*options)
1227 .expect("failed to find options")
1228 else {
1229 panic!("unexpected memory data model during waitable-set.poll");
1230 };
1231
1232 let instance_idx = instance.as_u32();
1233 let memory_idx = memory
1234 .expect("missing memory idx for waitable-set.poll")
1235 .as_u32();
1236 let waitable_set_poll_fn = self
1237 .bindgen
1238 .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableSetPoll));
1239
1240 uwriteln!(
1241 self.src.js,
1242 r#"
1243 const trampoline{i} = {waitable_set_poll_fn}.bind(
1244 null,
1245 {{
1246 componentIdx: {instance_idx},
1247 isAsync: {async_},
1248 isCancellable: {cancellable},
1249 memoryIdx: {memory_idx},
1250 getMemoryFn: () => memory{memory_idx},
1251 }}
1252 );
1253 "#,
1254 );
1255 }
1256
1257 Trampoline::WaitableSetDrop { instance } => {
1258 let waitable_set_drop_fn = self
1259 .bindgen
1260 .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableSetDrop));
1261 uwriteln!(
1262 self.src.js,
1263 "const trampoline{i} = {waitable_set_drop_fn}.bind(null, {instance_idx});\n",
1264 instance_idx = instance.as_u32(),
1265 );
1266 }
1267
1268 Trampoline::WaitableJoin { instance } => {
1269 let waitable_join_fn = self
1270 .bindgen
1271 .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableJoin));
1272 uwriteln!(
1273 self.src.js,
1274 "const trampoline{i} = {waitable_join_fn}.bind(null, {instance_idx});\n",
1275 instance_idx = instance.as_u32(),
1276 );
1277 }
1278
1279 Trampoline::StreamNew { ty, instance } => {
1280 let stream_new_fn = self
1281 .bindgen
1282 .intrinsic(Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamNew));
1283 let instance_idx = instance.as_u32();
1284 let stream_table_idx = ty.as_u32();
1285
1286 let table_ty = &self.types[*ty];
1288 let stream_ty_idx = table_ty.ty;
1289 let stream_ty_idx_js = stream_ty_idx.as_u32();
1290 let stream_ty = &self.types[stream_ty_idx];
1291
1292 let (
1294 align_32_js,
1295 size_32_js,
1296 flat_count_js,
1297 lift_fn_js,
1298 lower_fn_js,
1299 is_none_js,
1300 is_numeric_type_js,
1301 is_borrow_js,
1302 is_async_value_js,
1303 ) = match stream_ty.payload {
1304 None => (
1306 "0".into(),
1307 "0".into(),
1308 "0".into(),
1309 "null".into(),
1310 "null".into(),
1311 "true",
1312 "false".into(),
1313 "false".into(),
1314 "false".into(),
1315 ),
1316 Some(ty) => (
1318 self.types.canonical_abi(&ty).align32.to_string(),
1319 self.types.canonical_abi(&ty).size32.to_string(),
1320 self.types
1321 .canonical_abi(&ty)
1322 .flat_count
1323 .map(|v| v.to_string())
1324 .unwrap_or_else(|| "null".into()),
1325 gen_flat_lift_fn_js_expr(
1326 self,
1327 &ty,
1328 &wasmtime_environ::component::StringEncoding::Utf8,
1329 ),
1330 gen_flat_lower_fn_js_expr(
1331 self,
1332 self.types,
1333 &ty,
1334 &wasmtime_environ::component::StringEncoding::Utf8,
1335 ),
1336 "false",
1337 format!(
1338 "{}",
1339 matches!(
1340 ty,
1341 InterfaceType::U8
1342 | InterfaceType::U16
1343 | InterfaceType::U32
1344 | InterfaceType::U64
1345 | InterfaceType::S8
1346 | InterfaceType::S16
1347 | InterfaceType::S32
1348 | InterfaceType::S64
1349 | InterfaceType::Float32
1350 | InterfaceType::Float64
1351 )
1352 ),
1353 format!("{}", matches!(ty, InterfaceType::Borrow(_))),
1354 format!(
1355 "{}",
1356 matches!(ty, InterfaceType::Stream(_) | InterfaceType::Future(_))
1357 ),
1358 ),
1359 };
1360
1361 uwriteln!(
1362 self.src.js,
1363 "const trampoline{i} = {stream_new_fn}.bind(null, {{
1364 streamTableIdx: {stream_table_idx},
1365 callerComponentIdx: {instance_idx},
1366 elemMeta: {{
1367 liftFn: {lift_fn_js},
1368 lowerFn: {lower_fn_js},
1369 typeIdx: {stream_ty_idx_js},
1370 isNone: {is_none_js},
1371 isNumeric: {is_numeric_type_js},
1372 isBorrowed: {is_borrow_js},
1373 isAsyncValue: {is_async_value_js},
1374 flatCount: {flat_count_js},
1375 align32: {align_32_js},
1376 size32: {size_32_js},
1377 }},
1378 }});\n",
1379 );
1380 }
1381
1382 Trampoline::StreamRead {
1383 instance,
1384 ty,
1385 options,
1386 } => {
1387 let options = self
1388 .component
1389 .options
1390 .get(*options)
1391 .expect("failed to find options");
1392 assert_eq!(
1393 instance.as_u32(),
1394 options.instance.as_u32(),
1395 "options index instance must match trampoline"
1396 );
1397
1398 let CanonicalOptions {
1399 instance,
1400 string_encoding,
1401 async_,
1402 data_model:
1403 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1404 ..
1405 } = options
1406 else {
1407 unreachable!("missing/invalid data model for options during stream.read")
1408 };
1409 let memory_idx = memory.expect("missing memory idx for stream.read").as_u32();
1410 let (realloc_idx, get_realloc_fn_js) = match realloc {
1411 Some(v) => {
1412 let v = v.as_u32().to_string();
1413 (v.to_string(), format!("() => realloc{v}"))
1414 }
1415 None => ("null".into(), "null".into()),
1416 };
1417
1418 let component_instance_id = instance.as_u32();
1419 let string_encoding = string_encoding_js_literal(string_encoding);
1420 let stream_table_idx = ty.as_u32();
1421 let stream_read_fn = self
1422 .bindgen
1423 .intrinsic(Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamRead));
1424
1425 let register_global_memory_for_component_fn =
1430 Intrinsic::RegisterGlobalMemoryForComponent.name();
1431 uwriteln!(
1432 self.src.js_init,
1433 r#"{register_global_memory_for_component_fn}({{
1434 componentIdx: {component_instance_id},
1435 memoryIdx: {memory_idx},
1436 memory: memory{memory_idx},
1437 }});"#
1438 );
1439
1440 uwriteln!(
1441 self.src.js,
1442 r#"const trampoline{i} = new WebAssembly.Suspending({stream_read_fn}.bind(
1443 null,
1444 {{
1445 componentIdx: {component_instance_id},
1446 memoryIdx: {memory_idx},
1447 getMemoryFn: () => memory{memory_idx},
1448 reallocIdx: {realloc_idx},
1449 getReallocFn: {get_realloc_fn_js},
1450 stringEncoding: {string_encoding},
1451 isAsync: {async_},
1452 streamTableIdx: {stream_table_idx},
1453 }}
1454 ));
1455 "#,
1456 );
1457 }
1458
1459 Trampoline::StreamWrite {
1460 instance,
1461 ty,
1462 options,
1463 } => {
1464 let options = self
1465 .component
1466 .options
1467 .get(*options)
1468 .expect("failed to find options");
1469 assert_eq!(
1470 instance.as_u32(),
1471 options.instance.as_u32(),
1472 "options index instance must match trampoline"
1473 );
1474
1475 let CanonicalOptions {
1476 instance,
1477 string_encoding,
1478 async_,
1479 data_model:
1480 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1481 ..
1482 } = options
1483 else {
1484 unreachable!("unexpected memory data model during stream.write");
1485 };
1486 let component_instance_id = instance.as_u32();
1487 let memory_idx = memory
1488 .expect("missing memory idx for stream.write")
1489 .as_u32();
1490 let (realloc_idx, get_realloc_fn_js) = match realloc {
1491 Some(v) => {
1492 let v = v.as_u32().to_string();
1493 (v.to_string(), format!("() => realloc{v}"))
1494 }
1495 None => ("null".into(), "() => null".into()),
1496 };
1497
1498 let string_encoding = string_encoding_js_literal(string_encoding);
1499 let stream_table_idx = ty.as_u32();
1500 let stream_write_fn = self
1501 .bindgen
1502 .intrinsic(Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamWrite));
1503
1504 let register_global_memory_for_component_fn =
1508 Intrinsic::RegisterGlobalMemoryForComponent.name();
1509 uwriteln!(
1510 self.src.js_init,
1511 r#"{register_global_memory_for_component_fn}({{
1512 componentIdx: {component_instance_id},
1513 memoryIdx: {memory_idx},
1514 memory: memory{memory_idx},
1515 }});"#
1516 );
1517
1518 uwriteln!(
1519 self.src.js,
1520 r#"
1521 const trampoline{i} = new WebAssembly.Suspending({stream_write_fn}.bind(
1522 null,
1523 {{
1524 componentIdx: {component_instance_id},
1525 memoryIdx: {memory_idx},
1526 getMemoryFn: () => memory{memory_idx},
1527 reallocIdx: {realloc_idx},
1528 getReallocFn: {get_realloc_fn_js},
1529 stringEncoding: {string_encoding},
1530 isAsync: {async_},
1531 streamTableIdx: {stream_table_idx},
1532 }}
1533 ));
1534 "#,
1535 );
1536 }
1537
1538 Trampoline::StreamCancelRead {
1539 instance,
1540 ty,
1541 async_,
1542 }
1543 | Trampoline::StreamCancelWrite {
1544 instance,
1545 ty,
1546 async_,
1547 } => {
1548 let stream_cancel_fn = match trampoline {
1549 Trampoline::StreamCancelRead { .. } => self.bindgen.intrinsic(
1550 Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamCancelRead),
1551 ),
1552 Trampoline::StreamCancelWrite { .. } => self.bindgen.intrinsic(
1553 Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamCancelWrite),
1554 ),
1555 _ => unreachable!("unexpected trampoline"),
1556 };
1557
1558 let stream_table_idx = ty.as_u32();
1559 let component_idx = instance.as_u32();
1560 uwriteln!(
1561 self.src.js,
1562 r#"
1563 const trampoline{i} = new WebAssembly.Suspending({stream_cancel_fn}.bind(null, {{
1564 streamTableIdx: {stream_table_idx},
1565 isAsync: {async_},
1566 componentIdx: {component_idx},
1567 }}));
1568 "#,
1569 );
1570 }
1571
1572 Trampoline::StreamDropReadable { ty, instance }
1573 | Trampoline::StreamDropWritable { ty, instance } => {
1574 let intrinsic_fn = match trampoline {
1575 Trampoline::StreamDropReadable { .. } => self.bindgen.intrinsic(
1576 Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamDropReadable),
1577 ),
1578 Trampoline::StreamDropWritable { .. } => self.bindgen.intrinsic(
1579 Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamDropWritable),
1580 ),
1581 _ => unreachable!("unexpected trampoline"),
1582 };
1583 let stream_idx = ty.as_u32();
1584 let instance_idx = instance.as_u32();
1585 uwriteln!(
1586 self.src.js,
1587 "const trampoline{i} = {intrinsic_fn}.bind(null, {{
1588 streamTableIdx: {stream_idx},
1589 componentIdx: {instance_idx},
1590 }});\n",
1591 );
1592 }
1593
1594 Trampoline::StreamTransfer => {
1595 let stream_transfer_fn = self
1596 .bindgen
1597 .intrinsic(Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamTransfer));
1598 uwriteln!(self.src.js, "const trampoline{i} = {stream_transfer_fn};\n",);
1599 }
1600
1601 Trampoline::FutureNew { ty, .. } => {
1602 let future_new_fn = self
1603 .bindgen
1604 .intrinsic(Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureNew));
1605 uwriteln!(
1606 self.src.js,
1607 "const trampoline{i} = {future_new_fn}.bind(null, {});\n",
1608 ty.as_u32(),
1609 );
1610 }
1611
1612 Trampoline::FutureRead { ty, options, .. } => {
1613 let options = self
1614 .component
1615 .options
1616 .get(*options)
1617 .expect("failed to find options");
1618
1619 let future_idx = ty.as_u32();
1620
1621 let CanonicalOptions {
1622 instance,
1623 string_encoding,
1624 callback,
1625 post_return,
1626 async_,
1627 data_model:
1628 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1629 ..
1630 } = options
1631 else {
1632 unreachable!("unexpected memory data model during future.read");
1633 };
1634 let component_instance_id = instance.as_u32();
1635 let memory_idx = memory.expect("missing memory idx for future.read").as_u32();
1636 let realloc_idx = realloc
1637 .map(|v| v.as_u32().to_string())
1638 .unwrap_or_else(|| "null".into());
1639 let string_encoding = string_encoding_js_literal(string_encoding);
1640
1641 assert!(
1642 callback.is_none(),
1643 "callback should not be present for future read"
1644 );
1645 assert!(
1646 post_return.is_none(),
1647 "post_return should not be present for future read"
1648 );
1649
1650 let future_read_fn = self
1651 .bindgen
1652 .intrinsic(Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureRead));
1653 uwriteln!(
1654 self.src.js,
1655 r#"const trampoline{i} = {future_read_fn}.bind(
1656 null,
1657 {component_instance_id},
1658 {memory_idx},
1659 {realloc_idx},
1660 {string_encoding},
1661 {async_},
1662 {future_idx},
1663 );
1664 "#,
1665 );
1666 }
1667
1668 Trampoline::FutureWrite { ty, options, .. } => {
1669 let options = self
1670 .component
1671 .options
1672 .get(*options)
1673 .expect("failed to find options");
1674
1675 let future_idx = ty.as_u32();
1676 let CanonicalOptions {
1677 instance,
1678 string_encoding,
1679 async_,
1680 data_model:
1681 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1682 ..
1683 } = options
1684 else {
1685 unreachable!("unexpected memory data model during future.write");
1686 };
1687 let component_instance_id = instance.as_u32();
1688 let memory_idx = memory
1689 .expect("missing memory idx for future.write")
1690 .as_u32();
1691 let realloc_idx = realloc
1692 .map(|v| v.as_u32().to_string())
1693 .unwrap_or_else(|| "null".into());
1694 let string_encoding = string_encoding_js_literal(string_encoding);
1695
1696 let future_write_fn = self
1697 .bindgen
1698 .intrinsic(Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureWrite));
1699 uwriteln!(
1700 self.src.js,
1701 r#"const trampoline{i} = {future_write_fn}.bind(
1702 null,
1703 {component_instance_id},
1704 {memory_idx},
1705 {realloc_idx},
1706 {string_encoding},
1707 {async_},
1708 {future_idx},
1709 );
1710 "#,
1711 );
1712 }
1713
1714 Trampoline::FutureCancelRead { ty, async_, .. } => {
1715 let future_cancel_read_fn = self.bindgen.intrinsic(Intrinsic::AsyncFuture(
1716 AsyncFutureIntrinsic::FutureCancelRead,
1717 ));
1718 uwriteln!(
1719 self.src.js,
1720 "const trampoline{i} = {future_cancel_read_fn}.bind(null, {future_idx}, {async_});\n",
1721 future_idx = ty.as_u32(),
1722 );
1723 }
1724
1725 Trampoline::FutureCancelWrite { ty, async_, .. } => {
1726 let future_cancel_write_fn = self.bindgen.intrinsic(Intrinsic::AsyncFuture(
1727 AsyncFutureIntrinsic::FutureCancelWrite,
1728 ));
1729 uwriteln!(
1730 self.src.js,
1731 "const trampoline{i} = {future_cancel_write_fn}.bind(null, {future_idx}, {async_});\n",
1732 future_idx = ty.as_u32(),
1733 );
1734 }
1735
1736 Trampoline::FutureDropReadable { ty, .. } => {
1737 let future_drop_readable_fn = self.bindgen.intrinsic(Intrinsic::AsyncFuture(
1738 AsyncFutureIntrinsic::FutureDropReadable,
1739 ));
1740 uwriteln!(
1741 self.src.js,
1742 "const trampoline{i} = {future_drop_readable_fn}.bind(null, {future_idx});\n",
1743 future_idx = ty.as_u32(),
1744 );
1745 }
1746
1747 Trampoline::FutureDropWritable { ty, .. } => {
1748 let future_drop_writable_fn = self.bindgen.intrinsic(Intrinsic::AsyncFuture(
1749 AsyncFutureIntrinsic::FutureDropWritable,
1750 ));
1751 uwriteln!(
1752 self.src.js,
1753 "const trampoline{i} = {future_drop_writable_fn}.bind(null, {future_idx});\n",
1754 future_idx = ty.as_u32(),
1755 );
1756 }
1757
1758 Trampoline::FutureTransfer => todo!("Trampoline::FutureTransfer"),
1759
1760 Trampoline::ErrorContextNew { ty, options, .. } => {
1761 let CanonicalOptions {
1762 instance,
1763 string_encoding,
1764 data_model:
1765 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, .. }),
1766 ..
1767 } = self
1768 .component
1769 .options
1770 .get(*options)
1771 .expect("failed to find options")
1772 else {
1773 panic!("unexpected memory data model during error-context.new");
1774 };
1775
1776 self.ensure_error_context_local_table(*instance, *ty);
1777
1778 let local_err_tbl_idx = ty.as_u32();
1779 let component_idx = instance.as_u32();
1780
1781 let memory_idx = memory
1782 .expect("missing realloc fn idx for error-context.debug-message")
1783 .as_u32();
1784
1785 let decoder = match string_encoding {
1787 wasmtime_environ::component::StringEncoding::Utf8 => self
1788 .bindgen
1789 .intrinsic(Intrinsic::String(StringIntrinsic::GlobalTextDecoderUtf8)),
1790 wasmtime_environ::component::StringEncoding::Utf16 => self
1791 .bindgen
1792 .intrinsic(Intrinsic::String(StringIntrinsic::Utf16Decoder)),
1793 enc => panic!(
1794 "unsupported string encoding [{enc:?}] for error-context.debug-message"
1795 ),
1796 };
1797 uwriteln!(
1798 self.src.js,
1799 "function trampoline{i}InputStr(ptr, len) {{
1800 return {decoder}.decode(new DataView(memory{memory_idx}.buffer, ptr, len));
1801 }}"
1802 );
1803
1804 let err_ctx_new_fn = self
1805 .bindgen
1806 .intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::ErrorContextNew));
1807 uwriteln!(
1809 self.src.js,
1810 "const trampoline{i} = {err_ctx_new_fn}.bind(
1811 null,
1812 {{
1813 componentIdx: {component_idx},
1814 localTableIdx: {local_err_tbl_idx},
1815 readStrFn: trampoline{i}InputStr,
1816 }}
1817 );
1818 "
1819 );
1820 }
1821
1822 Trampoline::ErrorContextDebugMessage {
1823 instance, options, ..
1824 } => {
1825 let CanonicalOptions {
1826 async_,
1827 callback,
1828 post_return,
1829 string_encoding,
1830 data_model:
1831 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1832 ..
1833 } = self
1834 .component
1835 .options
1836 .get(*options)
1837 .expect("failed to find options")
1838 else {
1839 panic!("unexpected memory data model during error-context.debug-message");
1840 };
1841
1842 let debug_message_fn = self
1843 .bindgen
1844 .intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::ErrorContextDebugMessage));
1845
1846 let realloc_fn_idx = realloc
1847 .expect("missing realloc fn idx for error-context.debug-message")
1848 .as_u32();
1849 let memory_idx = memory
1850 .expect("missing realloc fn idx for error-context.debug-message")
1851 .as_u32();
1852
1853 match string_encoding {
1855 wasmtime_environ::component::StringEncoding::Utf8 => {
1856 let encode_fn = self
1857 .bindgen
1858 .intrinsic(Intrinsic::String(StringIntrinsic::Utf8Encode));
1859 uwriteln!(
1860 self.src.js,
1861 "function trampoline{i}OutputStr(s, outputPtr) {{
1862 const memory = memory{memory_idx};
1863 const reallocFn = realloc{realloc_fn_idx};
1864 let {{ ptr, len }} = {encode_fn}(s, reallocFn, memory);
1865 new DataView(memory.buffer).setUint32(outputPtr, ptr, true)
1866 new DataView(memory.buffer).setUint32(outputPtr + 4, len, true)
1867 }}"
1868 );
1869 }
1870 wasmtime_environ::component::StringEncoding::Utf16 => {
1871 let encode_fn = self
1872 .bindgen
1873 .intrinsic(Intrinsic::String(StringIntrinsic::Utf16Encode));
1874 uwriteln!(
1875 self.src.js,
1876 "function trampoline{i}OutputStr(s, outputPtr) {{
1877 const memory = memory{memory_idx};
1878 const reallocFn = realloc{realloc_fn_idx};
1879 let ptr = {encode_fn}(s, reallocFn, memory);
1880 let len = s.length;
1881 new DataView(memory.buffer).setUint32(outputPtr, ptr, true)
1882 new DataView(memory.buffer).setUint32(outputPtr + 4, len, true)
1883 }}"
1884 );
1885 }
1886 enc => panic!(
1887 "unsupported string encoding [{enc:?}] for error-context.debug-message"
1888 ),
1889 };
1890
1891 let options_obj = format!(
1892 "{{callback:{callback}, postReturn: {post_return}, async: {async_}}}",
1893 callback = callback
1894 .map(|v| v.as_u32().to_string())
1895 .unwrap_or_else(|| "null".into()),
1896 post_return = post_return
1897 .map(|v| v.as_u32().to_string())
1898 .unwrap_or_else(|| "null".into()),
1899 );
1900
1901 let component_idx = instance.as_u32();
1902 uwriteln!(
1903 self.src.js,
1904 "const trampoline{i} = {debug_message_fn}.bind(
1905 null,
1906 {{
1907 componentIdx: {component_idx},
1908 options: {options_obj},
1909 writeStrFn: trampoline{i}OutputStr,
1910 }}
1911 );"
1912 );
1913 }
1914
1915 Trampoline::ErrorContextDrop { instance, ty } => {
1916 let drop_fn = self
1917 .bindgen
1918 .intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::ErrorContextDrop));
1919 let local_err_tbl_idx = ty.as_u32();
1920 let component_idx = instance.as_u32();
1921 uwriteln!(
1922 self.src.js,
1923 r#"
1924 const trampoline{i} = {drop_fn}.bind(
1925 null,
1926 {{ componentIdx: {component_idx}, localTableIdx: {local_err_tbl_idx} }},
1927 );
1928 "#
1929 );
1930 }
1931
1932 Trampoline::ErrorContextTransfer => {
1933 let transfer_fn = self
1934 .bindgen
1935 .intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::ErrorContextTransfer));
1936 uwriteln!(self.src.js, "const trampoline{i} = {transfer_fn};");
1937 }
1938
1939 Trampoline::PrepareCall { memory } => {
1941 let prepare_call_fn = self
1942 .bindgen
1943 .intrinsic(Intrinsic::Host(HostIntrinsic::PrepareCall));
1944 let (memory_idx_js, memory_fn_js) = memory
1945 .map(|v| {
1946 (
1947 v.as_u32().to_string(),
1948 format!("() => memory{}", v.as_u32()),
1949 )
1950 })
1951 .unwrap_or_else(|| ("null".into(), "() => null".into()));
1952 uwriteln!(
1953 self.src.js,
1954 "const trampoline{i} = {prepare_call_fn}.bind(null, {memory_idx_js}, {memory_fn_js});",
1955 )
1956 }
1957
1958 Trampoline::SyncStartCall { callback } => {
1959 let sync_start_call_fn = self
1960 .bindgen
1961 .intrinsic(Intrinsic::Host(HostIntrinsic::SyncStartCall));
1962 uwriteln!(
1963 self.src.js,
1964 "const trampoline{i} = {sync_start_call_fn}.bind(null, {});",
1965 callback
1966 .map(|v| v.as_u32().to_string())
1967 .unwrap_or_else(|| "null".into()),
1968 );
1969 }
1970
1971 Trampoline::AsyncStartCall {
1974 callback,
1975 post_return,
1976 } => {
1977 let async_start_call_fn = self
1978 .bindgen
1979 .intrinsic(Intrinsic::Host(HostIntrinsic::AsyncStartCall));
1980 let (callback_idx, callback_fn) = callback
1981 .map(|v| (v.as_u32().to_string(), format!("callback_{}", v.as_u32())))
1982 .unwrap_or_else(|| ("null".into(), "null".into()));
1983 let (post_return_idx, post_return_fn) = post_return
1984 .map(|v| (v.as_u32().to_string(), format!("postReturn{}", v.as_u32())))
1985 .unwrap_or_else(|| ("null".into(), "null".into()));
1986
1987 uwriteln!(
1988 self.src.js,
1989 "const trampoline{i} = {async_start_call_fn}.bind(
1990 null,
1991 {{
1992 postReturnIdx: {post_return_idx},
1993 getPostReturnFn: () => {post_return_fn},
1994 callbackIdx: {callback_idx},
1995 getCallbackFn: () => {callback_fn},
1996 }},
1997 );",
1998 );
1999 }
2000
2001 Trampoline::LowerImport {
2002 index: _,
2003 lower_ty,
2004 options,
2005 } => {
2006 let canon_opts = self
2007 .component
2008 .options
2009 .get(*options)
2010 .expect("failed to find options");
2011
2012 let component_idx = canon_opts.instance.as_u32();
2018 let is_async = canon_opts.async_;
2019
2020 let cancellable = canon_opts.cancellable;
2021
2022 let func_ty = self.types.index(*lower_ty);
2023
2024 let param_types = &self.types.index(func_ty.params).types;
2026 let param_lift_fns_js =
2027 gen_flat_lift_fn_list_js_expr(self, param_types.iter().as_slice(), canon_opts);
2028
2029 let result_types = &self.types.index(func_ty.results).types;
2031 let result_lower_fns_js = gen_flat_lower_fn_list_js_expr(
2032 self,
2033 self.types,
2034 result_types.iter().as_slice(),
2035 &canon_opts.string_encoding,
2036 );
2037
2038 let get_callback_fn_js = canon_opts
2039 .callback
2040 .map(|idx| format!("() => callback_{}", idx.as_u32()))
2041 .unwrap_or_else(|| "() => null".into());
2042 let get_post_return_fn_js = canon_opts
2043 .post_return
2044 .map(|idx| format!("() => postReturn{}", idx.as_u32()))
2045 .unwrap_or_else(|| "() => null".into());
2046
2047 let (memory_exprs, realloc_expr_js) =
2049 if let CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions {
2050 memory,
2051 realloc,
2052 }) = canon_opts.data_model
2053 {
2054 (
2055 memory.map(|idx| {
2056 (
2057 idx.as_u32().to_string(),
2058 format!("() => memory{}", idx.as_u32()),
2059 )
2060 }),
2061 realloc.map(|idx| format!("() => realloc{}", idx.as_u32())),
2062 )
2063 } else {
2064 (None, None)
2065 };
2066 let (memory_idx_js, memory_expr_js) =
2067 memory_exprs.unwrap_or_else(|| ("null".into(), "() => null".into()));
2068 let realloc_expr_js = realloc_expr_js.unwrap_or_else(|| "() => null".into());
2069
2070 let func_ty_async = func_ty.async_;
2072 let call = format!(
2073 r#"{lower_import_intrinsic}.bind(
2074 null,
2075 {{
2076 trampolineIdx: {i},
2077 componentIdx: {component_idx},
2078 isAsync: {is_async},
2079 isManualAsync: _trampoline{i}.manuallyAsync,
2080 paramLiftFns: {param_lift_fns_js},
2081 resultLowerFns: {result_lower_fns_js},
2082 funcTypeIsAsync: {func_ty_async},
2083 getCallbackFn: {get_callback_fn_js},
2084 getPostReturnFn: {get_post_return_fn_js},
2085 isCancellable: {cancellable},
2086 memoryIdx: {memory_idx_js},
2087 getMemoryFn: {memory_expr_js},
2088 getReallocFn: {realloc_expr_js},
2089 importFn: _trampoline{i},
2090 }},
2091 )"#,
2092 lower_import_intrinsic = if is_async || func_ty_async {
2093 self.bindgen
2094 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::LowerImport))
2095 } else {
2096 self.bindgen.intrinsic(Intrinsic::AsyncTask(
2097 AsyncTaskIntrinsic::LowerImportBackwardsCompat,
2098 ))
2099 }
2100 );
2101
2102 if is_async || func_ty_async {
2105 uwriteln!(
2106 self.src.js,
2107 "let trampoline{i} = new WebAssembly.Suspending({call});"
2108 );
2109 } else {
2110 uwriteln!(
2113 self.src.js,
2114 "let trampoline{i} = _trampoline{i}.manuallyAsync ? new WebAssembly.Suspending({call}) : {call};"
2115 );
2116 }
2117 }
2118
2119 Trampoline::Transcoder {
2120 op,
2121 from,
2122 from64,
2123 to,
2124 to64,
2125 } => {
2126 if *from64 || *to64 {
2127 unimplemented!("memory 64 transcoder");
2128 }
2129 let from = from.as_u32();
2130 let to = to.as_u32();
2131 match op {
2132 Transcode::Copy(FixedEncoding::Utf8) => {
2133 uwriteln!(
2134 self.src.js,
2135 r#"
2136 function trampoline{i} (from_ptr, len, to_ptr) {{
2137 new Uint8Array(memory{to}.buffer, to_ptr, len).set(new Uint8Array(memory{from}.buffer, from_ptr, len));
2138 }}
2139 "#
2140 );
2141 }
2142 Transcode::Copy(FixedEncoding::Utf16) => unimplemented!("utf16 copier"),
2143 Transcode::Copy(FixedEncoding::Latin1) => unimplemented!("latin1 copier"),
2144 Transcode::Latin1ToUtf16 => unimplemented!("latin to utf16 transcoder"),
2145 Transcode::Latin1ToUtf8 => unimplemented!("latin to utf8 transcoder"),
2146 Transcode::Utf16ToCompactProbablyUtf16 => {
2147 unimplemented!("utf16 to compact wtf16 transcoder")
2148 }
2149 Transcode::Utf16ToCompactUtf16 => {
2150 unimplemented!("utf16 to compact utf16 transcoder")
2151 }
2152 Transcode::Utf16ToLatin1 => unimplemented!("utf16 to latin1 transcoder"),
2153 Transcode::Utf16ToUtf8 => {
2154 uwriteln!(
2155 self.src.js,
2156 r#"
2157 function trampoline{i} (src, src_len, dst, dst_len) {{
2158 const encoder = new TextEncoder();
2159 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));
2160 return [read, written];
2161 }}
2162 "#,
2163 );
2164 }
2165 Transcode::Utf8ToCompactUtf16 => {
2166 unimplemented!("utf8 to compact utf16 transcoder")
2167 }
2168 Transcode::Utf8ToLatin1 => unimplemented!("utf8 to latin1 transcoder"),
2169 Transcode::Utf8ToUtf16 => {
2170 uwriteln!(
2171 self.src.js,
2172 r#"
2173 function trampoline{i} (from_ptr, len, to_ptr) {{
2174 const decoder = new TextDecoder();
2175 const content = decoder.decode(new Uint8Array(memory{from}.buffer, from_ptr, len));
2176 const strlen = content.length
2177 const view = new Uint16Array(memory{to}.buffer, to_ptr, strlen * 2)
2178 for (var i = 0; i < strlen; i++) {{
2179 view[i] = content.charCodeAt(i);
2180 }}
2181 return strlen;
2182 }}
2183 "#,
2184 );
2185 }
2186 };
2187 }
2188
2189 Trampoline::ResourceNew {
2190 ty: resource_ty_idx,
2191 ..
2192 } => {
2193 self.ensure_resource_table(*resource_ty_idx);
2194 let rid = resource_ty_idx.as_u32();
2195 let rsc_table_create_own = self.bindgen.intrinsic(Intrinsic::Resource(
2196 ResourceIntrinsic::ResourceTableCreateOwn,
2197 ));
2198 uwriteln!(
2199 self.src.js,
2200 "const trampoline{i} = {rsc_table_create_own}.bind(null, handleTable{rid});"
2201 );
2202 }
2203
2204 Trampoline::ResourceRep {
2205 ty: resource_ty_idx,
2206 ..
2207 } => {
2208 self.ensure_resource_table(*resource_ty_idx);
2209 let rid = resource_ty_idx.as_u32();
2210 let rsc_flag = self
2211 .bindgen
2212 .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableFlag));
2213 uwriteln!(
2214 self.src.js,
2215 "function trampoline{i} (handle) {{
2216 return handleTable{rid}[(handle << 1) + 1] & ~{rsc_flag};
2217 }}"
2218 );
2219 }
2220
2221 Trampoline::ResourceDrop {
2222 ty: resource_table_ty_idx,
2223 ..
2224 } => {
2225 self.ensure_resource_table(*resource_table_ty_idx);
2226 let tid = resource_table_ty_idx.as_u32();
2227 let resource_table_ty = &self.types[*resource_table_ty_idx];
2228 let resource_ty = resource_table_ty.unwrap_concrete_ty();
2229 let rid = resource_ty.as_u32();
2230
2231 let dtor = if let Some(resource_idx) =
2233 self.component.defined_resource_index(resource_ty)
2234 {
2235 let resource_def = self
2236 .component
2237 .initializers
2238 .iter()
2239 .find_map(|i| match i {
2240 GlobalInitializer::Resource(r) if r.index == resource_idx => Some(r),
2241 _ => None,
2242 })
2243 .unwrap();
2244
2245 if let Some(dtor) = &resource_def.dtor {
2247 format!(
2248 "
2249 {}(handleEntry.rep);",
2250 self.core_def(dtor)
2251 )
2252 } else {
2253 "".into()
2254 }
2255 } else {
2256 let symbol_dispose = self.bindgen.intrinsic(Intrinsic::SymbolDispose);
2263 let symbol_cabi_dispose = self.bindgen.intrinsic(Intrinsic::SymbolCabiDispose);
2264
2265 if let Some(imported_resource_local_name) =
2267 self.bindgen.local_names.try_get(resource_ty)
2268 {
2269 format!(
2270 "
2271 const rsc = captureTable{rid}.get(handleEntry.rep);
2272 if (rsc) {{
2273 if (rsc[{symbol_dispose}]) rsc[{symbol_dispose}]();
2274 captureTable{rid}.delete(handleEntry.rep);
2275 }} else if ({imported_resource_local_name}[{symbol_cabi_dispose}]) {{
2276 {imported_resource_local_name}[{symbol_cabi_dispose}](handleEntry.rep);
2277 }}"
2278 )
2279 } else {
2280 format!(
2282 "throw new TypeError('unreachable trampoline for resource [{:?}]')",
2283 resource_ty
2284 )
2285 }
2286 };
2287
2288 let rsc_table_remove = self
2289 .bindgen
2290 .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableRemove));
2291 uwrite!(
2292 self.src.js,
2293 "function trampoline{i}(handle) {{
2294 const handleEntry = {rsc_table_remove}(handleTable{tid}, handle);
2295 if (handleEntry.own) {{
2296 {dtor}
2297 }}
2298 }}
2299 ",
2300 );
2301 }
2302
2303 Trampoline::ResourceTransferOwn => {
2304 let resource_transfer = self
2305 .bindgen
2306 .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTransferOwn));
2307 uwriteln!(self.src.js, "const trampoline{i} = {resource_transfer};");
2308 }
2309
2310 Trampoline::ResourceTransferBorrow => {
2311 let resource_transfer =
2312 self.bindgen
2313 .intrinsic(if self.bindgen.opts.valid_lifting_optimization {
2314 Intrinsic::Resource(
2315 ResourceIntrinsic::ResourceTransferBorrowValidLifting,
2316 )
2317 } else {
2318 Intrinsic::Resource(ResourceIntrinsic::ResourceTransferBorrow)
2319 });
2320 uwriteln!(self.src.js, "const trampoline{i} = {resource_transfer};");
2321 }
2322
2323 Trampoline::ResourceEnterCall => {
2324 let scope_id = self.bindgen.intrinsic(Intrinsic::ScopeId);
2325 uwrite!(self.src.js, "function trampoline{i}() {{ {scope_id}++; }}");
2326 }
2327
2328 Trampoline::ResourceExitCall => {
2329 let scope_id = self.bindgen.intrinsic(Intrinsic::ScopeId);
2330 let resource_borrows = self
2331 .bindgen
2332 .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceCallBorrows));
2333 let handle_tables = self.bindgen.intrinsic(Intrinsic::HandleTables);
2334 uwrite!(
2338 self.src.js,
2339 r#"function trampoline{i}() {{
2340 {scope_id}--;
2341 for (const {{ rid, handle }} of {resource_borrows}) {{
2342 const storedScopeId = {handle_tables}[rid][handle << 1]
2343 if (storedScopeId === {scope_id}) {{
2344 throw new TypeError('borrows not dropped for resource call');
2345 }}
2346 }}
2347 {resource_borrows} = [];
2348 }}
2349 "#,
2350 );
2351 }
2352
2353 Trampoline::ContextSet { instance, slot, .. } => {
2354 let context_set_fn = self
2355 .bindgen
2356 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::ContextSet));
2357 let component_idx = instance.as_u32();
2358 uwriteln!(
2359 self.src.js,
2360 r#"
2361 const trampoline{i} = {context_set_fn}.bind(null, {{
2362 componentIdx: {component_idx},
2363 slot: {slot},
2364 }});
2365 "#
2366 );
2367 }
2368
2369 Trampoline::ContextGet { instance, slot } => {
2370 let context_get_fn = self
2371 .bindgen
2372 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::ContextGet));
2373 let component_idx = instance.as_u32();
2374 uwriteln!(
2375 self.src.js,
2376 r#"
2377 const trampoline{i} = {context_get_fn}.bind(null, {{
2378 componentIdx: {component_idx},
2379 slot: {slot},
2380 }});
2381 "#
2382 );
2383 }
2384
2385 Trampoline::TaskReturn {
2386 results, options, ..
2387 } => {
2388 let canon_opts = self
2389 .component
2390 .options
2391 .get(*options)
2392 .expect("failed to find options");
2393 let CanonicalOptions {
2394 instance,
2395 async_,
2396 data_model:
2397 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
2398 callback,
2399 post_return,
2400 ..
2401 } = canon_opts
2402 else {
2403 unreachable!("unexpected memory data model during task.return");
2404 };
2405
2406 if realloc.is_some() && memory.is_none() {
2408 panic!("memory must be present if realloc is");
2409 }
2410 if *async_ && post_return.is_some() {
2411 panic!("async and post return must not be specified together");
2412 }
2413 if *async_ && callback.is_none() {
2414 panic!("callback must be specified for async");
2415 }
2416 if let Some(cb_idx) = callback {
2417 let cb_fn = &self.types[TypeFuncIndex::from_u32(cb_idx.as_u32())];
2418 match self.types[cb_fn.params].types[..] {
2419 [InterfaceType::S32, InterfaceType::S32, InterfaceType::S32] => {}
2420 _ => panic!("unexpected params for async callback fn"),
2421 }
2422 match self.types[cb_fn.results].types[..] {
2423 [InterfaceType::S32] => {}
2424 _ => panic!("unexpected results for async callback fn"),
2425 }
2426 }
2427
2428 let result_types = &self.types[*results].types;
2429
2430 let result_flat_param_total: usize = result_types
2433 .iter()
2434 .map(|t| {
2435 self.types
2436 .canonical_abi(t)
2437 .flat_count
2438 .map(usize::from)
2439 .unwrap_or(0)
2440 })
2441 .sum();
2442 let use_direct_params = result_flat_param_total < MAX_ASYNC_FLAT_PARAMS;
2443
2444 let mut lift_fns: Vec<String> = Vec::with_capacity(result_types.len());
2447 for result_ty in result_types {
2448 lift_fns.push(gen_flat_lift_fn_js_expr(
2449 self,
2450 result_ty,
2451 &canon_opts.string_encoding,
2452 ));
2453 }
2454 let lift_fns_js = format!("[{}]", lift_fns.join(","));
2455
2456 let mut lower_fns: Vec<String> = Vec::with_capacity(result_types.len());
2462 for result_ty in result_types {
2463 lower_fns.push(gen_flat_lower_fn_js_expr(
2464 self.bindgen,
2465 self.types,
2466 result_ty,
2467 &canon_opts.string_encoding,
2468 ));
2469 }
2470 let lower_fns_js = format!("[{}]", lower_fns.join(","));
2471
2472 let get_memory_fn_js = memory
2473 .map(|idx| format!("() => memory{}", idx.as_u32()))
2474 .unwrap_or_else(|| "() => null".into());
2475 let memory_idx_js = memory
2476 .map(|idx| idx.as_u32().to_string())
2477 .unwrap_or_else(|| "null".into());
2478 let component_idx = instance.as_u32();
2479 let task_return_fn = self
2480 .bindgen
2481 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::TaskReturn));
2482 let callback_fn_idx = callback
2483 .map(|v| v.as_u32().to_string())
2484 .unwrap_or_else(|| "null".into());
2485
2486 uwriteln!(
2487 self.src.js,
2488 "const trampoline{i} = {task_return_fn}.bind(
2489 null,
2490 {{
2491 componentIdx: {component_idx},
2492 useDirectParams: {use_direct_params},
2493 getMemoryFn: {get_memory_fn_js},
2494 memoryIdx: {memory_idx_js},
2495 callbackFnIdx: {callback_fn_idx},
2496 liftFns: {lift_fns_js},
2497 lowerFns: {lower_fns_js},
2498 }},
2499 );",
2500 );
2501 }
2502
2503 Trampoline::BackpressureInc { instance } => {
2504 let backpressure_inc_fn = self
2505 .bindgen
2506 .intrinsic(Intrinsic::Component(ComponentIntrinsic::BackpressureInc));
2507 uwriteln!(
2508 self.src.js,
2509 "const trampoline{i} = {backpressure_inc_fn}.bind(null, {instance});\n",
2510 instance = instance.as_u32(),
2511 );
2512 }
2513
2514 Trampoline::BackpressureDec { instance } => {
2515 let backpressure_dec_fn = self
2516 .bindgen
2517 .intrinsic(Intrinsic::Component(ComponentIntrinsic::BackpressureDec));
2518 uwriteln!(
2519 self.src.js,
2520 "const trampoline{i} = {backpressure_dec_fn}.bind(null, {instance});\n",
2521 instance = instance.as_u32(),
2522 );
2523 }
2524
2525 Trampoline::ThreadYield {
2526 cancellable,
2527 instance,
2528 } => {
2529 let yield_fn = self
2530 .bindgen
2531 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::Yield));
2532 let component_instance_idx = instance.as_u32();
2533 uwriteln!(
2534 self.src.js,
2535 r#"
2536 const trampoline{i} = {yield_fn}.bind(null, {{
2537 isCancellable: {cancellable},
2538 componentIdx: {component_instance_idx},
2539 }});
2540 "#,
2541 );
2542 }
2543 Trampoline::ThreadIndex => todo!("Trampoline::ThreadIndex"),
2544 Trampoline::ThreadNewIndirect { .. } => todo!("Trampoline::ThreadNewIndirect"),
2545 Trampoline::ThreadSwitchTo { .. } => todo!("Trampoline::ThreadSwitchTo"),
2546 Trampoline::ThreadSuspend { .. } => todo!("Trampoline::ThreadSuspend"),
2547 Trampoline::ThreadResumeLater { .. } => todo!("Trampoline::ThreadResumeLater"),
2548 Trampoline::ThreadYieldTo { .. } => todo!("Trampoline::ThreadYieldTo"),
2549
2550 Trampoline::Trap => {
2551 uwriteln!(
2552 self.src.js,
2553 "function trampoline{i}(rep) {{ throw new TypeError('Trap'); }}"
2554 );
2555 }
2556
2557 Trampoline::EnterSyncCall => {
2558 let enter_symmetric_sync_guest_call_fn = self.bindgen.intrinsic(
2559 Intrinsic::AsyncTask(AsyncTaskIntrinsic::EnterSymmetricSyncGuestCall),
2560 );
2561 uwriteln!(
2562 self.src.js,
2563 r#"
2564 const trampoline{i} = {enter_symmetric_sync_guest_call_fn};
2565 "#,
2566 );
2567 }
2568
2569 Trampoline::ExitSyncCall => {
2570 let exit_symmetric_sync_guest_call_fn = self.bindgen.intrinsic(
2571 Intrinsic::AsyncTask(AsyncTaskIntrinsic::ExitSymmetricSyncGuestCall),
2572 );
2573 uwriteln!(
2574 self.src.js,
2575 "const trampoline{i} = {exit_symmetric_sync_guest_call_fn};\n",
2576 );
2577 }
2578 }
2579 }
2580
2581 fn instantiation_global_initializer(&mut self, init: &GlobalInitializer) {
2582 match init {
2583 GlobalInitializer::ExtractCallback(ExtractCallback { index, def }) => {
2590 let callback_idx = index.as_u32();
2591 let core_def = self.core_def(def);
2592
2593 uwriteln!(self.src.js, "let callback_{callback_idx};",);
2594
2595 uwriteln!(
2604 self.src.js_init,
2605 r#"
2606 callback_{callback_idx} = WebAssembly.promising({core_def});
2607 callback_{callback_idx}.fnName = "{core_def}";
2608 "#
2609 );
2610 }
2611
2612 GlobalInitializer::InstantiateModule(m, _) => match m {
2613 InstantiateModule::Static(idx, args) => {
2614 self.instantiate_static_module(*idx, args);
2615 }
2616 InstantiateModule::Import(..) => unimplemented!(),
2620 },
2621
2622 GlobalInitializer::LowerImport { index, import } => {
2623 self.lower_import(*index, *import);
2624 }
2625
2626 GlobalInitializer::ExtractMemory(m) => {
2627 let def = self.core_export_var_name(&m.export);
2628 let idx = m.index.as_u32();
2629 uwriteln!(self.src.js, "let memory{idx};");
2630 uwriteln!(self.src.js_init, "memory{idx} = {def};");
2631 }
2632
2633 GlobalInitializer::ExtractRealloc(r) => {
2634 let def = self.core_def(&r.def);
2635 let idx = r.index.as_u32();
2636 uwriteln!(self.src.js, "let realloc{idx};");
2637 uwriteln!(self.src.js, "let realloc{idx}Async;");
2638 uwriteln!(self.src.js_init, "realloc{idx} = {def};",);
2639 uwriteln!(
2642 self.src.js_init,
2643 r#"
2644 try {{
2645 realloc{idx}Async = WebAssembly.promising({def});
2646 }} catch(err) {{
2647 realloc{idx}Async = {def};
2648 }}
2649 "#
2650 );
2651 }
2652
2653 GlobalInitializer::ExtractPostReturn(p) => {
2654 let def = self.core_def(&p.def);
2655 let idx = p.index.as_u32();
2656 uwriteln!(self.src.js, "let postReturn{idx};");
2657 uwriteln!(self.src.js, "let postReturn{idx}Async;");
2658 uwriteln!(self.src.js_init, "postReturn{idx} = {def};");
2659 uwriteln!(
2662 self.src.js_init,
2663 r#"
2664 try {{
2665 postReturn{idx}Async = WebAssembly.promising({def});
2666 }} catch(err) {{
2667 postReturn{idx}Async = {def};
2668 }}
2669 "#
2670 );
2671 }
2672
2673 GlobalInitializer::Resource(_) => {}
2674
2675 GlobalInitializer::ExtractTable(_) => {}
2676 }
2677 }
2678
2679 fn instantiate_static_module(&mut self, module_idx: StaticModuleIndex, args: &[CoreDef]) {
2680 let mut import_obj = BTreeMap::new();
2685 for (module, name, arg) in self.modules[module_idx].imports(args) {
2686 let def = self.augmented_import_def(&arg);
2687 let dst = import_obj.entry(module).or_insert(BTreeMap::new());
2688 let prev = dst.insert(name, def);
2689 assert!(
2690 prev.is_none(),
2691 "unsupported duplicate import of `{module}::{name}`"
2692 );
2693 assert!(prev.is_none());
2694 }
2695
2696 let mut imports = String::new();
2698 if !import_obj.is_empty() {
2699 imports.push_str(", {\n");
2700 for (module, names) in import_obj {
2701 imports.push_str(&maybe_quote_id(module));
2702 imports.push_str(": {\n");
2703 for (name, val) in names {
2704 imports.push_str(&maybe_quote_id(name));
2705 uwriteln!(imports, ": {val},");
2706 }
2707 imports.push_str("},\n");
2708 }
2709 imports.push('}');
2710 }
2711
2712 let i = self.instances.push(module_idx);
2713 let iu32 = i.as_u32();
2714 let instantiate = self.bindgen.intrinsic(Intrinsic::InstantiateCore);
2715 uwriteln!(self.src.js, "let exports{iu32};");
2716
2717 match self.bindgen.opts.instantiation {
2718 Some(InstantiationMode::Async) | None => {
2719 uwriteln!(
2720 self.src.js_init,
2721 "({{ exports: exports{iu32} }} = yield {instantiate}(yield module{}{imports}));",
2722 module_idx.as_u32(),
2723 )
2724 }
2725
2726 Some(InstantiationMode::Sync) => {
2727 uwriteln!(
2728 self.src.js_init,
2729 "({{ exports: exports{iu32} }} = {instantiate}(module{}{imports}));",
2730 module_idx.as_u32(),
2731 );
2732 }
2733 }
2734 }
2735
2736 fn create_resource_fn_map(
2744 &mut self,
2745 func: &Function,
2746 ty_func_idx: TypeFuncIndex,
2747 resource_map: &mut ResourceMap,
2748 ) {
2749 let params_ty = &self.types[self.types[ty_func_idx].params];
2751 for (p, iface_ty) in func.params.iter().zip(params_ty.types.iter()) {
2752 if let Type::Id(id) = p.ty {
2753 self.connect_resource_types(id, iface_ty, resource_map);
2754 }
2755 }
2756 let results_ty = &self.types[self.types[ty_func_idx].results];
2758 if let (Some(Type::Id(id)), Some(iface_ty)) = (func.result, results_ty.types.first()) {
2759 self.connect_resource_types(id, iface_ty, resource_map);
2760 }
2761 }
2762
2763 fn resource_name(
2764 resolve: &Resolve,
2765 local_names: &'a mut LocalNames,
2766 resource: TypeId,
2767 resource_map: &BTreeMap<TypeId, ResourceIndex>,
2768 ) -> &'a str {
2769 let resource = crate::dealias(resolve, resource);
2770 local_names
2771 .get_or_create(
2772 resource_map[&resource],
2773 &resolve.types[resource]
2774 .name
2775 .as_ref()
2776 .unwrap()
2777 .to_upper_camel_case(),
2778 )
2779 .0
2780 }
2781
2782 fn lower_import(&mut self, index: LoweredIndex, import: RuntimeImportIndex) {
2783 let (options, trampoline, func_ty) = self.lowering_options[index];
2784
2785 let (import_index, path) = &self.component.imports[import];
2787 let (import_name, _) = &self.component.import_types[*import_index];
2788 let world_key = &self.imports[import_name];
2789
2790 let (func, func_name, iface_name) =
2792 match &self.resolve.worlds[self.world].imports[world_key] {
2793 WorldItem::Function(func) => {
2794 assert_eq!(path.len(), 0);
2795 (func, import_name, None)
2796 }
2797 WorldItem::Interface { id, .. } => {
2798 assert_eq!(path.len(), 1);
2799 let iface = &self.resolve.interfaces[*id];
2800 let func = &iface.functions[&path[0]];
2801 (
2802 func,
2803 &path[0],
2804 Some(iface.name.as_deref().unwrap_or_else(|| import_name)),
2805 )
2806 }
2807 WorldItem::Type { .. } => unreachable!("unexpected imported world item type"),
2808 };
2809
2810 let is_async = is_async_fn(func, options);
2811
2812 if options.async_ {
2813 assert!(
2814 options.post_return.is_none(),
2815 "async function {func_name} (import {import_name}) can't have post return",
2816 );
2817 }
2818
2819 let requires_async_porcelain = requires_async_porcelain(
2821 FunctionIdentifier::Fn(func),
2822 import_name,
2823 &self.async_imports,
2824 );
2825
2826 let (import_specifier, maybe_iface_member) = map_import(
2828 &self.bindgen.opts.map,
2829 if iface_name.is_some() {
2830 import_name
2831 } else {
2832 match func.kind {
2833 FunctionKind::Method(_) => {
2834 let stripped = import_name.strip_prefix("[method]").unwrap();
2835 &stripped[0..stripped.find(".").unwrap()]
2836 }
2837 FunctionKind::AsyncMethod(_) => {
2838 let stripped = import_name.strip_prefix("[async method]").unwrap();
2839 &stripped[0..stripped.find(".").unwrap()]
2840 }
2841 FunctionKind::Static(_) => {
2842 let stripped = import_name.strip_prefix("[static]").unwrap();
2843 &stripped[0..stripped.find(".").unwrap()]
2844 }
2845 FunctionKind::AsyncStatic(_) => {
2846 let stripped = import_name.strip_prefix("[async static]").unwrap();
2847 &stripped[0..stripped.find(".").unwrap()]
2848 }
2849 FunctionKind::Constructor(_) => {
2850 import_name.strip_prefix("[constructor]").unwrap()
2851 }
2852 FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => import_name,
2853 }
2854 },
2855 );
2856
2857 let mut import_resource_map = ResourceMap::new();
2859
2860 self.create_resource_fn_map(func, func_ty, &mut import_resource_map);
2861
2862 let (callee_name, call_type) = match func.kind {
2863 FunctionKind::Freestanding => (
2864 self.bindgen
2865 .local_names
2866 .get_or_create(
2867 format!(
2868 "import:{import}-{maybe_iface_member}-{func_name}",
2869 import = import_specifier,
2870 maybe_iface_member = maybe_iface_member.as_deref().unwrap_or(""),
2871 func_name = &func.name
2872 ),
2873 &func.name,
2874 )
2875 .0
2876 .to_string(),
2877 CallType::Standard,
2878 ),
2879
2880 FunctionKind::AsyncFreestanding => (
2881 self.bindgen
2882 .local_names
2883 .get_or_create(
2884 format!(
2885 "import:async-{import}-{maybe_iface_member}-{func_name}",
2886 import = import_specifier,
2887 maybe_iface_member = maybe_iface_member.as_deref().unwrap_or(""),
2888 func_name = &func.name
2889 ),
2890 &func.name,
2891 )
2892 .0
2893 .to_string(),
2894 CallType::AsyncStandard,
2895 ),
2896
2897 FunctionKind::Method(_) => (
2898 func.item_name().to_lower_camel_case(),
2899 CallType::CalleeResourceDispatch,
2900 ),
2901
2902 FunctionKind::AsyncMethod(_) => (
2903 func.item_name().to_lower_camel_case(),
2904 CallType::AsyncCalleeResourceDispatch,
2905 ),
2906
2907 FunctionKind::Static(resource_id) => (
2908 format!(
2909 "{}.{}",
2910 Instantiator::resource_name(
2911 self.resolve,
2912 &mut self.bindgen.local_names,
2913 resource_id,
2914 &self.imports_resource_types
2915 ),
2916 func.item_name().to_lower_camel_case()
2917 ),
2918 CallType::Standard,
2919 ),
2920
2921 FunctionKind::AsyncStatic(resource_id) => (
2922 format!(
2923 "{}.{}",
2924 Instantiator::resource_name(
2925 self.resolve,
2926 &mut self.bindgen.local_names,
2927 resource_id,
2928 &self.imports_resource_types
2929 ),
2930 func.item_name().to_lower_camel_case()
2931 ),
2932 CallType::AsyncStandard,
2933 ),
2934
2935 FunctionKind::Constructor(resource_id) => (
2936 format!(
2937 "new {}",
2938 Instantiator::resource_name(
2939 self.resolve,
2940 &mut self.bindgen.local_names,
2941 resource_id,
2942 &self.imports_resource_types
2943 )
2944 ),
2945 CallType::Standard,
2946 ),
2947 };
2948
2949 let nparams = self
2950 .resolve
2951 .wasm_signature(AbiVariant::GuestImport, func)
2952 .params
2953 .len();
2954
2955 let trampoline_idx = trampoline.as_u32();
2957 match self.bindgen.opts.import_bindings {
2958 None | Some(BindingsMode::Js) | Some(BindingsMode::Hybrid) => {
2959 if is_async | requires_async_porcelain {
2961 uwrite!(
2966 self.src.js,
2967 "\nconst _trampoline{trampoline_idx} = async function"
2968 );
2969 } else {
2970 uwrite!(
2971 self.src.js,
2972 "\nconst _trampoline{trampoline_idx} = function"
2973 );
2974 }
2975
2976 let iface_name = if import_name.is_empty() {
2977 None
2978 } else {
2979 Some(import_name.to_string())
2980 };
2981
2982 self.bindgen(JsFunctionBindgenArgs {
2984 nparams,
2985 call_type,
2986 iface_name: iface_name.as_deref(),
2987 callee: &callee_name,
2988 opts: options,
2989 func,
2990 resource_map: &import_resource_map,
2991 abi: AbiVariant::GuestImport,
2992 requires_async_porcelain,
2993 is_async,
2994 });
2995 uwriteln!(self.src.js, "");
2996
2997 uwriteln!(
2998 self.src.js,
2999 "_trampoline{trampoline_idx}.fnName = '{}#{callee_name}';",
3000 iface_name.unwrap_or_default(),
3001 );
3002
3003 if requires_async_porcelain {
3005 uwriteln!(
3006 self.src.js,
3007 "_trampoline{trampoline_idx}.manuallyAsync = true;"
3008 );
3009 }
3010 }
3011
3012 Some(BindingsMode::Optimized) | Some(BindingsMode::DirectOptimized) => {
3013 uwriteln!(self.src.js, "let trampoline{trampoline_idx};");
3014 }
3015 };
3016
3017 if !matches!(
3022 self.bindgen.opts.import_bindings,
3023 None | Some(BindingsMode::Js)
3024 ) {
3025 let (memory, realloc) =
3026 if let CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions {
3027 memory,
3028 realloc,
3029 }) = options.data_model
3030 {
3031 (
3032 memory.map(|idx| format!(" memory: memory{},", idx.as_u32())),
3033 realloc.map(|idx| format!(" realloc: realloc{},", idx.as_u32())),
3034 )
3035 } else {
3036 (None, None)
3037 };
3038 let memory = memory.unwrap_or_default();
3039 let realloc = realloc.unwrap_or_default();
3040
3041 let post_return = options
3042 .post_return
3043 .map(|idx| format!(" postReturn: postReturn{},", idx.as_u32()))
3044 .unwrap_or("".into());
3045 let string_encoding = match options.string_encoding {
3046 wasmtime_environ::component::StringEncoding::Utf8 => "",
3047 wasmtime_environ::component::StringEncoding::Utf16 => " stringEncoding: 'utf16',",
3048 wasmtime_environ::component::StringEncoding::CompactUtf16 => {
3049 " stringEncoding: 'compact-utf16',"
3050 }
3051 };
3052
3053 let callee_name = match func.kind {
3054 FunctionKind::Constructor(_) => callee_name[4..].to_string(),
3055
3056 FunctionKind::Static(_)
3057 | FunctionKind::AsyncStatic(_)
3058 | FunctionKind::Freestanding
3059 | FunctionKind::AsyncFreestanding => callee_name.to_string(),
3060
3061 FunctionKind::Method(resource_id) | FunctionKind::AsyncMethod(resource_id) => {
3062 format!(
3063 "{}.prototype.{callee_name}",
3064 Instantiator::resource_name(
3065 self.resolve,
3066 &mut self.bindgen.local_names,
3067 resource_id,
3068 &self.imports_resource_types
3069 )
3070 )
3071 }
3072 };
3073
3074 self.resource_imports.extend(import_resource_map.clone());
3076
3077 let resource_tables = {
3078 let mut resource_tables: Vec<TypeResourceTableIndex> = Vec::new();
3079
3080 for (_, data) in import_resource_map {
3081 let ResourceTable {
3082 data: ResourceData::Host { tid, .. },
3083 ..
3084 } = &data
3085 else {
3086 unreachable!("unexpected non-host resource table");
3087 };
3088 resource_tables.push(*tid);
3089 }
3090
3091 if resource_tables.is_empty() {
3092 "".to_string()
3093 } else {
3094 format!(
3095 " resourceTables: [{}],",
3096 resource_tables
3097 .iter()
3098 .map(|x| format!("handleTable{}", x.as_u32()))
3099 .collect::<Vec<String>>()
3100 .join(", ")
3101 )
3102 }
3103 };
3104
3105 match self.bindgen.opts.import_bindings {
3107 Some(BindingsMode::Hybrid) => {
3108 let symbol_cabi_lower = self.bindgen.intrinsic(Intrinsic::SymbolCabiLower);
3109 uwriteln!(self.src.js_init, "if ({callee_name}[{symbol_cabi_lower}]) {{
3110 trampoline{} = {callee_name}[{symbol_cabi_lower}]({{{memory}{realloc}{post_return}{string_encoding}{resource_tables}}});
3111 }}", trampoline.as_u32());
3112 }
3113 Some(BindingsMode::Optimized) => {
3114 let symbol_cabi_lower = self.bindgen.intrinsic(Intrinsic::SymbolCabiLower);
3115 if !self.bindgen.opts.valid_lifting_optimization {
3116 uwriteln!(self.src.js_init, "if (!{callee_name}[{symbol_cabi_lower}]) {{
3117 throw new TypeError('import for \"{import_name}\" does not define a Symbol.for(\"cabiLower\") optimized binding');
3118 }}");
3119 }
3120 uwriteln!(
3121 self.src.js_init,
3122 "trampoline{} = {callee_name}[{symbol_cabi_lower}]({{{memory}{realloc}{post_return}{string_encoding}{resource_tables}}});",
3123 trampoline.as_u32()
3124 );
3125 }
3126 Some(BindingsMode::DirectOptimized) => {
3127 uwriteln!(
3128 self.src.js_init,
3129 "trampoline{} = {callee_name}({{{memory}{realloc}{post_return}{string_encoding}}});",
3130 trampoline.as_u32()
3131 );
3132 }
3133 None | Some(BindingsMode::Js) => unreachable!("invalid bindings mode"),
3134 };
3135 }
3136
3137 let (import_name, binding_name) = match func.kind {
3139 FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
3140 (func_name.to_lower_camel_case(), callee_name)
3141 }
3142
3143 FunctionKind::Method(tid)
3144 | FunctionKind::AsyncMethod(tid)
3145 | FunctionKind::Static(tid)
3146 | FunctionKind::AsyncStatic(tid)
3147 | FunctionKind::Constructor(tid) => {
3148 let ty = &self.resolve.types[tid];
3149 let class_name = ty.name.as_ref().unwrap().to_upper_camel_case();
3150 let resource_name = Instantiator::resource_name(
3151 self.resolve,
3152 &mut self.bindgen.local_names,
3153 tid,
3154 &self.imports_resource_types,
3155 )
3156 .to_string();
3157 (class_name, resource_name)
3158 }
3159 };
3160
3161 self.ensure_import(
3162 import_specifier,
3163 iface_name,
3164 maybe_iface_member.as_deref(),
3165 if iface_name.is_some() {
3166 Some(import_name.to_string())
3167 } else {
3168 None
3169 },
3170 binding_name,
3171 );
3172 }
3173
3174 fn ensure_import(
3185 &mut self,
3186 import_specifier: String,
3187 iface_name: Option<&str>,
3188 iface_member: Option<&str>,
3189 import_binding: Option<String>,
3190 local_name: String,
3191 ) {
3192 if import_specifier.starts_with("webidl:") {
3193 self.bindgen
3194 .intrinsic(Intrinsic::WebIdl(WebIdlIntrinsic::GlobalThisIdlProxy));
3195 }
3196
3197 let mut import_path = Vec::with_capacity(2);
3199 import_path.push(import_specifier);
3200 if let Some(_iface_name) = iface_name {
3201 if let Some(iface_member) = iface_member {
3204 import_path.push(iface_member.to_lower_camel_case());
3205 }
3206 import_path.push(import_binding.clone().unwrap());
3207 } else if let Some(iface_member) = iface_member {
3208 import_path.push(iface_member.into());
3209 } else if let Some(import_binding) = &import_binding {
3210 import_path.push(import_binding.into());
3211 }
3212
3213 self.bindgen
3215 .esm_bindgen
3216 .add_import_binding(&import_path, local_name);
3217 }
3218
3219 fn connect_p3_resources(
3228 &mut self,
3229 id: &TypeId,
3230 maybe_elem_ty: &Option<Type>,
3231 iface_ty: &InterfaceType,
3232 resource_map: &mut ResourceMap,
3233 ) {
3234 let remote_resource = match iface_ty {
3235 InterfaceType::Future(table_idx) => ResourceTable {
3236 imported: true,
3237 data: ResourceData::Guest {
3238 resource_name: "Future".into(),
3239 prefix: Some(format!("${}", table_idx.as_u32())),
3240 extra: Some(ResourceExtraData::Future {
3241 table_idx: *table_idx,
3242 elem_ty: *maybe_elem_ty,
3243 }),
3244 },
3245 },
3246 InterfaceType::Stream(table_idx) => ResourceTable {
3247 imported: true,
3248 data: ResourceData::Guest {
3249 resource_name: "Stream".into(),
3250 prefix: Some(format!("${}", table_idx.as_u32())),
3251 extra: Some(ResourceExtraData::Stream {
3252 table_idx: *table_idx,
3253 elem_ty: *maybe_elem_ty,
3254 }),
3255 },
3256 },
3257 InterfaceType::ErrorContext(table_idx) => ResourceTable {
3258 imported: true,
3259 data: ResourceData::Guest {
3260 resource_name: "ErrorContext".into(),
3261 prefix: Some(format!("${}", table_idx.as_u32())),
3262 extra: Some(ResourceExtraData::ErrorContext {
3263 table_idx: *table_idx,
3264 }),
3265 },
3266 },
3267 _ => unreachable!("unexpected interface type [{iface_ty:?}] with no type"),
3268 };
3269
3270 resource_map.insert(*id, remote_resource);
3271 }
3272
3273 fn connect_host_resource(
3282 &mut self,
3283 t: TypeId,
3284 resource_table_ty_idx: TypeResourceTableIndex,
3285 resource_map: &mut ResourceMap,
3286 ) {
3287 self.ensure_resource_table(resource_table_ty_idx);
3288
3289 let resource_table_ty = &self.types[resource_table_ty_idx];
3291 let resource_idx = resource_table_ty.unwrap_concrete_ty();
3292 let imported = self
3293 .component
3294 .defined_resource_index(resource_idx)
3295 .is_none();
3296
3297 let resource_id = crate::dealias(self.resolve, t);
3299 let ty = &self.resolve.types[resource_id];
3300
3301 let mut dtor_str = None;
3304 if let Some(resource_idx) = self.component.defined_resource_index(resource_idx) {
3305 assert!(!imported);
3306 let resource_def = self
3307 .component
3308 .initializers
3309 .iter()
3310 .find_map(|i| match i {
3311 GlobalInitializer::Resource(r) if r.index == resource_idx => Some(r),
3312 _ => None,
3313 })
3314 .unwrap();
3315
3316 if let Some(dtor) = &resource_def.dtor {
3317 dtor_str = Some(self.core_def(dtor));
3318 }
3319 }
3320
3321 let resource_name = ty.name.as_ref().unwrap().to_upper_camel_case();
3323
3324 let local_name = if imported {
3325 let (world_key, iface_name) = match ty.owner {
3326 wit_parser::TypeOwner::World(world) => (
3327 self.resolve.worlds[world]
3328 .imports
3329 .iter()
3330 .find(|&(_, item)| matches!(*item, WorldItem::Type { id, .. } if id == t))
3331 .unwrap()
3332 .0
3333 .clone(),
3334 None,
3335 ),
3336 wit_parser::TypeOwner::Interface(iface) => {
3337 match &self.resolve.interfaces[iface].name {
3338 Some(name) => (WorldKey::Interface(iface), Some(name.as_str())),
3339 None => {
3340 let key = self.resolve.worlds[self.world]
3341 .imports
3342 .iter()
3343 .find(|&(_, item)| match item {
3344 WorldItem::Interface { id, .. } => *id == iface,
3345 _ => false,
3346 })
3347 .unwrap()
3348 .0;
3349 (
3350 key.clone(),
3351 match key {
3352 WorldKey::Name(name) => Some(name.as_str()),
3353 WorldKey::Interface(_) => None,
3354 },
3355 )
3356 }
3357 }
3358 }
3359 wit_parser::TypeOwner::None => unimplemented!(),
3360 };
3361
3362 let import_name = self.resolve.name_world_key(&world_key);
3363 let (local_name, _) = self
3364 .bindgen
3365 .local_names
3366 .get_or_create(resource_idx, &resource_name);
3367
3368 let local_name_str = local_name.to_string();
3369
3370 let (import_specifier, maybe_iface_member) =
3372 map_import(&self.bindgen.opts.map, &import_name);
3373
3374 self.ensure_import(
3376 import_specifier,
3377 iface_name,
3378 maybe_iface_member.as_deref(),
3379 iface_name.map(|_| resource_name),
3380 local_name_str.to_string(),
3381 );
3382 local_name_str
3383 } else {
3384 let (local_name, _) = self
3385 .bindgen
3386 .local_names
3387 .get_or_create(resource_idx, &resource_name);
3388 local_name.to_string()
3389 };
3390
3391 let entry = ResourceTable {
3393 imported,
3394 data: ResourceData::Host {
3395 tid: resource_table_ty_idx,
3396 rid: resource_idx,
3397 local_name,
3398 dtor_name: dtor_str,
3399 },
3400 };
3401
3402 if let Some(existing) = resource_map.get(&resource_id) {
3405 assert_eq!(*existing, entry);
3406 return;
3407 }
3408
3409 resource_map.insert(resource_id, entry);
3411 }
3412
3413 fn connect_resource_types(
3426 &mut self,
3427 id: TypeId,
3428 iface_ty: &InterfaceType,
3429 resource_map: &mut ResourceMap,
3430 ) {
3431 let kind = &self.resolve.types[id].kind;
3432 match (kind, iface_ty) {
3433 (TypeDefKind::Flags(_), InterfaceType::Flags(_))
3435 | (TypeDefKind::Enum(_), InterfaceType::Enum(_)) => {}
3436
3437 (TypeDefKind::Record(t1), InterfaceType::Record(t2)) => {
3439 let t2 = &self.types[*t2];
3440 for (f1, f2) in t1.fields.iter().zip(t2.fields.iter()) {
3441 if let Type::Id(id) = f1.ty {
3442 self.connect_resource_types(id, &f2.ty, resource_map);
3443 }
3444 }
3445 }
3446
3447 (
3449 TypeDefKind::Handle(Handle::Own(t1) | Handle::Borrow(t1)),
3450 InterfaceType::Own(t2) | InterfaceType::Borrow(t2),
3451 ) => {
3452 self.connect_host_resource(*t1, *t2, resource_map);
3453 }
3454
3455 (TypeDefKind::Tuple(t1), InterfaceType::Tuple(t2)) => {
3457 let t2 = &self.types[*t2];
3458 for (f1, f2) in t1.types.iter().zip(t2.types.iter()) {
3459 if let Type::Id(id) = f1 {
3460 self.connect_resource_types(*id, f2, resource_map);
3461 }
3462 }
3463 }
3464
3465 (TypeDefKind::Variant(t1), InterfaceType::Variant(t2)) => {
3467 let t2 = &self.types[*t2];
3468 for (f1, f2) in t1.cases.iter().zip(t2.cases.iter()) {
3469 if let Some(Type::Id(id)) = &f1.ty {
3470 self.connect_resource_types(*id, f2.1.as_ref().unwrap(), resource_map);
3471 }
3472 }
3473 }
3474
3475 (TypeDefKind::Option(t1), InterfaceType::Option(t2)) => {
3477 let t2 = &self.types[*t2];
3478 if let Type::Id(id) = t1 {
3479 self.connect_resource_types(*id, &t2.ty, resource_map);
3480 }
3481 }
3482
3483 (TypeDefKind::Result(t1), InterfaceType::Result(t2)) => {
3485 let t2 = &self.types[*t2];
3486 if let Some(Type::Id(id)) = &t1.ok {
3487 self.connect_resource_types(*id, &t2.ok.unwrap(), resource_map);
3488 }
3489 if let Some(Type::Id(id)) = &t1.err {
3490 self.connect_resource_types(*id, &t2.err.unwrap(), resource_map);
3491 }
3492 }
3493
3494 (TypeDefKind::List(t1), InterfaceType::List(t2)) => {
3496 let t2 = &self.types[*t2];
3497 if let Type::Id(id) = t1 {
3498 self.connect_resource_types(*id, &t2.element, resource_map);
3499 }
3500 }
3501
3502 (TypeDefKind::FixedLengthList(t1, _len), InterfaceType::FixedLengthList(t2)) => {
3504 let t2 = &self.types[*t2];
3505 if let Type::Id(id) = t1 {
3506 self.connect_resource_types(*id, &t2.element, resource_map);
3507 }
3508 }
3509
3510 (TypeDefKind::Type(ty), _) => {
3512 if let Type::Id(id) = ty {
3513 self.connect_resource_types(*id, iface_ty, resource_map);
3514 }
3515 }
3516
3517 (TypeDefKind::Future(maybe_elem_ty), _iface_ty)
3519 | (TypeDefKind::Stream(maybe_elem_ty), _iface_ty) => {
3520 match maybe_elem_ty {
3521 None => {
3524 self.connect_p3_resources(&id, maybe_elem_ty, iface_ty, resource_map);
3525 }
3526 Some(elem_ty @ Type::Id(_t)) => {
3528 self.connect_p3_resources(&id, &Some(*elem_ty), iface_ty, resource_map);
3529 }
3530 Some(_) => {
3532 self.connect_p3_resources(&id, maybe_elem_ty, iface_ty, resource_map);
3533 }
3534 }
3535 }
3536
3537 (
3539 TypeDefKind::Result(Result_ { ok, err }),
3540 tk2 @ (InterfaceType::Future(_) | InterfaceType::Stream(_)),
3541 ) => {
3542 if let Some(Type::Id(ok_t)) = ok {
3543 self.connect_resource_types(*ok_t, tk2, resource_map)
3544 }
3545 if let Some(Type::Id(err_t)) = err {
3546 self.connect_resource_types(*err_t, tk2, resource_map)
3547 }
3548 }
3549
3550 (
3552 TypeDefKind::Option(ty),
3553 tk2 @ (InterfaceType::Future(_) | InterfaceType::Stream(_)),
3554 ) => {
3555 if let Type::Id(some_t) = ty {
3556 self.connect_resource_types(*some_t, tk2, resource_map)
3557 }
3558 }
3559
3560 (
3562 TypeDefKind::Handle(Handle::Own(t1) | Handle::Borrow(t1)),
3563 tk2 @ (InterfaceType::Future(_) | InterfaceType::Stream(_)),
3564 ) => self.connect_resource_types(*t1, tk2, resource_map),
3565
3566 (TypeDefKind::Resource, InterfaceType::Future(_) | InterfaceType::Stream(_)) => {}
3567
3568 (
3570 TypeDefKind::Variant(variant),
3571 tk2 @ (InterfaceType::Future(_) | InterfaceType::Stream(_)),
3572 ) => {
3573 for f1 in variant.cases.iter() {
3574 if let Some(Type::Id(id)) = &f1.ty {
3575 self.connect_resource_types(*id, tk2, resource_map);
3576 }
3577 }
3578 }
3579
3580 (
3582 TypeDefKind::Record(record),
3583 tk2 @ (InterfaceType::Future(_) | InterfaceType::Stream(_)),
3584 ) => {
3585 for f1 in record.fields.iter() {
3586 if let Type::Id(id) = f1.ty {
3587 self.connect_resource_types(id, tk2, resource_map);
3588 }
3589 }
3590 }
3591
3592 (
3595 TypeDefKind::Enum(_) | TypeDefKind::Flags(_),
3596 InterfaceType::Future(_) | InterfaceType::Stream(_),
3597 ) => {}
3598
3599 (TypeDefKind::Resource, tk2) => {
3600 unreachable!(
3601 "resource types do not need to be connected (in this case, to [{tk2:?}])"
3602 )
3603 }
3604
3605 (TypeDefKind::Unknown, tk2) => {
3606 unreachable!("unknown types cannot be connected (in this case to [{tk2:?}])")
3607 }
3608
3609 (tk1, tk2) => unreachable!("invalid typedef kind combination [{tk1:?}] [{tk2:?}]",),
3610 }
3611 }
3612
3613 fn bindgen(&mut self, args: JsFunctionBindgenArgs) {
3614 let JsFunctionBindgenArgs {
3615 nparams,
3616 call_type,
3617 iface_name,
3618 callee,
3619 opts,
3620 func,
3621 resource_map,
3622 abi,
3623 requires_async_porcelain,
3624 is_async,
3625 } = args;
3626
3627 let (memory, realloc) =
3628 if let CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions {
3629 memory,
3630 realloc,
3631 }) = opts.data_model
3632 {
3633 (
3634 memory.map(|idx| format!("memory{}", idx.as_u32())),
3635 realloc.map(|idx| {
3636 format!(
3637 "realloc{}{}",
3638 idx.as_u32(),
3639 if is_async {
3640 "Async"
3641 } else {
3642 Default::default()
3643 }
3644 )
3645 }),
3646 )
3647 } else {
3648 (None, None)
3649 };
3650
3651 let post_return = opts.post_return.map(|idx| {
3652 format!(
3653 "postReturn{}{}",
3654 idx.as_u32(),
3655 if is_async {
3656 "Async"
3657 } else {
3658 Default::default()
3659 }
3660 )
3661 });
3662
3663 let tracing_prefix = format!(
3664 "[iface=\"{}\", function=\"{}\"]",
3665 iface_name.unwrap_or("<no iface>"),
3666 func.name
3667 );
3668
3669 self.src.js("(");
3673 let mut params = Vec::new();
3674 let mut first = true;
3675 for i in 0..nparams {
3676 if i == 0
3677 && matches!(
3678 call_type,
3679 CallType::FirstArgIsThis | CallType::AsyncFirstArgIsThis
3680 )
3681 {
3682 params.push("this".into());
3683 continue;
3684 }
3685 if !first {
3686 self.src.js(", ");
3687 } else {
3688 first = false;
3689 }
3690 let param = format!("arg{i}");
3691 self.src.js(¶m);
3692 params.push(param);
3693 }
3694 uwriteln!(self.src.js, ") {{");
3695
3696 if self.bindgen.opts.tracing {
3698 let event_fields = func
3699 .params
3700 .iter()
3701 .enumerate()
3702 .map(|(i, p)| format!("{}=${{arguments[{i}]}}", p.name))
3703 .collect::<Vec<String>>();
3704 uwriteln!(
3705 self.src.js,
3706 "console.error(`{tracing_prefix} call {}`);",
3707 event_fields.join(", ")
3708 );
3709 }
3710
3711 if self.bindgen.opts.tla_compat
3713 && matches!(abi, AbiVariant::GuestExport)
3714 && self.bindgen.opts.instantiation.is_none()
3715 {
3716 let throw_uninitialized = self.bindgen.intrinsic(Intrinsic::ThrowUninitialized);
3717 uwrite!(
3718 self.src.js,
3719 "\
3720 if (!_initialized) {throw_uninitialized}();
3721 "
3722 );
3723 }
3724
3725 let mut f = FunctionBindgen {
3727 resource_map,
3728 clear_resource_borrows: false,
3729 intrinsics: &mut self.bindgen.all_intrinsics,
3730 valid_lifting_optimization: self.bindgen.opts.valid_lifting_optimization,
3731 sizes: &self.sizes,
3732 err: if get_thrown_type(self.resolve, func.result).is_some() {
3733 match abi {
3734 AbiVariant::GuestExport
3735 | AbiVariant::GuestExportAsync
3736 | AbiVariant::GuestExportAsyncStackful => ErrHandling::ThrowResultErr,
3737 AbiVariant::GuestImport | AbiVariant::GuestImportAsync => {
3738 ErrHandling::ResultCatchHandler
3739 }
3740 }
3741 } else {
3742 ErrHandling::None
3743 },
3744 block_storage: Vec::new(),
3745 blocks: Vec::new(),
3746 callee,
3747 callee_resource_dynamic: matches!(call_type, CallType::CalleeResourceDispatch),
3748 memory: memory.as_ref(),
3749 realloc: realloc.as_ref(),
3750 tmp: 0,
3751 params,
3752 post_return: post_return.as_ref(),
3753 tracing_prefix: &tracing_prefix,
3754 tracing_enabled: self.bindgen.opts.tracing,
3755 encoding: match opts.string_encoding {
3756 wasmtime_environ::component::StringEncoding::Utf8 => StringEncoding::UTF8,
3757 wasmtime_environ::component::StringEncoding::Utf16 => StringEncoding::UTF16,
3758 wasmtime_environ::component::StringEncoding::CompactUtf16 => {
3759 StringEncoding::CompactUTF16
3760 }
3761 },
3762 src: source::Source::default(),
3763 resolve: self.resolve,
3764 requires_async_porcelain,
3765 is_async,
3766 canon_opts: opts,
3767 iface_name,
3768 };
3769
3770 abi::call(
3773 self.resolve,
3774 abi,
3775 match abi {
3776 AbiVariant::GuestImport | AbiVariant::GuestImportAsync => {
3777 LiftLower::LiftArgsLowerResults
3778 }
3779 AbiVariant::GuestExport
3780 | AbiVariant::GuestExportAsync
3781 | AbiVariant::GuestExportAsyncStackful => LiftLower::LowerArgsLiftResults,
3782 },
3783 func,
3784 &mut f,
3785 is_async,
3786 );
3787
3788 self.src.js(&f.src);
3790
3791 self.src.js("}");
3793 }
3794
3795 fn augmented_import_def(&self, def: &core::AugmentedImport<'_>) -> String {
3796 match def {
3797 core::AugmentedImport::CoreDef(def) => self.core_def(def),
3798 core::AugmentedImport::Memory { mem, op } => {
3799 let mem = self.core_def(mem);
3800 match op {
3801 core::AugmentedOp::I32Load => {
3802 format!(
3803 "(ptr, off) => new DataView({mem}.buffer).getInt32(ptr + off, true)"
3804 )
3805 }
3806 core::AugmentedOp::I32Load8U => {
3807 format!(
3808 "(ptr, off) => new DataView({mem}.buffer).getUint8(ptr + off, true)"
3809 )
3810 }
3811 core::AugmentedOp::I32Load8S => {
3812 format!("(ptr, off) => new DataView({mem}.buffer).getInt8(ptr + off, true)")
3813 }
3814 core::AugmentedOp::I32Load16U => {
3815 format!(
3816 "(ptr, off) => new DataView({mem}.buffer).getUint16(ptr + off, true)"
3817 )
3818 }
3819 core::AugmentedOp::I32Load16S => {
3820 format!(
3821 "(ptr, off) => new DataView({mem}.buffer).getInt16(ptr + off, true)"
3822 )
3823 }
3824 core::AugmentedOp::I64Load => {
3825 format!(
3826 "(ptr, off) => new DataView({mem}.buffer).getBigInt64(ptr + off, true)"
3827 )
3828 }
3829 core::AugmentedOp::F32Load => {
3830 format!(
3831 "(ptr, off) => new DataView({mem}.buffer).getFloat32(ptr + off, true)"
3832 )
3833 }
3834 core::AugmentedOp::F64Load => {
3835 format!(
3836 "(ptr, off) => new DataView({mem}.buffer).getFloat64(ptr + off, true)"
3837 )
3838 }
3839 core::AugmentedOp::I32Store8 => {
3840 format!(
3841 "(ptr, val, offset) => {{
3842 new DataView({mem}.buffer).setInt8(ptr + offset, val, true);
3843 }}"
3844 )
3845 }
3846 core::AugmentedOp::I32Store16 => {
3847 format!(
3848 "(ptr, val, offset) => {{
3849 new DataView({mem}.buffer).setInt16(ptr + offset, val, true);
3850 }}"
3851 )
3852 }
3853 core::AugmentedOp::I32Store => {
3854 format!(
3855 "(ptr, val, offset) => {{
3856 new DataView({mem}.buffer).setInt32(ptr + offset, val, true);
3857 }}"
3858 )
3859 }
3860 core::AugmentedOp::I64Store => {
3861 format!(
3862 "(ptr, val, offset) => {{
3863 new DataView({mem}.buffer).setBigInt64(ptr + offset, val, true);
3864 }}"
3865 )
3866 }
3867 core::AugmentedOp::F32Store => {
3868 format!(
3869 "(ptr, val, offset) => {{
3870 new DataView({mem}.buffer).setFloat32(ptr + offset, val, true);
3871 }}"
3872 )
3873 }
3874 core::AugmentedOp::F64Store => {
3875 format!(
3876 "(ptr, val, offset) => {{
3877 new DataView({mem}.buffer).setFloat64(ptr + offset, val, true);
3878 }}"
3879 )
3880 }
3881 core::AugmentedOp::MemorySize => {
3882 format!("ptr => {mem}.buffer.byteLength / 65536")
3883 }
3884 }
3885 }
3886 }
3887 }
3888
3889 fn core_def(&self, def: &CoreDef) -> String {
3890 match def {
3891 CoreDef::Export(e) => self.core_export_var_name(e),
3892 CoreDef::TaskMayBlock => AsyncTaskIntrinsic::CurrentTaskMayBlock.name().into(),
3893 CoreDef::Trampoline(i) => format!("trampoline{}", i.as_u32()),
3894 CoreDef::InstanceFlags(i) => {
3895 self.used_instance_flags.borrow_mut().insert(*i);
3897 format!("instanceFlags{}", i.as_u32())
3898 }
3899 CoreDef::UnsafeIntrinsic(ui) => {
3900 let idx = ui.index();
3901 format!("unsafeIntrinsic{idx}")
3902 }
3903 }
3904 }
3905
3906 fn core_export_var_name<T>(&self, export: &CoreExport<T>) -> String
3907 where
3908 T: Into<EntityIndex> + Copy,
3909 {
3910 let name = match &export.item {
3911 ExportItem::Index(idx) => {
3912 let module_idx = self
3913 .instances
3914 .get(export.instance)
3915 .expect("unexpectedly missing export instance");
3916 let module = &self
3917 .modules
3918 .get(*module_idx)
3919 .expect("unexpectedly missing module by idx");
3920 let idx = (*idx).into();
3921 module
3922 .exports()
3923 .iter()
3924 .find_map(|(name, i)| if *i == idx { Some(name) } else { None })
3925 .unwrap()
3926 }
3927 ExportItem::Name(s) => s,
3928 };
3929 let i = export.instance.as_u32() as usize;
3930 let quoted = maybe_quote_member(name);
3931 format!("exports{i}{quoted}")
3932 }
3933
3934 fn exports(&mut self, exports: &NameMap<String, ExportIndex>) {
3935 for (export_name, export_idx) in exports.raw_iter() {
3936 let export = &self.component.export_items[*export_idx];
3937 let world_key = &self.exports[export_name];
3938 let item = &self.resolve.worlds[self.world].exports[world_key];
3939 let mut export_resource_map = ResourceMap::new();
3940
3941 match export {
3942 Export::LiftedFunction {
3943 func: def,
3944 options,
3945 ty: func_ty,
3946 } => {
3947 let func = match item {
3948 WorldItem::Function(f) => f,
3949 WorldItem::Interface { .. } | WorldItem::Type { .. } => {
3950 unreachable!("unexpectedly non-function lifted function export")
3951 }
3952 };
3953 self.create_resource_fn_map(func, *func_ty, &mut export_resource_map);
3954
3955 let local_name = String::from(match func.kind {
3956 FunctionKind::Constructor(resource_id)
3958 | FunctionKind::Method(resource_id)
3959 | FunctionKind::AsyncMethod(resource_id)
3960 | FunctionKind::Static(resource_id)
3961 | FunctionKind::AsyncStatic(resource_id) => Instantiator::resource_name(
3962 self.resolve,
3963 &mut self.bindgen.local_names,
3964 resource_id,
3965 &self.exports_resource_types,
3966 ),
3967 FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
3969 self.bindgen.local_names.create_once(export_name)
3970 }
3971 });
3972
3973 let options = self
3974 .component
3975 .options
3976 .get(*options)
3977 .expect("failed to find options");
3978
3979 self.export_bindgen(
3980 &local_name,
3981 def,
3982 options,
3983 func,
3984 func_ty,
3985 export_name,
3986 &export_resource_map,
3987 );
3988
3989 let js_binding_name = match func.kind {
3990 FunctionKind::Constructor(ty)
3992 | FunctionKind::Method(ty)
3993 | FunctionKind::AsyncMethod(ty)
3994 | FunctionKind::Static(ty)
3995 | FunctionKind::AsyncStatic(ty) => self.resolve.types[ty]
3996 .name
3997 .as_ref()
3998 .unwrap()
3999 .to_upper_camel_case(),
4000 FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
4002 export_name.to_lower_camel_case()
4003 }
4004 };
4005
4006 self.bindgen.esm_bindgen.add_export_binding(
4008 None,
4009 local_name,
4010 js_binding_name,
4011 func,
4012 );
4013 }
4014
4015 Export::Instance { exports, .. } => {
4016 let iface_id = match item {
4017 WorldItem::Interface { id, .. } => *id,
4018 WorldItem::Function(_) | WorldItem::Type { .. } => {
4019 unreachable!("unexpectedly non-interface export instance")
4020 }
4021 };
4022
4023 for (func_name, export_idx) in exports.raw_iter() {
4025 let export = &self.component.export_items[*export_idx];
4026
4027 let (def, options, func_ty) = match export {
4029 Export::LiftedFunction { func, options, ty } => (func, options, ty),
4030 Export::Type(_) => continue, _ => unreachable!("unexpected non-lifted function export"),
4032 };
4033
4034 let func = &self.resolve.interfaces[iface_id].functions[func_name];
4035
4036 self.create_resource_fn_map(func, *func_ty, &mut export_resource_map);
4037
4038 let local_name = String::from(match func.kind {
4039 FunctionKind::Constructor(resource_id)
4041 | FunctionKind::Method(resource_id)
4042 | FunctionKind::AsyncMethod(resource_id)
4043 | FunctionKind::Static(resource_id)
4044 | FunctionKind::AsyncStatic(resource_id) => {
4045 Instantiator::resource_name(
4046 self.resolve,
4047 &mut self.bindgen.local_names,
4048 resource_id,
4049 &self.exports_resource_types,
4050 )
4051 }
4052 FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
4054 self.bindgen.local_names.create_once(func_name)
4055 }
4056 });
4057
4058 let options = self
4059 .component
4060 .options
4061 .get(*options)
4062 .expect("failed to find options");
4063
4064 self.export_bindgen(
4065 &local_name,
4066 def,
4067 options,
4068 func,
4069 func_ty,
4070 export_name,
4071 &export_resource_map,
4072 );
4073
4074 let export_binding_name = match func.kind {
4076 FunctionKind::Constructor(ty)
4078 | FunctionKind::Method(ty)
4079 | FunctionKind::AsyncMethod(ty)
4080 | FunctionKind::Static(ty)
4081 | FunctionKind::AsyncStatic(ty) => self.resolve.types[ty]
4082 .name
4083 .as_ref()
4084 .unwrap()
4085 .to_upper_camel_case(),
4086 FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => {
4088 func_name.to_lower_camel_case()
4089 }
4090 };
4091
4092 self.bindgen.esm_bindgen.add_export_binding(
4094 Some(export_name),
4095 local_name,
4096 export_binding_name,
4097 func,
4098 );
4099 }
4100 }
4101
4102 Export::Type(_) => {}
4104
4105 Export::ModuleStatic { .. } | Export::ModuleImport { .. } => unimplemented!(),
4107 }
4108
4109 self.resource_exports.extend(export_resource_map);
4111 }
4112
4113 self.bindgen.esm_bindgen.populate_export_aliases();
4114 }
4115
4116 #[allow(clippy::too_many_arguments)]
4117 fn export_bindgen(
4118 &mut self,
4119 local_name: &str,
4120 def: &CoreDef,
4121 options: &CanonicalOptions,
4122 func: &Function,
4123 _func_ty_idx: &TypeFuncIndex,
4124 export_name: &String,
4125 export_resource_map: &ResourceMap,
4126 ) {
4127 let requires_async_porcelain = requires_async_porcelain(
4129 FunctionIdentifier::Fn(func),
4130 export_name,
4131 &self.async_exports,
4132 );
4133 if options.async_ {
4135 assert!(
4136 options.post_return.is_none(),
4137 "async function {local_name} (export {export_name}) can't have post return"
4138 );
4139 }
4140
4141 let is_async = is_async_fn(func, options);
4142
4143 let maybe_async = if requires_async_porcelain || is_async {
4144 "async "
4145 } else {
4146 ""
4147 };
4148
4149 let core_export_fn = self.core_def(def);
4151 let callee = match self
4152 .bindgen
4153 .local_names
4154 .get_or_create(&core_export_fn, &core_export_fn)
4155 {
4156 (local_name, true) => local_name.to_string(),
4157 (local_name, false) => {
4158 let local_name = local_name.to_string();
4159 uwriteln!(self.src.js, "let {local_name};");
4160 self.bindgen
4161 .all_core_exported_funcs
4162 .push((core_export_fn.clone(), is_async | requires_async_porcelain));
4166 local_name
4167 }
4168 };
4169
4170 let iface_name = if export_name.is_empty() {
4171 None
4172 } else {
4173 Some(export_name)
4174 };
4175
4176 match func.kind {
4178 FunctionKind::Freestanding => {
4179 uwrite!(self.src.js, "\n{maybe_async}function {local_name}")
4180 }
4181 FunctionKind::Method(_) => {
4182 self.ensure_local_resource_class(local_name.to_string());
4183 let method_name = func.item_name().to_lower_camel_case();
4184
4185 uwrite!(
4186 self.src.js,
4187 "\n{local_name}.prototype.{method_name} = {maybe_async}function {}",
4188 if !is_js_reserved_word(&method_name) {
4189 method_name.to_string()
4190 } else {
4191 format!("${method_name}")
4192 }
4193 );
4194 }
4195 FunctionKind::Static(_) => {
4196 self.ensure_local_resource_class(local_name.to_string());
4197 let method_name = func.item_name().to_lower_camel_case();
4198 uwrite!(
4199 self.src.js,
4200 "\n{local_name}.{method_name} = function {}",
4201 if !is_js_reserved_word(&method_name) {
4202 method_name.to_string()
4203 } else {
4204 format!("${method_name}")
4205 }
4206 );
4207 }
4208 FunctionKind::Constructor(_) => {
4209 if self.defined_resource_classes.contains(local_name) {
4210 panic!(
4211 "Internal error: Resource constructor must be defined before other methods and statics"
4212 );
4213 }
4214 uwrite!(
4215 self.src.js,
4216 "
4217 class {local_name} {{
4218 constructor"
4219 );
4220 self.defined_resource_classes.insert(local_name.to_string());
4221 }
4222 FunctionKind::AsyncFreestanding => {
4223 uwrite!(self.src.js, "\nasync function {local_name}")
4224 }
4225 FunctionKind::AsyncMethod(_) => {
4226 self.ensure_local_resource_class(local_name.to_string());
4227 let method_name = func.item_name().to_lower_camel_case();
4228 let fn_name = if !is_js_reserved_word(&method_name) {
4229 method_name.to_string()
4230 } else {
4231 format!("${method_name}")
4232 };
4233 uwrite!(
4234 self.src.js,
4235 "\n{local_name}.prototype.{method_name} = async function {fn_name}",
4236 );
4237 }
4238 FunctionKind::AsyncStatic(_) => {
4239 self.ensure_local_resource_class(local_name.to_string());
4240 let method_name = func.item_name().to_lower_camel_case();
4241 let fn_name = if !is_js_reserved_word(&method_name) {
4242 method_name.to_string()
4243 } else {
4244 format!("${method_name}")
4245 };
4246 uwrite!(
4247 self.src.js,
4248 "\n{local_name}.{method_name} = async function {fn_name}",
4249 );
4250 }
4251 };
4252
4253 self.bindgen(JsFunctionBindgenArgs {
4255 nparams: func.params.len(),
4256 call_type: match func.kind {
4257 FunctionKind::Method(_) => CallType::FirstArgIsThis,
4258 FunctionKind::AsyncMethod(_) => CallType::AsyncFirstArgIsThis,
4259 FunctionKind::Freestanding
4260 | FunctionKind::Static(_)
4261 | FunctionKind::Constructor(_) => CallType::Standard,
4262 FunctionKind::AsyncFreestanding | FunctionKind::AsyncStatic(_) => {
4263 CallType::AsyncStandard
4264 }
4265 },
4266 iface_name: iface_name.map(|v| v.as_str()),
4267 callee: &callee,
4268 opts: options,
4269 func,
4270 resource_map: export_resource_map,
4271 abi: AbiVariant::GuestExport,
4272 requires_async_porcelain,
4273 is_async,
4274 });
4275
4276 match func.kind {
4278 FunctionKind::AsyncFreestanding | FunctionKind::Freestanding => self.src.js("\n"),
4279 FunctionKind::AsyncMethod(_)
4280 | FunctionKind::AsyncStatic(_)
4281 | FunctionKind::Method(_)
4282 | FunctionKind::Static(_) => self.src.js(";\n"),
4283 FunctionKind::Constructor(_) => self.src.js("\n}\n"),
4284 }
4285 }
4286}
4287
4288#[derive(Default)]
4289pub struct Source {
4290 pub js: source::Source,
4291 pub js_init: source::Source,
4292}
4293
4294impl Source {
4295 pub fn js(&mut self, s: &str) {
4296 self.js.push_str(s);
4297 }
4298 pub fn js_init(&mut self, s: &str) {
4299 self.js_init.push_str(s);
4300 }
4301}
4302
4303fn semver_compat_key(version_str: &str) -> Option<(String, Version)> {
4314 let version = Version::parse(version_str).ok()?;
4315 if !version.pre.is_empty() {
4316 None
4317 } else if version.major != 0 {
4318 Some((format!("{}", version.major), version))
4319 } else if version.minor != 0 {
4320 Some((format!("0.{}", version.minor), version))
4321 } else {
4322 None
4323 }
4324}
4325
4326fn parse_mapping(mapping: &str) -> (String, Option<String>) {
4327 if mapping.len() > 1
4328 && let Some(hash_idx) = mapping[1..].find('#')
4329 {
4330 return (
4331 mapping[0..hash_idx + 1].to_string(),
4332 Some(mapping[hash_idx + 2..].into()),
4333 );
4334 }
4335 (mapping.into(), None)
4336}
4337
4338fn map_import(map: &Option<HashMap<String, String>>, impt: &str) -> (String, Option<String>) {
4339 let impt_sans_version = match impt.find('@') {
4340 Some(version_idx) => &impt[0..version_idx],
4341 None => impt,
4342 };
4343 if let Some(map) = map.as_ref() {
4344 if let Some(mapping) = map.get(impt) {
4346 return parse_mapping(mapping);
4347 }
4348 if let Some(mapping) = map.get(impt_sans_version) {
4350 return parse_mapping(mapping);
4351 }
4352 for (key, mapping) in map {
4354 if let Some(wildcard_idx) = key.find('*') {
4355 let lhs = &key[0..wildcard_idx];
4356 let rhs = &key[wildcard_idx + 1..];
4357 if impt_sans_version.starts_with(lhs) && impt_sans_version.ends_with(rhs) {
4358 let matched = &impt_sans_version[wildcard_idx
4359 ..wildcard_idx + impt_sans_version.len() - lhs.len() - rhs.len()];
4360 let mapping = mapping.replace('*', matched);
4361 return parse_mapping(&mapping);
4362 }
4363 if impt.starts_with(lhs) && impt.ends_with(rhs) {
4364 let matched =
4365 &impt[wildcard_idx..wildcard_idx + impt.len() - lhs.len() - rhs.len()];
4366 let mapping = mapping.replace('*', matched);
4367 return parse_mapping(&mapping);
4368 }
4369 }
4370 }
4371 if let Some(at) = impt.find('@') {
4375 let impt_ver_str = &impt[at + 1..];
4376 if let Some((impt_compat, _)) = semver_compat_key(impt_ver_str) {
4377 let mut best_match: Option<(String, Version)> = None;
4378
4379 for (key, mapping) in map {
4380 let key_at = match key.find('@') {
4382 Some(at) => at,
4383 None => continue,
4384 };
4385 let key_base = &key[..key_at];
4386 let key_ver_str = &key[key_at + 1..];
4387
4388 let (key_compat, key_ver) = match semver_compat_key(key_ver_str) {
4390 Some(k) => k,
4391 None => continue,
4392 };
4393 if impt_compat != key_compat {
4394 continue;
4395 }
4396
4397 let resolved = if let Some(wildcard_idx) = key_base.find('*') {
4400 let lhs = &key_base[..wildcard_idx];
4401 let rhs = &key_base[wildcard_idx + 1..];
4402 if impt_sans_version.starts_with(lhs) && impt_sans_version.ends_with(rhs) {
4403 let matched = &impt_sans_version[wildcard_idx
4404 ..wildcard_idx + impt_sans_version.len() - lhs.len() - rhs.len()];
4405 Some(mapping.replace('*', matched))
4406 } else {
4407 None
4408 }
4409 } else if key_base == impt_sans_version {
4410 Some(mapping.clone())
4411 } else {
4412 None
4413 };
4414
4415 if let Some(resolved_mapping) = resolved {
4416 match &best_match {
4418 Some((_, prev_ver)) if key_ver <= *prev_ver => {}
4419 _ => {
4420 best_match = Some((resolved_mapping, key_ver));
4421 }
4422 }
4423 }
4424 }
4425
4426 if let Some((mapping, _)) = best_match {
4427 return parse_mapping(&mapping);
4428 }
4429 }
4430 }
4431 }
4432 (impt_sans_version.to_string(), None)
4433}
4434
4435pub fn parse_world_key(name: &str) -> Option<(&str, &str, &str)> {
4436 let registry_idx = name.find(':')?;
4437 let ns = &name[0..registry_idx];
4438 match name.rfind('/') {
4439 Some(sep_idx) => {
4440 let end = if let Some(version_idx) = name.rfind('@') {
4441 version_idx
4442 } else {
4443 name.len()
4444 };
4445 Some((
4446 ns,
4447 &name[registry_idx + 1..sep_idx],
4448 &name[sep_idx + 1..end],
4449 ))
4450 }
4451 None => Some((ns, &name[registry_idx + 1..], "")),
4453 }
4454}
4455
4456fn core_file_name(name: &str, idx: u32) -> String {
4457 let i_str = if idx == 0 {
4458 String::from("")
4459 } else {
4460 (idx + 1).to_string()
4461 };
4462 format!("{name}.core{i_str}.wasm")
4463}
4464
4465fn string_encoding_js_literal(val: &wasmtime_environ::component::StringEncoding) -> &'static str {
4467 match val {
4468 wasmtime_environ::component::StringEncoding::Utf8 => "'utf8'",
4469 wasmtime_environ::component::StringEncoding::Utf16 => "'utf16'",
4470 wasmtime_environ::component::StringEncoding::CompactUtf16 => "'compact-utf16'",
4471 }
4472}
4473
4474pub fn gen_flat_lift_fn_list_js_expr(
4476 intrinsic_mgr: &mut Instantiator,
4477 types: &[InterfaceType],
4478 canon_opts: &CanonicalOptions,
4479) -> String {
4480 let mut lift_fns: Vec<String> = Vec::with_capacity(types.len());
4481 for ty in types.iter() {
4482 lift_fns.push(gen_flat_lift_fn_js_expr(
4483 intrinsic_mgr,
4484 ty,
4485 &canon_opts.string_encoding,
4486 ));
4487 }
4488 format!("[{}]", lift_fns.join(","))
4489}
4490
4491pub fn gen_flat_lift_fn_js_expr(
4506 intrinsic_mgr: &mut Instantiator,
4507 ty: &InterfaceType,
4508 string_encoding: &wasmtime_environ::component::StringEncoding,
4509) -> String {
4510 let component_types = intrinsic_mgr.types;
4511
4512 match ty {
4513 InterfaceType::Bool => {
4514 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatBool));
4515 Intrinsic::Lift(LiftIntrinsic::LiftFlatBool).name().into()
4516 }
4517
4518 InterfaceType::S8 => {
4519 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS8));
4520 Intrinsic::Lift(LiftIntrinsic::LiftFlatS8).name().into()
4521 }
4522
4523 InterfaceType::U8 => {
4524 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU8));
4525 Intrinsic::Lift(LiftIntrinsic::LiftFlatU8).name().into()
4526 }
4527
4528 InterfaceType::S16 => {
4529 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS16));
4530 Intrinsic::Lift(LiftIntrinsic::LiftFlatS16).name().into()
4531 }
4532
4533 InterfaceType::U16 => {
4534 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU16));
4535 Intrinsic::Lift(LiftIntrinsic::LiftFlatU16).name().into()
4536 }
4537
4538 InterfaceType::S32 => {
4539 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS32));
4540 Intrinsic::Lift(LiftIntrinsic::LiftFlatS32).name().into()
4541 }
4542
4543 InterfaceType::U32 => {
4544 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU32));
4545 Intrinsic::Lift(LiftIntrinsic::LiftFlatU32).name().into()
4546 }
4547
4548 InterfaceType::S64 => {
4549 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS64));
4550 Intrinsic::Lift(LiftIntrinsic::LiftFlatS64).name().into()
4551 }
4552
4553 InterfaceType::U64 => {
4554 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU64));
4555 Intrinsic::Lift(LiftIntrinsic::LiftFlatU64).name().into()
4556 }
4557
4558 InterfaceType::Float32 => {
4559 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFloat32));
4560 Intrinsic::Lift(LiftIntrinsic::LiftFlatFloat32)
4561 .name()
4562 .into()
4563 }
4564
4565 InterfaceType::Float64 => {
4566 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFloat64));
4567 Intrinsic::Lift(LiftIntrinsic::LiftFlatFloat64)
4568 .name()
4569 .into()
4570 }
4571
4572 InterfaceType::Char => {
4573 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatChar));
4574 Intrinsic::Lift(LiftIntrinsic::LiftFlatChar).name().into()
4575 }
4576
4577 InterfaceType::String => match string_encoding {
4578 wasmtime_environ::component::StringEncoding::Utf8 => {
4579 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatStringUtf8));
4580 Intrinsic::Lift(LiftIntrinsic::LiftFlatStringUtf8)
4581 .name()
4582 .into()
4583 }
4584 wasmtime_environ::component::StringEncoding::Utf16 => {
4585 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatStringUtf16));
4586 Intrinsic::Lift(LiftIntrinsic::LiftFlatStringUtf16)
4587 .name()
4588 .into()
4589 }
4590 wasmtime_environ::component::StringEncoding::CompactUtf16 => {
4591 todo!("latin1+utf8 not supported")
4592 }
4593 },
4594
4595 InterfaceType::Record(ty_idx) => {
4596 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatRecord));
4597 let lift_fn = Intrinsic::Lift(LiftIntrinsic::LiftFlatRecord).name();
4598 let record_ty = &component_types[*ty_idx];
4599 let mut keys_and_lifts_expr = String::from("[");
4600 for f in &record_ty.fields {
4604 keys_and_lifts_expr.push_str(&format!(
4605 "['{}', {}, {}, {}],",
4606 f.name.to_lower_camel_case(),
4607 gen_flat_lift_fn_js_expr(intrinsic_mgr, &f.ty, string_encoding),
4608 component_types.canonical_abi(ty).size32,
4609 component_types.canonical_abi(ty).align32,
4610 ));
4611 }
4612 keys_and_lifts_expr.push(']');
4613 format!("{lift_fn}({keys_and_lifts_expr})")
4614 }
4615
4616 InterfaceType::Variant(ty_idx) => {
4617 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatVariant));
4618 let lift_fn = Intrinsic::Lift(LiftIntrinsic::LiftFlatVariant).name();
4619 let variant_ty = &component_types[*ty_idx];
4620 let mut cases_and_lifts_expr = String::from("[");
4621 for (name, maybe_ty) in &variant_ty.cases {
4622 let lift_args = match maybe_ty {
4623 None => format!("['{}', null, 0, 0, 0],", name),
4624 Some(ty) => {
4625 format!(
4626 "['{name}', {}, {}, {}, {}],",
4627 gen_flat_lift_fn_js_expr(intrinsic_mgr, ty, string_encoding),
4628 variant_ty.abi.size32,
4629 variant_ty.abi.align32,
4630 variant_ty.info.payload_offset32,
4631 )
4632 }
4633 };
4634 cases_and_lifts_expr.push_str(&lift_args);
4635 }
4636 cases_and_lifts_expr.push(']');
4637 format!("{lift_fn}({cases_and_lifts_expr})")
4638 }
4639
4640 InterfaceType::List(ty_idx) => {
4641 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatList));
4642 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatList).name();
4643 let list_ty = &component_types[*ty_idx];
4644 let lift_fn_expr =
4645 gen_flat_lift_fn_js_expr(intrinsic_mgr, &list_ty.element, string_encoding);
4646 let elem_cabi = component_types.canonical_abi(&list_ty.element);
4647 let align_32 = elem_cabi.align32;
4648 let size_32 = elem_cabi.size32;
4649 format!("{f}({{ elemLiftFn: {lift_fn_expr}, align32: {align_32}, size32: {size_32} }})")
4650 }
4651
4652 InterfaceType::FixedLengthList(ty_idx) => {
4653 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatList));
4654 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatList).name();
4655 let list_ty = &component_types[*ty_idx];
4656 let lift_fn_expr =
4657 gen_flat_lift_fn_js_expr(intrinsic_mgr, &list_ty.element, string_encoding);
4658 let list_len = list_ty.size;
4659 let elem_cabi = component_types.canonical_abi(&list_ty.element);
4660 let align_32 = elem_cabi.align32;
4661 let size_32 = elem_cabi.size32;
4662 format!(
4663 "{f}({{ elemLiftFn: {lift_fn_expr}, align32: {align_32}, size32: {size_32}, knownLen: {list_len} }})"
4664 )
4665 }
4666
4667 InterfaceType::Tuple(ty_idx) => {
4668 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatTuple));
4669 let tuple_ty = &component_types[*ty_idx];
4670 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatTuple).name();
4671 let size_u32 = tuple_ty.abi.size32;
4672 let align_u32 = tuple_ty.abi.align32;
4673
4674 let mut elem_lifts_expr = String::from("[");
4675 for ty in &tuple_ty.types {
4676 let lift_fn_js = gen_flat_lift_fn_js_expr(intrinsic_mgr, ty, string_encoding);
4677 elem_lifts_expr.push_str(&format!("[{lift_fn_js}, {size_u32}, {align_u32}],"));
4678 }
4679 elem_lifts_expr.push(']');
4680
4681 format!("{f}({elem_lifts_expr})")
4682 }
4683
4684 InterfaceType::Flags(ty_idx) => {
4685 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFlags));
4686 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatFlags).name();
4687 let flags_ty = &component_types[*ty_idx];
4688 let size_u32 = flags_ty.abi.size32;
4689 let align_u32 = flags_ty.abi.align32;
4690 let names_expr = format!(
4691 "[{}]",
4692 flags_ty
4693 .names
4694 .iter()
4695 .map(|s| format!("'{s}'"))
4696 .collect::<Vec<_>>()
4697 .join(",")
4698 );
4699 let num_flags = flags_ty.names.len();
4700 let elem_size = if num_flags <= 8 {
4701 1
4702 } else if num_flags <= 16 {
4703 2
4704 } else {
4705 4
4706 };
4707 format!(
4708 "{f}({{ names: {names_expr}, size32: {size_u32}, align32: {align_u32}, intSize: {elem_size} }})"
4709 )
4710 }
4711
4712 InterfaceType::Enum(ty_idx) => {
4713 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatEnum));
4714 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatEnum).name();
4715 let enum_ty = &component_types[*ty_idx];
4716 let size_32 = enum_ty.abi.size32;
4717 let align_32 = enum_ty.abi.align32;
4718 let payload_offset_32 = enum_ty.info.payload_offset32;
4719
4720 let mut elem_lifts_expr = String::from("[");
4721 for name in &enum_ty.names {
4722 elem_lifts_expr.push_str(&format!(
4723 "['{name}', null, {size_32}, {align_32}, {payload_offset_32}],"
4724 ));
4725 }
4726 elem_lifts_expr.push(']');
4727
4728 format!("{f}({elem_lifts_expr})")
4729 }
4730
4731 InterfaceType::Option(ty_idx) => {
4732 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatOption));
4733 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatOption).name();
4734 let option_ty = &component_types[*ty_idx];
4735 let payload_offset_32 = option_ty.info.payload_offset32;
4736 let align_32 = option_ty.abi.align32;
4737 let size_32 = option_ty.abi.size32;
4738 let lift_fn_js =
4739 gen_flat_lift_fn_js_expr(intrinsic_mgr, &option_ty.ty, string_encoding);
4740 format!(
4742 "{f}([
4743 ['none', null, {size_32}, {align_32}, {payload_offset_32} ],
4744 ['some', {lift_fn_js}, {size_32}, {align_32}, {payload_offset_32} ],
4745 ])"
4746 )
4747 }
4748
4749 InterfaceType::Result(ty_idx) => {
4750 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatResult));
4751 let lift_fn = Intrinsic::Lift(LiftIntrinsic::LiftFlatResult).name();
4752 let result_ty = &component_types[*ty_idx];
4753 let mut cases_and_lifts_expr = String::from("[");
4754
4755 if let Some(ok_ty) = result_ty.ok {
4756 cases_and_lifts_expr.push_str(&format!(
4757 "['ok', {}, {}, {}, {}],",
4758 gen_flat_lift_fn_js_expr(intrinsic_mgr, &ok_ty, string_encoding),
4759 result_ty.abi.size32,
4760 result_ty.abi.align32,
4761 result_ty.info.payload_offset32,
4762 ))
4763 } else {
4764 cases_and_lifts_expr.push_str("['ok', null, 0, 0, 0],");
4765 }
4766
4767 if let Some(err_ty) = &result_ty.err {
4768 cases_and_lifts_expr.push_str(&format!(
4769 "['err', {}, {}, {}, {}],",
4770 gen_flat_lift_fn_js_expr(intrinsic_mgr, err_ty, string_encoding),
4771 result_ty.abi.size32,
4772 result_ty.abi.align32,
4773 result_ty.info.payload_offset32,
4774 ))
4775 } else {
4776 cases_and_lifts_expr.push_str("['err', null, 0, 0, 0],");
4777 }
4778
4779 cases_and_lifts_expr.push(']');
4780 format!("{lift_fn}({cases_and_lifts_expr})")
4781 }
4782
4783 InterfaceType::Own(ty_idx) => {
4784 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatOwn));
4785 intrinsic_mgr.add_intrinsic(Intrinsic::JsHelper(JsHelperIntrinsic::EmptyFunc));
4786 intrinsic_mgr.add_intrinsic(Intrinsic::SymbolResourceHandle);
4787 intrinsic_mgr.add_intrinsic(Intrinsic::SymbolDispose);
4788 intrinsic_mgr
4789 .add_intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableRemove));
4790 intrinsic_mgr.add_intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableFlag));
4791 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatOwn).name();
4792 let table_ty = &component_types[*ty_idx];
4793 let component_idx = table_ty.unwrap_concrete_instance().as_u32();
4794 let resource_idx = table_ty.unwrap_concrete_ty();
4795
4796 if let Some(resource_typedef) = intrinsic_mgr
4797 .exports_resource_index_types
4798 .get(&resource_idx)
4799 && let Some(ResourceTable { data, .. }) =
4800 intrinsic_mgr.resource_exports.get(resource_typedef)
4801 {
4802 let (resource_class_name, create_resource_fn_js) = match data {
4803 ResourceData::Guest { .. } => {
4804 unimplemented!(
4805 "owned resources created by guests should must have host-side data"
4806 )
4807 }
4808 ResourceData::Host {
4809 tid,
4810 local_name,
4811 dtor_name,
4812 ..
4813 } => {
4814 let empty_func = JsHelperIntrinsic::EmptyFunc.name();
4815 let symbol_resource_handle = Intrinsic::SymbolResourceHandle.name();
4816 let symbol_dispose = Intrinsic::SymbolDispose.name();
4817 let rsc_table_remove = ResourceIntrinsic::ResourceTableRemove.name();
4818 let tid = tid.as_u32();
4819 let rsc_flag = ResourceIntrinsic::ResourceTableFlag.name();
4820
4821 let dtor_setup_js = dtor_name.as_ref().map(|dtor|
4822 format!(
4823 r#"
4824 Object.defineProperty(
4825 resourceObj,
4826 {symbol_dispose},
4827 {{
4828 writable: true,
4829 value: function() {{
4830 finalizationRegistry{tid}.unregister(resourceObj);
4831 {rsc_table_remove}(handleTable{tid}, handle);
4832 resourceObj[{symbol_dispose}] = {empty_func};
4833 resourceObj[{symbol_resource_handle}] = undefined;
4834 {dtor}(handleTable{tid}[(handle << 1) + 1] & ~{rsc_flag});
4835 }}
4836 }}
4837 );
4838 "#
4839 )
4840 ).unwrap_or_default();
4841
4842 let create_resource_fn_js = format!(
4843 r#"
4844 (handle) => {{
4845 const resourceObj = Object.create({local_name}.prototype);
4846 Object.defineProperty(resourceObj, {symbol_resource_handle}, {{
4847 writable: true,
4848 value: handle,
4849 }});
4850 finalizationRegistry{tid}.register(resourceObj, handle, resourceObj);
4851 {dtor_setup_js}
4852 return resourceObj;
4853 }}
4854 "#
4855 );
4856
4857 (local_name.to_string(), create_resource_fn_js)
4858 }
4859 };
4860
4861 format!(
4862 r#"{f}({{
4863 componentIdx: {component_idx},
4864 className: {resource_class_name},
4865 createResourceFn: {create_resource_fn_js},
4866 }})
4867 "#,
4868 )
4869 } else {
4870 format!(
4872 r#"{f}({{
4873 componentIdx: {component_idx},
4874 className: null,
4875 createResourceFn: () => {{ throw new Error('invalid/missing resource type data'); }},
4876 }})
4877 "#,
4878 )
4879 }
4880 }
4881
4882 InterfaceType::Borrow(ty_idx) => {
4883 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatBorrow));
4884 let table_idx = ty_idx.as_u32();
4885 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatBorrow).name();
4886 format!("{f}.bind(null, {table_idx})")
4887 }
4888
4889 InterfaceType::Future(ty_idx) => {
4890 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFuture));
4891 let table_idx = ty_idx.as_u32();
4892 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatFuture).name();
4893 format!("{f}.bind(null, {table_idx})")
4894 }
4895
4896 InterfaceType::Stream(ty_idx) => {
4897 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatStream));
4898 let table_idx = ty_idx.as_u32();
4899 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatStream).name();
4900 let table_ty = &component_types[*ty_idx];
4901 let component_idx = table_ty.instance.as_u32();
4902 let stream_ty_idx = table_ty.ty;
4903 let stream_ty = &component_types[stream_ty_idx];
4904 let payload = stream_ty.payload;
4905 let (is_borrowed, is_none_type_js, is_numeric_type_js) = match payload {
4906 None => (false, true, false),
4907 Some(t) => (
4908 matches!(t, InterfaceType::Borrow(_)),
4909 false,
4910 matches!(
4911 ty,
4912 InterfaceType::U8
4913 | InterfaceType::U16
4914 | InterfaceType::U32
4915 | InterfaceType::U64
4916 | InterfaceType::S8
4917 | InterfaceType::S16
4918 | InterfaceType::S32
4919 | InterfaceType::S64
4920 | InterfaceType::Float32
4921 | InterfaceType::Float64
4922 ),
4923 ),
4924 };
4925
4926 format!(
4927 r#"{f}({{
4928 streamTableIdx: {table_idx},
4929 componentIdx: {component_idx},
4930 isBorrowedType: {is_borrowed},
4931 isNoneType: {is_none_type_js},
4932 isNumericTypeJs: {is_numeric_type_js},
4933 }})"#
4934 )
4935 }
4936
4937 InterfaceType::ErrorContext(ty_idx) => {
4938 intrinsic_mgr.add_intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatErrorContext));
4939 let table_idx = ty_idx.as_u32();
4940 let f = Intrinsic::Lift(LiftIntrinsic::LiftFlatErrorContext).name();
4941 format!("{f}.bind(null, {table_idx})")
4942 }
4943 }
4944}
4945
4946pub fn gen_flat_lower_fn_list_js_expr(
4948 intrinsic_mgr: &mut impl ManagesIntrinsics,
4949 component_types: &ComponentTypes,
4950 types: &[InterfaceType],
4951 string_encoding: &wasmtime_environ::component::StringEncoding,
4952) -> String {
4953 let mut lower_fns: Vec<String> = Vec::with_capacity(types.len());
4954 for ty in types.iter() {
4955 lower_fns.push(gen_flat_lower_fn_js_expr(
4956 intrinsic_mgr,
4957 component_types,
4958 ty,
4959 string_encoding,
4960 ));
4961 }
4962 format!("[{}]", lower_fns.join(","))
4963}
4964
4965pub fn gen_flat_lower_fn_js_expr(
4980 intrinsic_mgr: &mut impl ManagesIntrinsics,
4981 component_types: &ComponentTypes,
4982 ty: &InterfaceType,
4983 string_encoding: &wasmtime_environ::component::StringEncoding,
4984) -> String {
4985 match ty {
4986 InterfaceType::Bool => {
4987 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatBool));
4988 Intrinsic::Lower(LowerIntrinsic::LowerFlatBool)
4989 .name()
4990 .into()
4991 }
4992
4993 InterfaceType::S8 => {
4994 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatS8));
4995 Intrinsic::Lower(LowerIntrinsic::LowerFlatS8).name().into()
4996 }
4997
4998 InterfaceType::U8 => {
4999 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatU8));
5000 Intrinsic::Lower(LowerIntrinsic::LowerFlatU8).name().into()
5001 }
5002
5003 InterfaceType::S16 => {
5004 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatS16));
5005 Intrinsic::Lower(LowerIntrinsic::LowerFlatS16).name().into()
5006 }
5007
5008 InterfaceType::U16 => {
5009 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatU16));
5010 Intrinsic::Lower(LowerIntrinsic::LowerFlatU16).name().into()
5011 }
5012
5013 InterfaceType::S32 => {
5014 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatS32));
5015 Intrinsic::Lower(LowerIntrinsic::LowerFlatS32).name().into()
5016 }
5017
5018 InterfaceType::U32 => {
5019 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatU32));
5020 Intrinsic::Lower(LowerIntrinsic::LowerFlatU32).name().into()
5021 }
5022
5023 InterfaceType::S64 => {
5024 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatS64));
5025 Intrinsic::Lower(LowerIntrinsic::LowerFlatS64).name().into()
5026 }
5027
5028 InterfaceType::U64 => {
5029 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatU64));
5030 Intrinsic::Lower(LowerIntrinsic::LowerFlatU64).name().into()
5031 }
5032
5033 InterfaceType::Float32 => {
5034 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatFloat32));
5035 Intrinsic::Lower(LowerIntrinsic::LowerFlatFloat32)
5036 .name()
5037 .into()
5038 }
5039
5040 InterfaceType::Float64 => {
5041 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatFloat64));
5042 Intrinsic::Lower(LowerIntrinsic::LowerFlatFloat64)
5043 .name()
5044 .into()
5045 }
5046
5047 InterfaceType::Char => {
5048 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatChar));
5049 Intrinsic::Lower(LowerIntrinsic::LowerFlatChar)
5050 .name()
5051 .into()
5052 }
5053
5054 InterfaceType::String => match string_encoding {
5055 wasmtime_environ::component::StringEncoding::Utf8 => {
5056 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatStringUtf8));
5057 Intrinsic::Lower(LowerIntrinsic::LowerFlatStringUtf8)
5058 .name()
5059 .into()
5060 }
5061 wasmtime_environ::component::StringEncoding::Utf16 => {
5062 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatStringUtf16));
5063 Intrinsic::Lower(LowerIntrinsic::LowerFlatStringUtf16)
5064 .name()
5065 .into()
5066 }
5067 wasmtime_environ::component::StringEncoding::CompactUtf16 => {
5068 todo!("latin1+utf8 not supported")
5069 }
5070 },
5071
5072 InterfaceType::Record(ty_idx) => {
5073 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatRecord));
5074 let lower_fn = Intrinsic::Lower(LowerIntrinsic::LowerFlatRecord).name();
5075 let record_ty = &component_types[*ty_idx];
5076 let mut keys_and_lowers_expr = String::from("[");
5077 for f in &record_ty.fields {
5078 keys_and_lowers_expr.push_str(&format!(
5082 "['{}', {}, {}, {} ],",
5083 f.name.to_lower_camel_case(),
5084 gen_flat_lower_fn_js_expr(
5085 intrinsic_mgr,
5086 component_types,
5087 &f.ty,
5088 string_encoding
5089 ),
5090 component_types.canonical_abi(ty).size32,
5091 component_types.canonical_abi(ty).align32,
5092 ));
5093 }
5094 keys_and_lowers_expr.push(']');
5095 format!("{lower_fn}.bind(null, {keys_and_lowers_expr})")
5096 }
5097
5098 InterfaceType::Variant(ty_idx) => {
5099 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatVariant));
5100 let lower_fn = Intrinsic::Lower(LowerIntrinsic::LowerFlatVariant).name();
5101 let variant_ty = &component_types[*ty_idx];
5102 let size32 = variant_ty.abi.size32;
5103 let align32 = variant_ty.abi.align32;
5104 let payload_offset32 = variant_ty.info.payload_offset32;
5105
5106 let mut lower_metas_expr = String::from("[");
5107 for (name, maybe_ty) in variant_ty.cases.iter() {
5108 lower_metas_expr.push_str(&format!(
5109 "[ '{name}', {}, {size32}, {align32}, {payload_offset32} ],",
5110 maybe_ty
5111 .map(|ty| gen_flat_lower_fn_js_expr(
5112 intrinsic_mgr,
5113 component_types,
5114 &ty,
5115 string_encoding,
5116 ))
5117 .unwrap_or_else(|| "null".into()),
5118 ));
5119 }
5120 lower_metas_expr.push(']');
5121
5122 format!("{lower_fn}({lower_metas_expr})")
5123 }
5124
5125 InterfaceType::List(ty_idx) => {
5126 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatList));
5127 let list_ty = &component_types[*ty_idx];
5128 let elem_ty_lower_expr = gen_flat_lower_fn_js_expr(
5129 intrinsic_mgr,
5130 component_types,
5131 &list_ty.element,
5132 string_encoding,
5133 );
5134 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatList).name();
5135 let ty_idx = ty_idx.as_u32();
5136 format!("{f}({{ elemLowerFn: {elem_ty_lower_expr}, typeIdx: {ty_idx} }})")
5137 }
5138
5139 InterfaceType::FixedLengthList(ty_idx) => {
5140 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatList));
5142 let list_ty = &component_types[*ty_idx];
5143 let elem_ty_lower_expr = gen_flat_lower_fn_js_expr(
5144 intrinsic_mgr,
5145 component_types,
5146 &list_ty.element,
5147 string_encoding,
5148 );
5149 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatList).name();
5150 let ty_idx = ty_idx.as_u32();
5151 format!("{f}({{ elemLowerFn: {elem_ty_lower_expr}, typeIdx: {ty_idx} }})")
5152 }
5153
5154 InterfaceType::Tuple(ty_idx) => {
5155 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatTuple));
5156 let ty_idx = ty_idx.as_u32();
5157 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatTuple).name();
5158 format!("{f}.bind(null, {ty_idx})")
5159 }
5160
5161 InterfaceType::Flags(ty_idx) => {
5162 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatFlags));
5163 let ty_idx = ty_idx.as_u32();
5164 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatFlags).name();
5165 format!("{f}.bind(null, {ty_idx})")
5166 }
5167
5168 InterfaceType::Enum(ty_idx) => {
5169 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatEnum));
5170 let ty_idx = ty_idx.as_u32();
5171 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatEnum).name();
5172 format!("{f}.bind(null, {ty_idx})")
5173 }
5174
5175 InterfaceType::Option(ty_idx) => {
5176 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatOption));
5177 let option_ty = &component_types[*ty_idx];
5178 let size32 = option_ty.abi.size32;
5179 let align32 = option_ty.abi.align32;
5180 let payload_offset32 = option_ty.info.payload_offset32;
5181
5182 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatOption).name();
5183
5184 let mut cases_and_lowers_expr = String::from("[");
5185 cases_and_lowers_expr.push_str(&format!(
5186 "[ 'some', {}, {size32}, {align32}, {payload_offset32} ],",
5187 gen_flat_lower_fn_js_expr(
5188 intrinsic_mgr,
5189 component_types,
5190 &option_ty.ty,
5191 string_encoding,
5192 )
5193 ));
5194 cases_and_lowers_expr.push_str(&format!(
5195 "[ 'none', null, {size32}, {align32}, {payload_offset32} ],",
5196 ));
5197 cases_and_lowers_expr.push(']');
5198
5199 format!("{f}({cases_and_lowers_expr})")
5200 }
5201
5202 InterfaceType::Result(ty_idx) => {
5203 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatResult));
5204 let lower_fn = Intrinsic::Lower(LowerIntrinsic::LowerFlatResult).name();
5205 let result_ty = &component_types[*ty_idx];
5206 let size32 = result_ty.abi.size32;
5207 let align32 = result_ty.abi.align32;
5208 let payload_offset32 = result_ty.info.payload_offset32;
5209
5210 let mut cases_and_lowers_expr = String::from("[");
5211 cases_and_lowers_expr.push_str(&format!(
5212 "[ 'ok', {}, {size32}, {align32}, {payload_offset32} ],",
5213 result_ty
5214 .ok
5215 .map(|ty| gen_flat_lower_fn_js_expr(
5216 intrinsic_mgr,
5217 component_types,
5218 &ty,
5219 string_encoding,
5220 ))
5221 .unwrap_or_else(|| "null".into())
5222 ));
5223 cases_and_lowers_expr.push_str(&format!(
5224 "[ 'err', {}, {size32}, {align32}, {payload_offset32} ],",
5225 result_ty
5226 .err
5227 .map(|ty| gen_flat_lower_fn_js_expr(
5228 intrinsic_mgr,
5229 component_types,
5230 &ty,
5231 string_encoding,
5232 ))
5233 .unwrap_or_else(|| "null".into())
5234 ));
5235 cases_and_lowers_expr.push(']');
5236 format!("{lower_fn}({cases_and_lowers_expr})")
5237 }
5238
5239 InterfaceType::Own(ty_idx) => {
5240 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatOwn));
5241 let table_idx = ty_idx.as_u32();
5242 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatOwn).name();
5243 format!("{f}.bind(null, {table_idx})")
5244 }
5245
5246 InterfaceType::Borrow(ty_idx) => {
5247 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatBorrow));
5248 let table_idx = ty_idx.as_u32();
5249 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatBorrow).name();
5250 format!("{f}.bind(null, {table_idx})")
5251 }
5252
5253 InterfaceType::Future(ty_idx) => {
5254 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatFuture));
5255 let table_idx = ty_idx.as_u32();
5256 let f = Intrinsic::Lower(LowerIntrinsic::LowerFlatFuture).name();
5257
5258 format!("{f}.bind(null, {table_idx})")
5259 }
5260
5261 InterfaceType::Stream(ty_idx) => {
5262 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatStream));
5263 let table_idx = ty_idx.as_u32();
5264 let lower_flat_stream_fn = Intrinsic::Lower(LowerIntrinsic::LowerFlatStream).name();
5265 format!("{lower_flat_stream_fn}.bind(null, {table_idx})")
5266 }
5267
5268 InterfaceType::ErrorContext(ty_idx) => {
5269 intrinsic_mgr.add_intrinsic(Intrinsic::Lower(LowerIntrinsic::LowerFlatErrorContext));
5270 let table_idx = ty_idx.as_u32();
5271 let lower_flat_err_ctx_fn =
5272 Intrinsic::Lower(LowerIntrinsic::LowerFlatErrorContext).name();
5273 format!("{lower_flat_err_ctx_fn}.bind(null, {table_idx})")
5274 }
5275 }
5276}
5277
5278#[cfg(test)]
5279mod tests {
5280 use super::*;
5281
5282 fn compat_key(version_str: &str) -> Option<String> {
5284 semver_compat_key(version_str).map(|(key, _)| key)
5285 }
5286
5287 #[test]
5288 fn test_semver_compat_key() {
5289 assert_eq!(compat_key("1.0.0"), Some("1".into()));
5290 assert_eq!(compat_key("1.2.3"), Some("1".into()));
5291 assert_eq!(compat_key("2.0.0"), Some("2".into()));
5292 assert_eq!(compat_key("0.2.0"), Some("0.2".into()));
5293 assert_eq!(compat_key("0.2.10"), Some("0.2".into()));
5294 assert_eq!(compat_key("0.1.0"), Some("0.1".into()));
5295 assert_eq!(compat_key("0.0.1"), None);
5296 assert_eq!(compat_key("1.0.0-rc.1"), None);
5297 assert_eq!(compat_key("0.2.0-pre"), None);
5298 assert_eq!(compat_key("not-a-version"), None);
5299 }
5300
5301 #[test]
5302 fn test_semver_compat_key_returns_parsed_version() {
5303 let (key, ver) = semver_compat_key("1.2.3").unwrap();
5304 assert_eq!(key, "1");
5305 assert_eq!(ver, Version::new(1, 2, 3));
5306 }
5307
5308 #[test]
5309 fn test_map_import_exact_match() {
5310 let mut map = HashMap::new();
5311 map.insert("wasi:http/types@0.2.0".into(), "./http.js#types".into());
5312 let map = Some(map);
5313 assert_eq!(
5314 map_import(&map, "wasi:http/types@0.2.0"),
5315 ("./http.js".into(), Some("types".into()))
5316 );
5317 }
5318
5319 #[test]
5320 fn test_map_import_sans_version_match() {
5321 let mut map = HashMap::new();
5322 map.insert("wasi:http/types".into(), "./http.js".into());
5323 let map = Some(map);
5324 assert_eq!(
5325 map_import(&map, "wasi:http/types@0.2.10"),
5326 ("./http.js".into(), None)
5327 );
5328 }
5329
5330 #[test]
5331 fn test_map_import_wildcard_sans_version() {
5332 let mut map = HashMap::new();
5334 map.insert("wasi:http/*".into(), "./http.js#*".into());
5335 let map = Some(map);
5336 assert_eq!(
5337 map_import(&map, "wasi:http/types@0.2.10"),
5338 ("./http.js".into(), Some("types".into()))
5339 );
5340 }
5341
5342 #[test]
5343 fn test_map_import_semver_exact_key() {
5344 let mut map = HashMap::new();
5346 map.insert("wasi:http/types@0.2.0".into(), "./http.js".into());
5347 let map = Some(map);
5348 assert_eq!(
5349 map_import(&map, "wasi:http/types@0.2.10"),
5350 ("./http.js".into(), None)
5351 );
5352 }
5353
5354 #[test]
5355 fn test_map_import_semver_wildcard_key() {
5356 let mut map = HashMap::new();
5358 map.insert("wasi:http/*@0.2.1".into(), "./http.js#*".into());
5359 let map = Some(map);
5360 assert_eq!(
5361 map_import(&map, "wasi:http/types@0.2.10"),
5362 ("./http.js".into(), Some("types".into()))
5363 );
5364 }
5365
5366 #[test]
5367 fn test_map_import_semver_lower_import_version() {
5368 let mut map = HashMap::new();
5370 map.insert("wasi:http/types@0.2.10".into(), "./http.js".into());
5371 let map = Some(map);
5372 assert_eq!(
5373 map_import(&map, "wasi:http/types@0.2.1"),
5374 ("./http.js".into(), None)
5375 );
5376 }
5377
5378 #[test]
5379 fn test_map_import_semver_no_cross_minor() {
5380 let mut map = HashMap::new();
5382 map.insert("wasi:http/types@0.3.0".into(), "./http.js".into());
5383 let map = Some(map);
5384 assert_eq!(
5385 map_import(&map, "wasi:http/types@0.2.10"),
5386 ("wasi:http/types".into(), None)
5387 );
5388 }
5389
5390 #[test]
5391 fn test_map_import_semver_prefers_highest() {
5392 let mut map = HashMap::new();
5394 map.insert("wasi:http/types@0.2.1".into(), "./http-old.js".into());
5395 map.insert("wasi:http/types@0.2.5".into(), "./http-new.js".into());
5396 let map = Some(map);
5397 assert_eq!(
5398 map_import(&map, "wasi:http/types@0.2.10"),
5399 ("./http-new.js".into(), None)
5400 );
5401 }
5402
5403 #[test]
5404 fn test_map_import_no_match_prerelease() {
5405 let mut map = HashMap::new();
5406 map.insert("wasi:http/types@0.2.0-rc.1".into(), "./http.js".into());
5407 let map = Some(map);
5408 assert_eq!(
5409 map_import(&map, "wasi:http/types@0.2.0"),
5410 ("wasi:http/types".into(), None)
5411 );
5412 }
5413
5414 #[test]
5415 fn test_map_import_no_match_zero_zero() {
5416 let mut map = HashMap::new();
5417 map.insert("wasi:http/types@0.0.1".into(), "./http.js".into());
5418 let map = Some(map);
5419 assert_eq!(
5420 map_import(&map, "wasi:http/types@0.0.2"),
5421 ("wasi:http/types".into(), None)
5422 );
5423 }
5424
5425 #[test]
5426 fn test_map_import_semver_major_version() {
5427 let mut map = HashMap::new();
5429 map.insert("wasi:http/types@1.0.0".into(), "./http.js".into());
5430 let map = Some(map);
5431 assert_eq!(
5432 map_import(&map, "wasi:http/types@1.2.3"),
5433 ("./http.js".into(), None)
5434 );
5435 }
5436
5437 #[test]
5438 fn test_map_import_semver_no_cross_major() {
5439 let mut map = HashMap::new();
5441 map.insert("wasi:http/types@1.0.0".into(), "./http.js".into());
5442 let map = Some(map);
5443 assert_eq!(
5444 map_import(&map, "wasi:http/types@2.0.0"),
5445 ("wasi:http/types".into(), None)
5446 );
5447 }
5448
5449 #[test]
5450 fn test_map_import_no_map() {
5451 assert_eq!(
5453 map_import(&None, "wasi:http/types@0.2.0"),
5454 ("wasi:http/types".into(), None)
5455 );
5456 }
5457
5458 #[test]
5459 fn test_map_import_no_map_unversioned() {
5460 assert_eq!(
5462 map_import(&None, "wasi:http/types"),
5463 ("wasi:http/types".into(), None)
5464 );
5465 }
5466
5467 #[test]
5468 fn test_parse_mapping_with_hash() {
5469 assert_eq!(
5470 parse_mapping("./http.js#types"),
5471 ("./http.js".into(), Some("types".into()))
5472 );
5473 }
5474
5475 #[test]
5476 fn test_parse_mapping_without_hash() {
5477 assert_eq!(parse_mapping("./http.js"), ("./http.js".into(), None));
5478 }
5479
5480 #[test]
5481 fn test_parse_mapping_leading_hash() {
5482 assert_eq!(parse_mapping("#foo"), ("#foo".into(), None));
5484 }
5485
5486 #[test]
5487 fn test_parse_mapping_empty() {
5488 assert_eq!(parse_mapping(""), ("".into(), None));
5489 }
5490}