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        // Instances first, so a later type import can alias a type from an
1548        // instance export instead of re-encoding it as a local definition.
1549        let (instances, rest): (Vec<_>, Vec<_>) = aggregator
1550            .imports()
1551            .partition(|(_, kind)| matches!(kind, ItemKind::Instance(_)));
1552        for (name, kind) in instances.into_iter().chain(rest) {
1553            log::debug!("import `{name}` is being imported");
1554            let index = self.import(state, name, aggregator.types(), kind);
1555            encoded.insert(name, (kind.into(), index));
1556        }
1557
1558        // Populate the implicit argument map
1559        for (name, node) in implicit_imports {
1560            let canonical = aggregator.canonical_import_name(name);
1561            let (kind, index) = encoded[canonical];
1562            state
1563                .implicit_args
1564                .entry(node)
1565                .or_default()
1566                .push((name.to_owned(), kind, index));
1567        }
1568
1569        // Finally, populate the node indexes with the encoded explicit imports
1570        for (name, node_index) in explicit_imports {
1571            let canonical = aggregator.canonical_import_name(name);
1572            let (_, encoded_index) = encoded[canonical];
1573            state.node_indexes.insert(node_index, encoded_index);
1574        }
1575
1576        Ok(())
1577    }
1578
1579    /// Resolves the imports (both implicit and explicit) of the given nodes.
1580    ///
1581    /// Populates hashmaps that map the implicit and explicit import nodes to their import names.
1582    /// Returns a type aggregator that contains the resolved types of the imports.
1583    fn resolve_imports(
1584        &'a self,
1585        import_nodes: Vec<NodeIndex>,
1586        implicit_imports: &mut Vec<(&'a str, NodeIndex)>,
1587        explicit_imports: &mut HashMap<&'a str, NodeIndex>,
1588    ) -> Result<TypeAggregator, EncodeError> {
1589        let mut instantiations = HashMap::new();
1590        let mut aggregator = TypeAggregator::default();
1591        let mut cache = Default::default();
1592        let mut checker = SubtypeChecker::new(&mut cache);
1593
1594        log::debug!("populating implicit imports");
1595
1596        // Enumerate the instantiation nodes and populate the import types
1597        for index in self.0.graph.node_indices() {
1598            let node = &self.0.graph[index];
1599            if !matches!(node.kind, NodeKind::Instantiation(_)) {
1600                continue;
1601            }
1602
1603            let package = &self.0[node.package.unwrap()];
1604            let world = &self.0.types[package.ty()];
1605
1606            let unsatisfied_args = world
1607                .imports
1608                .iter()
1609                .enumerate()
1610                .filter(|(i, _)| !node.is_arg_satisfied(*i));
1611
1612            // Go through the unsatisfied arguments and import them
1613            for (_, (name, kind)) in unsatisfied_args {
1614                if let Some(import) = self.0.imports.get(name).copied() {
1615                    return Err(EncodeError::ImplicitImportConflict {
1616                        import: NodeId(import),
1617                        instantiation: NodeId(index),
1618                        package: PackageKey::new(package),
1619                        name: name.to_string(),
1620                    });
1621                }
1622
1623                instantiations.entry(name).or_insert(index);
1624
1625                aggregator = aggregator
1626                    .aggregate(name, &self.0.types, *kind, &mut checker)
1627                    .map_err(|e| EncodeError::ImportTypeMergeConflict {
1628                        import: name.clone(),
1629                        first: NodeId(instantiations[&name]),
1630                        second: NodeId(index),
1631                        source: e,
1632                    })?;
1633                implicit_imports.push((name, index));
1634            }
1635        }
1636
1637        log::debug!("populating explicit imports");
1638
1639        for n in import_nodes {
1640            let node = &self.0.graph[n];
1641            if let NodeKind::Import(name) = &node.kind {
1642                explicit_imports.insert(name.as_str(), n);
1643                aggregator = aggregator
1644                    .aggregate(name, self.0.types(), node.item_kind, &mut checker)
1645                    .unwrap();
1646            }
1647        }
1648        Ok(aggregator)
1649    }
1650
1651    fn definition(&self, state: &mut State, node: &Node) -> u32 {
1652        let name = node.export.as_deref().unwrap();
1653
1654        log::debug!(
1655            "encoding definition for {kind} `{name}`",
1656            kind = node.item_kind.desc(&self.0.types)
1657        );
1658
1659        // Check to see if the type is already
1660        let encoder = TypeEncoder::new(&self.0.types);
1661        let (ty, index) = match node.item_kind {
1662            ItemKind::Type(ty) => match ty {
1663                Type::Resource(_) => panic!("resources cannot be defined"),
1664                Type::Func(_) => (ty, encoder.ty(state, ty, None)),
1665                Type::Value(vt) => {
1666                    // Check for an alias and use the existing index
1667                    if let ValueType::Defined(id) = vt {
1668                        if let DefinedType::Alias(aliased @ ValueType::Defined(_)) =
1669                            &self.0.types()[id]
1670                        {
1671                            (ty, state.current.type_indexes[&Type::Value(*aliased)])
1672                        } else {
1673                            (ty, encoder.ty(state, ty, None))
1674                        }
1675                    } else {
1676                        (ty, encoder.ty(state, ty, None))
1677                    }
1678                }
1679                Type::Interface(id) => (ty, encoder.interface(state, id)),
1680                Type::World(id) => (ty, encoder.world(state, id)),
1681                Type::Module(_) => (ty, encoder.ty(state, ty, None)),
1682            },
1683            _ => panic!("only types can be defined"),
1684        };
1685
1686        let index = state
1687            .builder()
1688            .export(name, ComponentExportKind::Type, index, None);
1689
1690        // Remap to the exported index
1691        state.current.type_indexes.insert(ty, index);
1692
1693        log::debug!("definition `{name}` encoded to type index {index}");
1694        index
1695    }
1696
1697    fn import(&self, state: &mut State, name: &str, types: &Types, kind: ItemKind) -> u32 {
1698        // Check to see if this is an import of an interface that's already been
1699        // imported; this can happen based on importing of shared dependencies
1700        if let ItemKind::Instance(id) = kind {
1701            if let Some(id) = &types[id].id {
1702                if let Some(index) = state.current.instances.get(id) {
1703                    return *index;
1704                }
1705            }
1706        }
1707
1708        // Defer to special handling if the item being imported is a resource
1709        let encoder = TypeEncoder::new(types);
1710        if let ItemKind::Type(Type::Resource(id)) = kind {
1711            return encoder.import_resource(state, name, id);
1712        }
1713
1714        log::debug!(
1715            "encoding import of {kind} `{name}`",
1716            kind = kind.desc(types)
1717        );
1718
1719        // Encode the type and import
1720        let ty = encoder.ty(state, kind.ty(), None);
1721        let index = state.builder().import(
1722            name,
1723            match kind {
1724                ItemKind::Type(_) => ComponentTypeRef::Type(TypeBounds::Eq(ty)),
1725                ItemKind::Func(_) => ComponentTypeRef::Func(ty),
1726                ItemKind::Instance(_) => ComponentTypeRef::Instance(ty),
1727                ItemKind::Component(_) => ComponentTypeRef::Component(ty),
1728                ItemKind::Module(_) => ComponentTypeRef::Module(ty),
1729                ItemKind::Value(_) => ComponentTypeRef::Value(ComponentValType::Type(ty)),
1730            },
1731        );
1732
1733        log::debug!(
1734            "import `{name}` encoded to {desc} index {index}",
1735            desc = kind.desc(types)
1736        );
1737
1738        match kind {
1739            ItemKind::Type(ty) => {
1740                // Remap to the imported index
1741                state.current.type_indexes.insert(ty, index);
1742            }
1743            ItemKind::Instance(id) => {
1744                if let Some(id) = &types[id].id {
1745                    log::debug!(
1746                        "interface `{id}` is available for aliasing as instance index {index}"
1747                    );
1748                    state.current.instances.insert(id.clone(), index);
1749                }
1750            }
1751            _ => {}
1752        }
1753
1754        index
1755    }
1756
1757    fn instantiation(
1758        &self,
1759        state: &mut State,
1760        index: NodeIndex,
1761        node: &Node,
1762        options: EncodeOptions,
1763    ) -> u32 {
1764        let package_id = node.package.expect("instantiation requires a package");
1765        let package = self.0.packages[package_id.index].package.as_ref().unwrap();
1766        let imports = &self.0.types[package.ty()].imports;
1767
1768        let component_index = if let Some(index) = state.packages.get(&package_id) {
1769            *index
1770        } else {
1771            let index = if options.define_components {
1772                state.builder().component_raw(None, package.bytes())
1773            } else {
1774                let encoder = TypeEncoder::new(&self.0.types);
1775                let ty = encoder.component(state, package.ty());
1776                state.builder().import(
1777                    &Self::package_import_name(package),
1778                    ComponentTypeRef::Component(ty),
1779                )
1780            };
1781
1782            state.packages.insert(package_id, index);
1783            index
1784        };
1785
1786        let mut arguments = Vec::with_capacity(imports.len());
1787        arguments.extend(
1788            self.0
1789                .graph
1790                .edges_directed(index, Direction::Incoming)
1791                .map(|e| {
1792                    let kind = self.0.graph[e.source()].item_kind.into();
1793                    let index = state.node_indexes[&e.source()];
1794                    match e.weight() {
1795                        Edge::Alias(_) | Edge::Dependency => {
1796                            panic!("unexpected edge for an instantiation")
1797                        }
1798                        Edge::Argument(i) => (
1799                            Cow::Borrowed(imports.get_index(*i).unwrap().0.as_str()),
1800                            kind,
1801                            index,
1802                        ),
1803                    }
1804                }),
1805        );
1806
1807        if let Some(implicit) = state.implicit_args.remove(&index) {
1808            arguments.extend(implicit.into_iter().map(|(n, k, i)| (n.into(), k, i)));
1809        }
1810
1811        log::debug!(
1812            "instantiating package `{package}` with arguments: {arguments:?}",
1813            package = package.name(),
1814        );
1815
1816        let index = state
1817            .builder()
1818            .instantiate(None, component_index, arguments);
1819
1820        log::debug!(
1821            "instantiation of package `{package}` encoded to instance index {index}",
1822            package = package.name(),
1823        );
1824
1825        index
1826    }
1827
1828    fn alias(&self, state: &mut State, node: NodeIndex) -> u32 {
1829        let (source, export) = self
1830            .0
1831            .get_alias_source(NodeId(node))
1832            .expect("alias should have a source");
1833
1834        let source_node = &self.0[source];
1835        let exports = match source_node.item_kind {
1836            ItemKind::Instance(id) => &self.0.types[id].exports,
1837            _ => panic!("expected the source of an alias to be an instance"),
1838        };
1839
1840        let kind = exports[export];
1841        let instance = state.node_indexes[&source.0];
1842
1843        log::debug!(
1844            "encoding alias for {kind} export `{export}` of instance index {instance}",
1845            kind = kind.desc(&self.0.types),
1846        );
1847
1848        let index = state.builder().alias(
1849            None,
1850            Alias::InstanceExport {
1851                instance,
1852                kind: kind.into(),
1853                name: export,
1854            },
1855        );
1856
1857        log::debug!(
1858            "alias of export `{export}` encoded to {kind} index {index}",
1859            kind = kind.desc(&self.0.types)
1860        );
1861        index
1862    }
1863
1864    fn package_import_name(package: &Package) -> String {
1865        let mut name = String::new();
1866
1867        write!(&mut name, "unlocked-dep=<{name}", name = package.name()).unwrap();
1868        if let Some(version) = package.version() {
1869            write!(&mut name, "@{{>={version}}}", version = version).unwrap();
1870        }
1871
1872        write!(&mut name, ">").unwrap();
1873        name
1874    }
1875
1876    fn encode_names(&self, state: &State, encoded: &mut ComponentBuilder) {
1877        let mut names = ComponentNameSection::new();
1878        let mut types = NameMap::new();
1879        let mut funcs = NameMap::new();
1880        let mut instances = NameMap::new();
1881        let mut components = NameMap::new();
1882        let mut modules = NameMap::new();
1883        let mut values = NameMap::new();
1884
1885        for index in self.0.graph.node_indices() {
1886            let node = &self.0.graph[index];
1887            if let Some(name) = &node.name {
1888                let map = match node.item_kind {
1889                    ItemKind::Type(_) => &mut types,
1890                    ItemKind::Func(_) => &mut funcs,
1891                    ItemKind::Instance(_) => &mut instances,
1892                    ItemKind::Component(_) => &mut components,
1893                    ItemKind::Module(_) => &mut modules,
1894                    ItemKind::Value(_) => &mut values,
1895                };
1896
1897                let index = state.node_indexes[&index];
1898                map.append(index, name)
1899            }
1900        }
1901
1902        if !types.is_empty() {
1903            names.types(&types);
1904        }
1905
1906        if !funcs.is_empty() {
1907            names.funcs(&funcs);
1908        }
1909
1910        if !instances.is_empty() {
1911            names.instances(&instances);
1912        }
1913
1914        if !components.is_empty() {
1915            names.components(&components);
1916        }
1917
1918        if !modules.is_empty() {
1919            names.core_modules(&modules);
1920        }
1921
1922        if !values.is_empty() {
1923            names.values(&values);
1924        }
1925
1926        if !names.is_empty() {
1927            encoded.custom_section(&names.as_custom());
1928        }
1929    }
1930}
1931
1932#[cfg(test)]
1933mod test {
1934    use super::*;
1935    use wac_types::{DefinedType, PrimitiveType, Resource, ValueType};
1936
1937    #[test]
1938    fn it_adds_a_type_definition() {
1939        let mut graph = CompositionGraph::new();
1940        let id = graph
1941            .types_mut()
1942            .add_defined_type(DefinedType::Alias(ValueType::Primitive(
1943                PrimitiveType::Bool,
1944            )));
1945        assert!(graph
1946            .define_type("foo", Type::Value(ValueType::Defined(id)))
1947            .is_ok());
1948    }
1949
1950    #[test]
1951    fn it_cannot_define_a_resource() {
1952        let mut graph = CompositionGraph::new();
1953        let id = graph.types_mut().add_resource(Resource {
1954            name: "a".to_string(),
1955            alias: None,
1956        });
1957        assert!(matches!(
1958            graph.define_type("foo", Type::Resource(id)).unwrap_err(),
1959            DefineTypeError::CannotDefineResource
1960        ));
1961    }
1962
1963    #[test]
1964    fn it_must_export_a_type_definition() {
1965        let mut graph = CompositionGraph::new();
1966        let id = graph
1967            .types_mut()
1968            .add_defined_type(DefinedType::Alias(ValueType::Primitive(PrimitiveType::S32)));
1969        let id = graph
1970            .define_type("foo", Type::Value(ValueType::Defined(id)))
1971            .unwrap();
1972        assert!(matches!(
1973            graph.unexport(id).unwrap_err(),
1974            UnexportError::MustExportDefinition
1975        ));
1976    }
1977
1978    #[test]
1979    fn it_can_remove_a_type_definition() {
1980        let mut graph = CompositionGraph::new();
1981        let ty_id = graph
1982            .types_mut()
1983            .add_defined_type(DefinedType::Alias(ValueType::Primitive(PrimitiveType::S32)));
1984        let node_id = graph
1985            .define_type("foo", Type::Value(ValueType::Defined(ty_id)))
1986            .unwrap();
1987
1988        // Definition nodes store their name in `export`, not `name`.
1989        // Removing a definition node should not panic.
1990        graph.remove_node(node_id);
1991
1992        // Verify the definition and export were cleaned up
1993        assert!(graph.get_export("foo").is_none());
1994    }
1995    #[test]
1996    fn it_cleans_up_exports_on_unregister_package() {
1997        let mut graph = CompositionGraph::new();
1998        let bytes = wat::parse_str(
1999            r#"(component
2000                (import "f" (func))
2001                (export "g" (func 0))
2002            )"#,
2003        )
2004        .unwrap();
2005
2006        let package =
2007            wac_types::Package::from_bytes("test:pkg", None, bytes, graph.types_mut()).unwrap();
2008        let pkg_id = graph.register_package(package).unwrap();
2009
2010        // Create an instantiation and export an alias
2011        let inst_id = graph.instantiate(pkg_id);
2012        let alias_id = graph.alias_instance_export(inst_id, "g").unwrap();
2013        graph.export(alias_id, "g").unwrap();
2014
2015        assert!(graph.get_export("g").is_some());
2016
2017        // Unregistering the package should clean up exports
2018        graph.unregister_package(pkg_id);
2019        assert!(
2020            graph.get_export("g").is_none(),
2021            "exports should be cleaned up after unregister_package"
2022        );
2023    }
2024}