wasm_compose/
graph.rs

1//! Module for WebAssembly composition graphs.
2use crate::encoding::{CompositionGraphEncoder, TypeEncoder};
3use anyhow::{anyhow, bail, Context, Result};
4use indexmap::{IndexMap, IndexSet};
5use petgraph::{algo::toposort, graphmap::DiGraphMap, EdgeDirection};
6use std::{
7    borrow::Cow,
8    cell::RefCell,
9    collections::{hash_map::Entry, HashMap, HashSet},
10    path::{Path, PathBuf},
11    sync::atomic::{AtomicUsize, Ordering},
12};
13use wasmparser::{
14    component_types::{
15        ComponentAnyTypeId, ComponentEntityType, ComponentInstanceTypeId, Remap, Remapping,
16        ResourceId, SubtypeCx,
17    },
18    names::ComponentName,
19    types::{Types, TypesRef},
20    Chunk, ComponentExternalKind, ComponentTypeRef, Encoding, Parser, Payload, ValidPayload,
21    Validator, WasmFeatures,
22};
23
24pub(crate) fn type_desc(item: ComponentEntityType) -> &'static str {
25    match item {
26        ComponentEntityType::Instance(_) => "instance",
27        ComponentEntityType::Module(_) => "module",
28        ComponentEntityType::Func(_) => "function",
29        ComponentEntityType::Value(_) => "value",
30        ComponentEntityType::Type { .. } => "type",
31        ComponentEntityType::Component(_) => "component",
32    }
33}
34
35/// Represents a component in a composition graph.
36pub struct Component<'a> {
37    /// The name of the component.
38    pub(crate) name: String,
39    /// The path to the component file if parsed via `Component::from_file`.
40    pub(crate) path: Option<PathBuf>,
41    /// The raw bytes of the component.
42    pub(crate) bytes: Cow<'a, [u8]>,
43    /// The type information of the component.
44    pub(crate) types: Types,
45    /// The import map of the component.
46    pub(crate) imports: IndexMap<String, ComponentTypeRef>,
47    /// The export map of the component.
48    pub(crate) exports: IndexMap<String, (ComponentExternalKind, u32)>,
49}
50
51impl<'a> Component<'a> {
52    /// Constructs a new component from reading the given file.
53    pub fn from_file(name: &str, path: impl AsRef<Path>) -> Result<Self> {
54        let path = path.as_ref();
55        log::info!("parsing WebAssembly component file `{}`", path.display());
56        let component = Self::parse(
57            ComponentName::new(name, 0)?.to_string(),
58            Some(path.to_owned()),
59            wat::parse_file(path)
60                .with_context(|| {
61                    format!("failed to parse component `{path}`", path = path.display())
62                })?
63                .into(),
64        )
65        .with_context(|| format!("failed to parse component `{path}`", path = path.display()))?;
66
67        log::debug!(
68            "WebAssembly component `{path}` parsed:\n{component:#?}",
69            path = path.display()
70        );
71
72        Ok(component)
73    }
74
75    /// Constructs a new component from the given bytes.
76    pub fn from_bytes(name: impl Into<String>, bytes: impl Into<Cow<'a, [u8]>>) -> Result<Self> {
77        let mut bytes = bytes.into();
78
79        match wat::parse_bytes(bytes.as_ref()).context("failed to parse component")? {
80            Cow::Borrowed(_) => {
81                // Original bytes were not modified
82            }
83            Cow::Owned(v) => bytes = v.into(),
84        }
85
86        log::info!("parsing WebAssembly component from bytes");
87        let component = Self::parse(
88            ComponentName::new(&name.into(), 0)?.to_string(),
89            None,
90            bytes,
91        )
92        .context("failed to parse component")?;
93
94        log::debug!("WebAssembly component parsed:\n{component:#?}",);
95
96        Ok(component)
97    }
98
99    fn parse(name: String, path: Option<PathBuf>, bytes: Cow<'a, [u8]>) -> Result<Self> {
100        let mut parser = Parser::new(0);
101        let mut parsers = Vec::new();
102        let mut validator = Validator::new_with_features(WasmFeatures::all());
103        let mut imports = IndexMap::new();
104        let mut exports = IndexMap::new();
105
106        let mut cur = bytes.as_ref();
107        loop {
108            match parser.parse(cur, true)? {
109                Chunk::Parsed { payload, consumed } => {
110                    cur = &cur[consumed..];
111
112                    match validator.payload(&payload)? {
113                        ValidPayload::Ok => {
114                            // Don't parse any sub-components or sub-modules
115                            if !parsers.is_empty() {
116                                continue;
117                            }
118
119                            match payload {
120                                Payload::Version { encoding, .. } => {
121                                    if encoding != Encoding::Component {
122                                        bail!(
123                                            "the {} is not a WebAssembly component",
124                                            if path.is_none() { "given data" } else { "file" }
125                                        );
126                                    }
127                                }
128                                Payload::ComponentImportSection(s) => {
129                                    for import in s {
130                                        let import = import?;
131                                        let name = import.name.0.to_string();
132                                        imports.insert(name, import.ty);
133                                    }
134                                }
135                                Payload::ComponentExportSection(s) => {
136                                    for export in s {
137                                        let export = export?;
138                                        let name = export.name.0.to_string();
139                                        exports.insert(name, (export.kind, export.index));
140                                    }
141                                }
142                                _ => {}
143                            }
144                        }
145                        ValidPayload::Func(_, _) => {}
146                        ValidPayload::Parser(next) => {
147                            parsers.push(parser);
148                            parser = next;
149                        }
150                        ValidPayload::End(types) => match parsers.pop() {
151                            Some(parent) => parser = parent,
152                            None => {
153                                return Ok(Component {
154                                    name,
155                                    path,
156                                    bytes,
157                                    types,
158                                    imports,
159                                    exports,
160                                });
161                            }
162                        },
163                    }
164                }
165                Chunk::NeedMoreData(_) => unreachable!(),
166            }
167        }
168    }
169
170    /// Gets the name of the component.
171    ///
172    /// Names must be unique within a composition graph.
173    pub fn name(&self) -> &str {
174        &self.name
175    }
176
177    /// Gets the path of the component.
178    ///
179    /// Returns `None` if the component was not loaded from a file.
180    pub fn path(&self) -> Option<&Path> {
181        self.path.as_deref()
182    }
183
184    /// Gets the bytes of the component.
185    pub fn bytes(&self) -> &[u8] {
186        self.bytes.as_ref()
187    }
188
189    /// Gets the type information of the component.
190    pub fn types(&self) -> TypesRef {
191        self.types.as_ref()
192    }
193
194    /// Gets an export from the component for the given export index.
195    pub fn export(
196        &self,
197        index: impl Into<ExportIndex>,
198    ) -> Option<(&str, ComponentExternalKind, u32)> {
199        let index = index.into();
200        self.exports
201            .get_index(index.0)
202            .map(|(name, (kind, index))| (name.as_str(), *kind, *index))
203    }
204
205    /// Gets an export from the component for the given export name.
206    pub fn export_by_name(&self, name: &str) -> Option<(ExportIndex, ComponentExternalKind, u32)> {
207        self.exports
208            .get_full(name)
209            .map(|(i, _, (kind, index))| (ExportIndex(i), *kind, *index))
210    }
211
212    /// Gets an iterator over the component's exports.
213    pub fn exports(
214        &self,
215    ) -> impl ExactSizeIterator<Item = (ExportIndex, &str, ComponentExternalKind, u32)> {
216        self.exports
217            .iter()
218            .enumerate()
219            .map(|(i, (name, (kind, index)))| (ExportIndex(i), name.as_str(), *kind, *index))
220    }
221
222    /// Gets an import from the component for the given import index.
223    pub fn import(&self, index: impl Into<ImportIndex>) -> Option<(&str, ComponentTypeRef)> {
224        let index = index.into();
225        self.imports
226            .get_index(index.0)
227            .map(|(name, ty)| (name.as_str(), *ty))
228    }
229
230    /// Gets an import from the component for the given import name.
231    pub fn import_by_name(&self, name: &str) -> Option<(ImportIndex, ComponentTypeRef)> {
232        self.imports
233            .get_full(name)
234            .map(|(i, _, ty)| (ImportIndex(i), *ty))
235    }
236
237    /// Gets an iterator over the component's imports.
238    pub fn imports(&self) -> impl ExactSizeIterator<Item = (ImportIndex, &str, ComponentTypeRef)> {
239        self.imports
240            .iter()
241            .enumerate()
242            .map(|(i, (name, ty))| (ImportIndex(i), name.as_str(), *ty))
243    }
244
245    pub(crate) fn ty(&self) -> wasm_encoder::ComponentType {
246        let encoder = TypeEncoder::new(self);
247
248        encoder.component(
249            &mut Default::default(),
250            self.imports()
251                .map(|(i, ..)| self.import_entity_type(i).unwrap()),
252            self.exports()
253                .map(|(i, ..)| self.export_entity_type(i).unwrap()),
254        )
255    }
256
257    pub(crate) fn export_entity_type(
258        &self,
259        index: ExportIndex,
260    ) -> Option<(&str, ComponentEntityType)> {
261        let (name, _kind, _index) = self.export(index)?;
262        Some((
263            name,
264            self.types.as_ref().component_entity_type_of_export(name)?,
265        ))
266    }
267
268    pub(crate) fn import_entity_type(
269        &self,
270        index: ImportIndex,
271    ) -> Option<(&str, ComponentEntityType)> {
272        let (name, _ty) = self.import(index)?;
273        Some((
274            name,
275            self.types.as_ref().component_entity_type_of_import(name)?,
276        ))
277    }
278
279    /// Finds a compatible instance export on the component for the given instance type.
280    pub(crate) fn find_compatible_export(
281        &self,
282        ty: ComponentInstanceTypeId,
283        types: TypesRef,
284        export_component_id: ComponentId,
285        graph: &CompositionGraph,
286    ) -> Option<ExportIndex> {
287        self.exports
288            .iter()
289            .position(|(_, (kind, index))| {
290                if *kind != ComponentExternalKind::Instance {
291                    return false;
292                }
293
294                graph.try_connection(
295                    export_component_id,
296                    ComponentEntityType::Instance(
297                        self.types.as_ref().component_instance_at(*index),
298                    ),
299                    self.types(),
300                    ComponentEntityType::Instance(ty),
301                    types,
302                )
303            })
304            .map(ExportIndex)
305    }
306
307    /// Checks to see if an instance of this component would be a
308    /// subtype of the given instance type.
309    pub(crate) fn is_instance_subtype_of(
310        &self,
311        ty: ComponentInstanceTypeId,
312        types: TypesRef,
313    ) -> bool {
314        let exports = types[ty].exports.iter();
315
316        for (k, b) in exports {
317            match self.exports.get_full(k.as_str()) {
318                Some((ai, _, _)) => {
319                    let (_, a) = self.export_entity_type(ExportIndex(ai)).unwrap();
320                    if !ComponentEntityType::is_subtype_of(&a, self.types(), b, types) {
321                        return false;
322                    }
323                }
324                None => return false,
325            }
326        }
327
328        true
329    }
330}
331
332impl std::fmt::Debug for Component<'_> {
333    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
334        f.debug_struct("Component")
335            .field("imports", &self.imports)
336            .field("exports", &self.exports)
337            .finish_non_exhaustive()
338    }
339}
340
341static NEXT_COMPONENT_ID: AtomicUsize = AtomicUsize::new(0);
342
343/// Represents an identifier of a component in a composition graph.
344#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
345pub struct ComponentId(pub usize);
346
347impl ComponentId {
348    fn next() -> Result<Self> {
349        let next = NEXT_COMPONENT_ID.fetch_add(1, Ordering::SeqCst);
350        if next == usize::MAX {
351            bail!("component limit reached");
352        }
353        Ok(Self(next))
354    }
355}
356
357impl std::fmt::Display for ComponentId {
358    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
359        write!(f, "{}", self.0)
360    }
361}
362
363impl From<usize> for ComponentId {
364    fn from(id: usize) -> Self {
365        Self(id)
366    }
367}
368
369static NEXT_INSTANCE_ID: AtomicUsize = AtomicUsize::new(0);
370
371/// Represents an identifier of an instance in a composition graph.
372#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
373pub struct InstanceId(pub usize);
374
375impl std::fmt::Display for InstanceId {
376    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
377        write!(f, "{}", self.0)
378    }
379}
380
381impl InstanceId {
382    fn next() -> Result<Self> {
383        let next = NEXT_INSTANCE_ID.fetch_add(1, Ordering::SeqCst);
384        if next == usize::MAX {
385            bail!("instance limit reached");
386        }
387        Ok(Self(next))
388    }
389}
390
391impl From<usize> for InstanceId {
392    fn from(id: usize) -> Self {
393        Self(id)
394    }
395}
396
397/// Represents an index into a component's import list.
398#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
399pub struct ImportIndex(pub usize);
400
401impl std::fmt::Display for ImportIndex {
402    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
403        write!(f, "{}", self.0)
404    }
405}
406
407impl From<usize> for ImportIndex {
408    fn from(id: usize) -> Self {
409        Self(id)
410    }
411}
412
413/// Represents an index into a component's export list.
414#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
415pub struct ExportIndex(pub usize);
416
417impl std::fmt::Display for ExportIndex {
418    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
419        write!(f, "{}", self.0)
420    }
421}
422
423impl From<usize> for ExportIndex {
424    fn from(id: usize) -> Self {
425        Self(id)
426    }
427}
428
429#[derive(Debug)]
430pub(crate) struct ComponentEntry<'a> {
431    pub(crate) component: Component<'a>,
432    pub(crate) instances: HashSet<InstanceId>,
433}
434
435#[derive(Debug)]
436pub(crate) struct Instance {
437    pub(crate) component: ComponentId,
438    pub(crate) connected: IndexSet<ImportIndex>,
439}
440
441/// The options for encoding a composition graph.
442#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
443pub struct EncodeOptions {
444    /// Whether or not to define instantiated components.
445    ///
446    /// If `false`, components will be imported instead.
447    pub define_components: bool,
448
449    /// The instance in the graph to export.
450    ///
451    /// If non-empty, the instance's exports will be aliased and
452    /// exported from the resulting component.
453    pub export: Option<InstanceId>,
454
455    /// Whether or not to validate the encoded output.
456    pub validate: bool,
457}
458
459#[derive(Clone, Debug, Default)]
460pub(crate) struct ResourceMapping {
461    map: im_rc::HashMap<ResourceId, (ComponentId, ResourceId)>,
462}
463
464impl ResourceMapping {
465    fn add_pairs(
466        mut self,
467        export_component: ComponentId,
468        export_type: ComponentEntityType,
469        export_types: TypesRef,
470        import_type: ComponentEntityType,
471        import_types: TypesRef,
472    ) -> Option<Self> {
473        if let (
474            ComponentEntityType::Instance(export_type),
475            ComponentEntityType::Instance(import_type),
476        ) = (export_type, import_type)
477        {
478            let mut exports = HashMap::new();
479            for (export_name, ty) in &export_types[export_type].exports {
480                // TODO: support nested instances
481                if let ComponentEntityType::Type {
482                    referenced: ComponentAnyTypeId::Resource(resource_id),
483                    ..
484                } = ty
485                {
486                    exports.insert(export_name, (export_component, resource_id.resource()));
487                }
488            }
489
490            for (export_name, ty) in &import_types[import_type].exports {
491                // TODO: support nested instances
492                if let ComponentEntityType::Type {
493                    referenced: ComponentAnyTypeId::Resource(resource_id),
494                    ..
495                } = ty
496                {
497                    let import_resource = resource_id.resource();
498                    if let Some((export_component, export_resource)) =
499                        exports.get(&export_name).copied()
500                    {
501                        let value = self
502                            .map
503                            .get(&export_resource)
504                            .copied()
505                            .or_else(|| self.map.get(&import_resource).copied())
506                            .unwrap_or((export_component, export_resource));
507
508                        if value.1 == export_resource {
509                            self.map.insert(export_resource, value);
510                            self.map.insert(import_resource, value);
511                        }
512                    } else {
513                        // Couldn't find an export with a name that matches this
514                        // import -- give up.
515                        return None;
516                    }
517                }
518            }
519        }
520
521        Some(self)
522    }
523
524    pub(crate) fn remapping(&self) -> Remapping {
525        let mut remapping = Remapping::default();
526        for (old, (_, new)) in &self.map {
527            if old != new {
528                remapping.add(*old, *new)
529            }
530        }
531        remapping
532    }
533}
534
535/// Represents a composition graph used to compose a new component
536/// from other components.
537#[derive(Debug, Default)]
538pub struct CompositionGraph<'a> {
539    names: HashMap<String, ComponentId>,
540    pub(crate) components: IndexMap<ComponentId, ComponentEntry<'a>>,
541    pub(crate) instances: IndexMap<InstanceId, Instance>,
542    // Map where each node is an instance in the graph.
543    // An edge between nodes stores a map of target import index to source export index.
544    // A source export index of `None` means that the source instance itself is being used.
545    pub(crate) graph: DiGraphMap<InstanceId, IndexMap<ImportIndex, Option<ExportIndex>>>,
546    pub(crate) resource_mapping: RefCell<ResourceMapping>,
547}
548
549impl<'a> CompositionGraph<'a> {
550    /// Constructs a new composition graph.
551    pub fn new() -> Self {
552        Self::default()
553    }
554
555    /// Gather any remaining resource imports which have not already been
556    /// connected to exports, group them by name, and update the resource
557    /// mapping to make all resources within each group equivalent.
558    ///
559    /// This ensures that each set of identical imports in the composed
560    /// components can be merged into a single import in the output component.
561    //
562    // TODO: How do we balance the need to call this early (so we can match up
563    // imports with exports which mutually import the same resources) with the
564    // need to delay decisions about where resources are coming from (so that we
565    // can match up imported resources with exported resources)?  Right now I
566    // think we're erring on the side if the former at the expense of the
567    // latter.
568    pub(crate) fn unify_imported_resources(&self) {
569        let mut resource_mapping = self.resource_mapping.borrow_mut();
570
571        let mut resource_imports = IndexMap::<_, IndexSet<_>>::new();
572        for (component_id, component) in &self.components {
573            let component = &component.component;
574            for import_name in component.imports.keys() {
575                let ty = component
576                    .types
577                    .as_ref()
578                    .component_entity_type_of_import(import_name)
579                    .unwrap();
580
581                if let ComponentEntityType::Instance(instance_id) = ty {
582                    for (export_name, ty) in &component.types[instance_id].exports {
583                        // TODO: support nested instances
584                        if let ComponentEntityType::Type {
585                            referenced: ComponentAnyTypeId::Resource(resource_id),
586                            ..
587                        } = ty
588                        {
589                            let set = resource_imports
590                                .entry(vec![import_name.to_string(), export_name.to_string()])
591                                .or_default();
592
593                            if let Some(pair) = resource_mapping.map.get(&resource_id.resource()) {
594                                set.insert(*pair);
595                            }
596                            set.insert((*component_id, resource_id.resource()));
597                        }
598                    }
599                }
600            }
601        }
602
603        for resources in resource_imports.values() {
604            match &resources.iter().copied().collect::<Vec<_>>()[..] {
605                [] => unreachable!(),
606                [_] => {}
607                [first, rest @ ..] => {
608                    resource_mapping.map.insert(first.1, *first);
609                    for resource in rest {
610                        resource_mapping.map.insert(resource.1, *first);
611                    }
612                }
613            }
614        }
615    }
616
617    /// Attempt to connect the specified import to the specified export.
618    ///
619    /// This will attempt to match up any resource types by name by name and
620    /// optimistically produce a remapping that sets identically-named pairs
621    /// equal to each other, provided that remapping does not contradict any
622    /// previous remappings.  If the import is not a subtype of the export
623    /// (either because a consistent remapping could not be created or because
624    /// the instances were incompatible for other reasons), we discard the
625    /// remapping changes and return `false`.  Otherwise, we store the remapping
626    /// changes and return `true`.
627    ///
628    /// Note that although this method takes a shared reference, it uses
629    /// internal mutability to update the remapping.
630    pub(crate) fn try_connection(
631        &self,
632        export_component: ComponentId,
633        mut export_type: ComponentEntityType,
634        export_types: TypesRef,
635        mut import_type: ComponentEntityType,
636        import_types: TypesRef,
637    ) -> bool {
638        let resource_mapping = self.resource_mapping.borrow().clone();
639
640        if let Some(resource_mapping) = resource_mapping.add_pairs(
641            export_component,
642            export_type,
643            export_types,
644            import_type,
645            import_types,
646        ) {
647            let remapping = &mut resource_mapping.remapping();
648            let mut context = SubtypeCx::new_with_refs(export_types, import_types);
649
650            context
651                .a
652                .remap_component_entity(&mut export_type, remapping);
653            remapping.reset_type_cache();
654
655            context
656                .b
657                .remap_component_entity(&mut import_type, remapping);
658            remapping.reset_type_cache();
659
660            let v = context.component_entity_type(&export_type, &import_type, 0);
661            if v.is_ok() {
662                *self.resource_mapping.borrow_mut() = resource_mapping;
663                true
664            } else {
665                false
666            }
667        } else {
668            false
669        }
670    }
671
672    pub(crate) fn remapping_map<'b>(
673        &'b self,
674    ) -> HashMap<ResourceId, (&'b Component<'a>, ResourceId)> {
675        let mut map = HashMap::new();
676        for (old, (component, new)) in &self.resource_mapping.borrow().map {
677            if old != new {
678                let component = &self.components.get(component).unwrap().component;
679                map.insert(*old, (component, *new));
680            }
681        }
682        map
683    }
684
685    /// Adds a new component to the graph.
686    ///
687    /// The component name must be unique.
688    pub fn add_component(&mut self, component: Component<'a>) -> Result<ComponentId> {
689        let id = match self.names.entry(component.name.clone()) {
690            Entry::Occupied(e) => {
691                bail!(
692                    "a component with name `{name}` already exists",
693                    name = e.key()
694                )
695            }
696            Entry::Vacant(e) => *e.insert(ComponentId::next()?),
697        };
698
699        log::info!(
700            "adding WebAssembly component `{name}` ({id}) to the graph",
701            name = component.name(),
702        );
703
704        let entry = ComponentEntry {
705            component,
706            instances: HashSet::new(),
707        };
708
709        assert!(self.components.insert(id, entry).is_none());
710
711        if self.components.len() > 1 {
712            self.unify_imported_resources();
713        }
714
715        Ok(id)
716    }
717
718    /// Gets a component from the graph.
719    pub fn get_component(&self, id: impl Into<ComponentId>) -> Option<&Component<'a>> {
720        self.components.get(&id.into()).map(|e| &e.component)
721    }
722
723    /// Gets a component from the graph by name.
724    pub fn get_component_by_name(&self, name: &str) -> Option<(ComponentId, &Component<'a>)> {
725        let id = self.names.get(name)?;
726        let entry = &self.components[id];
727        Some((*id, &entry.component))
728    }
729
730    /// Removes a component from the graph.
731    ///
732    /// All instances and connections relating to the component
733    /// will also be removed.
734    pub fn remove_component(&mut self, id: impl Into<ComponentId>) {
735        let id = id.into();
736        if let Some(entry) = self.components.swap_remove(&id) {
737            log::info!(
738                "removing WebAssembly component `{name}` ({id}) from the graph",
739                name = entry.component.name(),
740            );
741
742            assert!(self.names.remove(&entry.component.name).is_some());
743
744            for instance_id in entry.instances.iter().copied() {
745                self.instances.swap_remove(&instance_id);
746
747                // Remove any connected indexes from outward edges from the instance being removed
748                for (_, target_id, map) in self
749                    .graph
750                    .edges_directed(instance_id, EdgeDirection::Outgoing)
751                {
752                    let target = self.instances.get_mut(&target_id).unwrap();
753                    for index in map.keys() {
754                        target.connected.swap_remove(index);
755                    }
756                }
757
758                self.graph.remove_node(instance_id);
759            }
760        }
761    }
762
763    /// Creates a new instance of a component in the composition graph.
764    pub fn instantiate(&mut self, id: impl Into<ComponentId>) -> Result<InstanceId> {
765        let id = id.into();
766        let entry = self
767            .components
768            .get_mut(&id)
769            .ok_or_else(|| anyhow!("component does not exist in the graph"))?;
770
771        let instance_id = InstanceId::next()?;
772
773        log::info!(
774            "instantiating WebAssembly component `{name}` ({id}) with instance identifier {instance_id}",
775            name = entry.component.name(),
776        );
777
778        self.instances.insert(
779            instance_id,
780            Instance {
781                component: id,
782                connected: Default::default(),
783            },
784        );
785
786        entry.instances.insert(instance_id);
787
788        Ok(instance_id)
789    }
790
791    /// Gets the component of the given instance.
792    pub fn get_component_of_instance(
793        &self,
794        id: impl Into<InstanceId>,
795    ) -> Option<(ComponentId, &Component)> {
796        let id = id.into();
797        let instance = self.instances.get(&id)?;
798
799        Some((
800            instance.component,
801            self.get_component(instance.component).unwrap(),
802        ))
803    }
804
805    /// Removes an instance from the graph.
806    ///
807    /// All connections relating to the instance will also be removed.
808    pub fn remove_instance(&mut self, id: impl Into<InstanceId>) {
809        let id = id.into();
810        if let Some(instance) = self.instances.swap_remove(&id) {
811            let entry = self.components.get_mut(&instance.component).unwrap();
812
813            log::info!(
814                "removing instance ({id}) of component `{name}` ({cid}) from the graph",
815                name = entry.component.name(),
816                cid = instance.component.0,
817            );
818
819            entry.instances.remove(&id);
820
821            // Remove any connected indexes from outward edges from this instance
822            for (_, target, map) in self.graph.edges_directed(id, EdgeDirection::Outgoing) {
823                let target = self.instances.get_mut(&target).unwrap();
824                for index in map.keys() {
825                    target.connected.swap_remove(index);
826                }
827            }
828
829            self.graph.remove_node(id);
830        }
831    }
832
833    /// Creates a connection (edge) between instances in the composition graph.
834    ///
835    /// A connection represents an instantiation argument.
836    ///
837    /// If `source_export` is `None`, the source instance itself
838    /// is used as the instantiation argument.
839    pub fn connect(
840        &mut self,
841        source: impl Into<InstanceId> + Copy,
842        source_export: Option<impl Into<ExportIndex> + Copy>,
843        target: impl Into<InstanceId> + Copy,
844        target_import: impl Into<ImportIndex> + Copy,
845    ) -> Result<()> {
846        self.validate_connection(source, source_export, target, target_import)?;
847
848        let source = source.into();
849        let source_export = source_export.map(Into::into);
850        let target = target.into();
851        let target_import = target_import.into();
852
853        match source_export {
854            Some(export) => log::info!("connecting export {export} of instance {source} to import `{target_import}` of instance {target}"),
855            None => log::info!("connecting instance {source} to import {target_import} of instance {target}"),
856        }
857
858        self.instances
859            .get_mut(&target)
860            .unwrap()
861            .connected
862            .insert(target_import);
863
864        if let Some(map) = self.graph.edge_weight_mut(source, target) {
865            assert!(map.insert(target_import, source_export).is_none());
866        } else {
867            let mut map = IndexMap::new();
868            map.insert(target_import, source_export);
869            self.graph.add_edge(source, target, map);
870        }
871
872        Ok(())
873    }
874
875    /// Disconnects a previous connection between instances.
876    ///
877    /// Requires that the source and target instances are valid.
878    ///
879    /// If the source and target are not connected via the target's import,
880    /// then this is a no-op.
881    pub fn disconnect(
882        &mut self,
883        source: impl Into<InstanceId>,
884        target: impl Into<InstanceId>,
885        target_import: impl Into<ImportIndex>,
886    ) -> Result<()> {
887        let source = source.into();
888        let target = target.into();
889        let target_import = target_import.into();
890
891        log::info!("disconnecting import {target_import} of instance {target}");
892
893        if !self.instances.contains_key(&source) {
894            bail!("the source instance does not exist in the graph");
895        }
896
897        let target_instance = self
898            .instances
899            .get_mut(&target)
900            .ok_or_else(|| anyhow!("the target instance does not exist in the graph"))?;
901
902        target_instance.connected.swap_remove(&target_import);
903
904        let remove_edge = if let Some(set) = self.graph.edge_weight_mut(source, target) {
905            set.swap_remove(&target_import);
906            set.is_empty()
907        } else {
908            false
909        };
910
911        if remove_edge {
912            self.graph.remove_edge(source, target);
913        }
914
915        Ok(())
916    }
917
918    /// Validates a connection between two instances in the graph.
919    ///
920    /// Use `None` for `source_export` to signify that the instance
921    /// itself should be the source for the connection.
922    ///
923    /// Returns `Err(_)` if the connection would not be valid.
924    pub fn validate_connection(
925        &self,
926        source: impl Into<InstanceId>,
927        source_export: Option<impl Into<ExportIndex>>,
928        target: impl Into<InstanceId>,
929        target_import: impl Into<ImportIndex>,
930    ) -> Result<()> {
931        let source = source.into();
932        let source_export = source_export.map(Into::into);
933        let target = target.into();
934        let target_import = target_import.into();
935
936        if source == target {
937            bail!("an instance cannot be connected to itself");
938        }
939
940        let source_instance = self
941            .instances
942            .get(&source)
943            .ok_or_else(|| anyhow!("the source instance does not exist in the graph"))?;
944
945        let source_component = &self.components[&source_instance.component].component;
946
947        let target_instance = self
948            .instances
949            .get(&target)
950            .ok_or_else(|| anyhow!("the target instance does not exist in the graph"))?;
951
952        let target_component = &self.components[&target_instance.component].component;
953        let (import_name, import_ty) = target_component
954            .import_entity_type(target_import)
955            .ok_or_else(|| anyhow!("the target import index is invalid"))?;
956
957        if target_instance.connected.contains(&target_import) {
958            bail!(
959                "{import_ty} import `{import_name}` is already connected",
960                import_ty = type_desc(import_ty)
961            );
962        }
963
964        if let Some(export_index) = source_export {
965            let (export_name, export_ty) = source_component
966                .export_entity_type(export_index)
967                .ok_or_else(|| anyhow!("the source export index is invalid"))?;
968
969            if !self.try_connection(
970                source_instance.component,
971                export_ty,
972                source_component.types(),
973                import_ty,
974                target_component.types(),
975            ) {
976                bail!(
977                    "source {export_ty} export `{export_name}` is not compatible with target \
978                         {import_ty} import `{import_name}`",
979                    export_ty = type_desc(export_ty),
980                    import_ty = type_desc(import_ty),
981                );
982            }
983        } else {
984            let ty = match import_ty {
985                ComponentEntityType::Instance(id) => id,
986                _ => bail!(
987                    "source instance is not compatible with target {import_ty} import `{import_name}`",
988                    import_ty = type_desc(import_ty)
989                ),
990            };
991
992            if !source_component.is_instance_subtype_of(ty, target_component.types()) {
993                bail!(
994                    "source instance is not compatible with target {import_ty} import `{import_name}`",
995                    import_ty = type_desc(import_ty)
996                );
997            }
998        };
999
1000        Ok(())
1001    }
1002
1003    /// Encodes the current composition graph as a WebAssembly component.
1004    pub fn encode(&self, options: EncodeOptions) -> Result<Vec<u8>> {
1005        let bytes = CompositionGraphEncoder::new(options, self).encode()?;
1006
1007        if options.validate {
1008            Validator::new()
1009                .validate_all(&bytes)
1010                .context("failed to validate encoded graph bytes")?;
1011        }
1012
1013        Ok(bytes)
1014    }
1015
1016    /// Gets the topological instantiation order based on the composition graph.
1017    ///
1018    /// If an instance is not in the returned set, it is considered to be
1019    /// "independent" (i.e it has no dependencies on other instances).
1020    pub(crate) fn instantiation_order(&self) -> Result<Vec<InstanceId>> {
1021        toposort(&self.graph, None).map_err(|e| {
1022            let id = e.node_id();
1023            let instance = &self.instances[&id];
1024            anyhow!(
1025                "an instantiation of component `{name}` and its dependencies form a cycle in the instantiation graph",
1026                name = self.components[&instance.component].component.name,
1027            )
1028        })
1029    }
1030}
1031
1032#[cfg(test)]
1033mod test {
1034    use super::*;
1035    use pretty_assertions::assert_eq;
1036
1037    #[test]
1038    fn it_rejects_modules() -> Result<()> {
1039        match Component::from_bytes("a", b"(module)".as_ref()) {
1040            Ok(_) => panic!("expected a failure to parse"),
1041            Err(e) => assert_eq!(
1042                format!("{e:#}"),
1043                "failed to parse component: the given data is not a WebAssembly component"
1044            ),
1045        }
1046
1047        Ok(())
1048    }
1049
1050    #[test]
1051    fn it_rejects_invalid_components() -> Result<()> {
1052        match Component::from_bytes("a", b"(component (export \"x\" (func 0)))".as_ref()) {
1053            Ok(_) => panic!("expected a failure to parse"),
1054            Err(e) => assert_eq!(format!("{e:#}"), "failed to parse component: unknown function 0: function index out of bounds (at offset 0xb)"),
1055        }
1056
1057        Ok(())
1058    }
1059
1060    #[test]
1061    fn it_ensures_unique_component_names() -> Result<()> {
1062        let mut graph = CompositionGraph::new();
1063        graph.add_component(Component::from_bytes("a", b"(component)".as_ref())?)?;
1064
1065        match graph.add_component(Component::from_bytes("a", b"(component)".as_ref())?) {
1066            Ok(_) => panic!("expected a failure to add component"),
1067            Err(e) => assert_eq!(format!("{e:#}"), "a component with name `a` already exists"),
1068        }
1069
1070        Ok(())
1071    }
1072
1073    #[test]
1074    fn it_fails_to_instantiate_a_missing_component() -> Result<()> {
1075        let mut graph = CompositionGraph::new();
1076        match graph.instantiate(ComponentId(0)) {
1077            Ok(_) => panic!("expected a failure to instantiate"),
1078            Err(e) => assert_eq!(format!("{e:#}"), "component does not exist in the graph"),
1079        }
1080
1081        Ok(())
1082    }
1083
1084    #[test]
1085    fn it_instantiates_a_component() -> Result<()> {
1086        let mut graph = CompositionGraph::new();
1087        let id = graph.add_component(Component::from_bytes("a", b"(component)".as_ref())?)?;
1088        let id = graph.instantiate(id)?;
1089        assert_eq!(graph.get_component_of_instance(id).unwrap().1.name(), "a");
1090        Ok(())
1091    }
1092
1093    #[test]
1094    fn it_cannot_get_a_component_of_missing_instance() -> Result<()> {
1095        let graph = CompositionGraph::new();
1096        assert!(graph.get_component_of_instance(InstanceId(0)).is_none());
1097        Ok(())
1098    }
1099
1100    #[test]
1101    fn it_gets_a_component() -> Result<()> {
1102        let mut graph = CompositionGraph::new();
1103        let id = graph.add_component(Component::from_bytes("a", b"(component)".as_ref())?)?;
1104        assert_eq!(graph.get_component(id).unwrap().name(), "a");
1105        assert_eq!(graph.get_component_by_name("a").unwrap().1.name(), "a");
1106        Ok(())
1107    }
1108
1109    #[test]
1110    fn it_removes_a_component() -> Result<()> {
1111        let mut graph = CompositionGraph::new();
1112        let a = graph.add_component(Component::from_bytes(
1113            "a",
1114            b"(component (import \"x\" (func)))".as_ref(),
1115        )?)?;
1116        let b = graph.add_component(Component::from_bytes(
1117            "b",
1118            b"(component (import \"x\" (func)) (export \"y\" (func 0)))".as_ref(),
1119        )?)?;
1120        let ai = graph.instantiate(a)?;
1121        let bi = graph.instantiate(b)?;
1122        graph.connect(bi, Some(0), ai, 0)?;
1123
1124        assert!(graph.get_component(a).is_some());
1125        assert!(graph.get_component(b).is_some());
1126        assert_eq!(graph.components.len(), 2);
1127        assert_eq!(graph.instances.len(), 2);
1128        assert_eq!(graph.graph.node_count(), 2);
1129        assert_eq!(graph.graph.edge_count(), 1);
1130
1131        graph.remove_component(b);
1132
1133        assert!(graph.get_component(a).is_some());
1134        assert!(graph.get_component(b).is_none());
1135        assert_eq!(graph.components.len(), 1);
1136        assert_eq!(graph.instances.len(), 1);
1137        assert_eq!(graph.graph.node_count(), 1);
1138        assert_eq!(graph.graph.edge_count(), 0);
1139        Ok(())
1140    }
1141
1142    #[test]
1143    fn it_removes_a_connection() -> Result<()> {
1144        let mut graph = CompositionGraph::new();
1145        let a = graph.add_component(Component::from_bytes(
1146            "a",
1147            b"(component (import \"x\" (func)))".as_ref(),
1148        )?)?;
1149        let b = graph.add_component(Component::from_bytes(
1150            "b",
1151            b"(component (import \"x\" (func)) (export \"y\" (func 0)))".as_ref(),
1152        )?)?;
1153        let ai = graph.instantiate(a)?;
1154        let bi = graph.instantiate(b)?;
1155        graph.connect(bi, Some(0), ai, 0)?;
1156
1157        assert_eq!(graph.graph.node_count(), 2);
1158        assert_eq!(graph.graph.edge_count(), 1);
1159
1160        graph.disconnect(bi, ai, 0)?;
1161
1162        assert_eq!(graph.graph.node_count(), 2);
1163        assert_eq!(graph.graph.edge_count(), 0);
1164        Ok(())
1165    }
1166
1167    #[test]
1168    fn it_requires_source_to_disconnect() -> Result<()> {
1169        let mut graph = CompositionGraph::new();
1170        let a = graph.add_component(Component::from_bytes(
1171            "a",
1172            b"(component (import \"x\" (func)))".as_ref(),
1173        )?)?;
1174        let b = graph.add_component(Component::from_bytes(
1175            "b",
1176            b"(component (import \"x\" (func)) (export \"y\" (func 0)))".as_ref(),
1177        )?)?;
1178        let ai = graph.instantiate(a)?;
1179        let bi = graph.instantiate(b)?;
1180        graph.connect(bi, Some(0), ai, 0)?;
1181
1182        match graph.disconnect(101, ai, 0) {
1183            Ok(_) => panic!("expected a failure to disconnect"),
1184            Err(e) => assert_eq!(
1185                format!("{e:#}"),
1186                "the source instance does not exist in the graph"
1187            ),
1188        }
1189
1190        Ok(())
1191    }
1192
1193    #[test]
1194    fn it_requires_a_target_to_disconnect() -> Result<()> {
1195        let mut graph = CompositionGraph::new();
1196        let a = graph.add_component(Component::from_bytes(
1197            "a",
1198            b"(component (import \"x\" (func)))".as_ref(),
1199        )?)?;
1200        let b = graph.add_component(Component::from_bytes(
1201            "b",
1202            b"(component (import \"x\" (func)) (export \"y\" (func 0)))".as_ref(),
1203        )?)?;
1204        let ai = graph.instantiate(a)?;
1205        let bi = graph.instantiate(b)?;
1206        graph.connect(bi, Some(0), ai, 0)?;
1207
1208        match graph.disconnect(bi, 101, 0) {
1209            Ok(_) => panic!("expected a failure to disconnect"),
1210            Err(e) => assert_eq!(
1211                format!("{e:#}"),
1212                "the target instance does not exist in the graph"
1213            ),
1214        }
1215
1216        Ok(())
1217    }
1218
1219    #[test]
1220    fn it_validates_connections() -> Result<()> {
1221        let mut graph = CompositionGraph::new();
1222        let a = graph.add_component(Component::from_bytes(
1223            "a",
1224            b"(component (import \"i1\" (func)) (import \"i2\" (instance (export \"no\" (func)))))"
1225                .as_ref(),
1226        )?)?;
1227        let b = graph.add_component(Component::from_bytes(
1228            "b",
1229            b"(component (import \"i1\" (func)) (import \"i2\" (core module)) (export \"e1\" (func 0)) (export \"e2\" (core module 0)))".as_ref(),
1230        )?)?;
1231        let ai = graph.instantiate(a)?;
1232        let bi = graph.instantiate(b)?;
1233
1234        match graph.connect(ai, None::<ExportIndex>, ai, 0) {
1235            Ok(_) => panic!("expected a failure to connect"),
1236            Err(e) => assert_eq!(
1237                format!("{e:#}"),
1238                "an instance cannot be connected to itself"
1239            ),
1240        }
1241
1242        match graph.connect(ai, Some(0), bi, 0) {
1243            Ok(_) => panic!("expected a failure to connect"),
1244            Err(e) => assert_eq!(format!("{e:#}"), "the source export index is invalid"),
1245        }
1246
1247        match graph.connect(101, Some(0), ai, 0) {
1248            Ok(_) => panic!("expected a failure to connect"),
1249            Err(e) => assert_eq!(
1250                format!("{e:#}"),
1251                "the source instance does not exist in the graph"
1252            ),
1253        }
1254
1255        match graph.connect(bi, Some(0), 101, 0) {
1256            Ok(_) => panic!("expected a failure to connect"),
1257            Err(e) => assert_eq!(
1258                format!("{e:#}"),
1259                "the target instance does not exist in the graph"
1260            ),
1261        }
1262
1263        match graph.connect(bi, Some(101), ai, 0) {
1264            Ok(_) => panic!("expected a failure to connect"),
1265            Err(e) => assert_eq!(format!("{e:#}"), "the source export index is invalid"),
1266        }
1267
1268        match graph.connect(bi, Some(0), ai, 101) {
1269            Ok(_) => panic!("expected a failure to connect"),
1270            Err(e) => assert_eq!(format!("{e:#}"), "the target import index is invalid"),
1271        }
1272
1273        match graph.connect(bi, Some(1), ai, 0) {
1274            Ok(_) => panic!("expected a failure to connect"),
1275            Err(e) => assert_eq!(
1276                format!("{e:#}"),
1277                "source module export `e2` is not compatible with target function import `i1`"
1278            ),
1279        }
1280
1281        match graph.connect(bi, None::<ExportIndex>, ai, 0) {
1282            Ok(_) => panic!("expected a failure to connect"),
1283            Err(e) => assert_eq!(
1284                format!("{e:#}"),
1285                "source instance is not compatible with target function import `i1`"
1286            ),
1287        }
1288
1289        match graph.connect(bi, None::<ExportIndex>, ai, 1) {
1290            Ok(_) => panic!("expected a failure to connect"),
1291            Err(e) => assert_eq!(
1292                format!("{e:#}"),
1293                "source instance is not compatible with target instance import `i2`"
1294            ),
1295        }
1296
1297        Ok(())
1298    }
1299
1300    #[test]
1301    fn it_cannot_encode_a_cycle() -> Result<()> {
1302        let mut graph = CompositionGraph::new();
1303        let a = graph.add_component(Component::from_bytes(
1304            "a",
1305            b"(component (import \"i1\" (func)) (export \"e1\" (func 0)))".as_ref(),
1306        )?)?;
1307        let b = graph.add_component(Component::from_bytes(
1308            "b",
1309            b"(component (import \"i1\" (func)) (export \"e1\" (func 0)))".as_ref(),
1310        )?)?;
1311        let ai = graph.instantiate(a)?;
1312        let bi = graph.instantiate(b)?;
1313
1314        graph.connect(ai, Some(0), bi, 0)?;
1315        graph.connect(bi, Some(0), ai, 0)?;
1316
1317        match graph.encode(EncodeOptions {
1318            define_components: false,
1319            export: None,
1320            validate: true,
1321        }) {
1322            Ok(_) => panic!("graph should not encode"),
1323            Err(e) => assert_eq!(format!("{e:#}"), "an instantiation of component `b` and its dependencies form a cycle in the instantiation graph"),
1324        }
1325
1326        Ok(())
1327    }
1328
1329    #[test]
1330    fn it_encodes_an_empty_component() -> Result<()> {
1331        let mut graph = CompositionGraph::new();
1332        graph.add_component(Component::from_bytes("a", b"(component)".as_ref())?)?;
1333        graph.add_component(Component::from_bytes("b", b"(component)".as_ref())?)?;
1334
1335        let encoded = graph.encode(EncodeOptions {
1336            define_components: false,
1337            export: None,
1338            validate: true,
1339        })?;
1340
1341        let wat = wasmprinter::print_bytes(encoded)?;
1342        assert_eq!(
1343            r#"(component)
1344"#,
1345            wat
1346        );
1347
1348        Ok(())
1349    }
1350
1351    #[test]
1352    fn it_encodes_component_imports() -> Result<()> {
1353        let mut graph = CompositionGraph::new();
1354        // Add a component that doesn't get instantiated (shouldn't be imported)
1355        graph.add_component(Component::from_bytes("a", b"(component)".as_ref())?)?;
1356        let b = graph.add_component(Component::from_bytes("b", b"(component)".as_ref())?)?;
1357        graph.instantiate(b)?;
1358
1359        let encoded = graph.encode(EncodeOptions {
1360            define_components: false,
1361            export: None,
1362            validate: true,
1363        })?;
1364
1365        let wat = wasmprinter::print_bytes(encoded)?.replace("\r\n", "\n");
1366        assert_eq!(
1367            r#"(component
1368  (type (;0;)
1369    (component)
1370  )
1371  (import "b" (component (;0;) (type 0)))
1372  (instance (;0;) (instantiate 0))
1373)
1374"#,
1375            wat
1376        );
1377
1378        Ok(())
1379    }
1380
1381    #[test]
1382    fn it_encodes_defined_components() -> Result<()> {
1383        let mut graph = CompositionGraph::new();
1384        // Add a component that doesn't get instantiated (shouldn't be imported)
1385        graph.add_component(Component::from_bytes("a", b"(component)".as_ref())?)?;
1386        let b = graph.add_component(Component::from_bytes("b", b"(component)".as_ref())?)?;
1387        graph.instantiate(b)?;
1388
1389        let encoded = graph.encode(EncodeOptions {
1390            define_components: true,
1391            export: None,
1392            validate: true,
1393        })?;
1394
1395        let wat = wasmprinter::print_bytes(encoded)?.replace("\r\n", "\n");
1396        assert_eq!(
1397            r#"(component
1398  (component (;0;))
1399  (instance (;0;) (instantiate 0))
1400)
1401"#,
1402            wat
1403        );
1404
1405        Ok(())
1406    }
1407
1408    #[test]
1409    fn it_encodes_a_simple_composition() -> Result<()> {
1410        let mut graph = CompositionGraph::new();
1411        let a = graph.add_component(Component::from_bytes(
1412            "a",
1413            b"(component
1414  (type (tuple u32 u32))
1415  (import \"i1\" (instance (export \"e1\" (func)) (export \"e3\" (func (param \"a\" u32)))))
1416  (import \"i2\" (func))
1417  (import \"i3\" (component))
1418  (import \"i4\" (core module))
1419  (import \"i5\" (type (eq 0)))
1420  (export \"e1\" (instance 0))
1421  (export \"e2\" (func 0))
1422  (export \"e3\" (component 0))
1423  (export \"e4\" (core module 0))
1424  (export \"e5\" (type 1))
1425)"
1426            .as_ref(),
1427        )?)?;
1428        let b = graph.add_component(Component::from_bytes(
1429            "b",
1430            b"(component
1431  (type (tuple u32 u32))
1432  (import \"i1\" (instance (export \"e2\" (func)) (export \"e3\" (func (param \"a\" u32)))))
1433  (import \"i2\" (func))
1434  (import \"i3\" (component))
1435  (import \"i4\" (core module))
1436  (import \"i5\" (type (eq 0)))
1437)"
1438            .as_ref(),
1439        )?)?;
1440
1441        let ai = graph.instantiate(a)?;
1442        let bi1 = graph.instantiate(b)?;
1443        let bi2 = graph.instantiate(b)?;
1444        let bi3 = graph.instantiate(b)?;
1445
1446        // Skip the instance arguments so a merged instance is imported
1447        for i in 1..=3 {
1448            graph.connect(ai, Some(i), bi1, i)?;
1449            graph.connect(ai, Some(i), bi2, i)?;
1450            graph.connect(ai, Some(i), bi3, i)?;
1451        }
1452
1453        let encoded = graph.encode(EncodeOptions {
1454            define_components: true,
1455            export: None,
1456            validate: true,
1457        })?;
1458
1459        let wat = wasmprinter::print_bytes(encoded)?.replace("\r\n", "\n");
1460        assert_eq!(
1461            r#"(component
1462  (type (;0;)
1463    (instance
1464      (type (;0;) (func))
1465      (export (;0;) "e1" (func (type 0)))
1466      (type (;1;) (func (param "a" u32)))
1467      (export (;1;) "e3" (func (type 1)))
1468      (type (;2;) (func))
1469      (export (;2;) "e2" (func (type 2)))
1470    )
1471  )
1472  (import "i1" (instance (;0;) (type 0)))
1473  (type (;1;) (func))
1474  (import "i2" (func (;0;) (type 1)))
1475  (type (;2;)
1476    (component)
1477  )
1478  (import "i3" (component (;0;) (type 2)))
1479  (core type (;0;)
1480    (module)
1481  )
1482  (import "i4" (core module (;0;) (type 0)))
1483  (type (;3;) (tuple u32 u32))
1484  (import "i5" (type (;4;) (eq 3)))
1485  (component (;1;)
1486    (type (;0;) (tuple u32 u32))
1487    (type (;1;)
1488      (instance
1489        (type (;0;) (func))
1490        (export (;0;) "e1" (func (type 0)))
1491        (type (;1;) (func (param "a" u32)))
1492        (export (;1;) "e3" (func (type 1)))
1493      )
1494    )
1495    (import "i1" (instance (;0;) (type 1)))
1496    (type (;2;) (func))
1497    (import "i2" (func (;0;) (type 2)))
1498    (type (;3;)
1499      (component)
1500    )
1501    (import "i3" (component (;0;) (type 3)))
1502    (core type (;0;)
1503      (module)
1504    )
1505    (import "i4" (core module (;0;) (type 0)))
1506    (import "i5" (type (;4;) (eq 0)))
1507    (export (;1;) "e1" (instance 0))
1508    (export (;1;) "e2" (func 0))
1509    (export (;1;) "e3" (component 0))
1510    (export (;1;) "e4" (core module 0))
1511    (export (;5;) "e5" (type 1))
1512  )
1513  (component (;2;)
1514    (type (;0;) (tuple u32 u32))
1515    (type (;1;)
1516      (instance
1517        (type (;0;) (func))
1518        (export (;0;) "e2" (func (type 0)))
1519        (type (;1;) (func (param "a" u32)))
1520        (export (;1;) "e3" (func (type 1)))
1521      )
1522    )
1523    (import "i1" (instance (;0;) (type 1)))
1524    (type (;2;) (func))
1525    (import "i2" (func (;0;) (type 2)))
1526    (type (;3;)
1527      (component)
1528    )
1529    (import "i3" (component (;0;) (type 3)))
1530    (core type (;0;)
1531      (module)
1532    )
1533    (import "i4" (core module (;0;) (type 0)))
1534    (import "i5" (type (;4;) (eq 0)))
1535  )
1536  (instance (;1;) (instantiate 1
1537      (with "i1" (instance 0))
1538      (with "i2" (func 0))
1539      (with "i3" (component 0))
1540      (with "i4" (core module 0))
1541      (with "i5" (type 4))
1542    )
1543  )
1544  (alias export 1 "e2" (func (;1;)))
1545  (alias export 1 "e3" (component (;3;)))
1546  (alias export 1 "e4" (core module (;1;)))
1547  (instance (;2;) (instantiate 2
1548      (with "i2" (func 1))
1549      (with "i3" (component 3))
1550      (with "i4" (core module 1))
1551      (with "i1" (instance 0))
1552      (with "i5" (type 4))
1553    )
1554  )
1555  (instance (;3;) (instantiate 2
1556      (with "i2" (func 1))
1557      (with "i3" (component 3))
1558      (with "i4" (core module 1))
1559      (with "i1" (instance 0))
1560      (with "i5" (type 4))
1561    )
1562  )
1563  (instance (;4;) (instantiate 2
1564      (with "i2" (func 1))
1565      (with "i3" (component 3))
1566      (with "i4" (core module 1))
1567      (with "i1" (instance 0))
1568      (with "i5" (type 4))
1569    )
1570  )
1571)
1572"#,
1573            wat
1574        );
1575
1576        Ok(())
1577    }
1578}