1use crate::path::{ForeignInterfacePath, InterfacePath, InterfacePathParseError};
2use crate::{DynInterfaceTrampoline, DynPackageTrampoline, ImportFilter, ImportRule};
3use derivative::Derivative;
4use indexmap::{IndexMap, IndexSet};
5use semver::Version;
6use slab::Slab;
7use snafu::{ResultExt, Snafu};
8use std::collections::HashMap;
9use std::ops::{Deref, Index};
10use std::rc::Rc;
11use std::str::FromStr;
12use std::sync::Arc;
13use wac_types::{InterfaceId, ItemKind, Package};
14use wasm_component_semver::VersionMap;
15use wasmtime::component::{Component, Instance, LinkerInstance};
16use wasmtime::{AsContextMut, component};
17
18#[derive(Derivative)]
21#[derivative(Debug)]
22#[derivative(Default(bound = ""))]
23pub struct CompositionGraph<D, C: Clone = ()> {
24 nonce: usize,
25 types: wac_types::Types,
26 packages: Slab<PackageWrapper>,
27 package_map: HashMap<String, VersionMap<PackageId>>,
28 exported_interfaces: HashMap<ForeignInterfacePath, InterfaceExport<D, C>>,
29 imported_interfaces: HashMap<PackageId, IndexSet<ForeignInterfacePath>>,
30 #[derivative(Debug = "ignore")]
31 import_filter: Box<dyn ImportFilter>,
32}
33
34impl<D, C: Clone> CompositionGraph<D, C> {
35 #[must_use]
37 pub fn new() -> Self {
38 Self::default()
39 }
40
41 pub fn set_import_filter<F>(&mut self, filter: F)
44 where
45 F: ImportFilter + 'static,
46 {
47 self.import_filter = Box::new(filter);
48 }
49
50 pub fn add_package(
54 &mut self,
55 name: String,
56 version: Version,
57 bytes: impl Into<Vec<u8>>,
58 trampoline: impl DynPackageTrampoline<D, C>,
59 ) -> Result<PackageId, AddPackageError> {
60 let package = Package::from_bytes(name.as_str(), Some(&version), bytes, &mut self.types)
61 .context(add_package_error::PackageParseSnafu)?;
62
63 let package_id = PackageId {
64 id: self.packages.insert(PackageWrapper {
65 package,
66 nonce: self.nonce,
67 }),
68 nonce: self.nonce,
69 };
70 self.nonce += 1;
71
72 let version_set = self.package_map.entry(name.to_string()).or_default();
73
74 if let Err((version, _)) = version_set.try_insert(version, package_id) {
75 return Err(AddPackageError::DuplicatePackage {
76 name: name.to_string(),
77 version: version.clone(),
78 });
79 }
80
81 let package = self.packages.get_mut(package_id.id).unwrap();
82
83 let package_prefix = format!("{}/", package.name());
84 let version_suffix = package.version().map_or(String::new(), |v| format!("@{v}"));
85
86 let exports = &self.types[package.ty()].exports;
87
88 for (export_name, export_kind) in exports {
89 let ItemKind::Instance(interface_id) = export_kind else {
90 continue;
91 };
92
93 let interface_name = export_name
94 .strip_prefix(&package_prefix)
95 .and_then(|export_name| export_name.strip_suffix(&version_suffix));
96
97 if let Some(interface_name) = interface_name {
98 let path = ForeignInterfacePath::new(
99 package.name().to_string(),
100 interface_name.to_string(),
101 package.version().cloned(),
102 );
103
104 let interface_trampoline = InterfaceExport {
105 package: package_id,
106 interface: *interface_id,
107 trampoline: trampoline.interface_trampoline(interface_name),
108 };
109
110 if self
111 .exported_interfaces
112 .insert(path.clone(), interface_trampoline)
113 .is_some()
114 {
115 panic!("duplicate exported interface key {path:?}");
118 }
119 }
120 }
121
122 let mut import = |package_id: PackageId, interface_id: InterfaceId, import_name: &str| {
123 let import_interface_path = InterfacePath::from_str(import_name).context(
124 add_package_error::ImportParseSnafu {
125 interface: import_name.to_string(),
126 },
127 )?;
128
129 if let Some(import) = import_interface_path.into_foreign() {
130 match self.import_filter.filter_rule(&import) {
131 ImportRule::Skip => return Ok(()),
132
133 ImportRule::Include => {
134 let interface = &self.types[interface_id];
136 let interface_has_func = interface
137 .exports
138 .iter()
139 .any(|(_item_name, item_kind)| matches!(item_kind, ItemKind::Func(_)));
140 if !interface_has_func {
141 return Ok(());
142 }
143 }
144
145 ImportRule::Force => { }
146 }
147
148 self.imported_interfaces
150 .entry(package_id)
151 .or_default()
152 .insert(import);
153 }
154
155 Ok(())
156 };
157
158 for (package_id, package) in &self.packages {
159 let package_id = PackageId {
160 id: package_id,
161 nonce: package.nonce,
162 };
163 let package_ty = &self.types[package.ty()];
164
165 for (import_name, import_kind) in &package_ty.imports {
166 let ItemKind::Instance(interface_id) = import_kind else {
167 continue;
168 };
169
170 import(package_id, *interface_id, import_name)?;
171 }
172 }
173
174 Ok(package_id)
175 }
176
177 pub fn instantiate(
182 &mut self,
183 package_id: PackageId,
184 linker: &mut component::Linker<D>,
185 mut store: impl AsContextMut<Data = D>,
186 engine: &wasmtime::Engine,
187 ) -> Result<Instance, InstantiateError>
188 where
189 D: 'static,
190 C: Send + Sync + 'static,
191 {
192 let mut interfaces = IndexMap::<PackageId, IndexSet<String>>::new();
193
194 let load_order = self
195 .package_load_order(package_id, &mut interfaces)
196 .context(instantiate_error::LoadPackageSnafu)?;
197
198 let package = self
199 .packages
200 .get(package_id.id)
201 .ok_or(InstantiateError::PackageNotFound { id: package_id })?;
202
203 let component = Component::new(engine, package.bytes())
204 .context(instantiate_error::ComponentInstantiationSnafu)?;
205
206 for shadow_package_id in load_order {
207 if shadow_package_id == package_id {
208 break;
209 }
210
211 let shadow_package = self.packages.get(shadow_package_id.id).ok_or(
212 InstantiateError::PackageNotFound {
213 id: shadow_package_id,
214 },
215 )?;
216
217 let empty_set = IndexSet::new();
218 let shadow_interfaces = interfaces.get(&shadow_package_id).unwrap_or(&empty_set);
219
220 self.instantiate_shadowed_package(
221 shadow_package,
222 linker,
223 &mut store,
224 engine,
225 shadow_interfaces,
226 )
227 .with_context(|_err| {
228 instantiate_error::InstantiatePackageDependencySnafu {
229 name: shadow_package.name().to_string(),
230 version: shadow_package.version().cloned(),
231 }
232 })?;
233 }
234
235 let instance = linker
236 .instantiate(&mut store, &component)
237 .context(instantiate_error::ComponentInstantiationSnafu)?;
238
239 Ok(instance)
240 }
241
242 pub async fn instantiate_async(
244 &mut self,
245 package_id: PackageId,
246 linker: &mut component::Linker<D>,
247 mut store: impl AsContextMut<Data = D>,
248 engine: &wasmtime::Engine,
249 ) -> Result<Instance, InstantiateError>
250 where
251 D: Send + 'static,
252 C: Send + Sync + 'static,
253 {
254 let mut interfaces = IndexMap::<PackageId, IndexSet<String>>::new();
255
256 let load_order = self
257 .package_load_order(package_id, &mut interfaces)
258 .context(instantiate_error::LoadPackageSnafu)?;
259
260 let package = self
261 .packages
262 .get(package_id.id)
263 .ok_or(InstantiateError::PackageNotFound { id: package_id })?;
264
265 let component = Component::new(engine, package.bytes())
266 .context(instantiate_error::ComponentInstantiationSnafu)?;
267
268 for shadow_package_id in load_order {
269 if shadow_package_id == package_id {
270 break;
271 }
272
273 let shadow_package = self.packages.get(shadow_package_id.id).ok_or(
274 InstantiateError::PackageNotFound {
275 id: shadow_package_id,
276 },
277 )?;
278
279 let empty_set = IndexSet::new();
280 let shadow_interfaces = interfaces.get(&shadow_package_id).unwrap_or(&empty_set);
281
282 self.instantiate_shadowed_package_async(
283 shadow_package,
284 linker,
285 &mut store,
286 engine,
287 shadow_interfaces,
288 )
289 .await
290 .with_context(|_err| {
291 instantiate_error::InstantiatePackageDependencySnafu {
292 name: shadow_package.name().to_string(),
293 version: shadow_package.version().cloned(),
294 }
295 })?;
296 }
297
298 let instance = linker
299 .instantiate_async(&mut store, &component)
300 .await
301 .context(instantiate_error::ComponentInstantiationSnafu)?;
302
303 Ok(instance)
304 }
305
306 #[must_use]
308 pub fn types(&self) -> &wac_types::Types {
309 &self.types
310 }
311
312 pub fn types_mut(&mut self) -> &mut wac_types::Types {
316 &mut self.types
317 }
318
319 fn package_load_order(
320 &self,
321 origin: PackageId,
322 interfaces: &mut IndexMap<PackageId, IndexSet<String>>,
323 ) -> Result<impl IntoIterator<Item = PackageId> + 'static, LoadPackageError> {
324 let mut package_stack = vec![(origin, 0)];
325
326 let mut load_order = IndexSet::<PackageId>::new();
327 let mut load_stack = IndexSet::<PackageId>::new();
328
329 while let Some((package_id, offset)) = package_stack.pop() {
330 load_order.extend(load_stack.drain(offset..).rev());
331
332 if let Some(cycle_start) = load_stack.get_index_of(&package_id) {
333 let self_import = (cycle_start == load_stack.len() - 1)
334 && load_stack.index(cycle_start) == &package_id;
335
336 if self_import {
337 continue;
338 }
339
340 let mut cycle = load_stack
341 .iter()
342 .skip(cycle_start)
343 .copied()
344 .collect::<Vec<_>>();
345
346 cycle.push(package_id);
347
348 return Err(LoadPackageError::PackageCycle {
349 cycle: cycle
350 .into_iter()
351 .map(|package| {
352 self.packages
353 .get(package.id)
354 .map_or("{{UNKNOWN_PACKAGE}}".to_string(), |package| {
355 package.name().to_string()
356 })
357 })
358 .collect(),
359 });
360 }
361
362 if load_order.contains(&package_id) {
363 continue;
364 }
365
366 load_stack.insert(package_id);
367
368 let imports = self
369 .imported_interfaces
370 .get(&package_id)
371 .map(IndexSet::as_slice)
372 .unwrap_or_default();
373
374 for import in imports {
375 let version_map = self.package_map.get(import.package_name()).ok_or_else(|| {
376 LoadPackageError::MissingPackageDependency {
377 package_name: import.package_name().to_string(),
378 }
379 })?;
380
381 let import_package =
382 version_map.get_or_latest(import.version()).ok_or_else(|| {
383 LoadPackageError::CannotResolvePackageVersion {
384 name: import.package_name().to_string(),
385 version: import.version().cloned(),
386 }
387 })?;
388
389 package_stack.push((*import_package, load_stack.len()));
390
391 interfaces
392 .entry(*import_package)
393 .or_default()
394 .insert(import.interface_name().to_string());
395 }
396 }
397
398 Ok(load_order.into_iter().chain(load_stack.into_iter().rev()))
399 }
400
401 fn instantiate_shadowed_package(
402 &self,
403 package: &Package,
404 linker: &mut component::Linker<D>,
405 mut store: impl AsContextMut<Data = D>,
406 engine: &wasmtime::Engine,
407 interfaces: &IndexSet<String>,
408 ) -> Result<(), InstantiatePackageError>
409 where
410 D: 'static,
411 C: Send + Sync + 'static,
412 {
413 let component = Component::new(engine, package.bytes())
414 .context(instantiate_package_error::ComponentInstantiationSnafu)?;
415
416 let shadow_instance = linker
417 .instantiate(&mut store, &component)
418 .context(instantiate_package_error::ComponentInstantiationSnafu)?;
419
420 self.shadow_package(
421 package,
422 Rc::new(shadow_instance),
423 linker,
424 store,
425 interfaces,
426 SyncInstanceShadower,
427 )
428 }
429
430 async fn instantiate_shadowed_package_async(
431 &self,
432 package: &Package,
433 linker: &mut component::Linker<D>,
434 mut store: impl AsContextMut<Data = D>,
435 engine: &wasmtime::Engine,
436 interfaces: &IndexSet<String>,
437 ) -> Result<(), InstantiatePackageError>
438 where
439 D: Send + 'static,
440 C: Send + Sync + 'static,
441 {
442 let component = Component::new(engine, package.bytes())
443 .context(instantiate_package_error::ComponentInstantiationSnafu)?;
444
445 let shadow_instance = linker
446 .instantiate_async(&mut store, &component)
447 .await
448 .context(instantiate_package_error::ComponentInstantiationSnafu)?;
449
450 self.shadow_package(
451 package,
452 Rc::new(shadow_instance),
453 linker,
454 store,
455 interfaces,
456 AsyncInstanceShadower,
457 )
458 }
459
460 fn shadow_package(
461 &self,
462 package: &Package,
463 shadow_instance: Rc<Instance>,
464 linker: &mut component::Linker<D>,
465 mut store: impl AsContextMut<Data = D>,
466 interfaces: &IndexSet<String>,
467 shadower: impl InstanceShadower<D, C>,
468 ) -> Result<(), InstantiatePackageError> {
469 for interface_name in interfaces {
470 let interface_path = ForeignInterfacePath::new(
471 package.name().to_string(),
472 interface_name.to_string(),
473 package.version().cloned(),
474 );
475
476 let interface_full_name = interface_path.to_string();
477
478 let (_, shadow_interface_export_id) = shadow_instance
479 .get_export(&mut store, None, &interface_full_name)
480 .ok_or_else(|| InstantiatePackageError::InstanceMissingInterfaceExport {
481 interface_name: interface_full_name.to_string(),
482 })?;
483
484 let interface_export =
485 self.exported_interfaces
486 .get(&interface_path)
487 .ok_or_else(|| InstantiatePackageError::MissingInterfaceExport {
488 path: interface_path.clone(),
489 })?;
490
491 let mut front_instance = linker
492 .instance(interface_full_name.as_str())
493 .context(instantiate_package_error::LinkerInstanceSnafu)?;
494
495 let interface = &self.types[interface_export.interface];
496
497 for (export_name, export_kind) in &interface.exports {
498 let ItemKind::Func(func_id) = export_kind else {
499 continue;
500 };
501
502 let (_, shadow_func_export_id) = shadow_instance
503 .get_export(&mut store, Some(&shadow_interface_export_id), export_name)
504 .ok_or_else(
505 || InstantiatePackageError::InstanceMissingInterfaceFuncExport {
506 interface_name: interface_full_name.to_string(),
507 func_name: export_name.to_string(),
508 },
509 )?;
510
511 let shadow_func = shadow_instance
512 .get_func(&mut store, shadow_func_export_id)
513 .ok_or_else(|| InstantiatePackageError::ComponentFuncRetrievalError {
514 interface_name: interface_full_name.to_string(),
515 func_name: export_name.to_string(),
516 })?;
517
518 shadower.shadow_func(
519 &mut front_instance,
520 export_name,
521 shadow_func,
522 interface_path.clone(),
523 self.types[*func_id].clone(),
524 &interface_export.trampoline,
525 )?;
526 }
527 }
528
529 Ok(())
530 }
531}
532
533impl<D, C: Clone> Index<PackageId> for CompositionGraph<D, C> {
534 type Output = Package;
535
536 fn index(&self, index: PackageId) -> &Self::Output {
537 let package = self
538 .packages
539 .get(index.id)
540 .expect("package id out of bounds");
541
542 assert_eq!(
543 package.nonce, index.nonce,
544 "package nonce mismatch for id {index:?}"
545 );
546
547 &package.package
548 }
549}
550
551#[derive(Debug)]
552struct PackageWrapper {
553 package: Package,
554 nonce: usize,
555}
556
557impl Deref for PackageWrapper {
558 type Target = Package;
559
560 fn deref(&self) -> &Self::Target {
561 &self.package
562 }
563}
564
565trait InstanceShadower<D, C: Clone> {
566 fn shadow_func(
567 &self,
568 instance: &mut LinkerInstance<D>,
569 export_name: &str,
570 shadow_func: component::Func,
571 interface_path: ForeignInterfacePath,
572 func_ty: wac_types::FuncType,
573 trampoline: &DynInterfaceTrampoline<D, C>,
574 ) -> Result<(), InstantiatePackageError>;
575}
576
577#[derive(Copy, Clone, Default, Debug)]
578struct SyncInstanceShadower;
579
580impl<D: 'static, C: Clone + Send + Sync + 'static> InstanceShadower<D, C> for SyncInstanceShadower {
581 fn shadow_func(
582 &self,
583 instance: &mut LinkerInstance<D>,
584 export_name: &str,
585 shadow_func: component::Func,
586 interface_path: ForeignInterfacePath,
587 func_ty: wac_types::FuncType,
588 trampoline: &DynInterfaceTrampoline<D, C>,
589 ) -> Result<(), InstantiatePackageError> {
590 let fn_export_name = Arc::new(export_name.to_string());
591 let fn_interface_path = Arc::new(interface_path);
592 let fn_ty = Arc::new(func_ty);
593
594 match &trampoline {
595 DynInterfaceTrampoline::Sync(trampoline) => {
596 let fn_trampoline = trampoline.clone();
597
598 instance
599 .func_new(export_name, move |store, _ty, arguments, result| {
600 let mut result = fn_trampoline.bounce(
601 &shadow_func,
602 store,
603 fn_interface_path.as_ref(),
604 fn_export_name.as_str(),
605 fn_ty.as_ref(),
606 arguments,
607 result,
608 )?;
609
610 result.post_return()?;
611
612 Ok(())
613 })
614 .context(instantiate_package_error::LinkFuncInstantiationSnafu)
615 }
616
617 DynInterfaceTrampoline::Async(_trampoline) => {
618 Err(InstantiatePackageError::InvalidTrampolineSynchronicity)
619 }
620 }
621 }
622}
623
624#[derive(Copy, Clone, Default, Debug)]
625struct AsyncInstanceShadower;
626
627impl<D: Send + 'static, C: Clone + Send + Sync + 'static> InstanceShadower<D, C>
628 for AsyncInstanceShadower
629{
630 fn shadow_func(
631 &self,
632 instance: &mut LinkerInstance<D>,
633 export_name: &str,
634 shadow_func: component::Func,
635 interface_path: ForeignInterfacePath,
636 func_ty: wac_types::FuncType,
637 trampoline: &DynInterfaceTrampoline<D, C>,
638 ) -> Result<(), InstantiatePackageError> {
639 let fn_export_name = Arc::new(export_name.to_string());
640 let fn_interface_path = Arc::new(interface_path);
641 let fn_ty = Arc::new(func_ty);
642
643 match &trampoline {
644 DynInterfaceTrampoline::Sync(trampoline) => {
645 let fn_trampoline = trampoline.clone();
646
647 instance
648 .func_new(export_name, move |store, _ty, arguments, result| {
649 let mut result = fn_trampoline.bounce(
650 &shadow_func,
651 store,
652 fn_interface_path.as_ref(),
653 fn_export_name.as_str(),
654 fn_ty.as_ref(),
655 arguments,
656 result,
657 )?;
658
659 result.post_return()?;
660
661 Ok(())
662 })
663 .context(instantiate_package_error::LinkFuncInstantiationSnafu)
664 }
665
666 #[cfg(feature = "async")]
667 DynInterfaceTrampoline::Async(trampoline) => {
668 let fn_trampoline = trampoline.clone();
669
670 instance
671 .func_new_async(export_name, move |store, _ty, arguments, result| {
672 let export_name = fn_export_name.clone();
673 let trampoline = fn_trampoline.clone();
674 let interface_path = fn_interface_path.clone();
675 let ty = fn_ty.clone();
676
677 Box::new(async move {
678 let mut result = trampoline
679 .bounce_async(
680 &shadow_func,
681 store,
682 interface_path.as_ref(),
683 export_name.as_str(),
684 ty.as_ref(),
685 arguments,
686 result,
687 )
688 .await?;
689
690 result.post_return_async().await?;
691
692 Ok(())
693 })
694 })
695 .context(instantiate_package_error::LinkFuncInstantiationSnafu)
696 }
697 }
698 }
699}
700
701#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
703pub struct PackageId {
704 id: usize,
705 nonce: usize,
706}
707
708#[derive(Derivative)]
709#[derivative(Debug(bound = ""))]
710struct InterfaceExport<D, C: Clone> {
711 package: PackageId,
712 interface: InterfaceId,
713
714 #[derivative(Debug = "ignore")]
715 trampoline: DynInterfaceTrampoline<D, C>,
716}
717
718#[derive(Snafu, Debug)]
719#[snafu(module)]
720pub enum AddPackageError {
721 #[snafu(display("Duplicate package: {name}@{version:?}"))]
722 DuplicatePackage { name: String, version: Version },
723
724 #[snafu(display("Failed to parse package"))]
725 PackageParseError { source: anyhow::Error },
726
727 #[snafu(display("Failed to parse import '{interface}'"))]
728 ImportParseError {
729 interface: String,
730 source: InterfacePathParseError,
731 },
732}
733
734#[derive(Snafu, Debug)]
735#[snafu(module)]
736pub enum InstantiateError {
737 #[snafu(display("Package id '{id:?}' not found"))]
738 PackageNotFound { id: PackageId },
739
740 #[snafu(display("Failed to load package"))]
741 LoadPackageError { source: LoadPackageError },
742
743 #[snafu(display("Failed to instantiate package dependency '{name}@{version:?}'"))]
744 InstantiatePackageDependencyError {
745 name: String,
746 version: Option<Version>,
747 source: InstantiatePackageError,
748 },
749
750 #[snafu(display("Failed to instantiate wasm component"))]
751 ComponentInstantiationError { source: anyhow::Error },
752}
753
754#[derive(Snafu, Debug)]
755#[snafu(module)]
756pub enum LoadPackageError {
757 #[snafu(display("Package import cycle detected: {cycle:?}"))]
758 PackageCycle { cycle: Vec<String> },
759
760 #[snafu(display("Package dependency {package_name} not found"))]
761 MissingPackageDependency { package_name: String },
762
763 #[snafu(display("Cannot resolve package version for {name}@{version:?}"))]
764 CannotResolvePackageVersion {
765 name: String,
766 version: Option<Version>,
767 },
768}
769
770#[derive(Snafu, Debug)]
771#[snafu(module)]
772pub enum InstantiatePackageError {
773 #[snafu(display("Failed to instantiate wasm component"))]
774 ComponentInstantiationError { source: anyhow::Error },
775
776 #[snafu(display("Failed to create linker instance"))]
777 LinkerInstanceError { source: anyhow::Error },
778
779 #[snafu(display("Instance is missing interface export with name '{interface_name}'"))]
780 InstanceMissingInterfaceExport { interface_name: String },
781
782 #[snafu(display(
783 "Instance is missing interface func export with name '{interface_name}/{func_name}'",
784 ))]
785 InstanceMissingInterfaceFuncExport {
786 interface_name: String,
787 func_name: String,
788 },
789
790 #[snafu(display("Failed to retrieve component function '{interface_name}/{func_name}'"))]
791 ComponentFuncRetrievalError {
792 interface_name: String,
793 func_name: String,
794 },
795
796 #[snafu(display("Failed to instantiate function"))]
797 LinkFuncInstantiationError { source: anyhow::Error },
798
799 #[snafu(display("Invalid trampoline sync/async call match"))]
800 InvalidTrampolineSynchronicity,
801
802 #[snafu(display("Missing interface export {path}"))]
803 MissingInterfaceExport { path: ForeignInterfacePath },
804}