Skip to main content

wasm_component_trampoline/
graph.rs

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/// A graph for composing multiple WebAssembly components into a single linker, while allowing for
19/// automatic insertion of "trampoline" functions between cross-component calls.
20#[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    /// Creates a new empty `CompositionGraph`.
36    #[must_use]
37    pub fn new() -> Self {
38        Self::default()
39    }
40
41    /// Filters package imports for graph inclusion.
42    /// The filter can be removed by using the default `ImportRule::default()` filter.
43    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    /// Adds a package (component) to the composition graph.
51    ///
52    /// Components can be added in any order, and dependencies will be resolved at instantiation time.
53    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                    // This would be a programming error, since the package name/version tuple is
116                    // guaranteed to be unique.
117                    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                        // If the interface defines no functions, skip it.
135                        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 => { /* continue */ }
146                }
147
148                // Add the interface to the list of imports.
149                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    /// Instantiates a component from the composition graph, resolving all component dependencies.
178    ///
179    /// Host functions and other resources can be provided through the `linker` argument prior to
180    /// instantiation.
181    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    /// Like `instantiate`, but for asynchronous contexts.
243    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    /// Gets a reference to the type collection of the graph.
307    #[must_use]
308    pub fn types(&self) -> &wac_types::Types {
309        &self.types
310    }
311
312    /// Gets a mutable reference to the type collection of the graph.
313    ///
314    /// This type collection is used to define types directly in the graph.
315    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/// Represents a unique identifier for a package within the composition graph.
702#[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}