Skip to main content

wac_graph/
graph.rs

1use crate::encoding::{State, TypeEncoder};
2use indexmap::IndexMap;
3use petgraph::{
4    dot::{Config, Dot},
5    graph::NodeIndex,
6    stable_graph::StableDiGraph,
7    visit::{Dfs, EdgeRef, IntoNodeIdentifiers, Reversed, VisitMap, Visitable},
8    Direction,
9};
10use semver::Version;
11use std::{
12    borrow::Cow,
13    collections::{HashMap, HashSet},
14    fmt::{self, Write},
15    ops::Index,
16};
17use thiserror::Error;
18use wac_types::{
19    BorrowedKey, BorrowedPackageKey, DefinedType, ItemKind, Package, PackageKey, SubtypeChecker,
20    Type, TypeAggregator, Types, ValueType,
21};
22use wasm_encoder::{
23    Alias, ComponentBuilder, ComponentExportKind, ComponentNameSection, ComponentTypeRef,
24    ComponentValType, NameMap, TypeBounds,
25};
26use wasmparser::{
27    names::{ComponentName, ComponentNameKind},
28    BinaryReaderError, Validator, WasmFeatures,
29};
30
31/// Represents an error that can occur when defining a type in
32/// a composition graph.
33#[derive(Debug, Error)]
34pub enum DefineTypeError {
35    /// The provided type has already been defined in the graph.
36    #[error("the provided type has already been defined in the graph")]
37    TypeAlreadyDefined,
38    /// A resource type cannot be defined in the graph.
39    #[error("a resource type cannot be defined in the graph")]
40    CannotDefineResource,
41    /// The specified type name conflicts with an existing export.
42    #[error("type name `{name}` conflicts with an existing export of the same name")]
43    ExportConflict {
44        /// The name of the existing export.
45        name: String,
46    },
47    /// The specified type name is not a valid extern name.
48    #[error("type name `{name}` is not valid")]
49    InvalidExternName {
50        /// The name of the type.
51        name: String,
52        /// The underlying validation error.
53        #[source]
54        source: anyhow::Error,
55    },
56}
57
58/// Represents an error that can occur when importing an item in
59/// a composition graph.
60#[derive(Debug, Error)]
61pub enum ImportError {
62    /// An import name already exists in the graph.
63    #[error("import name `{name}` already exists in the graph")]
64    ImportAlreadyExists {
65        /// The name of the existing extern.
66        name: String,
67        /// The node that is already imported by that name.
68        node: NodeId,
69    },
70    /// An invalid import name was given.
71    #[error("import name `{name}` is not valid")]
72    InvalidImportName {
73        /// The invalid name.
74        name: String,
75        /// The underlying validation error.
76        #[source]
77        source: anyhow::Error,
78    },
79}
80
81/// Represents an error that can occur when registering a
82/// package with a composition graph.
83#[derive(Debug, Error)]
84pub enum RegisterPackageError {
85    /// The package is already registered in the graph.
86    #[error("package `{key}` has already been registered in the graph")]
87    PackageAlreadyRegistered {
88        /// The key representing the already registered package
89        key: PackageKey,
90    },
91}
92
93/// Represents an error that can occur when aliasing an instance
94/// export in a composition graph.
95#[derive(Debug, Error)]
96pub enum AliasError {
97    /// The provided source node is not an instance.
98    #[error("expected source node to be an instance, but the node is a {kind}")]
99    NodeIsNotAnInstance {
100        /// The source node that is not an instance.
101        node: NodeId,
102        /// The kind of the node.
103        kind: String,
104    },
105    /// The instance does not export an item with the given name.
106    #[error("instance does not have an export named `{export}`")]
107    InstanceMissingExport {
108        /// The instance node that is missing the export.
109        node: NodeId,
110        /// The export that was missing.
111        export: String,
112    },
113}
114
115/// Represents an error that can occur when exporting a node from
116/// a composition graph.
117#[derive(Debug, Error)]
118pub enum ExportError {
119    /// An export name already exists in the graph.
120    #[error("an export with the name `{name}` already exists")]
121    ExportAlreadyExists {
122        /// The name of the existing export.
123        name: String,
124        /// The node that is already exported with that name.
125        node: NodeId,
126    },
127    /// An invalid export name was given.
128    #[error("export name `{name}` is not valid")]
129    InvalidExportName {
130        /// The invalid name.
131        name: String,
132        /// The underlying validation error.
133        #[source]
134        source: anyhow::Error,
135    },
136}
137
138/// Represents an error that can occur when unexporting a node in
139/// a composition graph.
140#[derive(Debug, Error)]
141pub enum UnexportError {
142    /// The node cannot be unexported as it is a type definition.
143    #[error("cannot unexport a type definition")]
144    MustExportDefinition,
145}
146
147/// Represents an error that can occur when setting an instantiation
148/// argument in a composition graph.
149#[derive(Debug, Error)]
150pub enum InstantiationArgumentError {
151    /// The node is not an instantiation.
152    #[error("the specified node is not an instantiation")]
153    NodeIsNotAnInstantiation {
154        /// The node that is not an instantiation.
155        node: NodeId,
156    },
157    /// The provided argument name is invalid.
158    #[error("argument name `{name}` is not an import of package `{package}`")]
159    InvalidArgumentName {
160        /// The instantiation node that does not have the provided argument name.
161        node: NodeId,
162        /// The name of the invalid argument.
163        name: String,
164        /// The name of the package that does not have the argument.
165        package: String,
166    },
167    /// The source type does not match the target argument type.
168    #[error("mismatched instantiation argument `{name}`")]
169    ArgumentTypeMismatch {
170        /// The name of the argument.
171        name: String,
172        /// The source of the error.
173        #[source]
174        source: anyhow::Error,
175    },
176    /// The argument has been passed to the instantiation.
177    #[error("argument `{name}` has already been passed to the instantiation")]
178    ArgumentAlreadyPassed {
179        /// The instantiation node.
180        node: NodeId,
181        /// The name of the argument.
182        name: String,
183    },
184}
185
186/// Represents an error that can occur when encoding a composition graph.
187#[derive(Debug, Error)]
188pub enum EncodeError {
189    /// The encoding of the graph failed validation.
190    #[error("encoding produced a component that failed validation")]
191    ValidationFailure {
192        /// The source of the validation error.
193        #[source]
194        source: BinaryReaderError,
195    },
196    /// The graph contains a cycle.
197    #[error("the graph contains a cycle and cannot be encoded")]
198    GraphContainsCycle {
199        /// The node where the cycle was detected.
200        node: NodeId,
201    },
202    /// An implicit import on an instantiation conflicts with an explicit import node.
203    #[error("an instantiation of package `{package}` implicitly imports an item named `{name}`, but it conflicts with an explicit import of the same name")]
204    ImplicitImportConflict {
205        /// The existing import node.
206        import: NodeId,
207        /// The instantiation node with the implicit import
208        instantiation: NodeId,
209        /// The package associated with the instantiation node.
210        package: PackageKey,
211        /// The name of the conflicting import.
212        name: String,
213    },
214    /// An error occurred while merging an implicit import type.
215    #[error("failed to merge the type definition for implicit import `{import}` due to conflicting types")]
216    ImportTypeMergeConflict {
217        /// The name of the import.
218        import: String,
219        /// The first conflicting instantiation node that introduced the implicit import.
220        first: NodeId,
221        /// The second conflicting instantiation node.
222        second: NodeId,
223        /// The type merge error.
224        #[source]
225        source: anyhow::Error,
226    },
227}
228
229/// Represents an error that can occur when decoding a composition graph.
230#[derive(Debug, Error)]
231pub enum DecodeError {}
232
233/// An identifier for a package in a composition graph.
234#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
235pub struct PackageId {
236    /// The index into the graph's packages list.
237    index: usize,
238    /// The generation of the package.
239    ///
240    /// This is used to invalidate identifiers on package removal.
241    generation: usize,
242}
243
244/// An identifier for a node in a composition graph.
245#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
246pub struct NodeId(NodeIndex);
247
248impl fmt::Display for NodeId {
249    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250        self.0.index().fmt(f)
251    }
252}
253
254/// Represents the kind of a node in a composition graph.
255#[derive(Debug, Clone)]
256pub enum NodeKind {
257    /// The node is a type definition.
258    Definition,
259    /// The node is an import of an item.
260    ///
261    /// The string is the import name.
262    Import(String),
263    /// The node is an instantiation of a package.
264    ///
265    /// The set is the currently satisfied argument indexes.
266    Instantiation(HashSet<usize>),
267    /// The node is an alias of an export of an instance.
268    Alias,
269}
270
271/// Represents a package registered with a composition graph.
272#[derive(Debug, Clone)]
273struct RegisteredPackage {
274    /// The underlying package.
275    package: Option<Package>,
276    /// The generation of the package.
277    ///
278    /// The generation is incremented each time a package is removed from the graph.
279    ///
280    /// This ensures referring package identifiers are invalidated when a package is removed.
281    generation: usize,
282}
283
284impl RegisteredPackage {
285    fn new(generation: usize) -> Self {
286        Self {
287            package: None,
288            generation,
289        }
290    }
291}
292
293/// Represents a node in a composition graph.
294#[derive(Debug, Clone)]
295pub struct Node {
296    /// The node kind.
297    kind: NodeKind,
298    /// The package associated with the node, if any.
299    package: Option<PackageId>,
300    /// The item kind of the node.
301    item_kind: ItemKind,
302    /// The optional name to associate with the node.
303    ///
304    /// When the graph is encoded, node names are recorded in a `names` custom section.
305    name: Option<String>,
306    /// The name to use for exporting the node.
307    export: Option<String>,
308}
309
310impl Node {
311    fn new(kind: NodeKind, item_kind: ItemKind, package: Option<PackageId>) -> Self {
312        Self {
313            kind,
314            item_kind,
315            package,
316            name: None,
317            export: None,
318        }
319    }
320
321    /// Gets the kind of the node.
322    pub fn kind(&self) -> &NodeKind {
323        &self.kind
324    }
325
326    /// Gets the package id associated with the node.
327    ///
328    /// Returns `None` if the node is not directly associated with a package.
329    pub fn package(&self) -> Option<PackageId> {
330        self.package
331    }
332
333    /// Gets the item kind of the node.
334    pub fn item_kind(&self) -> ItemKind {
335        self.item_kind
336    }
337
338    /// Gets the name of the node.
339    ///
340    /// Node names are encoded in a `names` custom section.
341    ///
342    /// Returns `None` if the node is unnamed.
343    pub fn name(&self) -> Option<&str> {
344        self.name.as_deref()
345    }
346
347    /// Gets the import name of the node.
348    ///
349    /// Returns `Some` if the node is an import or `None` if the node is not an import.
350    pub fn import_name(&self) -> Option<&str> {
351        match &self.kind {
352            NodeKind::Import(name) => Some(name),
353            _ => None,
354        }
355    }
356
357    /// Gets the export name of the node.
358    ///
359    /// Returns `None` if the node is not exported.
360    pub fn export_name(&self) -> Option<&str> {
361        self.export.as_deref()
362    }
363
364    fn add_satisfied_arg(&mut self, index: usize) {
365        match &mut self.kind {
366            NodeKind::Instantiation(satisfied) => {
367                let inserted = satisfied.insert(index);
368                assert!(inserted);
369            }
370            _ => panic!("expected the node to be an instantiation"),
371        }
372    }
373
374    fn remove_satisfied_arg(&mut self, index: usize) {
375        match &mut self.kind {
376            NodeKind::Instantiation(satisfied) => {
377                let removed = satisfied.remove(&index);
378                assert!(removed);
379            }
380            _ => panic!("expected the node to be an instantiation"),
381        }
382    }
383
384    fn is_arg_satisfied(&self, index: usize) -> bool {
385        match &self.kind {
386            NodeKind::Instantiation(satisfied) => satisfied.contains(&index),
387            _ => panic!("expected the node to be an instantiation"),
388        }
389    }
390}
391
392/// Represents an edge in a composition graph.
393#[derive(Clone)]
394enum Edge {
395    /// The edge is from an instance to an aliased exported item.
396    ///
397    /// The data is the index of the export on the source node.
398    Alias(usize),
399    /// The edge is from any node to an instantiation node.
400    ///
401    /// The data is the import index on the instantiation node being
402    /// satisfied by the argument.
403    Argument(usize),
404    /// A dependency from one type to another.
405    Dependency,
406}
407
408impl fmt::Debug for Edge {
409    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
410        match self {
411            Edge::Alias(_) => write!(f, "aliased export"),
412            Edge::Argument(_) => write!(f, "argument to"),
413            Edge::Dependency => write!(f, "dependency of"),
414        }
415    }
416}
417
418/// Represents a composition graph.
419///
420/// A composition graph is a directed acyclic graph (DAG) that represents a WebAssembly composition.
421///
422/// Types may be defined directly in a composition graph or referenced from packages registered with
423/// the graph.
424///
425/// There are four supported node types:
426///
427/// * a *type definition node* representing the definition of an exported named type.
428/// * an *import node* representing an imported item.
429/// * an *instantiation node* representing an instantiation of a package.
430/// * an *alias node* representing an alias of an exported item from an instance.
431///
432/// There are three supported edge types:
433///
434/// * a *type dependency* edge from a type definition node to any dependent defined types;
435///   type dependency edges are implicitly added to the graph when a type is defined.
436/// * an *alias edge* from an any node that is an instance to an alias node; alias edges are
437///   implicitly added to the graph when an alias node is added.
438/// * an *instantiation argument edge* from any node to an instantiation node; instantiation
439///   argument edges contain a set of instantiation arguments satisfied by the target node;
440///   unsatisfied arguments are automatically imported when encoding the composition graph.
441///
442/// Any node in the graph may be marked to be exported from the encoded graph; note that
443/// type definition nodes are _always_ exported and may not be unmarked.
444#[derive(Default, Clone)]
445pub struct CompositionGraph {
446    /// The underlying graph data structure.
447    graph: StableDiGraph<Node, Edge>,
448    /// The map of import names to node indices.
449    imports: HashMap<String, NodeIndex>,
450    /// The map of export names to node ids.
451    exports: IndexMap<String, NodeIndex>,
452    /// The map of defined types to node ids.
453    defined: HashMap<Type, NodeIndex>,
454    /// The map of package keys to package ids.
455    package_map: HashMap<PackageKey, PackageId>,
456    /// The registered packages.
457    packages: Vec<RegisteredPackage>,
458    /// The list of free entries in the packages list.
459    free_packages: Vec<usize>,
460    /// The types that are directly defined by the graph.
461    types: Types,
462    /// The cache used for subtype checks.
463    type_check_cache: HashSet<(ItemKind, ItemKind)>,
464}
465
466impl CompositionGraph {
467    /// Creates a new composition graph.
468    pub fn new() -> Self {
469        Self::default()
470    }
471
472    /// Gets a reference to the type collection of the graph.
473    pub fn types(&self) -> &Types {
474        &self.types
475    }
476
477    /// Gets a mutable reference to the type collection of the graph.
478    ///
479    /// This type collection is used to define types directly in the graph.
480    pub fn types_mut(&mut self) -> &mut Types {
481        &mut self.types
482    }
483
484    /// Gets the nodes in the graph.
485    pub fn nodes(&self) -> impl Iterator<Item = &Node> + '_ {
486        self.graph.node_weights()
487    }
488
489    /// Gets the identifiers for every node in the graph.
490    pub fn node_ids(&self) -> impl Iterator<Item = NodeId> + '_ {
491        self.graph.node_indices().map(NodeId)
492    }
493
494    /// Gets the packages currently registered with the graph.
495    pub fn packages(&self) -> impl Iterator<Item = &Package> + '_ {
496        self.packages.iter().filter_map(|p| p.package.as_ref())
497    }
498
499    /// Registers a package with the graph.
500    ///
501    /// Packages are used to create instantiations, but are not directly
502    /// a part of the graph.
503    ///
504    /// # Panics
505    ///
506    /// Panics if the given package's type is not contained within the
507    /// graph's types collection.
508    pub fn register_package(
509        &mut self,
510        package: wac_types::Package,
511    ) -> Result<PackageId, RegisterPackageError> {
512        let key = PackageKey::new(&package);
513        if self.package_map.contains_key(&key) {
514            return Err(RegisterPackageError::PackageAlreadyRegistered { key });
515        }
516
517        assert!(
518            self.types.contains(Type::World(package.ty())),
519            "the package type is not present in the types collection"
520        );
521
522        log::debug!("registering package `{key}` with the graph");
523        let id = self.alloc_package(package);
524        let prev = self.package_map.insert(key, id);
525        assert!(prev.is_none());
526        Ok(id)
527    }
528
529    /// Unregisters a package from the graph.
530    ///
531    /// Any edges and nodes associated with the package are also removed.
532    ///
533    /// # Panics
534    ///
535    /// Panics if the given package identifier is invalid.
536    pub fn unregister_package(&mut self, package: PackageId) {
537        assert_eq!(
538            self.packages
539                .get(package.index)
540                .expect("invalid package id")
541                .generation,
542            package.generation,
543            "invalid package id"
544        );
545
546        // Remove exports and definitions associated with the package before
547        // removing nodes, as retain_nodes invalidates the node indices.
548        self.exports
549            .retain(|_, n| self.graph[*n].package != Some(package));
550        self.defined
551            .retain(|_, n| self.graph[*n].package != Some(package));
552        self.imports
553            .retain(|_, n| self.graph[*n].package != Some(package));
554
555        // Remove all nodes associated with the package
556        self.graph
557            .retain_nodes(|g, i| g[i].package != Some(package));
558
559        // Remove the package from the package map
560        let entry = &mut self.packages[package.index];
561        let key = entry.package.as_ref().unwrap().key();
562        log::debug!("unregistering package `{key}` with the graph");
563        let prev = self.package_map.remove(&key as &dyn BorrowedKey);
564        assert!(
565            prev.is_some(),
566            "package should exist in the package map (this is a bug)"
567        );
568
569        // Finally free the package
570        *entry = RegisteredPackage::new(entry.generation.wrapping_add(1));
571        self.free_packages.push(package.index);
572    }
573
574    /// Gets the registered package of the given package name and version.
575    ///
576    /// Returns `None` if a package with the specified name and version has
577    /// not been registered with the graph.
578    pub fn get_package_by_name(
579        &self,
580        name: &str,
581        version: Option<&Version>,
582    ) -> Option<(PackageId, &wac_types::Package)> {
583        let key: BorrowedPackageKey<'_> = BorrowedPackageKey::from_name_and_version(name, version);
584        let id = self.package_map.get(&key as &dyn BorrowedKey)?;
585        Some((*id, self.packages[id.index].package.as_ref().unwrap()))
586    }
587
588    /// Adds a *type definition node* to the graph.
589    ///
590    /// The graph must not already have a node exported with the same name.
591    ///
592    /// This method will implicitly add dependency edges to other defined
593    /// types.
594    ///
595    /// If the provided type has already been defined, the previous node
596    /// will be returned and an additional export name will be associated
597    /// with the node.
598    ///
599    /// # Panics
600    ///
601    /// This method panics if the provided type is not contained within the
602    /// graph's types collection.
603    pub fn define_type(
604        &mut self,
605        name: impl Into<String>,
606        ty: Type,
607    ) -> Result<NodeId, DefineTypeError> {
608        assert!(
609            self.types.contains(ty),
610            "type not contained in types collection"
611        );
612
613        if self.defined.contains_key(&ty) {
614            return Err(DefineTypeError::TypeAlreadyDefined);
615        }
616
617        if let Type::Resource(_) = ty {
618            return Err(DefineTypeError::CannotDefineResource);
619        }
620
621        let name = name.into();
622        if self.exports.contains_key(&name) {
623            return Err(DefineTypeError::ExportConflict { name });
624        }
625
626        // Ensure that the given name is a valid extern name
627        ComponentName::new(&name, 0).map_err(|e| {
628            let msg = e.to_string();
629            DefineTypeError::InvalidExternName {
630                name: name.to_string(),
631                source: anyhow::anyhow!(
632                    "{msg}",
633                    msg = msg.strip_suffix(" (at offset 0x0)").unwrap_or(&msg)
634                ),
635            }
636        })?;
637
638        let mut node = Node::new(NodeKind::Definition, ItemKind::Type(ty), None);
639        node.export = Some(name.clone());
640        let index = self.graph.add_node(node);
641        log::debug!(
642            "adding type definition `{name}` to the graph as node index {index}",
643            index = index.index()
644        );
645
646        // Add dependency edges between the given type and any referenced defined types
647        ty.visit_defined_types(&self.types, &mut |_, id| {
648            let dep_ty = Type::Value(ValueType::Defined(id));
649            if dep_ty != ty {
650                if let Some(dep) = self.defined.get(&dep_ty) {
651                    if !self
652                        .graph
653                        .edges_connecting(*dep, index)
654                        .any(|e| matches!(e.weight(), Edge::Dependency))
655                    {
656                        log::debug!(
657                            "adding dependency edge from type `{from}` (dependency) to type `{name}` (dependent)",
658                            from = self.graph[*dep].export.as_ref().unwrap()
659                        );
660                        self.graph.add_edge(*dep, index, Edge::Dependency);
661                    }
662                }
663            }
664
665            Ok(())
666        })?;
667
668        // Add dependency edges to any existing defined types that reference this one
669        for (other_ty, other) in &self.defined {
670            other_ty.visit_defined_types(&self.types, &mut |_, id| {
671                let dep_ty = Type::Value(ValueType::Defined(id));
672                if dep_ty == ty
673                    && !self
674                        .graph
675                        .edges_connecting(index, *other)
676                        .any(|e| matches!(e.weight(), Edge::Dependency))
677                {
678                    log::debug!(
679                        "adding dependency edge from type `{name}` (dependency) to type `{to}` (dependent)",
680                        to = self.graph[index].export.as_ref().unwrap(),
681                    );
682                    self.graph.add_edge(index, *other, Edge::Dependency);
683                }
684
685                Ok(())
686            })?;
687        }
688
689        self.defined.insert(ty, index);
690        let prev = self.exports.insert(name, index);
691        assert!(prev.is_none());
692        Ok(NodeId(index))
693    }
694
695    /// Adds an *import node* to the graph.
696    ///
697    /// If the provided import name is invalid or if an import already exists
698    /// with the same name, an error is returned.
699    ///
700    /// # Panics
701    ///
702    /// This method panics if the provided kind is not contained within the
703    /// graph's types collection.
704    pub fn import(
705        &mut self,
706        name: impl Into<String>,
707        kind: ItemKind,
708    ) -> Result<NodeId, ImportError> {
709        assert!(
710            self.types.contains(kind.ty()),
711            "provided type is not in the types collection"
712        );
713
714        let name = name.into();
715        if let Some(existing) = self.imports.get(&name) {
716            return Err(ImportError::ImportAlreadyExists {
717                name,
718                node: NodeId(*existing),
719            });
720        }
721
722        // Ensure that the given import name is a valid extern name
723        ComponentName::new(&name, 0).map_err(|e| {
724            let msg = e.to_string();
725            ImportError::InvalidImportName {
726                name: name.to_string(),
727                source: anyhow::anyhow!(
728                    "{msg}",
729                    msg = msg.strip_suffix(" (at offset 0x0)").unwrap_or(&msg)
730                ),
731            }
732        })?;
733
734        let node = Node::new(NodeKind::Import(name.clone()), kind, None);
735        let index = self.graph.add_node(node);
736        log::debug!(
737            "adding import `{name}` to the graph as node index {index}",
738            index = index.index()
739        );
740        let prev = self.imports.insert(name, index);
741        assert!(prev.is_none());
742        Ok(NodeId(index))
743    }
744
745    /// Gets the name used by an import node.
746    ///
747    /// Returns `None` if the specified node is not an import node.
748    ///
749    /// # Panics
750    ///
751    /// Panics if the specified node id is invalid.
752    pub fn get_import_name(&self, node: NodeId) -> Option<&str> {
753        let node = self.graph.node_weight(node.0).expect("invalid node id");
754        match &node.kind {
755            NodeKind::Import(name) => Some(name),
756            _ => None,
757        }
758    }
759
760    /// Adds an *instantiation node* to the graph.
761    ///
762    /// Initially the instantiation will have no satisfied arguments.
763    ///
764    /// Use `set_instantiation_argument` to set an argument on an instantiation node.
765    ///
766    /// # Panics
767    ///
768    /// This method panics if the provided package id is invalid.
769    pub fn instantiate(&mut self, package: PackageId) -> NodeId {
770        let pkg = &self[package];
771        let node = Node::new(
772            NodeKind::Instantiation(Default::default()),
773            ItemKind::Instance(pkg.instance_type()),
774            Some(package),
775        );
776        let index = self.graph.add_node(node);
777        log::debug!(
778            "adding instantiation of package `{key}` to the graph as node index {index}",
779            key = self[package].key(),
780            index = index.index()
781        );
782        NodeId(index)
783    }
784
785    /// Adds an *alias node* to the graph.
786    ///
787    /// The provided node must be an instance and the export name must match an export
788    /// of the instance.
789    ///
790    /// If an alias already exists for the export, the existing alias node will be returned.
791    ///
792    /// An implicit *alias edge* will be added from the instance to the alias node.
793    ///
794    /// # Panics
795    ///
796    /// Panics if the provided node id is invalid.
797    pub fn alias_instance_export(
798        &mut self,
799        instance: NodeId,
800        export: &str,
801    ) -> Result<NodeId, AliasError> {
802        let instance_node = self.graph.node_weight(instance.0).expect("invalid node id");
803
804        // Ensure the source is an instance
805        let exports = match instance_node.item_kind {
806            ItemKind::Instance(id) => &self.types[id].exports,
807            _ => {
808                return Err(AliasError::NodeIsNotAnInstance {
809                    node: instance,
810                    kind: instance_node.item_kind.desc(&self.types).to_string(),
811                });
812            }
813        };
814
815        // Ensure the export exists
816        let (index, _, kind) =
817            exports
818                .get_full(export)
819                .ok_or_else(|| AliasError::InstanceMissingExport {
820                    node: instance,
821                    export: export.to_string(),
822                })?;
823
824        // Check to see if there already is an edge for this alias
825        for e in self.graph.edges_directed(instance.0, Direction::Outgoing) {
826            assert_eq!(e.source(), instance.0);
827            if let Edge::Alias(i) = e.weight() {
828                if *i == index {
829                    return Ok(NodeId(e.target()));
830                }
831            }
832        }
833
834        // Allocate the alias node
835        let node = Node::new(NodeKind::Alias, *kind, instance_node.package);
836        let node_index = self.graph.add_node(node);
837        log::debug!(
838            "adding alias for export `{export}` to the graph as node index {index}",
839            index = node_index.index()
840        );
841        self.graph
842            .add_edge(instance.0, node_index, Edge::Alias(index));
843        Ok(NodeId(node_index))
844    }
845
846    /// Gets the source node and export name of an alias node.
847    ///
848    /// Returns `None` if the node is not an alias.
849    pub fn get_alias_source(&self, alias: NodeId) -> Option<(NodeId, &str)> {
850        for e in self.graph.edges_directed(alias.0, Direction::Incoming) {
851            assert_eq!(e.target(), alias.0);
852
853            if let Edge::Alias(index) = e.weight() {
854                match self.graph[e.source()].item_kind {
855                    ItemKind::Instance(id) => {
856                        let export = self.types[id].exports.get_index(*index).unwrap().0;
857                        return Some((NodeId(e.source()), export));
858                    }
859                    _ => panic!("alias source should be an instance"),
860                }
861            }
862        }
863
864        None
865    }
866
867    /// Gets the satisfied arguments of an instantiation node.
868    ///
869    /// Returns an iterator over the argument names and the node id used to satisfy the argument.
870    ///
871    /// If the node identifier is invalid or if the node is not an instantiation node, an
872    /// empty iterator is returned.
873    pub fn get_instantiation_arguments(
874        &self,
875        instantiation: NodeId,
876    ) -> impl Iterator<Item = (&str, NodeId)> {
877        self.graph
878            .edges_directed(instantiation.0, Direction::Incoming)
879            .filter_map(|e| {
880                let target = &self.graph[e.target()];
881                let imports = match target.kind {
882                    NodeKind::Instantiation(_) => {
883                        let package = &self.packages[target.package?.index].package.as_ref()?;
884                        &self.types[package.ty()].imports
885                    }
886                    _ => return None,
887                };
888
889                match e.weight() {
890                    Edge::Alias(_) | Edge::Dependency => None,
891                    Edge::Argument(i) => Some((
892                        imports.get_index(*i).unwrap().0.as_ref(),
893                        NodeId(e.source()),
894                    )),
895                }
896            })
897    }
898
899    /// Sets the name of a node in the graph.
900    ///
901    /// Node names are recorded in a `names` custom section when the graph is encoded.
902    ///
903    /// # Panics
904    ///
905    /// This method panics if the provided node id is invalid.
906    pub fn set_node_name(&mut self, node: NodeId, name: impl Into<String>) {
907        let node = &mut self.graph[node.0];
908        node.name = Some(name.into());
909    }
910
911    /// Marks the given node for export when the composition graph is encoded.
912    ///
913    /// Returns an error if the provided export name is invalid.
914    ///
915    /// # Panics
916    ///
917    /// This method panics if the provided node id is invalid.
918    pub fn export(&mut self, node: NodeId, name: impl Into<String>) -> Result<(), ExportError> {
919        let name = name.into();
920        if let Some(existing) = self.exports.get(&name) {
921            return Err(ExportError::ExportAlreadyExists {
922                name,
923                node: NodeId(*existing),
924            });
925        }
926
927        let map_reader_err = |e: BinaryReaderError| {
928            let msg = e.to_string();
929            ExportError::InvalidExportName {
930                name: name.to_string(),
931                source: anyhow::anyhow!(
932                    "{msg}",
933                    msg = msg.strip_suffix(" (at offset 0x0)").unwrap_or(&msg)
934                ),
935            }
936        };
937
938        // Ensure that the given export name is a valid extern name
939        match ComponentName::new(&name, 0).map_err(map_reader_err)?.kind() {
940            ComponentNameKind::Hash(_)
941            | ComponentNameKind::Url(_)
942            | ComponentNameKind::Dependency(_) => {
943                return Err(ExportError::InvalidExportName {
944                    name: name.to_string(),
945                    source: anyhow::anyhow!("export name cannot be a hash, url, or dependency"),
946                });
947            }
948            _ => {}
949        };
950
951        log::debug!("exporting node {index} as `{name}`", index = node.0.index());
952        self.graph[node.0].export = Some(name.clone());
953        let prev = self.exports.insert(name, node.0);
954        assert!(prev.is_none());
955        Ok(())
956    }
957
958    /// Gets the node being exported by the given name.
959    ///
960    /// Returns `None` if there is no node exported by that name.
961    pub fn get_export(&self, name: &str) -> Option<NodeId> {
962        self.exports.get(name).map(|i| NodeId(*i))
963    }
964
965    /// Unmarks the given node from being exported from an encoding of the graph.
966    ///
967    /// Returns an error if the given node is a type definition, as type
968    /// definitions must be exported.
969    ///
970    /// # Panics
971    ///
972    /// This method panics if the provided node id is invalid.
973    pub fn unexport(&mut self, node: NodeId) -> Result<(), UnexportError> {
974        let node = &mut self.graph[node.0];
975        if let NodeKind::Definition = node.kind {
976            return Err(UnexportError::MustExportDefinition);
977        }
978
979        if let Some(name) = node.export.take() {
980            log::debug!("unmarked node for export as `{name}`");
981            let removed = self.exports.swap_remove(&name);
982            assert!(removed.is_some());
983        }
984
985        Ok(())
986    }
987
988    /// Removes a node from the graph.
989    ///
990    /// All incoming and outgoing edges of the node are also removed.
991    ///
992    /// If the node has dependent defined types, the dependent define
993    /// types are also removed.
994    ///
995    /// If the node has aliases, the aliased nodes are also removed.
996    ///
997    /// # Panics
998    ///
999    /// This method panics if the provided node id is invalid.
1000    pub fn remove_node(&mut self, node: NodeId) {
1001        // Recursively remove any dependent nodes
1002        for node in self
1003            .graph
1004            .edges_directed(node.0, Direction::Outgoing)
1005            .filter_map(|e| {
1006                assert_eq!(e.source(), node.0);
1007                match e.weight() {
1008                    Edge::Alias(_) | Edge::Dependency => Some(NodeId(e.target())),
1009                    Edge::Argument(_) => None,
1010                }
1011            })
1012            .collect::<Vec<_>>()
1013        {
1014            self.remove_node(node);
1015        }
1016
1017        // Remove the node from the graph
1018        log::debug!(
1019            "removing node {index} from the graph",
1020            index = node.0.index()
1021        );
1022        let node = self.graph.remove_node(node.0).expect("invalid node id");
1023
1024        // Remove any import entry
1025        if let Some(name) = node.import_name() {
1026            log::debug!("removing import node `{name}`");
1027            let removed = self.imports.remove(name);
1028            assert!(removed.is_some());
1029        }
1030
1031        // Remove any export entry
1032        if let Some(name) = &node.export {
1033            log::debug!("removing export of node as `{name}`");
1034            let removed = self.exports.swap_remove(name);
1035            assert!(removed.is_some());
1036        }
1037
1038        if let NodeKind::Definition = node.kind {
1039            log::debug!(
1040                "removing type definition `{name}`",
1041                name = node.export.as_ref().unwrap()
1042            );
1043            let removed = self.defined.remove(&node.item_kind.ty());
1044            assert!(removed.is_some());
1045        }
1046    }
1047
1048    /// Sets an argument of an instantiation node to the provided argument
1049    /// node.
1050    ///
1051    /// This method adds an _instantiation argument_ edge from the argument
1052    /// node to the instantiation node.
1053    ///
1054    /// The provided instantiation node must be an instantiation.
1055    ///
1056    /// The argument name must be a valid import on the instantiation node
1057    /// and not already have an incoming edge from a different argument node.
1058    ///
1059    /// The argument node must be type-compatible with the argument of the
1060    /// instantiation node.
1061    ///
1062    /// If an edge already exists between the argument and the instantiation
1063    /// node, this method returns `Ok(_)`.
1064    ///
1065    /// # Panics
1066    ///
1067    /// This method will panic if the provided node ids are invalid.
1068    pub fn set_instantiation_argument(
1069        &mut self,
1070        instantiation: NodeId,
1071        argument_name: &str,
1072        argument: NodeId,
1073    ) -> Result<(), InstantiationArgumentError> {
1074        fn add_edge(
1075            graph: &mut CompositionGraph,
1076            argument: NodeId,
1077            instantiation: NodeId,
1078            argument_name: &str,
1079            cache: &mut HashSet<(ItemKind, ItemKind)>,
1080        ) -> Result<(), InstantiationArgumentError> {
1081            // Ensure the target is an instantiation node
1082            let instantiation_node = &graph.graph[instantiation.0];
1083
1084            if !matches!(instantiation_node.kind, NodeKind::Instantiation(_)) {
1085                return Err(InstantiationArgumentError::NodeIsNotAnInstantiation {
1086                    node: instantiation,
1087                });
1088            }
1089
1090            // Ensure the argument is a valid import of the target package
1091            let package = graph.packages[instantiation_node.package.unwrap().index]
1092                .package
1093                .as_ref()
1094                .unwrap();
1095            let package_type = &graph.types[package.ty()];
1096
1097            // Ensure the argument isn't already satisfied
1098            let (argument_index, _, expected_argument_kind) = package_type
1099                .imports
1100                .get_full(argument_name)
1101                .ok_or(InstantiationArgumentError::InvalidArgumentName {
1102                    node: instantiation,
1103                    name: argument_name.to_string(),
1104                    package: package.name().to_string(),
1105                })?;
1106
1107            for e in graph
1108                .graph
1109                .edges_directed(instantiation.0, Direction::Incoming)
1110            {
1111                assert_eq!(e.target(), instantiation.0);
1112                match e.weight() {
1113                    Edge::Alias(_) | Edge::Dependency => {
1114                        panic!("unexpected edge for an instantiation")
1115                    }
1116                    Edge::Argument(i) => {
1117                        if *i == argument_index {
1118                            if e.source() == argument.0 {
1119                                return Ok(());
1120                            }
1121
1122                            return Err(InstantiationArgumentError::ArgumentAlreadyPassed {
1123                                node: instantiation,
1124                                name: argument_name.to_string(),
1125                            });
1126                        }
1127                    }
1128                }
1129            }
1130
1131            // Perform a subtype check on the source and target
1132            let argument_node = &graph.graph[argument.0];
1133
1134            let mut checker = SubtypeChecker::new(cache);
1135            checker
1136                .is_subtype(
1137                    argument_node.item_kind,
1138                    &graph.types,
1139                    *expected_argument_kind,
1140                    &graph.types,
1141                )
1142                .map_err(|e| InstantiationArgumentError::ArgumentTypeMismatch {
1143                    name: argument_name.to_string(),
1144                    source: e,
1145                })?;
1146
1147            // Finally, insert the argument edge
1148            graph
1149                .graph
1150                .add_edge(argument.0, instantiation.0, Edge::Argument(argument_index));
1151
1152            graph.graph[instantiation.0].add_satisfied_arg(argument_index);
1153            Ok(())
1154        }
1155
1156        // Temporarily take ownership of the cache to avoid borrowing issues
1157        let mut cache = std::mem::take(&mut self.type_check_cache);
1158        let result = add_edge(self, argument, instantiation, argument_name, &mut cache);
1159        self.type_check_cache = cache;
1160        result
1161    }
1162
1163    /// Unsets an argument of an instantiation node that was previously
1164    /// set to the provided argument node.
1165    ///
1166    /// This method removes an _instantiation argument_ edge from the
1167    /// argument node to the instantiation node if the nodes are connected;
1168    /// if they are not connected, this method is a no-op.
1169    ///
1170    /// The provided instantiation node must be an instantiation.
1171    ///
1172    /// The argument name must be a valid import on the instantiation node.
1173    ///
1174    /// # Panics
1175    ///
1176    /// This method will panic if the provided node ids are invalid.
1177    pub fn unset_instantiation_argument(
1178        &mut self,
1179        instantiation: NodeId,
1180        argument_name: &str,
1181        argument: NodeId,
1182    ) -> Result<(), InstantiationArgumentError> {
1183        // Ensure the target is an instantiation node
1184        let instantiation_node = &self.graph[instantiation.0];
1185        if !matches!(instantiation_node.kind, NodeKind::Instantiation(_)) {
1186            return Err(InstantiationArgumentError::NodeIsNotAnInstantiation {
1187                node: instantiation,
1188            });
1189        }
1190
1191        // Ensure the argument is a valid import of the target package
1192        let package = self.packages[instantiation_node.package.unwrap().index]
1193            .package
1194            .as_ref()
1195            .unwrap();
1196        let package_type = &self.types[package.ty()];
1197
1198        let argument_index = package_type.imports.get_index_of(argument_name).ok_or(
1199            InstantiationArgumentError::InvalidArgumentName {
1200                node: instantiation,
1201                name: argument_name.to_string(),
1202                package: package.name().to_string(),
1203            },
1204        )?;
1205
1206        // Finally remove the argument edge if a connection exists
1207        let mut edge = None;
1208        for e in self.graph.edges_connecting(argument.0, instantiation.0) {
1209            match e.weight() {
1210                Edge::Alias(_) | Edge::Dependency => {
1211                    panic!("unexpected edge for an instantiation")
1212                }
1213                Edge::Argument(i) => {
1214                    if *i == argument_index {
1215                        edge = Some(e.id());
1216                        break;
1217                    }
1218                }
1219            }
1220        }
1221
1222        if let Some(edge) = edge {
1223            self.graph[instantiation.0].remove_satisfied_arg(argument_index);
1224            self.graph.remove_edge(edge);
1225        }
1226
1227        Ok(())
1228    }
1229
1230    /// Encodes the composition graph as a new WebAssembly component.
1231    ///
1232    /// An error will be returned if the graph contains a dependency cycle.
1233    pub fn encode(&self, options: EncodeOptions) -> Result<Vec<u8>, EncodeError> {
1234        let bytes = CompositionGraphEncoder::new(self).encode(options)?;
1235
1236        if options.validate {
1237            Validator::new_with_features(WasmFeatures::all())
1238                .validate_all(&bytes)
1239                .map_err(|e| EncodeError::ValidationFailure { source: e })?;
1240        }
1241
1242        Ok(bytes)
1243    }
1244
1245    /// Decodes a composition graph from the bytes of a WebAssembly component.
1246    pub fn decode(_data: &[u8]) -> Result<CompositionGraph, DecodeError> {
1247        todo!("decoding of composition graphs is not yet implemented")
1248    }
1249
1250    fn alloc_package(&mut self, package: wac_types::Package) -> PackageId {
1251        let (index, entry) = if let Some(index) = self.free_packages.pop() {
1252            let entry = &mut self.packages[index];
1253            assert!(entry.package.is_none());
1254            (index, entry)
1255        } else {
1256            let index = self.packages.len();
1257            self.packages.push(RegisteredPackage::new(0));
1258            (index, &mut self.packages[index])
1259        };
1260
1261        entry.package = Some(package);
1262
1263        PackageId {
1264            index,
1265            generation: entry.generation,
1266        }
1267    }
1268
1269    /// Yields an iterator over the resolved imports (both implicit and explicit) of the graph.
1270    ///
1271    /// The iterator returns the name, the `ItemKind`, and an optional node id for explicit imports.
1272    pub fn imports(&self) -> impl Iterator<Item = (&str, ItemKind, Option<NodeId>)> {
1273        let mut imports = Vec::new();
1274        for index in self.graph.node_indices() {
1275            let node = &self.graph[index];
1276            if !matches!(node.kind, NodeKind::Instantiation(_)) {
1277                continue;
1278            }
1279
1280            let package = &self[node.package.unwrap()];
1281            let world = &self.types[package.ty()];
1282
1283            let unsatisfied_args = world
1284                .imports
1285                .iter()
1286                .enumerate()
1287                .filter(|(i, _)| !node.is_arg_satisfied(*i));
1288
1289            // Go through the unsatisfied arguments and import them
1290            for (_, (name, item_kind)) in unsatisfied_args {
1291                imports.push((name.as_str(), *item_kind, None));
1292            }
1293        }
1294
1295        for n in self.node_ids() {
1296            let node = &self.graph[n.0];
1297            if let NodeKind::Import(name) = &node.kind {
1298                imports.push((name.as_str(), node.item_kind, Some(n)));
1299            }
1300        }
1301        imports.into_iter()
1302    }
1303}
1304
1305impl Index<NodeId> for CompositionGraph {
1306    type Output = Node;
1307
1308    fn index(&self, index: NodeId) -> &Self::Output {
1309        &self.graph[index.0]
1310    }
1311}
1312
1313impl Index<PackageId> for CompositionGraph {
1314    type Output = Package;
1315
1316    fn index(&self, index: PackageId) -> &Self::Output {
1317        let PackageId { index, generation } = index;
1318        let entry = self.packages.get(index).expect("invalid package id");
1319        if entry.generation != generation {
1320            panic!("invalid package id");
1321        }
1322
1323        entry.package.as_ref().unwrap()
1324    }
1325}
1326
1327impl fmt::Debug for CompositionGraph {
1328    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1329        let node_attr = |_, (i, node): (_, &Node)| {
1330            let label = match &node.kind {
1331                NodeKind::Definition => format!(
1332                    r#"type definition \"{name}\""#,
1333                    name = node.export.as_ref().unwrap()
1334                ),
1335                NodeKind::Import(name) => format!(r#"import \"{name}\""#),
1336                NodeKind::Instantiation(_) => {
1337                    let package = &self[node.package.unwrap()];
1338                    format!(r#"instantiation of package \"{key}\""#, key = package.key())
1339                }
1340                NodeKind::Alias => {
1341                    let (_, source) = self.get_alias_source(NodeId(i)).unwrap();
1342                    format!(r#"alias of export \"{source}\""#)
1343                }
1344            };
1345
1346            let mut desc = String::new();
1347            write!(
1348                &mut desc,
1349                r#"label = "{label}"; kind = "{kind}""#,
1350                kind = node.item_kind.desc(&self.types)
1351            )
1352            .unwrap();
1353
1354            if let Some(export) = &node.export {
1355                write!(&mut desc, r#"; export = "{export}""#).unwrap();
1356            }
1357
1358            desc
1359        };
1360
1361        let dot = Dot::with_attr_getters(
1362            &self.graph,
1363            &[Config::NodeNoLabel],
1364            &|_, _| String::new(),
1365            &node_attr,
1366        );
1367
1368        write!(f, "{:?}", dot)
1369    }
1370}
1371
1372#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1373/// Information about the tool that processed the graph.
1374pub struct Processor<'a> {
1375    /// The name of the tool that processed the graph.
1376    pub name: &'a str,
1377    /// The version of the tool that processed the graph.
1378    pub version: &'a str,
1379}
1380
1381/// The options for encoding a composition graph.
1382#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1383pub struct EncodeOptions<'a> {
1384    /// Whether or not to define instantiated components.
1385    ///
1386    /// If `false`, components will be imported instead.
1387    ///
1388    /// Defaults to `true`.
1389    pub define_components: bool,
1390
1391    /// Whether or not to validate the encoded output.
1392    ///
1393    /// Defaults to `true`.
1394    pub validate: bool,
1395
1396    /// Information about the processor of the composition graph.
1397    pub processor: Option<Processor<'a>>,
1398}
1399
1400impl Default for EncodeOptions<'_> {
1401    fn default() -> Self {
1402        Self {
1403            define_components: true,
1404            validate: true,
1405            processor: None,
1406        }
1407    }
1408}
1409
1410/// Used to encode a composition graph as a new WebAssembly component.
1411struct CompositionGraphEncoder<'a>(&'a CompositionGraph);
1412
1413impl<'a> CompositionGraphEncoder<'a> {
1414    fn new(graph: &'a CompositionGraph) -> Self {
1415        Self(graph)
1416    }
1417
1418    fn encode(self, options: EncodeOptions) -> Result<Vec<u8>, EncodeError> {
1419        let mut state = State::new();
1420
1421        // Separate import nodes from other nodes keeping topological order
1422        let (import_nodes, other_nodes) = self
1423            .toposort()
1424            .map_err(|n| EncodeError::GraphContainsCycle { node: NodeId(n) })?
1425            .into_iter()
1426            .partition::<Vec<_>, _>(|index| {
1427                let node = &self.0.graph[*index];
1428                matches!(node.kind, NodeKind::Import(_))
1429            });
1430
1431        // First populate the state with both implicit instantiation arguments and explicit imports
1432        self.encode_imports(&mut state, import_nodes)?;
1433
1434        // Encode non-import nodes in the graph in topographical order
1435        for n in other_nodes {
1436            let node = &self.0.graph[n];
1437            let index = match &node.kind {
1438                NodeKind::Definition => self.definition(&mut state, node),
1439                NodeKind::Instantiation(_) => self.instantiation(&mut state, n, node, options),
1440                NodeKind::Alias => self.alias(&mut state, n),
1441                // `other_nodes` does not contain any import nodes
1442                NodeKind::Import(_) => unreachable!(),
1443            };
1444
1445            let prev = state.node_indexes.insert(n, index);
1446            assert!(prev.is_none());
1447        }
1448
1449        // Encode the exports, skipping any definitions as they've
1450        // already been exported
1451        for (name, node) in self
1452            .0
1453            .exports
1454            .iter()
1455            .filter(|(_, n)| !matches!(self.0.graph[**n].kind, NodeKind::Definition))
1456        {
1457            let index = state.node_indexes[node];
1458            let node = &self.0.graph[*node];
1459            state
1460                .builder()
1461                .export(name, node.item_kind.into(), index, None);
1462        }
1463
1464        let mut builder = std::mem::take(state.builder());
1465        self.encode_names(&state, &mut builder);
1466
1467        if let Some(processor) = &options.processor {
1468            let mut section = wasm_metadata::Producers::empty();
1469            section.add("processed-by", processor.name, processor.version);
1470            builder.raw_custom_section(&section.raw_custom_section());
1471        }
1472
1473        Ok(builder.finish())
1474    }
1475
1476    /// Performs a toposort of the composition graph.
1477    ///
1478    /// This differs from `toposort` in `petgraph` in that the
1479    /// nodes are iterated in *reverse order*, resulting in the
1480    /// returned topologically-sorted set to be in index order for
1481    /// independent nodes.
1482    fn toposort(&self) -> Result<Vec<NodeIndex>, NodeIndex> {
1483        let graph = &self.0.graph;
1484        let mut dfs = Dfs::empty(graph);
1485        dfs.reset(graph);
1486        let mut finished = graph.visit_map();
1487
1488        let mut finish_stack = Vec::new();
1489        for i in graph.node_identifiers().rev() {
1490            if dfs.discovered.is_visited(&i) {
1491                continue;
1492            }
1493            dfs.stack.push(i);
1494            while let Some(&nx) = dfs.stack.last() {
1495                if dfs.discovered.visit(nx) {
1496                    // First time visiting `nx`: Push neighbors, don't pop `nx`
1497                    for succ in graph.neighbors(nx) {
1498                        if succ == nx {
1499                            // self cycle
1500                            return Err(nx);
1501                        }
1502                        if !dfs.discovered.is_visited(&succ) {
1503                            dfs.stack.push(succ);
1504                        }
1505                    }
1506                } else {
1507                    dfs.stack.pop();
1508                    if finished.visit(nx) {
1509                        // Second time: All reachable nodes must have been finished
1510                        finish_stack.push(nx);
1511                    }
1512                }
1513            }
1514        }
1515        finish_stack.reverse();
1516
1517        dfs.reset(graph);
1518        for &i in &finish_stack {
1519            dfs.move_to(i);
1520            let mut cycle = false;
1521            while let Some(j) = dfs.next(Reversed(graph)) {
1522                if cycle {
1523                    return Err(j);
1524                }
1525                cycle = true;
1526            }
1527        }
1528
1529        Ok(finish_stack)
1530    }
1531
1532    /// Encode both implicit and explicit imports.
1533    ///
1534    /// `import_nodes` are expected to only be `NodeKind::Import` nodes.
1535    fn encode_imports(
1536        &self,
1537        state: &mut State,
1538        import_nodes: Vec<NodeIndex>,
1539    ) -> Result<(), EncodeError> {
1540        let mut explicit_imports = HashMap::new();
1541        let mut implicit_imports = Vec::new();
1542        let aggregator =
1543            self.resolve_imports(import_nodes, &mut implicit_imports, &mut explicit_imports)?;
1544
1545        let mut encoded = HashMap::new();
1546
1547        // Next encode the imports
1548        for (name, kind) in aggregator.imports() {
1549            log::debug!("import `{name}` is being imported");
1550            let index = self.import(state, name, aggregator.types(), kind);
1551            encoded.insert(name, (kind.into(), index));
1552        }
1553
1554        // Populate the implicit argument map
1555        for (name, node) in implicit_imports {
1556            let canonical = aggregator.canonical_import_name(name);
1557            let (kind, index) = encoded[canonical];
1558            state
1559                .implicit_args
1560                .entry(node)
1561                .or_default()
1562                .push((name.to_owned(), kind, index));
1563        }
1564
1565        // Finally, populate the node indexes with the encoded explicit imports
1566        for (name, node_index) in explicit_imports {
1567            let canonical = aggregator.canonical_import_name(name);
1568            let (_, encoded_index) = encoded[canonical];
1569            state.node_indexes.insert(node_index, encoded_index);
1570        }
1571
1572        Ok(())
1573    }
1574
1575    /// Resolves the imports (both implicit and explicit) of the given nodes.
1576    ///
1577    /// Populates hashmaps that map the implicit and explicit import nodes to their import names.
1578    /// Returns a type aggregator that contains the resolved types of the imports.
1579    fn resolve_imports(
1580        &'a self,
1581        import_nodes: Vec<NodeIndex>,
1582        implicit_imports: &mut Vec<(&'a str, NodeIndex)>,
1583        explicit_imports: &mut HashMap<&'a str, NodeIndex>,
1584    ) -> Result<TypeAggregator, EncodeError> {
1585        let mut instantiations = HashMap::new();
1586        let mut aggregator = TypeAggregator::default();
1587        let mut cache = Default::default();
1588        let mut checker = SubtypeChecker::new(&mut cache);
1589
1590        log::debug!("populating implicit imports");
1591
1592        // Enumerate the instantiation nodes and populate the import types
1593        for index in self.0.graph.node_indices() {
1594            let node = &self.0.graph[index];
1595            if !matches!(node.kind, NodeKind::Instantiation(_)) {
1596                continue;
1597            }
1598
1599            let package = &self.0[node.package.unwrap()];
1600            let world = &self.0.types[package.ty()];
1601
1602            let unsatisfied_args = world
1603                .imports
1604                .iter()
1605                .enumerate()
1606                .filter(|(i, _)| !node.is_arg_satisfied(*i));
1607
1608            // Go through the unsatisfied arguments and import them
1609            for (_, (name, kind)) in unsatisfied_args {
1610                if let Some(import) = self.0.imports.get(name).copied() {
1611                    return Err(EncodeError::ImplicitImportConflict {
1612                        import: NodeId(import),
1613                        instantiation: NodeId(index),
1614                        package: PackageKey::new(package),
1615                        name: name.to_string(),
1616                    });
1617                }
1618
1619                instantiations.entry(name).or_insert(index);
1620
1621                aggregator = aggregator
1622                    .aggregate(name, &self.0.types, *kind, &mut checker)
1623                    .map_err(|e| EncodeError::ImportTypeMergeConflict {
1624                        import: name.clone(),
1625                        first: NodeId(instantiations[&name]),
1626                        second: NodeId(index),
1627                        source: e,
1628                    })?;
1629                implicit_imports.push((name, index));
1630            }
1631        }
1632
1633        log::debug!("populating explicit imports");
1634
1635        for n in import_nodes {
1636            let node = &self.0.graph[n];
1637            if let NodeKind::Import(name) = &node.kind {
1638                explicit_imports.insert(name.as_str(), n);
1639                aggregator = aggregator
1640                    .aggregate(name, self.0.types(), node.item_kind, &mut checker)
1641                    .unwrap();
1642            }
1643        }
1644        Ok(aggregator)
1645    }
1646
1647    fn definition(&self, state: &mut State, node: &Node) -> u32 {
1648        let name = node.export.as_deref().unwrap();
1649
1650        log::debug!(
1651            "encoding definition for {kind} `{name}`",
1652            kind = node.item_kind.desc(&self.0.types)
1653        );
1654
1655        // Check to see if the type is already
1656        let encoder = TypeEncoder::new(&self.0.types);
1657        let (ty, index) = match node.item_kind {
1658            ItemKind::Type(ty) => match ty {
1659                Type::Resource(_) => panic!("resources cannot be defined"),
1660                Type::Func(_) => (ty, encoder.ty(state, ty, None)),
1661                Type::Value(vt) => {
1662                    // Check for an alias and use the existing index
1663                    if let ValueType::Defined(id) = vt {
1664                        if let DefinedType::Alias(aliased @ ValueType::Defined(_)) =
1665                            &self.0.types()[id]
1666                        {
1667                            (ty, state.current.type_indexes[&Type::Value(*aliased)])
1668                        } else {
1669                            (ty, encoder.ty(state, ty, None))
1670                        }
1671                    } else {
1672                        (ty, encoder.ty(state, ty, None))
1673                    }
1674                }
1675                Type::Interface(id) => (ty, encoder.interface(state, id)),
1676                Type::World(id) => (ty, encoder.world(state, id)),
1677                Type::Module(_) => (ty, encoder.ty(state, ty, None)),
1678            },
1679            _ => panic!("only types can be defined"),
1680        };
1681
1682        let index = state
1683            .builder()
1684            .export(name, ComponentExportKind::Type, index, None);
1685
1686        // Remap to the exported index
1687        state.current.type_indexes.insert(ty, index);
1688
1689        log::debug!("definition `{name}` encoded to type index {index}");
1690        index
1691    }
1692
1693    fn import(&self, state: &mut State, name: &str, types: &Types, kind: ItemKind) -> u32 {
1694        // Check to see if this is an import of an interface that's already been
1695        // imported; this can happen based on importing of shared dependencies
1696        if let ItemKind::Instance(id) = kind {
1697            if let Some(id) = &types[id].id {
1698                if let Some(index) = state.current.instances.get(id) {
1699                    return *index;
1700                }
1701            }
1702        }
1703
1704        // Defer to special handling if the item being imported is a resource
1705        let encoder = TypeEncoder::new(types);
1706        if let ItemKind::Type(Type::Resource(id)) = kind {
1707            return encoder.import_resource(state, name, id);
1708        }
1709
1710        log::debug!(
1711            "encoding import of {kind} `{name}`",
1712            kind = kind.desc(types)
1713        );
1714
1715        // Encode the type and import
1716        let ty = encoder.ty(state, kind.ty(), None);
1717        let index = state.builder().import(
1718            name,
1719            match kind {
1720                ItemKind::Type(_) => ComponentTypeRef::Type(TypeBounds::Eq(ty)),
1721                ItemKind::Func(_) => ComponentTypeRef::Func(ty),
1722                ItemKind::Instance(_) => ComponentTypeRef::Instance(ty),
1723                ItemKind::Component(_) => ComponentTypeRef::Component(ty),
1724                ItemKind::Module(_) => ComponentTypeRef::Module(ty),
1725                ItemKind::Value(_) => ComponentTypeRef::Value(ComponentValType::Type(ty)),
1726            },
1727        );
1728
1729        log::debug!(
1730            "import `{name}` encoded to {desc} index {index}",
1731            desc = kind.desc(types)
1732        );
1733
1734        match kind {
1735            ItemKind::Type(ty) => {
1736                // Remap to the imported index
1737                state.current.type_indexes.insert(ty, index);
1738            }
1739            ItemKind::Instance(id) => {
1740                if let Some(id) = &types[id].id {
1741                    log::debug!(
1742                        "interface `{id}` is available for aliasing as instance index {index}"
1743                    );
1744                    state.current.instances.insert(id.clone(), index);
1745                }
1746            }
1747            _ => {}
1748        }
1749
1750        index
1751    }
1752
1753    fn instantiation(
1754        &self,
1755        state: &mut State,
1756        index: NodeIndex,
1757        node: &Node,
1758        options: EncodeOptions,
1759    ) -> u32 {
1760        let package_id = node.package.expect("instantiation requires a package");
1761        let package = self.0.packages[package_id.index].package.as_ref().unwrap();
1762        let imports = &self.0.types[package.ty()].imports;
1763
1764        let component_index = if let Some(index) = state.packages.get(&package_id) {
1765            *index
1766        } else {
1767            let index = if options.define_components {
1768                state.builder().component_raw(None, package.bytes())
1769            } else {
1770                let encoder = TypeEncoder::new(&self.0.types);
1771                let ty = encoder.component(state, package.ty());
1772                state.builder().import(
1773                    &Self::package_import_name(package),
1774                    ComponentTypeRef::Component(ty),
1775                )
1776            };
1777
1778            state.packages.insert(package_id, index);
1779            index
1780        };
1781
1782        let mut arguments = Vec::with_capacity(imports.len());
1783        arguments.extend(
1784            self.0
1785                .graph
1786                .edges_directed(index, Direction::Incoming)
1787                .map(|e| {
1788                    let kind = self.0.graph[e.source()].item_kind.into();
1789                    let index = state.node_indexes[&e.source()];
1790                    match e.weight() {
1791                        Edge::Alias(_) | Edge::Dependency => {
1792                            panic!("unexpected edge for an instantiation")
1793                        }
1794                        Edge::Argument(i) => (
1795                            Cow::Borrowed(imports.get_index(*i).unwrap().0.as_str()),
1796                            kind,
1797                            index,
1798                        ),
1799                    }
1800                }),
1801        );
1802
1803        if let Some(implicit) = state.implicit_args.remove(&index) {
1804            arguments.extend(implicit.into_iter().map(|(n, k, i)| (n.into(), k, i)));
1805        }
1806
1807        log::debug!(
1808            "instantiating package `{package}` with arguments: {arguments:?}",
1809            package = package.name(),
1810        );
1811
1812        let index = state
1813            .builder()
1814            .instantiate(None, component_index, arguments);
1815
1816        log::debug!(
1817            "instantiation of package `{package}` encoded to instance index {index}",
1818            package = package.name(),
1819        );
1820
1821        index
1822    }
1823
1824    fn alias(&self, state: &mut State, node: NodeIndex) -> u32 {
1825        let (source, export) = self
1826            .0
1827            .get_alias_source(NodeId(node))
1828            .expect("alias should have a source");
1829
1830        let source_node = &self.0[source];
1831        let exports = match source_node.item_kind {
1832            ItemKind::Instance(id) => &self.0.types[id].exports,
1833            _ => panic!("expected the source of an alias to be an instance"),
1834        };
1835
1836        let kind = exports[export];
1837        let instance = state.node_indexes[&source.0];
1838
1839        log::debug!(
1840            "encoding alias for {kind} export `{export}` of instance index {instance}",
1841            kind = kind.desc(&self.0.types),
1842        );
1843
1844        let index = state.builder().alias(
1845            None,
1846            Alias::InstanceExport {
1847                instance,
1848                kind: kind.into(),
1849                name: export,
1850            },
1851        );
1852
1853        log::debug!(
1854            "alias of export `{export}` encoded to {kind} index {index}",
1855            kind = kind.desc(&self.0.types)
1856        );
1857        index
1858    }
1859
1860    fn package_import_name(package: &Package) -> String {
1861        let mut name = String::new();
1862
1863        write!(&mut name, "unlocked-dep=<{name}", name = package.name()).unwrap();
1864        if let Some(version) = package.version() {
1865            write!(&mut name, "@{{>={version}}}", version = version).unwrap();
1866        }
1867
1868        write!(&mut name, ">").unwrap();
1869        name
1870    }
1871
1872    fn encode_names(&self, state: &State, encoded: &mut ComponentBuilder) {
1873        let mut names = ComponentNameSection::new();
1874        let mut types = NameMap::new();
1875        let mut funcs = NameMap::new();
1876        let mut instances = NameMap::new();
1877        let mut components = NameMap::new();
1878        let mut modules = NameMap::new();
1879        let mut values = NameMap::new();
1880
1881        for index in self.0.graph.node_indices() {
1882            let node = &self.0.graph[index];
1883            if let Some(name) = &node.name {
1884                let map = match node.item_kind {
1885                    ItemKind::Type(_) => &mut types,
1886                    ItemKind::Func(_) => &mut funcs,
1887                    ItemKind::Instance(_) => &mut instances,
1888                    ItemKind::Component(_) => &mut components,
1889                    ItemKind::Module(_) => &mut modules,
1890                    ItemKind::Value(_) => &mut values,
1891                };
1892
1893                let index = state.node_indexes[&index];
1894                map.append(index, name)
1895            }
1896        }
1897
1898        if !types.is_empty() {
1899            names.types(&types);
1900        }
1901
1902        if !funcs.is_empty() {
1903            names.funcs(&funcs);
1904        }
1905
1906        if !instances.is_empty() {
1907            names.instances(&instances);
1908        }
1909
1910        if !components.is_empty() {
1911            names.components(&components);
1912        }
1913
1914        if !modules.is_empty() {
1915            names.core_modules(&modules);
1916        }
1917
1918        if !values.is_empty() {
1919            names.values(&values);
1920        }
1921
1922        if !names.is_empty() {
1923            encoded.custom_section(&names.as_custom());
1924        }
1925    }
1926}
1927
1928#[cfg(test)]
1929mod test {
1930    use super::*;
1931    use wac_types::{DefinedType, PrimitiveType, Resource, ValueType};
1932
1933    #[test]
1934    fn it_adds_a_type_definition() {
1935        let mut graph = CompositionGraph::new();
1936        let id = graph
1937            .types_mut()
1938            .add_defined_type(DefinedType::Alias(ValueType::Primitive(
1939                PrimitiveType::Bool,
1940            )));
1941        assert!(graph
1942            .define_type("foo", Type::Value(ValueType::Defined(id)))
1943            .is_ok());
1944    }
1945
1946    #[test]
1947    fn it_cannot_define_a_resource() {
1948        let mut graph = CompositionGraph::new();
1949        let id = graph.types_mut().add_resource(Resource {
1950            name: "a".to_string(),
1951            alias: None,
1952        });
1953        assert!(matches!(
1954            graph.define_type("foo", Type::Resource(id)).unwrap_err(),
1955            DefineTypeError::CannotDefineResource
1956        ));
1957    }
1958
1959    #[test]
1960    fn it_must_export_a_type_definition() {
1961        let mut graph = CompositionGraph::new();
1962        let id = graph
1963            .types_mut()
1964            .add_defined_type(DefinedType::Alias(ValueType::Primitive(PrimitiveType::S32)));
1965        let id = graph
1966            .define_type("foo", Type::Value(ValueType::Defined(id)))
1967            .unwrap();
1968        assert!(matches!(
1969            graph.unexport(id).unwrap_err(),
1970            UnexportError::MustExportDefinition
1971        ));
1972    }
1973
1974    #[test]
1975    fn it_can_remove_a_type_definition() {
1976        let mut graph = CompositionGraph::new();
1977        let ty_id = graph
1978            .types_mut()
1979            .add_defined_type(DefinedType::Alias(ValueType::Primitive(PrimitiveType::S32)));
1980        let node_id = graph
1981            .define_type("foo", Type::Value(ValueType::Defined(ty_id)))
1982            .unwrap();
1983
1984        // Definition nodes store their name in `export`, not `name`.
1985        // Removing a definition node should not panic.
1986        graph.remove_node(node_id);
1987
1988        // Verify the definition and export were cleaned up
1989        assert!(graph.get_export("foo").is_none());
1990    }
1991    #[test]
1992    fn it_cleans_up_exports_on_unregister_package() {
1993        let mut graph = CompositionGraph::new();
1994        let bytes = wat::parse_str(
1995            r#"(component
1996                (import "f" (func))
1997                (export "g" (func 0))
1998            )"#,
1999        )
2000        .unwrap();
2001
2002        let package =
2003            wac_types::Package::from_bytes("test:pkg", None, bytes, graph.types_mut()).unwrap();
2004        let pkg_id = graph.register_package(package).unwrap();
2005
2006        // Create an instantiation and export an alias
2007        let inst_id = graph.instantiate(pkg_id);
2008        let alias_id = graph.alias_instance_export(inst_id, "g").unwrap();
2009        graph.export(alias_id, "g").unwrap();
2010
2011        assert!(graph.get_export("g").is_some());
2012
2013        // Unregistering the package should clean up exports
2014        graph.unregister_package(pkg_id);
2015        assert!(
2016            graph.get_export("g").is_none(),
2017            "exports should be cleaned up after unregister_package"
2018        );
2019    }
2020}