1use std::cell::RefCell;
2use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
3use std::fmt::Write;
4use std::mem;
5
6use anyhow::{bail, Result};
7use base64::engine::general_purpose;
8use base64::Engine as _;
9use heck::{ToKebabCase, ToLowerCamelCase, ToUpperCamelCase};
10use wasmtime_environ::component::{
11 CanonicalOptions, CanonicalOptionsDataModel, Component, ComponentTranslation, ComponentTypes,
12 CoreDef, CoreExport, Export, ExportItem, FixedEncoding, GlobalInitializer, InstantiateModule,
13 InterfaceType, LinearMemoryOptions, LoweredIndex, ResourceIndex, RuntimeComponentInstanceIndex,
14 RuntimeImportIndex, RuntimeInstanceIndex, StaticModuleIndex, Trampoline, TrampolineIndex,
15 TypeDef, TypeFuncIndex, TypeResourceTableIndex,
16};
17use wasmtime_environ::component::{
18 ExportIndex, ExtractCallback, NameMap, NameMapNoIntern, Transcode,
19 TypeComponentLocalErrorContextTableIndex,
20};
21use wasmtime_environ::{EntityIndex, PrimaryMap};
22use wit_bindgen_core::abi::{self, LiftLower};
23use wit_component::StringEncoding;
24use wit_parser::abi::AbiVariant;
25use wit_parser::{
26 Function, FunctionKind, Handle, Resolve, SizeAlign, Type, TypeDefKind, TypeId, WorldId,
27 WorldItem, WorldKey,
28};
29
30use crate::esm_bindgen::EsmBindgen;
31use crate::files::Files;
32use crate::function_bindgen::{
33 ErrHandling, FunctionBindgen, RemoteResourceMap, ResourceData, ResourceMap, ResourceTable,
34};
35use crate::intrinsics::component::ComponentIntrinsic;
36use crate::intrinsics::lift::LiftIntrinsic;
37use crate::intrinsics::p3::async_future::AsyncFutureIntrinsic;
38use crate::intrinsics::p3::async_stream::AsyncStreamIntrinsic;
39use crate::intrinsics::p3::async_task::AsyncTaskIntrinsic;
40use crate::intrinsics::p3::error_context::ErrCtxIntrinsic;
41use crate::intrinsics::p3::waitable::WaitableIntrinsic;
42use crate::intrinsics::resource::ResourceIntrinsic;
43use crate::intrinsics::string::StringIntrinsic;
44use crate::intrinsics::webidl::WebIdlIntrinsic;
45use crate::intrinsics::{
46 render_intrinsics, AsyncDeterminismProfile, Intrinsic, RenderIntrinsicsArgs,
47};
48use crate::names::{is_js_reserved_word, maybe_quote_id, maybe_quote_member, LocalNames};
49use crate::{
50 core, get_thrown_type, is_guest_async_lifted_fn, requires_async_porcelain, source, uwrite,
51 uwriteln, FunctionIdentifier,
52};
53
54#[derive(Debug, Default, Clone)]
55pub struct TranspileOpts {
56 pub name: String,
57 pub no_typescript: bool,
60 pub instantiation: Option<InstantiationMode>,
63 pub import_bindings: Option<BindingsMode>,
66 pub map: Option<HashMap<String, String>>,
69 pub no_nodejs_compat: bool,
71 pub base64_cutoff: usize,
74 pub tla_compat: bool,
77 pub valid_lifting_optimization: bool,
80 pub tracing: bool,
82 pub no_namespaced_exports: bool,
85 pub multi_memory: bool,
88 pub guest: bool,
90 pub async_mode: Option<AsyncMode>,
93}
94
95#[derive(Default, Clone, Debug)]
96pub enum AsyncMode {
97 #[default]
98 Sync,
99 JavaScriptPromiseIntegration {
100 imports: Vec<String>,
101 exports: Vec<String>,
102 },
103}
104
105#[derive(Default, Clone, Debug)]
106pub enum InstantiationMode {
107 #[default]
108 Async,
109 Sync,
110}
111
112enum CallType {
114 Standard,
116 FirstArgIsThis,
118 CalleeResourceDispatch,
120}
121
122#[derive(Default, Clone, Debug)]
123pub enum BindingsMode {
124 Hybrid,
125 #[default]
126 Js,
127 Optimized,
128 DirectOptimized,
129}
130
131struct JsBindgen<'a> {
132 local_names: LocalNames,
133
134 esm_bindgen: EsmBindgen,
135
136 src: Source,
141
142 core_module_cnt: usize,
144
145 opts: &'a TranspileOpts,
147
148 all_intrinsics: BTreeSet<Intrinsic>,
150
151 all_core_exported_funcs: Vec<(String, bool)>,
154}
155
156struct JsFunctionBindgenArgs<'a> {
158 nparams: usize,
160 call_type: CallType,
162 iface_name: Option<&'a str>,
164 callee: &'a str,
166 opts: &'a CanonicalOptions,
168 func: &'a Function,
170 resource_map: &'a ResourceMap,
171 remote_resource_map: &'a RemoteResourceMap,
172 abi: AbiVariant,
174 requires_async_porcelain: bool,
176 is_guest_async_lifted: bool,
178}
179
180#[allow(clippy::too_many_arguments)]
181pub fn transpile_bindgen(
182 name: &str,
183 component: &ComponentTranslation,
184 modules: &PrimaryMap<StaticModuleIndex, core::Translation<'_>>,
185 types: &ComponentTypes,
186 resolve: &Resolve,
187 id: WorldId,
188 opts: TranspileOpts,
189 files: &mut Files,
190) -> (Vec<String>, Vec<(String, Export)>) {
191 let (async_imports, async_exports) = match opts.async_mode.clone() {
192 None | Some(AsyncMode::Sync) => (Default::default(), Default::default()),
193 Some(AsyncMode::JavaScriptPromiseIntegration { imports, exports }) => {
194 (imports.into_iter().collect(), exports.into_iter().collect())
195 }
196 };
197
198 let mut bindgen = JsBindgen {
199 local_names: LocalNames::default(),
200 src: Source::default(),
201 esm_bindgen: EsmBindgen::default(),
202 core_module_cnt: 0,
203 opts: &opts,
204 all_intrinsics: BTreeSet::new(),
205 all_core_exported_funcs: Vec::new(),
206 };
207 bindgen.local_names.exclude_globals(
208 &Intrinsic::get_global_names()
209 .into_iter()
210 .collect::<Vec<_>>(),
211 );
212 bindgen.core_module_cnt = modules.len();
213
214 let mut instantiator = Instantiator {
217 src: Source::default(),
218 sizes: SizeAlign::default(),
219 gen: &mut bindgen,
220 modules,
221 instances: Default::default(),
222 error_context_component_initialized: (0..component
223 .component
224 .num_runtime_component_instances)
225 .map(|_| false)
226 .collect(),
227 error_context_component_table_initialized: (0..component
228 .component
229 .num_error_context_tables)
230 .map(|_| false)
231 .collect(),
232 resolve,
233 world: id,
234 translation: component,
235 component: &component.component,
236 types,
237 async_imports,
238 async_exports,
239 imports: Default::default(),
240 exports: Default::default(),
241 lowering_options: Default::default(),
242 used_instance_flags: Default::default(),
243 defined_resource_classes: Default::default(),
244 imports_resource_types: Default::default(),
245 exports_resource_types: Default::default(),
246 resources_initialized: BTreeMap::new(),
247 resource_tables_initialized: BTreeMap::new(),
248 };
249 instantiator.sizes.fill(resolve);
250 instantiator.initialize();
251 instantiator.instantiate();
252
253 let mut intrinsic_definitions = source::Source::default();
254
255 instantiator.resource_definitions(&mut intrinsic_definitions);
256 instantiator.instance_flags();
257
258 instantiator.gen.src.js(&instantiator.src.js);
259 instantiator.gen.src.js_init(&instantiator.src.js_init);
260
261 instantiator
262 .gen
263 .finish_component(name, files, &opts, intrinsic_definitions);
264
265 let exports = instantiator
266 .gen
267 .esm_bindgen
268 .exports()
269 .iter()
270 .map(|(export_name, canon_export_name)| {
271 let export = if canon_export_name.contains(':') {
272 instantiator
273 .component
274 .exports
275 .get(canon_export_name, &NameMapNoIntern)
276 .unwrap()
277 } else {
278 instantiator
279 .component
280 .exports
281 .get(&canon_export_name.to_kebab_case(), &NameMapNoIntern)
282 .unwrap()
283 };
284 (
285 export_name.to_string(),
286 instantiator.component.export_items[*export].clone(),
287 )
288 })
289 .collect();
290
291 (bindgen.esm_bindgen.import_specifiers(), exports)
292}
293
294impl JsBindgen<'_> {
295 fn finish_component(
296 &mut self,
297 name: &str,
298 files: &mut Files,
299 opts: &TranspileOpts,
300 intrinsic_definitions: source::Source,
301 ) {
302 let mut output = source::Source::default();
303 let mut compilation_promises = source::Source::default();
304 let mut core_exported_funcs = source::Source::default();
305
306 for (core_export_fn, is_async) in self.all_core_exported_funcs.iter() {
307 let local_name = self.local_names.get(core_export_fn);
308 if *is_async {
309 uwriteln!(
310 core_exported_funcs,
311 "{local_name} = WebAssembly.promising({core_export_fn});",
312 );
313 } else {
314 uwriteln!(core_exported_funcs, "{local_name} = {core_export_fn};",);
315 }
316 }
317
318 if matches!(self.opts.instantiation, Some(InstantiationMode::Async)) {
320 uwriteln!(
321 compilation_promises,
322 "if (!getCoreModule) getCoreModule = (name) => {}(new URL(`./${{name}}`, import.meta.url));",
323 self.intrinsic(Intrinsic::FetchCompile)
324 );
325 }
326
327 let mut removed = BTreeSet::new();
329 for i in 0..self.core_module_cnt {
330 let local_name = format!("module{i}");
331 let mut name_idx = core_file_name(name, i as u32);
332 if self.opts.instantiation.is_some() {
333 uwriteln!(
334 compilation_promises,
335 "const {local_name} = getCoreModule('{name_idx}');"
336 );
337 } else if files.get_size(&name_idx).unwrap() < self.opts.base64_cutoff {
338 assert!(removed.insert(i));
339 let data = files.remove(&name_idx).unwrap();
340 uwriteln!(
341 compilation_promises,
342 "const {local_name} = {}('{}');",
343 self.intrinsic(Intrinsic::Base64Compile),
344 general_purpose::STANDARD_NO_PAD.encode(&data),
345 );
346 } else {
347 if let Some(&replacement) = removed.iter().next() {
350 assert!(removed.remove(&replacement) && removed.insert(i));
351 let data = files.remove(&name_idx).unwrap();
352 name_idx = core_file_name(name, replacement as u32);
353 files.push(&name_idx, &data);
354 }
355 uwriteln!(
356 compilation_promises,
357 "const {local_name} = {}(new URL('./{name_idx}', import.meta.url));",
358 self.intrinsic(Intrinsic::FetchCompile)
359 );
360 }
361 }
362
363 let js_intrinsics = render_intrinsics(RenderIntrinsicsArgs {
364 intrinsics: &mut self.all_intrinsics,
365 no_nodejs_compat: self.opts.no_nodejs_compat,
366 instantiation: self.opts.instantiation.is_some(),
367 determinism: AsyncDeterminismProfile::default(),
368 });
369
370 if let Some(instantiation) = &self.opts.instantiation {
371 uwrite!(
372 output,
373 "\
374 export function instantiate(getCoreModule, imports, instantiateCore = {}) {{
375 {}
376 {}
377 {}
378 ",
379 match instantiation {
380 InstantiationMode::Async => "WebAssembly.instantiate",
381 InstantiationMode::Sync =>
382 "(module, importObject) => new WebAssembly.Instance(module, importObject)",
383 },
384 &js_intrinsics as &str,
385 &intrinsic_definitions as &str,
386 &compilation_promises as &str,
387 );
388 }
389
390 let imports_object = if self.opts.instantiation.is_some() {
391 Some("imports")
392 } else {
393 None
394 };
395 self.esm_bindgen
396 .render_imports(&mut output, imports_object, &mut self.local_names);
397
398 if self.opts.instantiation.is_some() {
399 uwrite!(&mut self.src.js, "{}", &core_exported_funcs as &str);
400 self.esm_bindgen.render_exports(
401 &mut self.src.js,
402 self.opts.instantiation.is_some(),
403 &mut self.local_names,
404 opts,
405 );
406 uwrite!(
407 output,
408 "\
409 let gen = (function* init () {{
410 {}\
411 {};
412 }})();
413 let promise, resolve, reject;
414 function runNext (value) {{
415 try {{
416 let done;
417 do {{
418 ({{ value, done }} = gen.next(value));
419 }} while (!(value instanceof Promise) && !done);
420 if (done) {{
421 if (resolve) return resolve(value);
422 else return value;
423 }}
424 if (!promise) promise = new Promise((_resolve, _reject) => (resolve = _resolve, reject = _reject));
425 value.then(nextVal => done ? resolve() : runNext(nextVal), reject);
426 }}
427 catch (e) {{
428 if (reject) reject(e);
429 else throw e;
430 }}
431 }}
432 const maybeSyncReturn = runNext(null);
433 return promise || maybeSyncReturn;
434 }}
435 ",
436 &self.src.js_init as &str,
437 &self.src.js as &str,
438 );
439 } else {
440 let (maybe_init_export, maybe_init) =
441 if self.opts.tla_compat && opts.instantiation.is_none() {
442 uwriteln!(self.src.js_init, "_initialized = true;");
443 (
444 "\
445 let _initialized = false;
446 export ",
447 "",
448 )
449 } else {
450 (
451 "",
452 "
453 await $init;
454 ",
455 )
456 };
457
458 uwrite!(
459 output,
460 "\
461 {}
462 {}
463 {}
464 {maybe_init_export}const $init = (() => {{
465 let gen = (function* init () {{
466 {}\
467 {}\
468 {}\
469 }})();
470 let promise, resolve, reject;
471 function runNext (value) {{
472 try {{
473 let done;
474 do {{
475 ({{ value, done }} = gen.next(value));
476 }} while (!(value instanceof Promise) && !done);
477 if (done) {{
478 if (resolve) resolve(value);
479 else return value;
480 }}
481 if (!promise) promise = new Promise((_resolve, _reject) => (resolve = _resolve, reject = _reject));
482 value.then(runNext, reject);
483 }}
484 catch (e) {{
485 if (reject) reject(e);
486 else throw e;
487 }}
488 }}
489 const maybeSyncReturn = runNext(null);
490 return promise || maybeSyncReturn;
491 }})();
492 {maybe_init}\
493 ",
494 &js_intrinsics as &str,
495 &intrinsic_definitions as &str,
496 &self.src.js as &str,
497 &compilation_promises as &str,
498 &self.src.js_init as &str,
499 &core_exported_funcs as &str,
500 );
501
502 self.esm_bindgen.render_exports(
503 &mut output,
504 self.opts.instantiation.is_some(),
505 &mut self.local_names,
506 opts,
507 );
508 }
509
510 let mut bytes = output.as_bytes();
511 if bytes[0] == b'\n' {
513 bytes = &bytes[1..];
514 }
515 files.push(&format!("{name}.js"), bytes);
516 }
517
518 fn intrinsic(&mut self, intrinsic: Intrinsic) -> String {
519 self.all_intrinsics.insert(intrinsic);
520 intrinsic.name().to_string()
521 }
522}
523
524struct Instantiator<'a, 'b> {
528 src: Source,
529 gen: &'a mut JsBindgen<'b>,
530 modules: &'a PrimaryMap<StaticModuleIndex, core::Translation<'a>>,
531 instances: PrimaryMap<RuntimeInstanceIndex, StaticModuleIndex>,
532 types: &'a ComponentTypes,
533 resolve: &'a Resolve,
534 world: WorldId,
535 sizes: SizeAlign,
536 component: &'a Component,
537
538 error_context_component_initialized: PrimaryMap<RuntimeComponentInstanceIndex, bool>,
541 error_context_component_table_initialized:
542 PrimaryMap<TypeComponentLocalErrorContextTableIndex, bool>,
543
544 translation: &'a ComponentTranslation,
546
547 exports_resource_types: BTreeMap<TypeId, ResourceIndex>,
548 imports_resource_types: BTreeMap<TypeId, ResourceIndex>,
549 resources_initialized: BTreeMap<ResourceIndex, bool>,
550 resource_tables_initialized: BTreeMap<TypeResourceTableIndex, bool>,
551
552 exports: BTreeMap<String, WorldKey>,
553 imports: BTreeMap<String, WorldKey>,
554 used_instance_flags: RefCell<BTreeSet<RuntimeComponentInstanceIndex>>,
556 defined_resource_classes: BTreeSet<String>,
557 async_imports: HashSet<String>,
558 async_exports: HashSet<String>,
559 lowering_options:
560 PrimaryMap<LoweredIndex, (&'a CanonicalOptions, TrampolineIndex, TypeFuncIndex)>,
561}
562
563impl<'a> Instantiator<'a, '_> {
564 fn initialize(&mut self) {
565 for (key, _) in &self.resolve.worlds[self.world].imports {
567 let name = &self.resolve.name_world_key(key);
568 self.imports.insert(name.to_string(), key.clone());
569 }
570 for (key, _) in &self.resolve.worlds[self.world].exports {
571 let name = &self.resolve.name_world_key(key);
572 self.exports.insert(name.to_string(), key.clone());
573 }
574
575 for (key, item) in &self.resolve.worlds[self.world].imports {
578 let name = &self.resolve.name_world_key(key);
579 let Some((_, (_, import))) = self
580 .component
581 .import_types
582 .iter()
583 .find(|(_, (impt_name, _))| impt_name == name)
584 else {
585 match item {
586 WorldItem::Interface { .. } => {
587 unreachable!("unexpected interface in import types during initialization")
588 }
589 WorldItem::Function(_) => {
590 unreachable!("unexpected function in import types during initialization")
591 }
592 WorldItem::Type(ty) => {
593 assert!(!matches!(
594 self.resolve.types[*ty].kind,
595 TypeDefKind::Resource
596 ))
597 }
598 }
599 continue;
600 };
601 match item {
602 WorldItem::Interface { id, stability: _ } => {
603 let TypeDef::ComponentInstance(instance) = import else {
604 unreachable!("unexpectedly non-component instance import in interface")
605 };
606 let import_ty = &self.types[*instance];
607 let iface = &self.resolve.interfaces[*id];
608 for (ty_name, ty) in &iface.types {
609 match &import_ty.exports.get(ty_name) {
610 Some(TypeDef::Resource(resource)) => {
611 let ty = crate::dealias(self.resolve, *ty);
612 let resource_idx = self.types[*resource].ty;
613 self.imports_resource_types.insert(ty, resource_idx);
614 }
615 Some(TypeDef::Interface(_)) | None => {}
616 Some(_) => unreachable!("unexpected type in interface"),
617 }
618 }
619 }
620 WorldItem::Function(_) => {}
621 WorldItem::Type(ty) => match import {
622 TypeDef::Resource(resource) => {
623 let ty = crate::dealias(self.resolve, *ty);
624 let resource_idx = self.types[*resource].ty;
625 self.imports_resource_types.insert(ty, resource_idx);
626 }
627 TypeDef::Interface(_) => {}
628 _ => unreachable!("unexpected type in import world item"),
629 },
630 }
631 }
632 self.exports_resource_types = self.imports_resource_types.clone();
633
634 for (key, item) in &self.resolve.worlds[self.world].exports {
635 let name = &self.resolve.name_world_key(key);
636 let (_, export_idx) = self
637 .component
638 .exports
639 .raw_iter()
640 .find(|(expt_name, _)| *expt_name == name)
641 .unwrap();
642 let export = &self.component.export_items[*export_idx];
643 match item {
644 WorldItem::Interface { id, stability: _ } => {
645 let iface = &self.resolve.interfaces[*id];
646 let Export::Instance { exports, .. } = &export else {
647 unreachable!("unexpectedly non export instance item")
648 };
649 for (ty_name, ty) in &iface.types {
650 match self.component.export_items
651 [*exports.get(ty_name, &NameMapNoIntern).unwrap()]
652 {
653 Export::Type(TypeDef::Resource(resource)) => {
654 let ty = crate::dealias(self.resolve, *ty);
655 let resource_idx = self.types[resource].ty;
656 self.exports_resource_types.insert(ty, resource_idx);
657 }
658 Export::Type(_) => {}
659 _ => unreachable!(
660 "unexpected type in component export items on iface [{iface_name}]",
661 iface_name = iface.name.as_deref().unwrap_or("<unknown>"),
662 ),
663 }
664 }
665 }
666 WorldItem::Function(_) => {}
667 WorldItem::Type(_) => unreachable!("unexpected exported world item type"),
668 }
669 }
670 }
671
672 fn instantiate(&mut self) {
673 for (i, trampoline) in self.translation.trampolines.iter() {
674 let Trampoline::LowerImport {
675 index,
676 lower_ty,
677 options,
678 } = trampoline
679 else {
680 continue;
681 };
682
683 let options = self
684 .component
685 .options
686 .get(*options)
687 .expect("failed to find canon options");
688
689 let i = self.lowering_options.push((options, i, *lower_ty));
690 assert_eq!(i, *index);
691 }
692
693 if let Some(InstantiationMode::Async) = self.gen.opts.instantiation {
694 if self.modules.len() > 1 {
697 self.src.js_init.push_str("Promise.all([");
698 for i in 0..self.modules.len() {
699 if i > 0 {
700 self.src.js_init.push_str(", ");
701 }
702 self.src.js_init.push_str(&format!("module{i}"));
703 }
704 uwriteln!(self.src.js_init, "]).catch(() => {{}});");
705 }
706 }
707
708 let mut lower_import_initializers = Vec::new();
712 for init in self.component.initializers.iter() {
713 match init {
714 GlobalInitializer::InstantiateModule(_) => {
715 for init in lower_import_initializers.drain(..) {
716 self.instantiation_global_initializer(init);
717 }
718 }
719 GlobalInitializer::LowerImport { .. } => {
720 lower_import_initializers.push(init);
721 continue;
722 }
723 _ => {}
724 }
725 self.instantiation_global_initializer(init);
726 }
727
728 for init in lower_import_initializers.drain(..) {
729 self.instantiation_global_initializer(init);
730 }
731
732 for (i, trampoline) in self
735 .translation
736 .trampolines
737 .iter()
738 .filter(|(_, t)| Instantiator::is_early_trampoline(t))
739 {
740 self.trampoline(i, trampoline);
741 }
742
743 if self.gen.opts.instantiation.is_some() {
744 let js_init = mem::take(&mut self.src.js_init);
745 self.src.js.push_str(&js_init);
746 }
747
748 self.exports(&self.component.exports);
749
750 for (i, trampoline) in self
753 .translation
754 .trampolines
755 .iter()
756 .filter(|(_, t)| !Instantiator::is_early_trampoline(t))
757 {
758 self.trampoline(i, trampoline);
759 }
760 }
761
762 fn ensure_local_resource_class(&mut self, local_name: String) {
763 if !self.defined_resource_classes.contains(&local_name) {
764 uwriteln!(
765 self.src.js,
766 "\nclass {local_name} {{
767 constructor () {{
768 throw new Error('\"{local_name}\" resource does not define a constructor');
769 }}
770 }}"
771 );
772 self.defined_resource_classes.insert(local_name.to_string());
773 }
774 }
775
776 fn resource_definitions(&mut self, definitions: &mut source::Source) {
777 for resource in 0..self.component.num_resources {
780 let resource = ResourceIndex::from_u32(resource);
781 let is_imported = self.component.defined_resource_index(resource).is_none();
782 if is_imported {
783 continue;
784 }
785 if let Some(local_name) = self.gen.local_names.try_get(resource) {
786 self.ensure_local_resource_class(local_name.to_string());
787 }
788 }
789
790 if self.gen.all_intrinsics.contains(&Intrinsic::Resource(
792 ResourceIntrinsic::ResourceTransferBorrow,
793 )) || self.gen.all_intrinsics.contains(&Intrinsic::Resource(
794 ResourceIntrinsic::ResourceTransferBorrowValidLifting,
795 )) {
796 let defined_resource_tables = Intrinsic::DefinedResourceTables.name();
797 uwrite!(definitions, "const {defined_resource_tables} = [");
798 for tidx in 0..self.component.num_resources {
800 let tid = TypeResourceTableIndex::from_u32(tidx);
801 let rid = self.types[tid].ty;
802 if let Some(defined_index) = self.component.defined_resource_index(rid) {
803 if self.types[tid].instance
804 == self.component.defined_resource_instances[defined_index]
805 {
806 uwrite!(definitions, "true,");
807 }
808 } else {
809 uwrite!(definitions, ",");
810 };
811 }
812 uwrite!(definitions, "];\n");
813 }
814 }
815
816 fn ensure_error_context_local_table(
824 &mut self,
825 component_idx: RuntimeComponentInstanceIndex,
826 err_ctx_tbl_idx: TypeComponentLocalErrorContextTableIndex,
827 ) {
828 if self.error_context_component_initialized[component_idx]
829 && self.error_context_component_table_initialized[err_ctx_tbl_idx]
830 {
831 return;
832 }
833 let err_ctx_local_tables = self
834 .gen
835 .intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::ComponentLocalTable));
836 let c = component_idx.as_u32();
837 if !self.error_context_component_initialized[component_idx] {
838 uwriteln!(self.src.js, "{err_ctx_local_tables}.set({c}, new Map());");
839 self.error_context_component_initialized[component_idx] = true;
840 }
841 if !self.error_context_component_table_initialized[err_ctx_tbl_idx] {
842 let t = err_ctx_tbl_idx.as_u32();
843 uwriteln!(
844 self.src.js,
845 "{err_ctx_local_tables}.get({c}).set({t}, new Map());"
846 );
847 self.error_context_component_table_initialized[err_ctx_tbl_idx] = true;
848 }
849 }
850
851 fn ensure_resource_table(&mut self, id: TypeResourceTableIndex) {
858 if self.resource_tables_initialized.contains_key(&id) {
859 return;
860 }
861
862 let resource = self.types[id].ty;
863
864 let (is_imported, maybe_dtor) =
865 if let Some(resource_idx) = self.component.defined_resource_index(resource) {
866 let resource_def = self
867 .component
868 .initializers
869 .iter()
870 .find_map(|i| match i {
871 GlobalInitializer::Resource(r) if r.index == resource_idx => Some(r),
872 _ => None,
873 })
874 .unwrap();
875
876 if let Some(dtor) = &resource_def.dtor {
877 (false, format!("\n{}(rep);", self.core_def(dtor)))
878 } else {
879 (false, "".into())
880 }
881 } else {
882 (true, "".into())
883 };
884
885 let handle_tables = self.gen.intrinsic(Intrinsic::HandleTables);
886 let rsc_table_flag = self
887 .gen
888 .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableFlag));
889 let rsc_table_remove = self
890 .gen
891 .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableRemove));
892
893 let rtid = id.as_u32();
894 if is_imported {
895 uwriteln!(
896 self.src.js,
897 "const handleTable{rtid} = [{rsc_table_flag}, 0];",
898 );
899 if !self.resources_initialized.contains_key(&resource) {
900 let ridx = resource.as_u32();
901 uwriteln!(
902 self.src.js,
903 "const captureTable{ridx} = new Map();
904 let captureCnt{ridx} = 0;"
905 );
906 self.resources_initialized.insert(resource, true);
907 }
908 } else {
909 let finalization_registry_create =
910 self.gen.intrinsic(Intrinsic::FinalizationRegistryCreate);
911 uwriteln!(
912 self.src.js,
913 "const handleTable{rtid} = [{rsc_table_flag}, 0];
914 const finalizationRegistry{rtid} = {finalization_registry_create}((handle) => {{
915 const {{ rep }} = {rsc_table_remove}(handleTable{rtid}, handle);{maybe_dtor}
916 }});
917 ",
918 );
919 }
920 uwriteln!(self.src.js, "{handle_tables}[{rtid}] = handleTable{rtid};");
921 self.resource_tables_initialized.insert(id, true);
922 }
923
924 fn instance_flags(&mut self) {
925 let mut instance_flag_defs = String::new();
927 for used in self.used_instance_flags.borrow().iter() {
928 let i = used.as_u32();
929 uwriteln!(
930 &mut instance_flag_defs,
931 "const instanceFlags{i} = new WebAssembly.Global({{ value: \"i32\", mutable: true }}, {});",
932 wasmtime_environ::component::FLAG_MAY_LEAVE
933 | wasmtime_environ::component::FLAG_MAY_ENTER);
934 }
935 self.src.js_init.prepend_str(&instance_flag_defs);
936 }
937
938 fn is_early_trampoline(trampoline: &Trampoline) -> bool {
943 matches!(
944 trampoline,
945 Trampoline::BackpressureSet { .. }
946 | Trampoline::ContextGet(_)
947 | Trampoline::ContextSet(_)
948 | Trampoline::ErrorContextDebugMessage { .. }
949 | Trampoline::ErrorContextDrop { .. }
950 | Trampoline::ErrorContextNew { .. }
951 | Trampoline::ResourceDrop(_)
952 | Trampoline::ResourceNew(_)
953 | Trampoline::ResourceRep(_)
954 | Trampoline::ResourceTransferBorrow
955 | Trampoline::ResourceTransferOwn
956 | Trampoline::SubtaskDrop { .. }
957 | Trampoline::StreamNew { .. }
958 | Trampoline::TaskCancel { .. }
959 | Trampoline::TaskReturn { .. }
960 | Trampoline::WaitableSetDrop { .. }
961 | Trampoline::WaitableSetNew { .. }
962 | Trampoline::WaitableJoin { .. },
963 )
964 }
965
966 fn trampoline(&mut self, i: TrampolineIndex, trampoline: &'a Trampoline) {
967 let i = i.as_u32();
968 match trampoline {
969 Trampoline::TaskCancel { instance } => {
970 let task_cancel_fn = self
971 .gen
972 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::TaskCancel));
973 uwriteln!(
974 self.src.js,
975 "const trampoline{i} = {task_cancel_fn}.bind(null, {instance_idx});\n",
976 instance_idx = instance.as_u32(),
977 );
978 }
979
980 Trampoline::SubtaskCancel { instance, async_ } => {
981 let task_cancel_fn = self
982 .gen
983 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::SubtaskCancel));
984 uwriteln!(
985 self.src.js,
986 "const trampoline{i} = {task_cancel_fn}.bind(null, {instance_idx}, {async_});\n",
987 instance_idx = instance.as_u32(),
988 );
989 }
990
991 Trampoline::SubtaskDrop { instance } => {
992 let component_idx = instance.as_u32();
993 let subtask_drop_fn = self
994 .gen
995 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::SubtaskDrop));
996 uwriteln!(
997 self.src.js,
998 "const trampoline{i} = {subtask_drop_fn}.bind(
999 null,
1000 {component_idx},
1001 );"
1002 );
1003 }
1004
1005 Trampoline::BackpressureSet { instance } => {
1006 let backpressure_set_fn = self
1007 .gen
1008 .intrinsic(Intrinsic::Component(ComponentIntrinsic::BackpressureSet));
1009 uwriteln!(
1010 self.src.js,
1011 "const trampoline{i} = {backpressure_set_fn}.bind(null, {});\n",
1012 instance.as_u32(),
1013 );
1014 }
1015
1016 Trampoline::WaitableSetNew { instance } => {
1017 let waitable_set_new_fn = self
1018 .gen
1019 .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableSetNew));
1020 uwriteln!(
1021 self.src.js,
1022 "const trampoline{i} = {waitable_set_new_fn}.bind(null, {});\n",
1023 instance.as_u32(),
1024 );
1025 }
1026
1027 Trampoline::WaitableSetWait { options } => {
1028 let CanonicalOptions {
1029 instance,
1030 async_,
1031 data_model:
1032 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, .. }),
1033 ..
1034 } = self
1035 .component
1036 .options
1037 .get(*options)
1038 .expect("failed to find options")
1039 else {
1040 panic!("unexpected/missing memory data model during waitable-set.wait");
1041 };
1042
1043 let waitable_set_wait_fn = self
1044 .gen
1045 .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableSetWait));
1046 uwriteln!(
1047 self.src.js,
1048 "const trampoline{i} = {waitable_set_wait_fn}.bind(null, {instance_idx}, {async_}, memory{memory_idx});\n",
1049 instance_idx = instance.as_u32(),
1050 memory_idx = memory.expect("missing memory idx for waitable-set.wait").as_u32(),
1051 );
1052 }
1053
1054 Trampoline::WaitableSetPoll { options } => {
1055 let CanonicalOptions {
1056 instance,
1057 async_,
1058 data_model:
1059 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, .. }),
1060 ..
1061 } = self
1062 .component
1063 .options
1064 .get(*options)
1065 .expect("failed to find options")
1066 else {
1067 panic!("unexpected memory data model during waitable-set.poll");
1068 };
1069
1070 let waitable_set_poll_fn = self
1071 .gen
1072 .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableSetPoll));
1073 uwriteln!(
1074 self.src.js,
1075 "const trampoline{i} = {waitable_set_poll_fn}.bind(null, {instance_idx}, {async_}, memory{memory_idx});\n",
1076 instance_idx = instance.as_u32(),
1077 memory_idx = memory.expect("missing memory idx for waitable-set.poll").as_u32(),
1078 );
1079 }
1080
1081 Trampoline::WaitableSetDrop { instance } => {
1082 let waitable_set_drop_fn = self
1083 .gen
1084 .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableSetDrop));
1085 uwriteln!(
1086 self.src.js,
1087 "const trampoline{i} = {waitable_set_drop_fn}.bind(null, {instance_idx});\n",
1088 instance_idx = instance.as_u32(),
1089 );
1090 }
1091
1092 Trampoline::WaitableJoin { instance } => {
1093 let waitable_join_fn = self
1094 .gen
1095 .intrinsic(Intrinsic::Waitable(WaitableIntrinsic::WaitableJoin));
1096 uwriteln!(
1097 self.src.js,
1098 "const trampoline{i} = {waitable_join_fn}.bind(null, {instance_idx});\n",
1099 instance_idx = instance.as_u32(),
1100 );
1101 }
1102
1103 Trampoline::Yield { async_ } => {
1104 let yield_fn = self
1105 .gen
1106 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::Yield));
1107 uwriteln!(
1108 self.src.js,
1109 "const trampoline{i} = {yield_fn}.bind(null, {async_});\n",
1110 );
1111 }
1112
1113 Trampoline::StreamNew { ty } => {
1117 let stream_new_fn = self
1118 .gen
1119 .intrinsic(Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamNew));
1120 uwriteln!(
1121 self.src.js,
1122 "const trampoline{i} = {stream_new_fn}.bind(null, {});\n",
1123 ty.as_u32(),
1124 );
1125 }
1126
1127 Trampoline::StreamRead { ty, options } => {
1128 let options = self
1129 .component
1130 .options
1131 .get(*options)
1132 .expect("failed to find options");
1133
1134 is_valid_canonopt(options).expect("invalid canonopts");
1135
1136 let stream_idx = ty.as_u32();
1137 let CanonicalOptions {
1138 instance,
1139 string_encoding,
1140 async_,
1141 data_model:
1142 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1143 ..
1144 } = options
1145 else {
1146 unreachable!("missing/invalid data model for options during stream.read")
1147 };
1148 let component_instance_id = instance.as_u32();
1149 let memory_idx = memory.expect("missing memory idx for stream.read").as_u32();
1150 let realloc_idx = realloc
1151 .expect("missing realloc idx for stream.read")
1152 .as_u32();
1153 let string_encoding = string_encoding_js_literal(string_encoding);
1154
1155 let stream_read_fn = self
1156 .gen
1157 .intrinsic(Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamRead));
1158 uwriteln!(
1159 self.src.js,
1160 r#"const trampoline{i} = {stream_read_fn}.bind(
1161 null,
1162 {component_instance_id},
1163 {memory_idx},
1164 {realloc_idx},
1165 {string_encoding},
1166 {async_},
1167 {stream_idx},
1168 );
1169 "#,
1170 );
1171 }
1172
1173 Trampoline::StreamWrite { ty, options } => {
1174 let options = self
1175 .component
1176 .options
1177 .get(*options)
1178 .expect("failed to find options");
1179
1180 is_valid_canonopt(options).expect("invalid canonopts");
1181
1182 let stream_idx = ty.as_u32();
1183 let CanonicalOptions {
1184 instance,
1185 string_encoding,
1186 async_,
1187 data_model:
1188 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1189 ..
1190 } = options
1191 else {
1192 unreachable!("unexpected memory data model during stream.write");
1193 };
1194 let component_instance_id = instance.as_u32();
1195 let memory_idx = memory
1196 .expect("missing memory idx for stream.write")
1197 .as_u32();
1198 let realloc_idx = realloc
1199 .expect("missing realloc idx for stream.write")
1200 .as_u32();
1201 let string_encoding = string_encoding_js_literal(string_encoding);
1202
1203 let stream_write_fn = self
1204 .gen
1205 .intrinsic(Intrinsic::AsyncStream(AsyncStreamIntrinsic::StreamWrite));
1206 uwriteln!(
1207 self.src.js,
1208 r#"const trampoline{i} = {stream_write_fn}.bind(
1209 null,
1210 {component_instance_id},
1211 {memory_idx},
1212 {realloc_idx},
1213 {string_encoding},
1214 {async_},
1215 {stream_idx},
1216 );
1217 "#,
1218 );
1219 }
1220
1221 Trampoline::StreamCancelRead { ty, async_ } => {
1222 let stream_cancel_read_fn = self.gen.intrinsic(Intrinsic::AsyncStream(
1223 AsyncStreamIntrinsic::StreamCancelRead,
1224 ));
1225 uwriteln!(
1226 self.src.js,
1227 "const trampoline{i} = {stream_cancel_read_fn}.bind(null, {stream_idx}, {async_});\n",
1228 stream_idx = ty.as_u32(),
1229 );
1230 }
1231
1232 Trampoline::StreamCancelWrite { ty, async_ } => {
1233 let stream_cancel_write_fn = self.gen.intrinsic(Intrinsic::AsyncStream(
1234 AsyncStreamIntrinsic::StreamCancelWrite,
1235 ));
1236 uwriteln!(
1237 self.src.js,
1238 "const trampoline{i} = {stream_cancel_write_fn}.bind(null, {stream_idx}, {async_});\n",
1239 stream_idx = ty.as_u32(),
1240 );
1241 }
1242
1243 Trampoline::StreamDropReadable { ty } => {
1244 let stream_drop_readable_fn = self.gen.intrinsic(Intrinsic::AsyncStream(
1245 AsyncStreamIntrinsic::StreamDropReadable,
1246 ));
1247 uwriteln!(
1248 self.src.js,
1249 "const trampoline{i} = {stream_drop_readable_fn}.bind(null, {stream_idx});\n",
1250 stream_idx = ty.as_u32(),
1251 );
1252 }
1253
1254 Trampoline::StreamDropWritable { ty } => {
1255 let stream_drop_writable_fn = self.gen.intrinsic(Intrinsic::AsyncStream(
1256 AsyncStreamIntrinsic::StreamDropWritable,
1257 ));
1258 uwriteln!(
1259 self.src.js,
1260 "const trampoline{i} = {stream_drop_writable_fn}.bind(null, {stream_idx});\n",
1261 stream_idx = ty.as_u32(),
1262 );
1263 }
1264
1265 Trampoline::StreamTransfer => todo!("Trampoline::StreamTransfer"),
1266
1267 Trampoline::FutureNew { ty } => {
1268 let future_new_fn = self
1269 .gen
1270 .intrinsic(Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureNew));
1271 uwriteln!(
1272 self.src.js,
1273 "const trampoline{i} = {future_new_fn}.bind(null, {});\n",
1274 ty.as_u32(),
1275 );
1276 }
1277
1278 Trampoline::FutureRead { ty, options } => {
1279 let options = self
1280 .component
1281 .options
1282 .get(*options)
1283 .expect("failed to find options");
1284
1285 let future_idx = ty.as_u32();
1286
1287 let CanonicalOptions {
1288 instance,
1289 string_encoding,
1290 callback,
1291 post_return,
1292 async_,
1293 data_model:
1294 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1295 ..
1296 } = options
1297 else {
1298 unreachable!("unexpected memory data model during future.read");
1299 };
1300 let component_instance_id = instance.as_u32();
1301 let memory_idx = memory.expect("missing memory idx for future.read").as_u32();
1302 let realloc_idx = realloc
1303 .expect("missing realloc idx for future.read")
1304 .as_u32();
1305 let string_encoding = string_encoding_js_literal(string_encoding);
1306
1307 assert!(
1308 callback.is_none(),
1309 "callback should not be present for future read"
1310 );
1311 assert!(
1312 post_return.is_none(),
1313 "post_return should not be present for future read"
1314 );
1315
1316 let future_read_fn = self
1317 .gen
1318 .intrinsic(Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureRead));
1319 uwriteln!(
1320 self.src.js,
1321 r#"const trampoline{i} = {future_read_fn}.bind(
1322 null,
1323 {component_instance_id},
1324 {memory_idx},
1325 {realloc_idx},
1326 {string_encoding},
1327 {async_},
1328 {future_idx},
1329 );
1330 "#,
1331 );
1332 }
1333
1334 Trampoline::FutureWrite { ty, options } => {
1335 let options = self
1336 .component
1337 .options
1338 .get(*options)
1339 .expect("failed to find options");
1340
1341 is_valid_canonopt(options).expect("invalid canonopts");
1342
1343 let future_idx = ty.as_u32();
1344 let CanonicalOptions {
1345 instance,
1346 string_encoding,
1347 async_,
1348 data_model:
1349 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1350 ..
1351 } = options
1352 else {
1353 unreachable!("unexpected memory data model during future.write");
1354 };
1355 let component_instance_id = instance.as_u32();
1356 let memory_idx = memory
1357 .expect("missing memory idx for future.write")
1358 .as_u32();
1359 let realloc_idx = realloc
1360 .expect("missing realloc idx for future.write")
1361 .as_u32();
1362 let string_encoding = string_encoding_js_literal(string_encoding);
1363
1364 let future_write_fn = self
1365 .gen
1366 .intrinsic(Intrinsic::AsyncFuture(AsyncFutureIntrinsic::FutureWrite));
1367 uwriteln!(
1368 self.src.js,
1369 r#"const trampoline{i} = {future_write_fn}.bind(
1370 null,
1371 {component_instance_id},
1372 {memory_idx},
1373 {realloc_idx},
1374 {string_encoding},
1375 {async_},
1376 {future_idx},
1377 );
1378 "#,
1379 );
1380 }
1381
1382 Trampoline::FutureCancelRead { ty, async_ } => {
1383 let future_cancel_read_fn = self.gen.intrinsic(Intrinsic::AsyncFuture(
1384 AsyncFutureIntrinsic::FutureCancelRead,
1385 ));
1386 uwriteln!(
1387 self.src.js,
1388 "const trampoline{i} = {future_cancel_read_fn}.bind(null, {future_idx}, {async_});\n",
1389 future_idx = ty.as_u32(),
1390 );
1391 }
1392
1393 Trampoline::FutureCancelWrite { ty, async_ } => {
1394 let future_cancel_write_fn = self.gen.intrinsic(Intrinsic::AsyncFuture(
1395 AsyncFutureIntrinsic::FutureCancelWrite,
1396 ));
1397 uwriteln!(
1398 self.src.js,
1399 "const trampoline{i} = {future_cancel_write_fn}.bind(null, {future_idx}, {async_});\n",
1400 future_idx = ty.as_u32(),
1401 );
1402 }
1403
1404 Trampoline::FutureDropReadable { ty } => {
1405 let future_drop_readable_fn = self.gen.intrinsic(Intrinsic::AsyncFuture(
1406 AsyncFutureIntrinsic::FutureDropReadable,
1407 ));
1408 uwriteln!(
1409 self.src.js,
1410 "const trampoline{i} = {future_drop_readable_fn}.bind(null, {future_idx});\n",
1411 future_idx = ty.as_u32(),
1412 );
1413 }
1414
1415 Trampoline::FutureDropWritable { ty } => {
1416 let future_drop_writable_fn = self.gen.intrinsic(Intrinsic::AsyncFuture(
1417 AsyncFutureIntrinsic::FutureDropWritable,
1418 ));
1419 uwriteln!(
1420 self.src.js,
1421 "const trampoline{i} = {future_drop_writable_fn}.bind(null, {future_idx});\n",
1422 future_idx = ty.as_u32(),
1423 );
1424 }
1425
1426 Trampoline::FutureTransfer => todo!("Trampoline::FutureTransfer"),
1427
1428 Trampoline::ErrorContextNew { ty, options } => {
1429 let CanonicalOptions {
1430 instance,
1431 string_encoding,
1432 data_model:
1433 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, .. }),
1434 ..
1435 } = self
1436 .component
1437 .options
1438 .get(*options)
1439 .expect("failed to find options")
1440 else {
1441 panic!("unexpected memory data model during error-context.new");
1442 };
1443
1444 self.ensure_error_context_local_table(*instance, *ty);
1445
1446 let local_err_tbl_idx = ty.as_u32();
1447 let component_idx = instance.as_u32();
1448
1449 let memory_idx = memory
1450 .expect("missing realloc fn idx for error-context.debug-message")
1451 .as_u32();
1452
1453 let decoder = match string_encoding {
1455 wasmtime_environ::component::StringEncoding::Utf8 => self
1456 .gen
1457 .intrinsic(Intrinsic::String(StringIntrinsic::Utf8Decoder)),
1458 wasmtime_environ::component::StringEncoding::Utf16 => self
1459 .gen
1460 .intrinsic(Intrinsic::String(StringIntrinsic::Utf16Decoder)),
1461 enc => panic!(
1462 "unsupported string encoding [{enc:?}] for error-context.debug-message"
1463 ),
1464 };
1465 uwriteln!(
1466 self.src.js,
1467 "function trampoline{i}InputStr(ptr, len) {{
1468 return {decoder}.decode(new DataView(memory{memory_idx}.buffer, ptr, len));
1469 }}"
1470 );
1471
1472 let err_ctx_new_fn = self.gen.intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::New));
1473 uwriteln!(
1475 self.src.js,
1476 "const trampoline{i} = {err_ctx_new_fn}.bind(
1477 null,
1478 {component_idx},
1479 {local_err_tbl_idx},
1480 trampoline{i}InputStr,
1481 );
1482 "
1483 );
1484 }
1485
1486 Trampoline::ErrorContextDebugMessage { ty, options } => {
1487 let CanonicalOptions {
1488 instance,
1489 async_,
1490 callback,
1491 post_return,
1492 string_encoding,
1493 data_model:
1494 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1495 ..
1496 } = self
1497 .component
1498 .options
1499 .get(*options)
1500 .expect("failed to find options")
1501 else {
1502 panic!("unexpected memory data model during error-context.debug-message");
1503 };
1504
1505 let debug_message_fn = self
1506 .gen
1507 .intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::DebugMessage));
1508
1509 let realloc_fn_idx = realloc
1510 .expect("missing realloc fn idx for error-context.debug-message")
1511 .as_u32();
1512 let memory_idx = memory
1513 .expect("missing realloc fn idx for error-context.debug-message")
1514 .as_u32();
1515
1516 match string_encoding {
1518 wasmtime_environ::component::StringEncoding::Utf8 => {
1519 let encode_fn = self
1520 .gen
1521 .intrinsic(Intrinsic::String(StringIntrinsic::Utf8Encode));
1522 let encode_len_var =
1523 Intrinsic::String(StringIntrinsic::Utf8EncodedLen).name();
1524 uwriteln!(
1525 self.src.js,
1526 "function trampoline{i}OutputStr(s, outputPtr) {{
1527 const memory = memory{memory_idx};
1528 const reallocFn = realloc{realloc_fn_idx};
1529 let ptr = {encode_fn}(s, reallocFn, memory);
1530 let len = {encode_len_var};
1531 new DataView(memory.buffer).setUint32(outputPtr, ptr, true)
1532 new DataView(memory.buffer).setUint32(outputPtr + 4, len, true)
1533 }}"
1534 );
1535 }
1536 wasmtime_environ::component::StringEncoding::Utf16 => {
1537 let encode_fn = self
1538 .gen
1539 .intrinsic(Intrinsic::String(StringIntrinsic::Utf16Encode));
1540 uwriteln!(
1541 self.src.js,
1542 "function trampoline{i}OutputStr(s, outputPtr) {{
1543 const memory = memory{memory_idx};
1544 const reallocFn = realloc{realloc_fn_idx};
1545 let ptr = {encode_fn}(s, reallocFn, memory);
1546 let len = s.length;
1547 new DataView(memory.buffer).setUint32(outputPtr, ptr, true)
1548 new DataView(memory.buffer).setUint32(outputPtr + 4, len, true)
1549 }}"
1550 );
1551 }
1552 enc => panic!(
1553 "unsupported string encoding [{enc:?}] for error-context.debug-message"
1554 ),
1555 };
1556
1557 let options_obj = format!(
1558 "{{callback:{callback}, postReturn: {post_return}, async: {async_}}}",
1559 callback = callback
1560 .map(|v| v.as_u32().to_string())
1561 .unwrap_or_else(|| "null".into()),
1562 post_return = post_return
1563 .map(|v| v.as_u32().to_string())
1564 .unwrap_or_else(|| "null".into()),
1565 );
1566
1567 let component_idx = instance.as_u32();
1568 let local_err_tbl_idx = ty.as_u32();
1569 uwriteln!(
1570 self.src.js,
1571 "const trampoline{i} = {debug_message_fn}.bind(
1572 null,
1573 {component_idx},
1574 {local_err_tbl_idx},
1575 {options_obj},
1576 trampoline{i}OutputStr,
1577 );"
1578 );
1579 }
1580
1581 Trampoline::ErrorContextDrop { ty } => {
1582 let drop_fn = self.gen.intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::Drop));
1583 let local_err_tbl_idx = ty.as_u32();
1584 let component_idx = self.types[*ty].instance.as_u32();
1585 uwriteln!(
1586 self.src.js,
1587 "const trampoline{i} = {drop_fn}.bind(null, {component_idx}, {local_err_tbl_idx});"
1588 );
1589 }
1590
1591 Trampoline::ErrorContextTransfer => {
1592 let transfer_fn = self
1593 .gen
1594 .intrinsic(Intrinsic::ErrCtx(ErrCtxIntrinsic::Transfer));
1595 uwriteln!(self.src.js, "const trampoline{i} = {transfer_fn};");
1596 }
1597
1598 Trampoline::SyncStartCall { callback } => {
1599 let _ = callback;
1600 todo!("sync-start-call not implemented");
1601 }
1602
1603 Trampoline::AsyncStartCall {
1604 callback,
1605 post_return,
1606 } => {
1607 let _ = (callback, post_return);
1608 todo!("async-start-call not implemented");
1609 }
1610
1611 Trampoline::LowerImport {
1612 index,
1613 lower_ty,
1614 options,
1615 } => {
1616 let _ = (index, lower_ty, options);
1617 }
1619
1620 Trampoline::AlwaysTrap => {
1621 uwrite!(
1622 self.src.js,
1623 "function trampoline{i}(rep) {{
1624 throw new TypeError('AlwaysTrap');
1625 }}
1626 "
1627 );
1628 }
1629
1630 Trampoline::Transcoder {
1631 op,
1632 from,
1633 from64,
1634 to,
1635 to64,
1636 } => {
1637 if *from64 || *to64 {
1638 unimplemented!("memory 64 transcoder");
1639 }
1640 let from = from.as_u32();
1641 let to = to.as_u32();
1642 match op {
1643 Transcode::Copy(FixedEncoding::Utf8) => {
1644 uwriteln!(
1645 self.src.js,
1646 "function trampoline{i} (from_ptr, len, to_ptr) {{
1647 new Uint8Array(memory{to}.buffer, to_ptr, len).set(new Uint8Array(memory{from}.buffer, from_ptr, len));
1648 }}
1649 "
1650 );
1651 }
1652 Transcode::Copy(FixedEncoding::Utf16) => unimplemented!("utf16 copier"),
1653 Transcode::Copy(FixedEncoding::Latin1) => unimplemented!("latin1 copier"),
1654 Transcode::Latin1ToUtf16 => unimplemented!("latin to utf16 transcoder"),
1655 Transcode::Latin1ToUtf8 => unimplemented!("latin to utf8 transcoder"),
1656 Transcode::Utf16ToCompactProbablyUtf16 => {
1657 unimplemented!("utf16 to compact wtf16 transcoder")
1658 }
1659 Transcode::Utf16ToCompactUtf16 => {
1660 unimplemented!("utf16 to compact utf16 transcoder")
1661 }
1662 Transcode::Utf16ToLatin1 => unimplemented!("utf16 to latin1 transcoder"),
1663 Transcode::Utf16ToUtf8 => {
1664 uwriteln!(
1665 self.src.js,
1666 "function trampoline{i} (src, src_len, dst, dst_len) {{
1667 const encoder = new TextEncoder();
1668 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));
1669 return [read, written];
1670 }}
1671 "
1672 );
1673 }
1674 Transcode::Utf8ToCompactUtf16 => {
1675 unimplemented!("utf8 to compact utf16 transcoder")
1676 }
1677 Transcode::Utf8ToLatin1 => unimplemented!("utf8 to latin1 transcoder"),
1678 Transcode::Utf8ToUtf16 => {
1679 uwriteln!(
1680 self.src.js,
1681 "function trampoline{i} (from_ptr, len, to_ptr) {{
1682 const decoder = new TextDecoder();
1683 const content = decoder.decode(new Uint8Array(memory{from}.buffer, from_ptr, len));
1684 const strlen = content.length
1685 const view = new Uint16Array(memory{to}.buffer, to_ptr, strlen * 2)
1686 for (var i = 0; i < strlen; i++) {{
1687 view[i] = content.charCodeAt(i);
1688 }}
1689 return strlen;
1690 }}
1691 "
1692 );
1693 }
1694 };
1695 }
1696
1697 Trampoline::ResourceNew(resource) => {
1698 self.ensure_resource_table(*resource);
1699 let rid = resource.as_u32();
1700 let rsc_table_create_own = self.gen.intrinsic(Intrinsic::Resource(
1701 ResourceIntrinsic::ResourceTableCreateOwn,
1702 ));
1703 uwriteln!(
1704 self.src.js,
1705 "const trampoline{i} = {rsc_table_create_own}.bind(null, handleTable{rid});"
1706 );
1707 }
1708
1709 Trampoline::ResourceRep(resource) => {
1710 self.ensure_resource_table(*resource);
1711 let rid = resource.as_u32();
1712 let rsc_flag = self
1713 .gen
1714 .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableFlag));
1715 uwriteln!(
1716 self.src.js,
1717 "function trampoline{i} (handle) {{
1718 return handleTable{rid}[(handle << 1) + 1] & ~{rsc_flag};
1719 }}"
1720 );
1721 }
1722
1723 Trampoline::ResourceDrop(resource) => {
1724 self.ensure_resource_table(*resource);
1725 let tid = resource.as_u32();
1726 let resource_ty = &self.types[*resource];
1727 let rid = resource_ty.ty.as_u32();
1728
1729 let dtor = if let Some(resource_idx) =
1731 self.component.defined_resource_index(resource_ty.ty)
1732 {
1733 let resource_def = self
1734 .component
1735 .initializers
1736 .iter()
1737 .find_map(|i| match i {
1738 GlobalInitializer::Resource(r) if r.index == resource_idx => Some(r),
1739 _ => None,
1740 })
1741 .unwrap();
1742
1743 if let Some(dtor) = &resource_def.dtor {
1745 format!(
1746 "
1747 {}(handleEntry.rep);",
1748 self.core_def(dtor)
1749 )
1750 } else {
1751 "".into()
1752 }
1753 } else {
1754 let symbol_dispose = self.gen.intrinsic(Intrinsic::SymbolDispose);
1761 let symbol_cabi_dispose = self.gen.intrinsic(Intrinsic::SymbolCabiDispose);
1762
1763 if let Some(imported_resource_local_name) =
1765 self.gen.local_names.try_get(resource_ty.ty)
1766 {
1767 format!(
1768 "
1769 const rsc = captureTable{rid}.get(handleEntry.rep);
1770 if (rsc) {{
1771 if (rsc[{symbol_dispose}]) rsc[{symbol_dispose}]();
1772 captureTable{rid}.delete(handleEntry.rep);
1773 }} else if ({imported_resource_local_name}[{symbol_cabi_dispose}]) {{
1774 {imported_resource_local_name}[{symbol_cabi_dispose}](handleEntry.rep);
1775 }}"
1776 )
1777 } else {
1778 format!(
1780 "throw new TypeError('unreachable trampoline for resource [{:?}]')",
1781 resource_ty.ty
1782 )
1783 }
1784 };
1785
1786 let rsc_table_remove = self
1787 .gen
1788 .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableRemove));
1789 uwrite!(
1790 self.src.js,
1791 "function trampoline{i}(handle) {{
1792 const handleEntry = {rsc_table_remove}(handleTable{tid}, handle);
1793 if (handleEntry.own) {{
1794 {dtor}
1795 }}
1796 }}
1797 ",
1798 );
1799 }
1800
1801 Trampoline::ResourceTransferOwn => {
1802 let resource_transfer = self
1803 .gen
1804 .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTransferOwn));
1805 uwriteln!(self.src.js, "const trampoline{i} = {resource_transfer};");
1806 }
1807
1808 Trampoline::ResourceTransferBorrow => {
1809 let resource_transfer =
1810 self.gen
1811 .intrinsic(if self.gen.opts.valid_lifting_optimization {
1812 Intrinsic::Resource(
1813 ResourceIntrinsic::ResourceTransferBorrowValidLifting,
1814 )
1815 } else {
1816 Intrinsic::Resource(ResourceIntrinsic::ResourceTransferBorrow)
1817 });
1818 uwriteln!(self.src.js, "const trampoline{i} = {resource_transfer};");
1819 }
1820
1821 Trampoline::ResourceEnterCall => {
1822 let scope_id = self.gen.intrinsic(Intrinsic::ScopeId);
1823 uwrite!(
1824 self.src.js,
1825 "function trampoline{i}() {{
1826 {scope_id}++;
1827 }}
1828 ",
1829 );
1830 }
1831
1832 Trampoline::ResourceExitCall => {
1833 let scope_id = self.gen.intrinsic(Intrinsic::ScopeId);
1834 let resource_borrows = self
1835 .gen
1836 .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceCallBorrows));
1837 let handle_tables = self.gen.intrinsic(Intrinsic::HandleTables);
1838 uwrite!(
1842 self.src.js,
1843 "function trampoline{i}() {{
1844 {scope_id}--;
1845 for (const {{ rid, handle }} of {resource_borrows}) {{
1846 if ({handle_tables}[rid][handle << 1] === {scope_id})
1847 throw new TypeError('borrows not dropped for resource call');
1848 }}
1849 {resource_borrows} = [];
1850 }}
1851 ",
1852 );
1853 }
1854
1855 Trampoline::PrepareCall { memory } => {
1856 let _ = memory;
1857 todo!("prepare-call not implemented")
1858 }
1859
1860 Trampoline::ContextSet(slot) => {
1861 let context_set_fn = self
1862 .gen
1863 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::ContextSet));
1864 uwriteln!(
1865 self.src.js,
1866 "const trampoline{i} = {context_set_fn}.bind(null, {slot});"
1867 );
1868 }
1869
1870 Trampoline::ContextGet(slot) => {
1871 let context_get_fn = self
1872 .gen
1873 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::ContextGet));
1874 uwriteln!(
1875 self.src.js,
1876 "const trampoline{i} = {context_get_fn}.bind(null, {slot});"
1877 );
1878 }
1879
1880 Trampoline::TaskReturn { results, options } => {
1881 let CanonicalOptions {
1882 instance,
1883 async_,
1884 data_model:
1885 CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }),
1886 string_encoding,
1887 callback,
1888 post_return,
1889 ..
1890 } = self
1891 .component
1892 .options
1893 .get(*options)
1894 .expect("failed to find options")
1895 else {
1896 unreachable!("unexpected memory data model during task.return");
1897 };
1898
1899 if realloc.is_some() && memory.is_none() {
1902 panic!("memory must be present if realloc is");
1903 }
1904 if *async_ && post_return.is_some() {
1905 panic!("async and post return must not be specified together");
1906 }
1907 if *async_ && callback.is_none() {
1908 panic!("callback must be specified for async");
1909 }
1910 if let Some(cb_idx) = callback {
1911 let cb_fn = &self.types[TypeFuncIndex::from_u32(cb_idx.as_u32())];
1912 match self.types[cb_fn.params].types[..] {
1913 [InterfaceType::S32, InterfaceType::S32, InterfaceType::S32] => {}
1914 _ => panic!("unexpected params for async callback fn"),
1915 }
1916 match self.types[cb_fn.results].types[..] {
1917 [InterfaceType::S32] => {}
1918 _ => panic!("unexpected results for async callback fn"),
1919 }
1920 }
1921
1922 let result_types = &self.types[*results].types;
1928 let mut lift_fns: Vec<String> = Vec::with_capacity(result_types.len());
1929 for result_ty in result_types {
1930 let result_ty_abi = self.types.canonical_abi(result_ty);
1931 lift_fns.push(match result_ty {
1932 InterfaceType::Bool => self
1933 .gen
1934 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatBool)),
1935 InterfaceType::S8 => self
1936 .gen
1937 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS8)),
1938 InterfaceType::U8 => self
1939 .gen
1940 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU8)),
1941 InterfaceType::S16 => self
1942 .gen
1943 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS16)),
1944 InterfaceType::U16 => self
1945 .gen
1946 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU16)),
1947 InterfaceType::S32 => self
1948 .gen
1949 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS32)),
1950 InterfaceType::U32 => self
1951 .gen
1952 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU32)),
1953 InterfaceType::S64 => self
1954 .gen
1955 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatS64)),
1956 InterfaceType::U64 => self
1957 .gen
1958 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatU64)),
1959 InterfaceType::Float32 => self
1960 .gen
1961 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFloat32)),
1962 InterfaceType::Float64 => self
1963 .gen
1964 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFloat64)),
1965 InterfaceType::Char => match string_encoding {
1966 wasmtime_environ::component::StringEncoding::Utf8 => self
1967 .gen
1968 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatCharUtf8)),
1969 wasmtime_environ::component::StringEncoding::Utf16 => self
1970 .gen
1971 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatCharUtf16)),
1972 wasmtime_environ::component::StringEncoding::CompactUtf16 => {
1973 todo!("latin1+utf8 not supported")
1974 }
1975 },
1976 InterfaceType::String => match string_encoding {
1977 wasmtime_environ::component::StringEncoding::Utf8 => self
1978 .gen
1979 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatStringUtf8)),
1980 wasmtime_environ::component::StringEncoding::Utf16 => self
1981 .gen
1982 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatStringUtf16)),
1983 wasmtime_environ::component::StringEncoding::CompactUtf16 => {
1984 todo!("latin1+utf8 not supported")
1985 }
1986 },
1987 InterfaceType::Record(_ty_idx) => {
1988 let lift_fn = self
1989 .gen
1990 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatRecord));
1991 format!("{lift_fn}.bind(null, {})", result_ty_abi.size32)
1992 }
1993 InterfaceType::Variant(_ty_idx) => {
1994 let lift_fn = self
1995 .gen
1996 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatVariant));
1997 format!("{lift_fn}.bind(null, {})", result_ty_abi.size32)
1998 }
1999 InterfaceType::List(_ty_idx) => {
2000 let lift_fn = self
2001 .gen
2002 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatList));
2003 format!("{lift_fn}.bind(null, {})", result_ty_abi.size32)
2004 }
2005 InterfaceType::Tuple(_ty_idx) => {
2006 let lift_fn = self
2007 .gen
2008 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatTuple));
2009 format!("{lift_fn}.bind(null, {})", result_ty_abi.size32)
2010 }
2011 InterfaceType::Flags(_ty_idx) => {
2012 let lift_fn = self
2013 .gen
2014 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFlags));
2015 format!("{lift_fn}.bind(null, {})", result_ty_abi.size32)
2016 }
2017 InterfaceType::Enum(_ty_idx) => {
2018 let lift_fn = self
2019 .gen
2020 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatEnum));
2021 format!("{lift_fn}.bind(null, {})", result_ty_abi.size32)
2022 }
2023 InterfaceType::Option(_ty_idx) => {
2024 let lift_fn = self
2025 .gen
2026 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatOption));
2027 format!("{lift_fn}.bind(null, {})", result_ty_abi.size32)
2028 }
2029 InterfaceType::Result(_ty_idx) => {
2030 let lift_fn = self
2031 .gen
2032 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatResult));
2033 format!("{lift_fn}.bind(null, {})", result_ty_abi.size32)
2034 }
2035 InterfaceType::Own(_ty_idx) => {
2036 let lift_fn = self
2037 .gen
2038 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatOwn));
2039 format!("{lift_fn}.bind(null, {})", result_ty_abi.size32)
2040 }
2041 InterfaceType::Borrow(_ty_idx) => {
2042 let lift_fn = self
2043 .gen
2044 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatOwn));
2045 format!("{lift_fn}.bind(null, {})", result_ty_abi.size32)
2046 }
2047 InterfaceType::Future(_ty_idx) => {
2048 let lift_fn = self
2049 .gen
2050 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatFuture));
2051 format!("{lift_fn}.bind(null, 4)")
2052 }
2053 InterfaceType::Stream(_ty_idx) => {
2054 let lift_fn = self
2055 .gen
2056 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatStream));
2057 format!("{lift_fn}.bind(null {})", result_ty_abi.size32)
2058 }
2059 InterfaceType::ErrorContext(_ty_idx) => {
2060 let lift_fn = self
2061 .gen
2062 .intrinsic(Intrinsic::Lift(LiftIntrinsic::LiftFlatErrorContext));
2063 format!("{lift_fn}.bind(null, {})", result_ty_abi.size32)
2064 }
2065 });
2066 }
2067 let lift_fns_js = format!("[{}]", lift_fns.join(","));
2068
2069 let memory_js = memory
2075 .map(|idx| format!("memory{}", idx.as_u32()))
2076 .unwrap_or_else(|| "null".into());
2077 let component_idx = instance.as_u32();
2078 let task_return_fn = self
2079 .gen
2080 .intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::TaskReturn));
2081 let callback_fn_idx = callback
2082 .map(|v| v.as_u32().to_string())
2083 .unwrap_or_else(|| "null".into());
2084
2085 uwriteln!(
2086 self.src.js,
2087 "const trampoline{i} = {task_return_fn}.bind(
2088 null,
2089 {component_idx},
2090 {memory_js},
2091 {callback_fn_idx},
2092 {lift_fns_js},
2093 );",
2094 );
2095 }
2096 }
2097 }
2098
2099 fn instantiation_global_initializer(&mut self, init: &GlobalInitializer) {
2100 match init {
2101 GlobalInitializer::ExtractCallback(ExtractCallback { index, def }) => {
2108 let callback_idx = index.as_u32();
2109 let core_def = self.core_def(def);
2110 uwriteln!(self.src.js, "let callback_{callback_idx};",);
2111 uwriteln!(self.src.js_init, "callback_{callback_idx} = {core_def}",);
2112 }
2113
2114 GlobalInitializer::InstantiateModule(m) => match m {
2115 InstantiateModule::Static(idx, args) => self.instantiate_static_module(*idx, args),
2116 InstantiateModule::Import(..) => unimplemented!(),
2120 },
2121 GlobalInitializer::LowerImport { index, import } => {
2122 self.lower_import(*index, *import);
2123 }
2124 GlobalInitializer::ExtractMemory(m) => {
2125 let def = self.core_export_var_name(&m.export);
2126 let idx = m.index.as_u32();
2127 uwriteln!(self.src.js, "let memory{idx};");
2128 uwriteln!(self.src.js_init, "memory{idx} = {def};");
2129 }
2130 GlobalInitializer::ExtractRealloc(r) => {
2131 let def = self.core_def(&r.def);
2132 let idx = r.index.as_u32();
2133 uwriteln!(self.src.js, "let realloc{idx};");
2134 uwriteln!(self.src.js_init, "realloc{idx} = {def};",);
2135 }
2136 GlobalInitializer::ExtractPostReturn(p) => {
2137 let def = self.core_def(&p.def);
2138 let idx = p.index.as_u32();
2139 uwriteln!(self.src.js, "let postReturn{idx};");
2140 uwriteln!(self.src.js_init, "postReturn{idx} = {def};");
2141 }
2142 GlobalInitializer::Resource(_) => {}
2143 GlobalInitializer::ExtractTable(extract_table) => {
2144 let _ = extract_table;
2145 }
2146 }
2147 }
2148
2149 fn instantiate_static_module(&mut self, idx: StaticModuleIndex, args: &[CoreDef]) {
2150 let mut import_obj = BTreeMap::new();
2155 for (module, name, arg) in self.modules[idx].imports(args) {
2156 let def = self.augmented_import_def(arg);
2157 let dst = import_obj.entry(module).or_insert(BTreeMap::new());
2158 let prev = dst.insert(name, def);
2159 assert!(
2160 prev.is_none(),
2161 "unsupported duplicate import of `{module}::{name}`"
2162 );
2163 assert!(prev.is_none());
2164 }
2165 let mut imports = String::new();
2166 if !import_obj.is_empty() {
2167 imports.push_str(", {\n");
2168 for (module, names) in import_obj {
2169 imports.push_str(&maybe_quote_id(module));
2170 imports.push_str(": {\n");
2171 for (name, val) in names {
2172 imports.push_str(&maybe_quote_id(name));
2173 uwriteln!(imports, ": {val},");
2174 }
2175 imports.push_str("},\n");
2176 }
2177 imports.push('}');
2178 }
2179
2180 let i = self.instances.push(idx);
2181 let iu32 = i.as_u32();
2182 let instantiate = self.gen.intrinsic(Intrinsic::InstantiateCore);
2183 uwriteln!(self.src.js, "let exports{iu32};");
2184
2185 match self.gen.opts.instantiation {
2186 Some(InstantiationMode::Async) | None => {
2187 uwriteln!(
2188 self.src.js_init,
2189 "({{ exports: exports{iu32} }} = yield {instantiate}(yield module{}{imports}));",
2190 idx.as_u32(),
2191 )
2192 }
2193
2194 Some(InstantiationMode::Sync) => {
2195 uwriteln!(
2196 self.src.js_init,
2197 "({{ exports: exports{iu32} }} = {instantiate}(module{}{imports}));",
2198 idx.as_u32(),
2199 )
2200 }
2201 }
2202 }
2203
2204 fn create_resource_fn_map(
2214 &mut self,
2215 func: &Function,
2216 ty_func_idx: TypeFuncIndex,
2217 resource_map: &mut ResourceMap,
2218 remote_resource_map: &mut RemoteResourceMap,
2219 ) {
2220 let params_ty = &self.types[self.types[ty_func_idx].params];
2222 for ((_, ty), iface_ty) in func.params.iter().zip(params_ty.types.iter()) {
2223 if let Type::Id(id) = ty {
2224 self.connect_resource_types(*id, iface_ty, resource_map, remote_resource_map);
2225 }
2226 }
2227 let results_ty = &self.types[self.types[ty_func_idx].results];
2229 if let (Some(Type::Id(id)), Some(iface_ty)) = (func.result, results_ty.types.first()) {
2230 self.connect_resource_types(id, iface_ty, resource_map, remote_resource_map);
2231 }
2232 }
2233
2234 fn resource_name(
2235 resolve: &Resolve,
2236 local_names: &'a mut LocalNames,
2237 resource: TypeId,
2238 resource_map: &BTreeMap<TypeId, ResourceIndex>,
2239 ) -> &'a str {
2240 let resource = crate::dealias(resolve, resource);
2241 local_names
2242 .get_or_create(
2243 resource_map[&resource],
2244 &resolve.types[resource]
2245 .name
2246 .as_ref()
2247 .unwrap()
2248 .to_upper_camel_case(),
2249 )
2250 .0
2251 }
2252
2253 fn lower_import(&mut self, index: LoweredIndex, import: RuntimeImportIndex) {
2254 let (options, trampoline, func_ty) = self.lowering_options[index];
2255
2256 let (import_index, path) = &self.component.imports[import];
2257 let (import_name, _) = &self.component.import_types[*import_index];
2258 let world_key = &self.imports[import_name];
2259
2260 let (func, func_name, iface_name) =
2261 match &self.resolve.worlds[self.world].imports[world_key] {
2262 WorldItem::Function(func) => {
2263 assert_eq!(path.len(), 0);
2264 (func, import_name, None)
2265 }
2266 WorldItem::Interface { id, stability: _ } => {
2267 assert_eq!(path.len(), 1);
2268 let iface = &self.resolve.interfaces[*id];
2269 let func = &iface.functions[&path[0]];
2270 let bundle = (
2271 func,
2272 &path[0],
2273 Some(iface.name.as_deref().unwrap_or_else(|| import_name)),
2274 );
2275 bundle
2276 }
2277 WorldItem::Type(_) => unreachable!("unexpected imported world item type"),
2278 };
2279
2280 let is_guest_async_lifted = is_guest_async_lifted_fn(func, options);
2282 if options.async_ {
2283 assert!(
2284 options.post_return.is_none(),
2285 "async function {func_name} (import {import_name}) can't have post return",
2286 );
2287 }
2288
2289 let requires_async_porcelain = requires_async_porcelain(
2291 FunctionIdentifier::Fn(func),
2292 import_name,
2293 &self.async_imports,
2294 );
2295
2296 let (import_specifier, maybe_iface_member) = map_import(
2298 &self.gen.opts.map,
2299 if iface_name.is_some() {
2300 import_name
2301 } else {
2302 match func.kind {
2303 FunctionKind::Method(_) => {
2304 let stripped = import_name.strip_prefix("[method]").unwrap();
2305 &stripped[0..stripped.find(".").unwrap()]
2306 }
2307 FunctionKind::Static(_) => {
2308 let stripped = import_name.strip_prefix("[static]").unwrap();
2309 &stripped[0..stripped.find(".").unwrap()]
2310 }
2311 FunctionKind::Constructor(_) => {
2312 import_name.strip_prefix("[constructor]").unwrap()
2313 }
2314 FunctionKind::Freestanding => import_name,
2315 FunctionKind::AsyncFreestanding => {
2316 todo!("[lower_import()] FunctionKind::AsyncFreeStanding (import specifier)")
2317 }
2318 FunctionKind::AsyncMethod(id) => {
2319 let _ = id;
2320 todo!("[lower_import()] FunctionKind::AsyncMethod (import specifier)")
2321 }
2322 FunctionKind::AsyncStatic(id) => {
2323 let _ = id;
2324 todo!("[lower_import()] FunctionKind::AsyncStatic (import specifier)")
2325 }
2326 }
2327 },
2328 );
2329
2330 let mut import_resource_map = ResourceMap::new();
2331 let mut import_remote_resource_map = RemoteResourceMap::new();
2332 self.create_resource_fn_map(
2333 func,
2334 func_ty,
2335 &mut import_resource_map,
2336 &mut import_remote_resource_map,
2337 );
2338
2339 let (callee_name, call_type) = match func.kind {
2340 FunctionKind::Freestanding => (
2341 self.gen
2342 .local_names
2343 .get_or_create(
2344 format!(
2345 "import:{}-{}-{}",
2346 import_specifier,
2347 maybe_iface_member.as_deref().unwrap_or(""),
2348 &func.name
2349 ),
2350 &func.name,
2351 )
2352 .0
2353 .to_string(),
2354 CallType::Standard,
2355 ),
2356 FunctionKind::Method(_) => (
2357 func.item_name().to_lower_camel_case(),
2358 CallType::CalleeResourceDispatch,
2359 ),
2360 FunctionKind::Static(resource_id) => (
2361 format!(
2362 "{}.{}",
2363 Instantiator::resource_name(
2364 self.resolve,
2365 &mut self.gen.local_names,
2366 resource_id,
2367 &self.imports_resource_types
2368 ),
2369 func.item_name().to_lower_camel_case()
2370 ),
2371 CallType::Standard,
2372 ),
2373
2374 FunctionKind::Constructor(resource_id) => (
2375 format!(
2376 "new {}",
2377 Instantiator::resource_name(
2378 self.resolve,
2379 &mut self.gen.local_names,
2380 resource_id,
2381 &self.imports_resource_types
2382 )
2383 ),
2384 CallType::Standard,
2385 ),
2386
2387 FunctionKind::AsyncFreestanding => {
2388 todo!("[lower_import()] FunctionKind::AsyncFreeStanding")
2389 }
2390 FunctionKind::AsyncMethod(id) => {
2391 let _ = id;
2392 todo!("[lower_import()] FunctionKind::AsyncMethod");
2393 }
2394 FunctionKind::AsyncStatic(id) => {
2395 let _ = id;
2396 todo!("[lower_import()] FunctionKind::AsyncStatic");
2397 }
2398 };
2399
2400 let nparams = self
2401 .resolve
2402 .wasm_signature(AbiVariant::GuestImport, func)
2403 .params
2404 .len();
2405 match self.gen.opts.import_bindings {
2406 None | Some(BindingsMode::Js) | Some(BindingsMode::Hybrid) => {
2407 if requires_async_porcelain {
2408 uwrite!(
2409 self.src.js,
2410 "\nconst trampoline{} = new WebAssembly.Suspending(async function",
2411 trampoline.as_u32()
2412 );
2413 } else {
2414 uwrite!(self.src.js, "\nfunction trampoline{}", trampoline.as_u32());
2415 }
2416 self.bindgen(JsFunctionBindgenArgs {
2417 nparams,
2418 call_type,
2419 iface_name: if import_name.is_empty() {
2420 None
2421 } else {
2422 Some(import_name)
2423 },
2424 callee: &callee_name,
2425 opts: options,
2426 func,
2427 resource_map: &import_resource_map,
2428 remote_resource_map: &import_remote_resource_map,
2429 abi: AbiVariant::GuestImport,
2430 requires_async_porcelain,
2431 is_guest_async_lifted,
2432 });
2433
2434 uwriteln!(self.src.js, "");
2435 if requires_async_porcelain {
2436 uwriteln!(self.src.js, ");");
2437 } else {
2438 uwriteln!(self.src.js, "");
2439 }
2440 }
2441 Some(BindingsMode::Optimized) | Some(BindingsMode::DirectOptimized) => {
2442 uwriteln!(self.src.js, "let trampoline{};", trampoline.as_u32());
2443 }
2444 };
2445
2446 if !matches!(self.gen.opts.import_bindings, None | Some(BindingsMode::Js)) {
2451 let (memory, realloc) =
2452 if let CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions {
2453 memory,
2454 realloc,
2455 }) = options.data_model
2456 {
2457 (
2458 memory.map(|idx| format!(" memory: memory{},", idx.as_u32())),
2459 realloc.map(|idx| format!(" realloc: realloc{},", idx.as_u32())),
2460 )
2461 } else {
2462 (None, None)
2463 };
2464 let memory = memory.unwrap_or_default();
2465 let realloc = realloc.unwrap_or_default();
2466
2467 let post_return = options
2468 .post_return
2469 .map(|idx| format!(" postReturn: postReturn{},", idx.as_u32()))
2470 .unwrap_or("".into());
2471 let string_encoding = match options.string_encoding {
2472 wasmtime_environ::component::StringEncoding::Utf8 => "",
2473 wasmtime_environ::component::StringEncoding::Utf16 => " stringEncoding: 'utf16',",
2474 wasmtime_environ::component::StringEncoding::CompactUtf16 => {
2475 " stringEncoding: 'compact-utf16',"
2476 }
2477 };
2478
2479 let callee_name = match func.kind {
2480 FunctionKind::Static(_) | FunctionKind::Freestanding => callee_name.to_string(),
2481 FunctionKind::Method(resource_id) => format!(
2482 "{}.prototype.{callee_name}",
2483 Instantiator::resource_name(
2484 self.resolve,
2485 &mut self.gen.local_names,
2486 resource_id,
2487 &self.imports_resource_types
2488 )
2489 ),
2490 FunctionKind::Constructor(_) => callee_name[4..].to_string(),
2491 FunctionKind::AsyncFreestanding => {
2492 todo!("[lower_import()] FunctionKind::AsyncFreeStanding (callee name gen)")
2493 }
2494 FunctionKind::AsyncMethod(id) => {
2495 let _ = id;
2496 todo!("[lower_import()] FunctionKind::AsyncMethod (callee name gen)")
2497 }
2498 FunctionKind::AsyncStatic(id) => {
2499 let _ = id;
2500 todo!("[lower_import()] FunctionKind::AsyncFreeStanding (callee name gen)")
2501 }
2502 };
2503
2504 let resource_tables = {
2505 let mut resource_tables: Vec<TypeResourceTableIndex> = Vec::new();
2506
2507 for (_, data) in import_resource_map {
2508 let ResourceTable {
2509 data: ResourceData::Host { tid, .. },
2510 ..
2511 } = &data
2512 else {
2513 unreachable!("unexpected non-host resource table");
2514 };
2515 resource_tables.push(*tid);
2516 }
2517
2518 if resource_tables.is_empty() {
2519 "".to_string()
2520 } else {
2521 format!(
2522 " resourceTables: [{}],",
2523 resource_tables
2524 .iter()
2525 .map(|x| format!("handleTable{}", x.as_u32()))
2526 .collect::<Vec<String>>()
2527 .join(", ")
2528 )
2529 }
2530 };
2531
2532 match self.gen.opts.import_bindings {
2534 Some(BindingsMode::Hybrid) => {
2535 let symbol_cabi_lower = self.gen.intrinsic(Intrinsic::SymbolCabiLower);
2536 uwriteln!(self.src.js_init, "if ({callee_name}[{symbol_cabi_lower}]) {{
2537 trampoline{} = {callee_name}[{symbol_cabi_lower}]({{{memory}{realloc}{post_return}{string_encoding}{resource_tables}}});
2538 }}", trampoline.as_u32());
2539 }
2540 Some(BindingsMode::Optimized) => {
2541 let symbol_cabi_lower = self.gen.intrinsic(Intrinsic::SymbolCabiLower);
2542 if !self.gen.opts.valid_lifting_optimization {
2543 uwriteln!(self.src.js_init, "if (!{callee_name}[{symbol_cabi_lower}]) {{
2544 throw new TypeError('import for \"{import_name}\" does not define a Symbol.for(\"cabiLower\") optimized binding');
2545 }}");
2546 }
2547 uwriteln!(self.src.js_init, "trampoline{} = {callee_name}[{symbol_cabi_lower}]({{{memory}{realloc}{post_return}{string_encoding}{resource_tables}}});", trampoline.as_u32());
2548 }
2549 Some(BindingsMode::DirectOptimized) => {
2550 uwriteln!(
2551 self.src.js_init,
2552 "trampoline{} = {callee_name}({{{memory}{realloc}{post_return}{string_encoding}}});",
2553 trampoline.as_u32()
2554 );
2555 }
2556 None | Some(BindingsMode::Js) => unreachable!("invalid bindings mode"),
2557 };
2558 }
2559
2560 let (import_name, binding_name) = match func.kind {
2562 FunctionKind::Freestanding => (func_name.to_lower_camel_case(), callee_name),
2563 FunctionKind::Method(tid)
2564 | FunctionKind::Static(tid)
2565 | FunctionKind::Constructor(tid) => {
2566 let ty = &self.resolve.types[tid];
2567 (
2568 ty.name.as_ref().unwrap().to_upper_camel_case(),
2569 Instantiator::resource_name(
2570 self.resolve,
2571 &mut self.gen.local_names,
2572 tid,
2573 &self.imports_resource_types,
2574 )
2575 .to_string(),
2576 )
2577 }
2578 FunctionKind::AsyncFreestanding => {
2579 todo!("[lower_import()] FunctionKind::AsyncFreeStanding (import name gen)")
2580 }
2581 FunctionKind::AsyncMethod(id) => {
2582 let _ = id;
2583 todo!("[lower_import()] FunctionKind::AsyncMethod (import name gen)")
2584 }
2585 FunctionKind::AsyncStatic(id) => {
2586 let _ = id;
2587 todo!("[lower_import()] FunctionKind::AsyncStatic (import name gen)")
2588 }
2589 };
2590
2591 self.ensure_import(
2592 import_specifier,
2593 iface_name,
2594 maybe_iface_member.as_deref(),
2595 if iface_name.is_some() {
2596 Some(import_name.to_string())
2597 } else {
2598 None
2599 },
2600 binding_name,
2601 );
2602 }
2603
2604 fn ensure_import(
2615 &mut self,
2616 import_specifier: String,
2617 iface_name: Option<&str>,
2618 iface_member: Option<&str>,
2619 import_binding: Option<String>,
2620 local_name: String,
2621 ) {
2622 if import_specifier.starts_with("webidl:") {
2623 self.gen
2624 .intrinsic(Intrinsic::WebIdl(WebIdlIntrinsic::GlobalThisIdlProxy));
2625 }
2626
2627 let mut import_path = Vec::with_capacity(2);
2629 import_path.push(import_specifier);
2630 if let Some(_iface_name) = iface_name {
2631 if let Some(iface_member) = iface_member {
2634 import_path.push(iface_member.to_lower_camel_case());
2635 }
2636 import_path.push(import_binding.clone().unwrap());
2637 } else if let Some(iface_member) = iface_member {
2638 import_path.push(iface_member.into());
2639 } else if let Some(import_binding) = &import_binding {
2640 import_path.push(import_binding.into());
2641 }
2642
2643 self.gen
2645 .esm_bindgen
2646 .add_import_binding(&import_path, local_name);
2647 }
2648
2649 fn connect_remote_resources(
2658 &mut self,
2659 iface_ty: &InterfaceType,
2660 remote_resource_map: &mut RemoteResourceMap,
2661 ) {
2662 let (idx, entry) = match iface_ty {
2664 InterfaceType::Future(idx) => {
2665 (
2667 idx.as_u32(),
2668 ResourceTable {
2669 imported: true,
2670 data: ResourceData::Guest {
2671 resource_name: "Future".into(),
2672 prefix: Some(format!("${}", idx.as_u32())),
2673 },
2674 },
2675 )
2676 }
2677 InterfaceType::Stream(idx) => (
2678 idx.as_u32(),
2679 ResourceTable {
2680 imported: true,
2681 data: ResourceData::Guest {
2682 resource_name: "Stream".into(),
2683 prefix: Some(format!("${}", idx.as_u32())),
2684 },
2685 },
2686 ),
2687 InterfaceType::ErrorContext(idx) => (
2688 idx.as_u32(),
2689 ResourceTable {
2690 imported: true,
2691 data: ResourceData::Guest {
2692 resource_name: "ErrorContext".into(),
2693 prefix: Some(format!("${}", idx.as_u32())),
2694 },
2695 },
2696 ),
2697 _ => unreachable!("unexpected interface type [{iface_ty:?}] with no type"),
2698 };
2699
2700 remote_resource_map.insert(idx, entry);
2701 }
2702
2703 fn connect_host_resource(
2712 &mut self,
2713 t: TypeId,
2714 tid: TypeResourceTableIndex,
2715 resource_map: &mut ResourceMap,
2716 ) {
2717 self.ensure_resource_table(tid);
2718
2719 let resource_idx = self.types[tid].ty;
2721 let imported = self
2722 .component
2723 .defined_resource_index(resource_idx)
2724 .is_none();
2725
2726 let resource_id = crate::dealias(self.resolve, t);
2728 let ty = &self.resolve.types[resource_id];
2729
2730 let mut dtor_str = None;
2733 if let Some(resource_idx) = self.component.defined_resource_index(resource_idx) {
2734 assert!(!imported);
2735 let resource_def = self
2736 .component
2737 .initializers
2738 .iter()
2739 .find_map(|i| match i {
2740 GlobalInitializer::Resource(r) if r.index == resource_idx => Some(r),
2741 _ => None,
2742 })
2743 .unwrap();
2744
2745 if let Some(dtor) = &resource_def.dtor {
2746 dtor_str = Some(self.core_def(dtor));
2747 }
2748 }
2749
2750 let resource_name = ty.name.as_ref().unwrap().to_upper_camel_case();
2752
2753 let local_name = if imported {
2754 let (world_key, iface_name) = match ty.owner {
2755 wit_parser::TypeOwner::World(world) => (
2756 self.resolve.worlds[world]
2757 .imports
2758 .iter()
2759 .find(|&(_, item)| *item == WorldItem::Type(t))
2760 .unwrap()
2761 .0
2762 .clone(),
2763 None,
2764 ),
2765 wit_parser::TypeOwner::Interface(iface) => {
2766 match &self.resolve.interfaces[iface].name {
2767 Some(name) => (WorldKey::Interface(iface), Some(name.as_str())),
2768 None => {
2769 let key = self.resolve.worlds[self.world]
2770 .imports
2771 .iter()
2772 .find(|&(_, item)| match item {
2773 WorldItem::Interface { id, .. } => *id == iface,
2774 _ => false,
2775 })
2776 .unwrap()
2777 .0;
2778 (
2779 key.clone(),
2780 match key {
2781 WorldKey::Name(ref name) => Some(name.as_str()),
2782 WorldKey::Interface(_) => None,
2783 },
2784 )
2785 }
2786 }
2787 }
2788 wit_parser::TypeOwner::None => unimplemented!(),
2789 };
2790
2791 let import_name = self.resolve.name_world_key(&world_key);
2792 let (local_name, _) = self
2793 .gen
2794 .local_names
2795 .get_or_create(resource_idx, &resource_name);
2796
2797 let local_name_str = local_name.to_string();
2798
2799 let (import_specifier, maybe_iface_member) =
2801 map_import(&self.gen.opts.map, &import_name);
2802
2803 self.ensure_import(
2805 import_specifier,
2806 iface_name,
2807 maybe_iface_member.as_deref(),
2808 iface_name.map(|_| resource_name),
2809 local_name_str.to_string(),
2810 );
2811 local_name_str
2812 } else {
2813 let (local_name, _) = self
2814 .gen
2815 .local_names
2816 .get_or_create(resource_idx, &resource_name);
2817 local_name.to_string()
2818 };
2819
2820 let entry = ResourceTable {
2822 imported,
2823 data: ResourceData::Host {
2824 tid,
2825 rid: self.types[tid].ty,
2826 local_name,
2827 dtor_name: dtor_str,
2828 },
2829 };
2830
2831 if let Some(existing) = resource_map.get(&resource_id) {
2834 assert_eq!(*existing, entry);
2835 return;
2836 }
2837
2838 resource_map.insert(resource_id, entry);
2840 }
2841
2842 fn connect_resource_types(
2855 &mut self,
2856 id: TypeId,
2857 iface_ty: &InterfaceType,
2858 resource_map: &mut ResourceMap,
2859 remote_resource_map: &mut RemoteResourceMap,
2860 ) {
2861 match (&self.resolve.types[id].kind, iface_ty) {
2862 (TypeDefKind::Flags(_), InterfaceType::Flags(_))
2864 | (TypeDefKind::Enum(_), InterfaceType::Enum(_)) => {}
2865
2866 (TypeDefKind::Record(t1), InterfaceType::Record(t2)) => {
2868 let t2 = &self.types[*t2];
2869 for (f1, f2) in t1.fields.iter().zip(t2.fields.iter()) {
2870 if let Type::Id(id) = f1.ty {
2871 self.connect_resource_types(id, &f2.ty, resource_map, remote_resource_map);
2872 }
2873 }
2874 }
2875
2876 (
2878 TypeDefKind::Handle(Handle::Own(t1) | Handle::Borrow(t1)),
2879 InterfaceType::Own(t2) | InterfaceType::Borrow(t2),
2880 ) => {
2881 self.connect_host_resource(*t1, *t2, resource_map);
2882 }
2883
2884 (TypeDefKind::Tuple(t1), InterfaceType::Tuple(t2)) => {
2886 let t2 = &self.types[*t2];
2887 for (f1, f2) in t1.types.iter().zip(t2.types.iter()) {
2888 if let Type::Id(id) = f1 {
2889 self.connect_resource_types(*id, f2, resource_map, remote_resource_map);
2890 }
2891 }
2892 }
2893
2894 (TypeDefKind::Variant(t1), InterfaceType::Variant(t2)) => {
2896 let t2 = &self.types[*t2];
2897 for (f1, f2) in t1.cases.iter().zip(t2.cases.iter()) {
2898 if let Some(Type::Id(id)) = &f1.ty {
2899 self.connect_resource_types(
2900 *id,
2901 f2.1.as_ref().unwrap(),
2902 resource_map,
2903 remote_resource_map,
2904 );
2905 }
2906 }
2907 }
2908
2909 (TypeDefKind::Option(t1), InterfaceType::Option(t2)) => {
2911 let t2 = &self.types[*t2];
2912 if let Type::Id(id) = t1 {
2913 self.connect_resource_types(*id, &t2.ty, resource_map, remote_resource_map);
2914 }
2915 }
2916
2917 (TypeDefKind::Result(t1), InterfaceType::Result(t2)) => {
2919 let t2 = &self.types[*t2];
2920 if let Some(Type::Id(id)) = &t1.ok {
2921 self.connect_resource_types(
2922 *id,
2923 &t2.ok.unwrap(),
2924 resource_map,
2925 remote_resource_map,
2926 );
2927 }
2928 if let Some(Type::Id(id)) = &t1.err {
2929 self.connect_resource_types(
2930 *id,
2931 &t2.err.unwrap(),
2932 resource_map,
2933 remote_resource_map,
2934 );
2935 }
2936 }
2937
2938 (TypeDefKind::List(t1), InterfaceType::List(t2)) => {
2940 let t2 = &self.types[*t2];
2941 if let Type::Id(id) = t1 {
2942 self.connect_resource_types(
2943 *id,
2944 &t2.element,
2945 resource_map,
2946 remote_resource_map,
2947 );
2948 }
2949 }
2950
2951 (TypeDefKind::Type(ty), _) => {
2953 if let Type::Id(id) = ty {
2954 self.connect_resource_types(*id, iface_ty, resource_map, remote_resource_map);
2955 }
2956 }
2957
2958 (TypeDefKind::Future(maybe_ty), InterfaceType::Future(_))
2960 | (TypeDefKind::Stream(maybe_ty), InterfaceType::Stream(_)) => {
2961 match maybe_ty {
2962 None => {
2965 self.connect_remote_resources(iface_ty, remote_resource_map);
2966 }
2967 Some(Type::Id(t)) => {
2969 self.connect_resource_types(*t, iface_ty, resource_map, remote_resource_map)
2970 }
2971 Some(_) => {
2972 unreachable!("unexpected interface type [{iface_ty:?}]")
2973 }
2974 }
2975 }
2976
2977 (TypeDefKind::Resource, _) => {
2979 unreachable!("resource types do not need to be connected")
2980 }
2981 (TypeDefKind::Unknown, _) => unreachable!("unknown types cannot be connected"),
2982 (tk1, tk2) => unreachable!("invalid typedef kind combination [{tk1:?}] [{tk2:?}]",),
2983 }
2984 }
2985
2986 fn bindgen(&mut self, args: JsFunctionBindgenArgs) {
2987 let JsFunctionBindgenArgs {
2988 nparams,
2989 call_type,
2990 iface_name,
2991 callee,
2992 opts,
2993 func,
2994 resource_map,
2995 remote_resource_map,
2996 abi,
2997 requires_async_porcelain,
2998 is_guest_async_lifted,
2999 } = args;
3000
3001 let (memory, realloc) =
3002 if let CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions {
3003 memory,
3004 realloc,
3005 }) = opts.data_model
3006 {
3007 (
3008 memory.map(|idx| format!("memory{}", idx.as_u32())),
3009 realloc.map(|idx| format!("realloc{}", idx.as_u32())),
3010 )
3011 } else {
3012 (None, None)
3013 };
3014
3015 let post_return = opts
3016 .post_return
3017 .map(|idx| format!("postReturn{}", idx.as_u32()));
3018
3019 let tracing_prefix = format!(
3020 "[iface=\"{}\", function=\"{}\"]",
3021 iface_name.unwrap_or("<no iface>"),
3022 func.name
3023 );
3024
3025 self.src.js("(");
3029 let mut params = Vec::new();
3030 let mut first = true;
3031 for i in 0..nparams {
3032 if i == 0 && matches!(call_type, CallType::FirstArgIsThis) {
3033 params.push("this".into());
3034 continue;
3035 }
3036 if !first {
3037 self.src.js(", ");
3038 } else {
3039 first = false;
3040 }
3041 let param = format!("arg{i}");
3042 self.src.js(¶m);
3043 params.push(param);
3044 }
3045 uwriteln!(self.src.js, ") {{");
3046
3047 if self.gen.opts.tracing {
3049 let event_fields = func
3050 .params
3051 .iter()
3052 .enumerate()
3053 .map(|(i, (name, _ty))| format!("{name}=${{arguments[{i}]}}"))
3054 .collect::<Vec<String>>();
3055 uwriteln!(
3056 self.src.js,
3057 "console.error(`{tracing_prefix} call {}`);",
3058 event_fields.join(", ")
3059 );
3060 }
3061
3062 if self.gen.opts.tla_compat
3064 && matches!(abi, AbiVariant::GuestExport)
3065 && self.gen.opts.instantiation.is_none()
3066 {
3067 let throw_uninitialized = self.gen.intrinsic(Intrinsic::ThrowUninitialized);
3068 uwrite!(
3069 self.src.js,
3070 "\
3071 if (!_initialized) {throw_uninitialized}();
3072 "
3073 );
3074 }
3075
3076 let mut f = FunctionBindgen {
3078 resource_map,
3079 remote_resource_map,
3080 clear_resource_borrows: false,
3081 intrinsics: &mut self.gen.all_intrinsics,
3082 valid_lifting_optimization: self.gen.opts.valid_lifting_optimization,
3083 sizes: &self.sizes,
3084 err: if get_thrown_type(self.resolve, func.result).is_some() {
3085 match abi {
3086 AbiVariant::GuestExport => ErrHandling::ThrowResultErr,
3087 AbiVariant::GuestImport => ErrHandling::ResultCatchHandler,
3088 AbiVariant::GuestImportAsync => todo!("[transpile_bindgen::bindgen()] GuestImportAsync (ERR) not yet implemented"),
3089 AbiVariant::GuestExportAsync => todo!("[transpile_bindgen::bindgen()] GuestExportAsync (ERR) not yet implemented"),
3090 AbiVariant::GuestExportAsyncStackful => todo!("[transpile_bindgen::bindgen()] GuestExportAsyncStackful (ERR) not yet implemented"),
3091 }
3092 } else {
3093 ErrHandling::None
3094 },
3095 block_storage: Vec::new(),
3096 blocks: Vec::new(),
3097 callee,
3098 callee_resource_dynamic: matches!(call_type, CallType::CalleeResourceDispatch),
3099 memory: memory.as_ref(),
3100 realloc: realloc.as_ref(),
3101 tmp: 0,
3102 params,
3103 post_return: post_return.as_ref(),
3104 tracing_prefix: &tracing_prefix,
3105 tracing_enabled: self.gen.opts.tracing,
3106 encoding: match opts.string_encoding {
3107 wasmtime_environ::component::StringEncoding::Utf8 => StringEncoding::UTF8,
3108 wasmtime_environ::component::StringEncoding::Utf16 => StringEncoding::UTF16,
3109 wasmtime_environ::component::StringEncoding::CompactUtf16 => {
3110 StringEncoding::CompactUTF16
3111 }
3112 },
3113 src: source::Source::default(),
3114 resolve: self.resolve,
3115 requires_async_porcelain,
3116 is_guest_async_lifted,
3117 canon_opts: opts,
3118 iface_name,
3119 };
3120
3121 abi::call(
3124 self.resolve,
3125 abi,
3126 match abi {
3127 AbiVariant::GuestImport => LiftLower::LiftArgsLowerResults,
3128 AbiVariant::GuestExport => if is_guest_async_lifted {
3129 LiftLower::LiftArgsLowerResults
3130 } else {
3131 LiftLower::LowerArgsLiftResults
3132 },
3133 AbiVariant::GuestImportAsync => todo!("[transpile_bindgen::bindgen()] GuestImportAsync (LIFT_LOWER) not yet implemented"),
3134 AbiVariant::GuestExportAsync => todo!("[transpile_bindgen::bindgen()] GuestExportAsync (LIFT_LOWER) not yet implemented"),
3135 AbiVariant::GuestExportAsyncStackful => todo!("[transpile_bindgen::bindgen()] GuestExportAsyncStackful (LIFT_LOWER) not yet implemented"),
3136 },
3137 func,
3138 &mut f,
3139 is_guest_async_lifted,
3140 );
3141
3142 self.src.js(&f.src);
3144
3145 self.src.js("}");
3147 }
3148
3149 fn augmented_import_def(&self, def: core::AugmentedImport<'_>) -> String {
3150 match def {
3151 core::AugmentedImport::CoreDef(def) => self.core_def(def),
3152 core::AugmentedImport::Memory { mem, op } => {
3153 let mem = self.core_def(mem);
3154 match op {
3155 core::AugmentedOp::I32Load => {
3156 format!(
3157 "(ptr, off) => new DataView({mem}.buffer).getInt32(ptr + off, true)"
3158 )
3159 }
3160 core::AugmentedOp::I32Load8U => {
3161 format!(
3162 "(ptr, off) => new DataView({mem}.buffer).getUint8(ptr + off, true)"
3163 )
3164 }
3165 core::AugmentedOp::I32Load8S => {
3166 format!("(ptr, off) => new DataView({mem}.buffer).getInt8(ptr + off, true)")
3167 }
3168 core::AugmentedOp::I32Load16U => {
3169 format!(
3170 "(ptr, off) => new DataView({mem}.buffer).getUint16(ptr + off, true)"
3171 )
3172 }
3173 core::AugmentedOp::I32Load16S => {
3174 format!(
3175 "(ptr, off) => new DataView({mem}.buffer).getInt16(ptr + off, true)"
3176 )
3177 }
3178 core::AugmentedOp::I64Load => {
3179 format!(
3180 "(ptr, off) => new DataView({mem}.buffer).getBigInt64(ptr + off, true)"
3181 )
3182 }
3183 core::AugmentedOp::F32Load => {
3184 format!(
3185 "(ptr, off) => new DataView({mem}.buffer).getFloat32(ptr + off, true)"
3186 )
3187 }
3188 core::AugmentedOp::F64Load => {
3189 format!(
3190 "(ptr, off) => new DataView({mem}.buffer).getFloat64(ptr + off, true)"
3191 )
3192 }
3193 core::AugmentedOp::I32Store8 => {
3194 format!(
3195 "(ptr, val, offset) => {{
3196 new DataView({mem}.buffer).setInt8(ptr + offset, val, true);
3197 }}"
3198 )
3199 }
3200 core::AugmentedOp::I32Store16 => {
3201 format!(
3202 "(ptr, val, offset) => {{
3203 new DataView({mem}.buffer).setInt16(ptr + offset, val, true);
3204 }}"
3205 )
3206 }
3207 core::AugmentedOp::I32Store => {
3208 format!(
3209 "(ptr, val, offset) => {{
3210 new DataView({mem}.buffer).setInt32(ptr + offset, val, true);
3211 }}"
3212 )
3213 }
3214 core::AugmentedOp::I64Store => {
3215 format!(
3216 "(ptr, val, offset) => {{
3217 new DataView({mem}.buffer).setBigInt64(ptr + offset, val, true);
3218 }}"
3219 )
3220 }
3221 core::AugmentedOp::F32Store => {
3222 format!(
3223 "(ptr, val, offset) => {{
3224 new DataView({mem}.buffer).setFloat32(ptr + offset, val, true);
3225 }}"
3226 )
3227 }
3228 core::AugmentedOp::F64Store => {
3229 format!(
3230 "(ptr, val, offset) => {{
3231 new DataView({mem}.buffer).setFloat64(ptr + offset, val, true);
3232 }}"
3233 )
3234 }
3235 core::AugmentedOp::MemorySize => {
3236 format!("ptr => {mem}.buffer.byteLength / 65536")
3237 }
3238 }
3239 }
3240 }
3241 }
3242
3243 fn core_def(&self, def: &CoreDef) -> String {
3244 match def {
3245 CoreDef::Export(e) => self.core_export_var_name(e),
3246 CoreDef::Trampoline(i) => format!("trampoline{}", i.as_u32()),
3247 CoreDef::InstanceFlags(i) => {
3248 self.used_instance_flags.borrow_mut().insert(*i);
3250 format!("instanceFlags{}", i.as_u32())
3251 }
3252 }
3253 }
3254
3255 fn core_export_var_name<T>(&self, export: &CoreExport<T>) -> String
3256 where
3257 T: Into<EntityIndex> + Copy,
3258 {
3259 let name = match &export.item {
3260 ExportItem::Index(idx) => {
3261 let module = &self.modules[self.instances[export.instance]];
3262 let idx = (*idx).into();
3263 module
3264 .exports()
3265 .iter()
3266 .find_map(|(name, i)| if *i == idx { Some(name) } else { None })
3267 .unwrap()
3268 }
3269 ExportItem::Name(s) => s,
3270 };
3271 let i = export.instance.as_u32() as usize;
3272 format!("exports{i}{}", maybe_quote_member(name))
3273 }
3274
3275 fn exports(&mut self, exports: &NameMap<String, ExportIndex>) {
3276 for (export_name, export_idx) in exports.raw_iter() {
3277 let export = &self.component.export_items[*export_idx];
3278 let world_key = &self.exports[export_name];
3279 let item = &self.resolve.worlds[self.world].exports[world_key];
3280 let mut export_resource_map = ResourceMap::new();
3281 let mut export_remote_resource_map = RemoteResourceMap::new();
3282 match export {
3283 Export::LiftedFunction {
3284 func: def,
3285 options,
3286 ty: func_ty,
3287 } => {
3288 let func = match item {
3289 WorldItem::Function(f) => f,
3290 WorldItem::Interface { .. } | WorldItem::Type(_) => {
3291 unreachable!("unexpectedly non-function lifted function export")
3292 }
3293 };
3294 self.create_resource_fn_map(
3295 func,
3296 *func_ty,
3297 &mut export_resource_map,
3298 &mut export_remote_resource_map,
3299 );
3300
3301 let local_name = if let FunctionKind::Constructor(resource_id)
3302 | FunctionKind::Method(resource_id)
3303 | FunctionKind::Static(resource_id) = func.kind
3304 {
3305 Instantiator::resource_name(
3306 self.resolve,
3307 &mut self.gen.local_names,
3308 resource_id,
3309 &self.exports_resource_types,
3310 )
3311 } else {
3312 self.gen.local_names.create_once(export_name)
3313 }
3314 .to_string();
3315
3316 let options = self
3317 .component
3318 .options
3319 .get(*options)
3320 .expect("failed to find options");
3321
3322 self.export_bindgen(
3323 &local_name,
3324 def,
3325 options,
3326 func,
3327 export_name,
3328 &export_resource_map,
3329 &export_remote_resource_map,
3330 );
3331 if let FunctionKind::Constructor(ty)
3332 | FunctionKind::Method(ty)
3333 | FunctionKind::Static(ty) = func.kind
3334 {
3335 let ty = &self.resolve.types[ty];
3336 self.gen.esm_bindgen.add_export_binding(
3337 None,
3338 local_name,
3339 ty.name.as_ref().unwrap().to_upper_camel_case(),
3340 );
3341 } else {
3342 self.gen.esm_bindgen.add_export_binding(
3343 None,
3344 local_name,
3345 export_name.to_lower_camel_case(),
3346 );
3347 }
3348 }
3349
3350 Export::Instance { exports, .. } => {
3351 let id = match item {
3352 WorldItem::Interface { id, stability: _ } => *id,
3353 WorldItem::Function(_) | WorldItem::Type(_) => {
3354 unreachable!("unexpectedly non-interface export instance")
3355 }
3356 };
3357 for (func_name, export_idx) in exports.raw_iter() {
3358 let export = &self.component.export_items[*export_idx];
3359 let (def, options, func_ty) = match export {
3360 Export::LiftedFunction { func, options, ty } => (func, options, ty),
3361 Export::Type(_) => continue, _ => unreachable!("unexpected non-lifted function export"),
3363 };
3364
3365 let func = &self.resolve.interfaces[id].functions[func_name];
3366
3367 self.create_resource_fn_map(
3368 func,
3369 *func_ty,
3370 &mut export_resource_map,
3371 &mut export_remote_resource_map,
3372 );
3373
3374 let local_name = if let FunctionKind::Constructor(resource_id)
3375 | FunctionKind::Method(resource_id)
3376 | FunctionKind::Static(resource_id) = func.kind
3377 {
3378 Instantiator::resource_name(
3379 self.resolve,
3380 &mut self.gen.local_names,
3381 resource_id,
3382 &self.exports_resource_types,
3383 )
3384 } else {
3385 self.gen.local_names.create_once(func_name)
3386 }
3387 .to_string();
3388
3389 let options = self
3390 .component
3391 .options
3392 .get(*options)
3393 .expect("failed to find options");
3394
3395 self.export_bindgen(
3396 &local_name,
3397 def,
3398 options,
3399 func,
3400 export_name,
3401 &export_resource_map,
3402 &export_remote_resource_map,
3403 );
3404
3405 if let FunctionKind::Constructor(ty)
3406 | FunctionKind::Method(ty)
3407 | FunctionKind::Static(ty) = func.kind
3408 {
3409 let ty = &self.resolve.types[ty];
3410 let resource = ty.name.as_ref().unwrap();
3411 self.gen.esm_bindgen.add_export_binding(
3412 Some(export_name),
3413 local_name,
3414 resource.to_upper_camel_case(),
3415 );
3416 } else {
3417 self.gen.esm_bindgen.add_export_binding(
3418 Some(export_name),
3419 local_name,
3420 func_name.to_lower_camel_case(),
3421 );
3422 }
3423 }
3424 }
3425
3426 Export::Type(_) => {}
3428
3429 Export::ModuleStatic { .. } | Export::ModuleImport { .. } => unimplemented!(),
3431 }
3432 }
3433 self.gen.esm_bindgen.populate_export_aliases();
3434 }
3435
3436 #[allow(clippy::too_many_arguments)]
3437 fn export_bindgen(
3438 &mut self,
3439 local_name: &str,
3440 def: &CoreDef,
3441 options: &CanonicalOptions,
3442 func: &Function,
3443 export_name: &String,
3444 export_resource_map: &ResourceMap,
3445 export_remote_resource_map: &RemoteResourceMap,
3446 ) {
3447 let requires_async_porcelain = requires_async_porcelain(
3449 FunctionIdentifier::Fn(func),
3450 export_name,
3451 &self.async_exports,
3452 );
3453 if options.async_ {
3455 assert!(
3456 options.post_return.is_none(),
3457 "async function {local_name} (export {export_name}) can't have post return"
3458 );
3459 }
3460
3461 let maybe_async = if requires_async_porcelain {
3462 "async "
3463 } else {
3464 ""
3465 };
3466
3467 let core_export_fn = self.core_def(def);
3469 let callee = match self
3470 .gen
3471 .local_names
3472 .get_or_create(&core_export_fn, &core_export_fn)
3473 {
3474 (local_name, true) => local_name.to_string(),
3475 (local_name, false) => {
3476 let local_name = local_name.to_string();
3477 uwriteln!(self.src.js, "let {local_name};");
3478 self.gen
3479 .all_core_exported_funcs
3480 .push((core_export_fn.clone(), requires_async_porcelain));
3481 local_name
3482 }
3483 };
3484
3485 match func.kind {
3486 FunctionKind::Freestanding => {
3487 uwrite!(self.src.js, "\n{maybe_async}function {local_name}")
3488 }
3489 FunctionKind::Method(_) => {
3490 self.ensure_local_resource_class(local_name.to_string());
3491 let method_name = func.item_name().to_lower_camel_case();
3492
3493 uwrite!(
3494 self.src.js,
3495 "\n{local_name}.prototype.{method_name} = {maybe_async}function {}",
3496 if !is_js_reserved_word(&method_name) {
3497 method_name.to_string()
3498 } else {
3499 format!("${method_name}")
3500 }
3501 );
3502 }
3503 FunctionKind::Static(_) => {
3504 self.ensure_local_resource_class(local_name.to_string());
3505 let method_name = func.item_name().to_lower_camel_case();
3506 uwrite!(
3507 self.src.js,
3508 "\n{local_name}.{method_name} = function {}",
3509 if !is_js_reserved_word(&method_name) {
3510 method_name.to_string()
3511 } else {
3512 format!("${method_name}")
3513 }
3514 );
3515 }
3516 FunctionKind::Constructor(_) => {
3517 if self.defined_resource_classes.contains(local_name) {
3518 panic!("Internal error: Resource constructor must be defined before other methods and statics");
3519 }
3520 uwrite!(
3521 self.src.js,
3522 "
3523 class {local_name} {{
3524 constructor"
3525 );
3526 self.defined_resource_classes.insert(local_name.to_string());
3527 }
3528 FunctionKind::AsyncFreestanding => {
3529 uwrite!(self.src.js, "\nasync function {local_name}")
3530 }
3531 FunctionKind::AsyncMethod(_) => {
3532 self.ensure_local_resource_class(local_name.to_string());
3533 let method_name = func.item_name().to_lower_camel_case();
3534 let fn_name = if !is_js_reserved_word(&method_name) {
3535 method_name.to_string()
3536 } else {
3537 format!("${method_name}")
3538 };
3539 uwrite!(
3540 self.src.js,
3541 "\n{local_name}.prototype.{method_name} = async function {fn_name}",
3542 );
3543 }
3544 FunctionKind::AsyncStatic(_) => {
3545 self.ensure_local_resource_class(local_name.to_string());
3546 let method_name = func.item_name().to_lower_camel_case();
3547 let fn_name = if !is_js_reserved_word(&method_name) {
3548 method_name.to_string()
3549 } else {
3550 format!("${method_name}")
3551 };
3552 uwrite!(
3553 self.src.js,
3554 "\n{local_name}.{method_name} = async function {fn_name}",
3555 );
3556 }
3557 }
3558
3559 self.bindgen(JsFunctionBindgenArgs {
3561 nparams: func.params.len(),
3562 call_type: match func.kind {
3563 FunctionKind::Method(_) => CallType::FirstArgIsThis,
3564 _ => CallType::Standard,
3565 },
3566 iface_name: if export_name.is_empty() {
3567 None
3568 } else {
3569 Some(export_name)
3570 },
3571 callee: &callee,
3572 opts: options,
3573 func,
3574 resource_map: export_resource_map,
3575 remote_resource_map: export_remote_resource_map,
3576 abi: AbiVariant::GuestExport,
3577 requires_async_porcelain,
3578 is_guest_async_lifted: is_guest_async_lifted_fn(func, options),
3579 });
3580
3581 match func.kind {
3583 FunctionKind::AsyncFreestanding | FunctionKind::Freestanding => self.src.js("\n"),
3584 FunctionKind::AsyncMethod(_)
3585 | FunctionKind::AsyncStatic(_)
3586 | FunctionKind::Method(_)
3587 | FunctionKind::Static(_) => self.src.js(";\n"),
3588 FunctionKind::Constructor(_) => self.src.js("\n}\n"),
3589 }
3590 }
3591}
3592
3593#[derive(Default)]
3594pub struct Source {
3595 pub js: source::Source,
3596 pub js_init: source::Source,
3597}
3598
3599impl Source {
3600 pub fn js(&mut self, s: &str) {
3601 self.js.push_str(s);
3602 }
3603 pub fn js_init(&mut self, s: &str) {
3604 self.js_init.push_str(s);
3605 }
3606}
3607
3608fn map_import(map: &Option<HashMap<String, String>>, impt: &str) -> (String, Option<String>) {
3609 let impt_sans_version = match impt.find('@') {
3610 Some(version_idx) => &impt[0..version_idx],
3611 None => impt,
3612 };
3613 if let Some(map) = map.as_ref() {
3614 if let Some(mapping) = map.get(impt) {
3615 return if let Some(hash_idx) = mapping[1..].find('#') {
3616 (
3617 mapping[0..hash_idx + 1].to_string(),
3618 Some(mapping[hash_idx + 2..].into()),
3619 )
3620 } else {
3621 (mapping.into(), None)
3622 };
3623 }
3624 if let Some(mapping) = map.get(impt_sans_version) {
3625 return if let Some(hash_idx) = mapping[1..].find('#') {
3626 (
3627 mapping[0..hash_idx + 1].to_string(),
3628 Some(mapping[hash_idx + 2..].into()),
3629 )
3630 } else {
3631 (mapping.into(), None)
3632 };
3633 }
3634 for (key, mapping) in map {
3635 if let Some(wildcard_idx) = key.find('*') {
3636 let lhs = &key[0..wildcard_idx];
3637 let rhs = &key[wildcard_idx + 1..];
3638 if impt_sans_version.starts_with(lhs) && impt_sans_version.ends_with(rhs) {
3639 let matched = &impt_sans_version[wildcard_idx
3640 ..wildcard_idx + impt_sans_version.len() - lhs.len() - rhs.len()];
3641 let mapping = mapping.replace('*', matched);
3642 return if let Some(hash_idx) = mapping[1..].find('#') {
3643 (
3644 mapping[0..hash_idx + 1].to_string(),
3645 Some(mapping[hash_idx + 2..].into()),
3646 )
3647 } else {
3648 (mapping, None)
3649 };
3650 }
3651 if impt.starts_with(lhs) && impt.ends_with(rhs) {
3652 let matched =
3653 &impt[wildcard_idx..wildcard_idx + impt.len() - lhs.len() - rhs.len()];
3654 let mapping = mapping.replace('*', matched);
3655 return if let Some(hash_idx) = mapping[1..].find('#') {
3656 (
3657 mapping[0..hash_idx + 1].to_string(),
3658 Some(mapping[hash_idx + 2..].into()),
3659 )
3660 } else {
3661 (mapping, None)
3662 };
3663 }
3664 }
3665 }
3666 }
3667 (impt_sans_version.to_string(), None)
3668}
3669
3670pub fn parse_world_key(name: &str) -> Option<(&str, &str, &str)> {
3671 let registry_idx = name.find(':')?;
3672 let ns = &name[0..registry_idx];
3673 match name.rfind('/') {
3674 Some(sep_idx) => {
3675 let end = if let Some(version_idx) = name.rfind('@') {
3676 version_idx
3677 } else {
3678 name.len()
3679 };
3680 Some((
3681 ns,
3682 &name[registry_idx + 1..sep_idx],
3683 &name[sep_idx + 1..end],
3684 ))
3685 }
3686 None => Some((ns, &name[registry_idx + 1..], "")),
3688 }
3689}
3690
3691fn core_file_name(name: &str, idx: u32) -> String {
3692 let i_str = if idx == 0 {
3693 String::from("")
3694 } else {
3695 (idx + 1).to_string()
3696 };
3697 format!("{name}.core{i_str}.wasm")
3698}
3699
3700fn string_encoding_js_literal(val: &wasmtime_environ::component::StringEncoding) -> &'static str {
3702 match val {
3703 wasmtime_environ::component::StringEncoding::Utf8 => "'utf8'",
3704 wasmtime_environ::component::StringEncoding::Utf16 => "'utf16'",
3705 wasmtime_environ::component::StringEncoding::CompactUtf16 => "'compact-utf16'",
3706 }
3707}
3708
3709fn is_valid_canonopt(
3711 CanonicalOptions {
3712 data_model,
3713 callback,
3714 post_return,
3715 async_,
3716 ..
3717 }: &CanonicalOptions,
3718) -> Result<()> {
3719 if let CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions { memory, realloc }) =
3720 data_model
3721 {
3722 if realloc.is_some() && memory.is_none() {
3723 bail!("memory must be present if realloc is");
3724 }
3725 }
3726 if *async_ && post_return.is_some() {
3727 bail!("async and post return must not be specified together");
3728 }
3729 if *async_ && callback.is_none() {
3730 bail!("callback must be specified for async");
3731 }
3732 Ok(())
3733}