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 all nodes associated with the package
547        self.graph
548            .retain_nodes(|g, i| g[i].package != Some(package));
549
550        // Remove the package from the package map
551        let entry = &mut self.packages[package.index];
552        let key = entry.package.as_ref().unwrap().key();
553        log::debug!("unregistering package `{key}` with the graph");
554        let prev = self.package_map.remove(&key as &dyn BorrowedKey);
555        assert!(
556            prev.is_some(),
557            "package should exist in the package map (this is a bug)"
558        );
559
560        // Finally free the package
561        *entry = RegisteredPackage::new(entry.generation.wrapping_add(1));
562        self.free_packages.push(package.index);
563    }
564
565    /// Gets the registered package of the given package name and version.
566    ///
567    /// Returns `None` if a package with the specified name and version has
568    /// not been registered with the graph.
569    pub fn get_package_by_name(
570        &self,
571        name: &str,
572        version: Option<&Version>,
573    ) -> Option<(PackageId, &wac_types::Package)> {
574        let key: BorrowedPackageKey<'_> = BorrowedPackageKey::from_name_and_version(name, version);
575        let id = self.package_map.get(&key as &dyn BorrowedKey)?;
576        Some((*id, self.packages[id.index].package.as_ref().unwrap()))
577    }
578
579    /// Adds a *type definition node* to the graph.
580    ///
581    /// The graph must not already have a node exported with the same name.
582    ///
583    /// This method will implicitly add dependency edges to other defined
584    /// types.
585    ///
586    /// If the provided type has already been defined, the previous node
587    /// will be returned and an additional export name will be associated
588    /// with the node.
589    ///
590    /// # Panics
591    ///
592    /// This method panics if the provided type is not contained within the
593    /// graph's types collection.
594    pub fn define_type(
595        &mut self,
596        name: impl Into<String>,
597        ty: Type,
598    ) -> Result<NodeId, DefineTypeError> {
599        assert!(
600            self.types.contains(ty),
601            "type not contained in types collection"
602        );
603
604        if self.defined.contains_key(&ty) {
605            return Err(DefineTypeError::TypeAlreadyDefined);
606        }
607
608        if let Type::Resource(_) = ty {
609            return Err(DefineTypeError::CannotDefineResource);
610        }
611
612        let name = name.into();
613        if self.exports.contains_key(&name) {
614            return Err(DefineTypeError::ExportConflict { name });
615        }
616
617        // Ensure that the given name is a valid extern name
618        ComponentName::new(&name, 0).map_err(|e| {
619            let msg = e.to_string();
620            DefineTypeError::InvalidExternName {
621                name: name.to_string(),
622                source: anyhow::anyhow!(
623                    "{msg}",
624                    msg = msg.strip_suffix(" (at offset 0x0)").unwrap_or(&msg)
625                ),
626            }
627        })?;
628
629        let mut node = Node::new(NodeKind::Definition, ItemKind::Type(ty), None);
630        node.export = Some(name.clone());
631        let index = self.graph.add_node(node);
632        log::debug!(
633            "adding type definition `{name}` to the graph as node index {index}",
634            index = index.index()
635        );
636
637        // Add dependency edges between the given type and any referenced defined types
638        ty.visit_defined_types(&self.types, &mut |_, id| {
639            let dep_ty = Type::Value(ValueType::Defined(id));
640            if dep_ty != ty {
641                if let Some(dep) = self.defined.get(&dep_ty) {
642                    if !self
643                        .graph
644                        .edges_connecting(*dep, index)
645                        .any(|e| matches!(e.weight(), Edge::Dependency))
646                    {
647                        log::debug!(
648                            "adding dependency edge from type `{from}` (dependency) to type `{name}` (dependent)",
649                            from = self.graph[*dep].export.as_ref().unwrap()
650                        );
651                        self.graph.add_edge(*dep, index, Edge::Dependency);
652                    }
653                }
654            }
655
656            Ok(())
657        })?;
658
659        // Add dependency edges to any existing defined types that reference this one
660        for (other_ty, other) in &self.defined {
661            other_ty.visit_defined_types(&self.types, &mut |_, id| {
662                let dep_ty = Type::Value(ValueType::Defined(id));
663                if dep_ty == ty
664                    && !self
665                        .graph
666                        .edges_connecting(index, *other)
667                        .any(|e| matches!(e.weight(), Edge::Dependency))
668                {
669                    log::debug!(
670                        "adding dependency edge from type `{name}` (dependency) to type `{to}` (dependent)",
671                        to = self.graph[index].export.as_ref().unwrap(),
672                    );
673                    self.graph.add_edge(index, *other, Edge::Dependency);
674                }
675
676                Ok(())
677            })?;
678        }
679
680        self.defined.insert(ty, index);
681        let prev = self.exports.insert(name, index);
682        assert!(prev.is_none());
683        Ok(NodeId(index))
684    }
685
686    /// Adds an *import node* to the graph.
687    ///
688    /// If the provided import name is invalid or if an import already exists
689    /// with the same name, an error is returned.
690    ///
691    /// # Panics
692    ///
693    /// This method panics if the provided kind is not contained within the
694    /// graph's types collection.
695    pub fn import(
696        &mut self,
697        name: impl Into<String>,
698        kind: ItemKind,
699    ) -> Result<NodeId, ImportError> {
700        assert!(
701            self.types.contains(kind.ty()),
702            "provided type is not in the types collection"
703        );
704
705        let name = name.into();
706        if let Some(existing) = self.imports.get(&name) {
707            return Err(ImportError::ImportAlreadyExists {
708                name,
709                node: NodeId(*existing),
710            });
711        }
712
713        // Ensure that the given import name is a valid extern name
714        ComponentName::new(&name, 0).map_err(|e| {
715            let msg = e.to_string();
716            ImportError::InvalidImportName {
717                name: name.to_string(),
718                source: anyhow::anyhow!(
719                    "{msg}",
720                    msg = msg.strip_suffix(" (at offset 0x0)").unwrap_or(&msg)
721                ),
722            }
723        })?;
724
725        let node = Node::new(NodeKind::Import(name.clone()), kind, None);
726        let index = self.graph.add_node(node);
727        log::debug!(
728            "adding import `{name}` to the graph as node index {index}",
729            index = index.index()
730        );
731        let prev = self.imports.insert(name, index);
732        assert!(prev.is_none());
733        Ok(NodeId(index))
734    }
735
736    /// Gets the name used by an import node.
737    ///
738    /// Returns `None` if the specified node is not an import node.
739    ///
740    /// # Panics
741    ///
742    /// Panics if the specified node id is invalid.
743    pub fn get_import_name(&self, node: NodeId) -> Option<&str> {
744        let node = self.graph.node_weight(node.0).expect("invalid node id");
745        match &node.kind {
746            NodeKind::Import(name) => Some(name),
747            _ => None,
748        }
749    }
750
751    /// Adds an *instantiation node* to the graph.
752    ///
753    /// Initially the instantiation will have no satisfied arguments.
754    ///
755    /// Use `set_instantiation_argument` to set an argument on an instantiation node.
756    ///
757    /// # Panics
758    ///
759    /// This method panics if the provided package id is invalid.
760    pub fn instantiate(&mut self, package: PackageId) -> NodeId {
761        let pkg = &self[package];
762        let node = Node::new(
763            NodeKind::Instantiation(Default::default()),
764            ItemKind::Instance(pkg.instance_type()),
765            Some(package),
766        );
767        let index = self.graph.add_node(node);
768        log::debug!(
769            "adding instantiation of package `{key}` to the graph as node index {index}",
770            key = self[package].key(),
771            index = index.index()
772        );
773        NodeId(index)
774    }
775
776    /// Adds an *alias node* to the graph.
777    ///
778    /// The provided node must be an instance and the export name must match an export
779    /// of the instance.
780    ///
781    /// If an alias already exists for the export, the existing alias node will be returned.
782    ///
783    /// An implicit *alias edge* will be added from the instance to the alias node.
784    ///
785    /// # Panics
786    ///
787    /// Panics if the provided node id is invalid.
788    pub fn alias_instance_export(
789        &mut self,
790        instance: NodeId,
791        export: &str,
792    ) -> Result<NodeId, AliasError> {
793        let instance_node = self.graph.node_weight(instance.0).expect("invalid node id");
794
795        // Ensure the source is an instance
796        let exports = match instance_node.item_kind {
797            ItemKind::Instance(id) => &self.types[id].exports,
798            _ => {
799                return Err(AliasError::NodeIsNotAnInstance {
800                    node: instance,
801                    kind: instance_node.item_kind.desc(&self.types).to_string(),
802                });
803            }
804        };
805
806        // Ensure the export exists
807        let (index, _, kind) =
808            exports
809                .get_full(export)
810                .ok_or_else(|| AliasError::InstanceMissingExport {
811                    node: instance,
812                    export: export.to_string(),
813                })?;
814
815        // Check to see if there already is an edge for this alias
816        for e in self.graph.edges_directed(instance.0, Direction::Outgoing) {
817            assert_eq!(e.source(), instance.0);
818            if let Edge::Alias(i) = e.weight() {
819                if *i == index {
820                    return Ok(NodeId(e.target()));
821                }
822            }
823        }
824
825        // Allocate the alias node
826        let node = Node::new(NodeKind::Alias, *kind, instance_node.package);
827        let node_index = self.graph.add_node(node);
828        log::debug!(
829            "adding alias for export `{export}` to the graph as node index {index}",
830            index = node_index.index()
831        );
832        self.graph
833            .add_edge(instance.0, node_index, Edge::Alias(index));
834        Ok(NodeId(node_index))
835    }
836
837    /// Gets the source node and export name of an alias node.
838    ///
839    /// Returns `None` if the node is not an alias.
840    pub fn get_alias_source(&self, alias: NodeId) -> Option<(NodeId, &str)> {
841        for e in self.graph.edges_directed(alias.0, Direction::Incoming) {
842            assert_eq!(e.target(), alias.0);
843
844            if let Edge::Alias(index) = e.weight() {
845                match self.graph[e.source()].item_kind {
846                    ItemKind::Instance(id) => {
847                        let export = self.types[id].exports.get_index(*index).unwrap().0;
848                        return Some((NodeId(e.source()), export));
849                    }
850                    _ => panic!("alias source should be an instance"),
851                }
852            }
853        }
854
855        None
856    }
857
858    /// Gets the satisfied arguments of an instantiation node.
859    ///
860    /// Returns an iterator over the argument names and the node id used to satisfy the argument.
861    ///
862    /// If the node identifier is invalid or if the node is not an instantiation node, an
863    /// empty iterator is returned.
864    pub fn get_instantiation_arguments(
865        &self,
866        instantiation: NodeId,
867    ) -> impl Iterator<Item = (&str, NodeId)> {
868        self.graph
869            .edges_directed(instantiation.0, Direction::Incoming)
870            .filter_map(|e| {
871                let target = &self.graph[e.target()];
872                let imports = match target.kind {
873                    NodeKind::Instantiation(_) => {
874                        let package = &self.packages[target.package?.index].package.as_ref()?;
875                        &self.types[package.ty()].imports
876                    }
877                    _ => return None,
878                };
879
880                match e.weight() {
881                    Edge::Alias(_) | Edge::Dependency => None,
882                    Edge::Argument(i) => Some((
883                        imports.get_index(*i).unwrap().0.as_ref(),
884                        NodeId(e.source()),
885                    )),
886                }
887            })
888    }
889
890    /// Sets the name of a node in the graph.
891    ///
892    /// Node names are recorded in a `names` custom section when the graph is encoded.
893    ///
894    /// # Panics
895    ///
896    /// This method panics if the provided node id is invalid.
897    pub fn set_node_name(&mut self, node: NodeId, name: impl Into<String>) {
898        let node = &mut self.graph[node.0];
899        node.name = Some(name.into());
900    }
901
902    /// Marks the given node for export when the composition graph is encoded.
903    ///
904    /// Returns an error if the provided export name is invalid.
905    ///
906    /// # Panics
907    ///
908    /// This method panics if the provided node id is invalid.
909    pub fn export(&mut self, node: NodeId, name: impl Into<String>) -> Result<(), ExportError> {
910        let name = name.into();
911        if let Some(existing) = self.exports.get(&name) {
912            return Err(ExportError::ExportAlreadyExists {
913                name,
914                node: NodeId(*existing),
915            });
916        }
917
918        let map_reader_err = |e: BinaryReaderError| {
919            let msg = e.to_string();
920            ExportError::InvalidExportName {
921                name: name.to_string(),
922                source: anyhow::anyhow!(
923                    "{msg}",
924                    msg = msg.strip_suffix(" (at offset 0x0)").unwrap_or(&msg)
925                ),
926            }
927        };
928
929        // Ensure that the given export name is a valid extern name
930        match ComponentName::new(&name, 0).map_err(map_reader_err)?.kind() {
931            ComponentNameKind::Hash(_)
932            | ComponentNameKind::Url(_)
933            | ComponentNameKind::Dependency(_) => {
934                return Err(ExportError::InvalidExportName {
935                    name: name.to_string(),
936                    source: anyhow::anyhow!("export name cannot be a hash, url, or dependency"),
937                });
938            }
939            _ => {}
940        };
941
942        log::debug!("exporting node {index} as `{name}`", index = node.0.index());
943        self.graph[node.0].export = Some(name.clone());
944        let prev = self.exports.insert(name, node.0);
945        assert!(prev.is_none());
946        Ok(())
947    }
948
949    /// Gets the node being exported by the given name.
950    ///
951    /// Returns `None` if there is no node exported by that name.
952    pub fn get_export(&self, name: &str) -> Option<NodeId> {
953        self.exports.get(name).map(|i| NodeId(*i))
954    }
955
956    /// Unmarks the given node from being exported from an encoding of the graph.
957    ///
958    /// Returns an error if the given node is a type definition, as type
959    /// definitions must be exported.
960    ///
961    /// # Panics
962    ///
963    /// This method panics if the provided node id is invalid.
964    pub fn unexport(&mut self, node: NodeId) -> Result<(), UnexportError> {
965        let node = &mut self.graph[node.0];
966        if let NodeKind::Definition = node.kind {
967            return Err(UnexportError::MustExportDefinition);
968        }
969
970        if let Some(name) = node.export.take() {
971            log::debug!("unmarked node for export as `{name}`");
972            let removed = self.exports.swap_remove(&name);
973            assert!(removed.is_some());
974        }
975
976        Ok(())
977    }
978
979    /// Removes a node from the graph.
980    ///
981    /// All incoming and outgoing edges of the node are also removed.
982    ///
983    /// If the node has dependent defined types, the dependent define
984    /// types are also removed.
985    ///
986    /// If the node has aliases, the aliased nodes are also removed.
987    ///
988    /// # Panics
989    ///
990    /// This method panics if the provided node id is invalid.
991    pub fn remove_node(&mut self, node: NodeId) {
992        // Recursively remove any dependent nodes
993        for node in self
994            .graph
995            .edges_directed(node.0, Direction::Outgoing)
996            .filter_map(|e| {
997                assert_eq!(e.source(), node.0);
998                match e.weight() {
999                    Edge::Alias(_) | Edge::Dependency => Some(NodeId(e.target())),
1000                    Edge::Argument(_) => None,
1001                }
1002            })
1003            .collect::<Vec<_>>()
1004        {
1005            self.remove_node(node);
1006        }
1007
1008        // Remove the node from the graph
1009        log::debug!(
1010            "removing node {index} from the graph",
1011            index = node.0.index()
1012        );
1013        let node = self.graph.remove_node(node.0).expect("invalid node id");
1014
1015        // Remove any import entry
1016        if let Some(name) = node.import_name() {
1017            log::debug!("removing import node `{name}`");
1018            let removed = self.imports.remove(name);
1019            assert!(removed.is_some());
1020        }
1021
1022        // Remove any export entry
1023        if let Some(name) = &node.export {
1024            log::debug!("removing export of node as `{name}`");
1025            let removed = self.exports.swap_remove(name);
1026            assert!(removed.is_some());
1027        }
1028
1029        if let NodeKind::Definition = node.kind {
1030            log::debug!(
1031                "removing type definition `{name}`",
1032                name = node.name.as_ref().unwrap()
1033            );
1034            let removed = self.defined.remove(&node.item_kind.ty());
1035            assert!(removed.is_some());
1036        }
1037    }
1038
1039    /// Sets an argument of an instantiation node to the provided argument
1040    /// node.
1041    ///
1042    /// This method adds an _instantiation argument_ edge from the argument
1043    /// node to the instantiation node.
1044    ///
1045    /// The provided instantiation node must be an instantiation.
1046    ///
1047    /// The argument name must be a valid import on the instantiation node
1048    /// and not already have an incoming edge from a different argument node.
1049    ///
1050    /// The argument node must be type-compatible with the argument of the
1051    /// instantiation node.
1052    ///
1053    /// If an edge already exists between the argument and the instantiation
1054    /// node, this method returns `Ok(_)`.
1055    ///
1056    /// # Panics
1057    ///
1058    /// This method will panic if the provided node ids are invalid.
1059    pub fn set_instantiation_argument(
1060        &mut self,
1061        instantiation: NodeId,
1062        argument_name: &str,
1063        argument: NodeId,
1064    ) -> Result<(), InstantiationArgumentError> {
1065        fn add_edge(
1066            graph: &mut CompositionGraph,
1067            argument: NodeId,
1068            instantiation: NodeId,
1069            argument_name: &str,
1070            cache: &mut HashSet<(ItemKind, ItemKind)>,
1071        ) -> Result<(), InstantiationArgumentError> {
1072            // Ensure the target is an instantiation node
1073            let instantiation_node = &graph.graph[instantiation.0];
1074
1075            if !matches!(instantiation_node.kind, NodeKind::Instantiation(_)) {
1076                return Err(InstantiationArgumentError::NodeIsNotAnInstantiation {
1077                    node: instantiation,
1078                });
1079            }
1080
1081            // Ensure the argument is a valid import of the target package
1082            let package = graph.packages[instantiation_node.package.unwrap().index]
1083                .package
1084                .as_ref()
1085                .unwrap();
1086            let package_type = &graph.types[package.ty()];
1087
1088            // Ensure the argument isn't already satisfied
1089            let (argument_index, _, expected_argument_kind) = package_type
1090                .imports
1091                .get_full(argument_name)
1092                .ok_or(InstantiationArgumentError::InvalidArgumentName {
1093                    node: instantiation,
1094                    name: argument_name.to_string(),
1095                    package: package.name().to_string(),
1096                })?;
1097
1098            for e in graph
1099                .graph
1100                .edges_directed(instantiation.0, Direction::Incoming)
1101            {
1102                assert_eq!(e.target(), instantiation.0);
1103                match e.weight() {
1104                    Edge::Alias(_) | Edge::Dependency => {
1105                        panic!("unexpected edge for an instantiation")
1106                    }
1107                    Edge::Argument(i) => {
1108                        if *i == argument_index {
1109                            if e.source() == argument.0 {
1110                                return Ok(());
1111                            }
1112
1113                            return Err(InstantiationArgumentError::ArgumentAlreadyPassed {
1114                                node: instantiation,
1115                                name: argument_name.to_string(),
1116                            });
1117                        }
1118                    }
1119                }
1120            }
1121
1122            // Perform a subtype check on the source and target
1123            let argument_node = &graph.graph[argument.0];
1124
1125            let mut checker = SubtypeChecker::new(cache);
1126            checker
1127                .is_subtype(
1128                    argument_node.item_kind,
1129                    &graph.types,
1130                    *expected_argument_kind,
1131                    &graph.types,
1132                )
1133                .map_err(|e| InstantiationArgumentError::ArgumentTypeMismatch {
1134                    name: argument_name.to_string(),
1135                    source: e,
1136                })?;
1137
1138            // Finally, insert the argument edge
1139            graph
1140                .graph
1141                .add_edge(argument.0, instantiation.0, Edge::Argument(argument_index));
1142
1143            graph.graph[instantiation.0].add_satisfied_arg(argument_index);
1144            Ok(())
1145        }
1146
1147        // Temporarily take ownership of the cache to avoid borrowing issues
1148        let mut cache = std::mem::take(&mut self.type_check_cache);
1149        let result = add_edge(self, argument, instantiation, argument_name, &mut cache);
1150        self.type_check_cache = cache;
1151        result
1152    }
1153
1154    /// Unsets an argument of an instantiation node that was previously
1155    /// set to the provided argument node.
1156    ///
1157    /// This method removes an _instantiation argument_ edge from the
1158    /// argument node to the instantiation node if the nodes are connected;
1159    /// if they are not connected, this method is a no-op.
1160    ///
1161    /// The provided instantiation node must be an instantiation.
1162    ///
1163    /// The argument name must be a valid import on the instantiation node.
1164    ///
1165    /// # Panics
1166    ///
1167    /// This method will panic if the provided node ids are invalid.
1168    pub fn unset_instantiation_argument(
1169        &mut self,
1170        instantiation: NodeId,
1171        argument_name: &str,
1172        argument: NodeId,
1173    ) -> Result<(), InstantiationArgumentError> {
1174        // Ensure the target is an instantiation node
1175        let instantiation_node = &self.graph[instantiation.0];
1176        if !matches!(instantiation_node.kind, NodeKind::Instantiation(_)) {
1177            return Err(InstantiationArgumentError::NodeIsNotAnInstantiation {
1178                node: instantiation,
1179            });
1180        }
1181
1182        // Ensure the argument is a valid import of the target package
1183        let package = self.packages[instantiation_node.package.unwrap().index]
1184            .package
1185            .as_ref()
1186            .unwrap();
1187        let package_type = &self.types[package.ty()];
1188
1189        let argument_index = package_type.imports.get_index_of(argument_name).ok_or(
1190            InstantiationArgumentError::InvalidArgumentName {
1191                node: instantiation,
1192                name: argument_name.to_string(),
1193                package: package.name().to_string(),
1194            },
1195        )?;
1196
1197        // Finally remove the argument edge if a connection exists
1198        let mut edge = None;
1199        for e in self.graph.edges_connecting(argument.0, instantiation.0) {
1200            match e.weight() {
1201                Edge::Alias(_) | Edge::Dependency => {
1202                    panic!("unexpected edge for an instantiation")
1203                }
1204                Edge::Argument(i) => {
1205                    if *i == argument_index {
1206                        edge = Some(e.id());
1207                        break;
1208                    }
1209                }
1210            }
1211        }
1212
1213        if let Some(edge) = edge {
1214            self.graph[instantiation.0].remove_satisfied_arg(argument_index);
1215            self.graph.remove_edge(edge);
1216        }
1217
1218        Ok(())
1219    }
1220
1221    /// Encodes the composition graph as a new WebAssembly component.
1222    ///
1223    /// An error will be returned if the graph contains a dependency cycle.
1224    pub fn encode(&self, options: EncodeOptions) -> Result<Vec<u8>, EncodeError> {
1225        let bytes = CompositionGraphEncoder::new(self).encode(options)?;
1226
1227        if options.validate {
1228            Validator::new_with_features(WasmFeatures::all())
1229                .validate_all(&bytes)
1230                .map_err(|e| EncodeError::ValidationFailure { source: e })?;
1231        }
1232
1233        Ok(bytes)
1234    }
1235
1236    /// Decodes a composition graph from the bytes of a WebAssembly component.
1237    pub fn decode(_data: &[u8]) -> Result<CompositionGraph, DecodeError> {
1238        todo!("decoding of composition graphs is not yet implemented")
1239    }
1240
1241    fn alloc_package(&mut self, package: wac_types::Package) -> PackageId {
1242        let (index, entry) = if let Some(index) = self.free_packages.pop() {
1243            let entry = &mut self.packages[index];
1244            assert!(entry.package.is_none());
1245            (index, entry)
1246        } else {
1247            let index = self.packages.len();
1248            self.packages.push(RegisteredPackage::new(0));
1249            (index, &mut self.packages[index])
1250        };
1251
1252        entry.package = Some(package);
1253
1254        PackageId {
1255            index,
1256            generation: entry.generation,
1257        }
1258    }
1259
1260    /// Yields an iterator over the resolved imports (both implicit and explicit) of the graph.
1261    ///
1262    /// The iterator returns the name, the `ItemKind`, and an optional node id for explicit imports.
1263    pub fn imports(&self) -> impl Iterator<Item = (&str, ItemKind, Option<NodeId>)> {
1264        let mut imports = Vec::new();
1265        for index in self.graph.node_indices() {
1266            let node = &self.graph[index];
1267            if !matches!(node.kind, NodeKind::Instantiation(_)) {
1268                continue;
1269            }
1270
1271            let package = &self[node.package.unwrap()];
1272            let world = &self.types[package.ty()];
1273
1274            let unsatisfied_args = world
1275                .imports
1276                .iter()
1277                .enumerate()
1278                .filter(|(i, _)| !node.is_arg_satisfied(*i));
1279
1280            // Go through the unsatisfied arguments and import them
1281            for (_, (name, item_kind)) in unsatisfied_args {
1282                imports.push((name.as_str(), *item_kind, None));
1283            }
1284        }
1285
1286        for n in self.node_ids() {
1287            let node = &self.graph[n.0];
1288            if let NodeKind::Import(name) = &node.kind {
1289                imports.push((name.as_str(), node.item_kind, Some(n)));
1290            }
1291        }
1292        imports.into_iter()
1293    }
1294}
1295
1296impl Index<NodeId> for CompositionGraph {
1297    type Output = Node;
1298
1299    fn index(&self, index: NodeId) -> &Self::Output {
1300        &self.graph[index.0]
1301    }
1302}
1303
1304impl Index<PackageId> for CompositionGraph {
1305    type Output = Package;
1306
1307    fn index(&self, index: PackageId) -> &Self::Output {
1308        let PackageId { index, generation } = index;
1309        let entry = self.packages.get(index).expect("invalid package id");
1310        if entry.generation != generation {
1311            panic!("invalid package id");
1312        }
1313
1314        entry.package.as_ref().unwrap()
1315    }
1316}
1317
1318impl fmt::Debug for CompositionGraph {
1319    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1320        let node_attr = |_, (i, node): (_, &Node)| {
1321            let label = match &node.kind {
1322                NodeKind::Definition => format!(
1323                    r#"type definition \"{name}\""#,
1324                    name = node.export.as_ref().unwrap()
1325                ),
1326                NodeKind::Import(name) => format!(r#"import \"{name}\""#),
1327                NodeKind::Instantiation(_) => {
1328                    let package = &self[node.package.unwrap()];
1329                    format!(r#"instantiation of package \"{key}\""#, key = package.key())
1330                }
1331                NodeKind::Alias => {
1332                    let (_, source) = self.get_alias_source(NodeId(i)).unwrap();
1333                    format!(r#"alias of export \"{source}\""#)
1334                }
1335            };
1336
1337            let mut desc = String::new();
1338            write!(
1339                &mut desc,
1340                r#"label = "{label}"; kind = "{kind}""#,
1341                kind = node.item_kind.desc(&self.types)
1342            )
1343            .unwrap();
1344
1345            if let Some(export) = &node.export {
1346                write!(&mut desc, r#"; export = "{export}""#).unwrap();
1347            }
1348
1349            desc
1350        };
1351
1352        let dot = Dot::with_attr_getters(
1353            &self.graph,
1354            &[Config::NodeNoLabel],
1355            &|_, _| String::new(),
1356            &node_attr,
1357        );
1358
1359        write!(f, "{:?}", dot)
1360    }
1361}
1362
1363#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1364/// Information about the tool that processed the graph.
1365pub struct Processor<'a> {
1366    /// The name of the tool that processed the graph.
1367    pub name: &'a str,
1368    /// The version of the tool that processed the graph.
1369    pub version: &'a str,
1370}
1371
1372/// The options for encoding a composition graph.
1373#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1374pub struct EncodeOptions<'a> {
1375    /// Whether or not to define instantiated components.
1376    ///
1377    /// If `false`, components will be imported instead.
1378    ///
1379    /// Defaults to `true`.
1380    pub define_components: bool,
1381
1382    /// Whether or not to validate the encoded output.
1383    ///
1384    /// Defaults to `true`.
1385    pub validate: bool,
1386
1387    /// Information about the processor of the composition graph.
1388    pub processor: Option<Processor<'a>>,
1389}
1390
1391impl Default for EncodeOptions<'_> {
1392    fn default() -> Self {
1393        Self {
1394            define_components: true,
1395            validate: true,
1396            processor: None,
1397        }
1398    }
1399}
1400
1401/// Used to encode a composition graph as a new WebAssembly component.
1402struct CompositionGraphEncoder<'a>(&'a CompositionGraph);
1403
1404impl<'a> CompositionGraphEncoder<'a> {
1405    fn new(graph: &'a CompositionGraph) -> Self {
1406        Self(graph)
1407    }
1408
1409    fn encode(self, options: EncodeOptions) -> Result<Vec<u8>, EncodeError> {
1410        let mut state = State::new();
1411
1412        // Separate import nodes from other nodes keeping topological order
1413        let (import_nodes, other_nodes) = self
1414            .toposort()
1415            .map_err(|n| EncodeError::GraphContainsCycle { node: NodeId(n) })?
1416            .into_iter()
1417            .partition::<Vec<_>, _>(|index| {
1418                let node = &self.0.graph[*index];
1419                matches!(node.kind, NodeKind::Import(_))
1420            });
1421
1422        // First populate the state with both implicit instantiation arguments and explicit imports
1423        self.encode_imports(&mut state, import_nodes)?;
1424
1425        // Encode non-import nodes in the graph in topographical order
1426        for n in other_nodes {
1427            let node = &self.0.graph[n];
1428            let index = match &node.kind {
1429                NodeKind::Definition => self.definition(&mut state, node),
1430                NodeKind::Instantiation(_) => self.instantiation(&mut state, n, node, options),
1431                NodeKind::Alias => self.alias(&mut state, n),
1432                // `other_nodes` does not contain any import nodes
1433                NodeKind::Import(_) => unreachable!(),
1434            };
1435
1436            let prev = state.node_indexes.insert(n, index);
1437            assert!(prev.is_none());
1438        }
1439
1440        // Encode the exports, skipping any definitions as they've
1441        // already been exported
1442        for (name, node) in self
1443            .0
1444            .exports
1445            .iter()
1446            .filter(|(_, n)| !matches!(self.0.graph[**n].kind, NodeKind::Definition))
1447        {
1448            let index = state.node_indexes[node];
1449            let node = &self.0.graph[*node];
1450            state
1451                .builder()
1452                .export(name, node.item_kind.into(), index, None);
1453        }
1454
1455        let mut builder = std::mem::take(state.builder());
1456        self.encode_names(&state, &mut builder);
1457
1458        if let Some(processor) = &options.processor {
1459            let mut section = wasm_metadata::Producers::empty();
1460            section.add("processed-by", processor.name, processor.version);
1461            builder.raw_custom_section(&section.raw_custom_section());
1462        }
1463
1464        Ok(builder.finish())
1465    }
1466
1467    /// Performs a toposort of the composition graph.
1468    ///
1469    /// This differs from `toposort` in `petgraph` in that the
1470    /// nodes are iterated in *reverse order*, resulting in the
1471    /// returned topologically-sorted set to be in index order for
1472    /// independent nodes.
1473    fn toposort(&self) -> Result<Vec<NodeIndex>, NodeIndex> {
1474        let graph = &self.0.graph;
1475        let mut dfs = Dfs::empty(graph);
1476        dfs.reset(graph);
1477        let mut finished = graph.visit_map();
1478
1479        let mut finish_stack = Vec::new();
1480        for i in graph.node_identifiers().rev() {
1481            if dfs.discovered.is_visited(&i) {
1482                continue;
1483            }
1484            dfs.stack.push(i);
1485            while let Some(&nx) = dfs.stack.last() {
1486                if dfs.discovered.visit(nx) {
1487                    // First time visiting `nx`: Push neighbors, don't pop `nx`
1488                    for succ in graph.neighbors(nx) {
1489                        if succ == nx {
1490                            // self cycle
1491                            return Err(nx);
1492                        }
1493                        if !dfs.discovered.is_visited(&succ) {
1494                            dfs.stack.push(succ);
1495                        }
1496                    }
1497                } else {
1498                    dfs.stack.pop();
1499                    if finished.visit(nx) {
1500                        // Second time: All reachable nodes must have been finished
1501                        finish_stack.push(nx);
1502                    }
1503                }
1504            }
1505        }
1506        finish_stack.reverse();
1507
1508        dfs.reset(graph);
1509        for &i in &finish_stack {
1510            dfs.move_to(i);
1511            let mut cycle = false;
1512            while let Some(j) = dfs.next(Reversed(graph)) {
1513                if cycle {
1514                    return Err(j);
1515                }
1516                cycle = true;
1517            }
1518        }
1519
1520        Ok(finish_stack)
1521    }
1522
1523    /// Encode both implicit and explicit imports.
1524    ///
1525    /// `import_nodes` are expected to only be `NodeKind::Import` nodes.
1526    fn encode_imports(
1527        &self,
1528        state: &mut State,
1529        import_nodes: Vec<NodeIndex>,
1530    ) -> Result<(), EncodeError> {
1531        let mut explicit_imports = HashMap::new();
1532        let mut implicit_imports = Vec::new();
1533        let aggregator =
1534            self.resolve_imports(import_nodes, &mut implicit_imports, &mut explicit_imports)?;
1535
1536        let mut encoded = HashMap::new();
1537
1538        // Next encode the imports
1539        for (name, kind) in aggregator.imports() {
1540            log::debug!("import `{name}` is being imported");
1541            let index = self.import(state, name, aggregator.types(), kind);
1542            encoded.insert(name, (kind.into(), index));
1543        }
1544
1545        // Populate the implicit argument map
1546        for (name, node) in implicit_imports {
1547            let (kind, index) = encoded[name];
1548            state
1549                .implicit_args
1550                .entry(node)
1551                .or_default()
1552                .push((name.to_owned(), kind, index));
1553        }
1554
1555        // Finally, populate the node indexes with the encoded explicit imports
1556        for (name, node_index) in explicit_imports {
1557            let (_, encoded_index) = encoded[name];
1558            state.node_indexes.insert(node_index, encoded_index);
1559        }
1560
1561        Ok(())
1562    }
1563
1564    /// Resolves the imports (both implicit and explicit) of the given nodes.
1565    ///
1566    /// Populates hashmaps that map the implicit and explicit import nodes to their import names.
1567    /// Returns a type aggregator that contains the resolved types of the imports.
1568    fn resolve_imports(
1569        &'a self,
1570        import_nodes: Vec<NodeIndex>,
1571        implicit_imports: &mut Vec<(&'a str, NodeIndex)>,
1572        explicit_imports: &mut HashMap<&'a str, NodeIndex>,
1573    ) -> Result<TypeAggregator, EncodeError> {
1574        let mut instantiations = HashMap::new();
1575        let mut aggregator = TypeAggregator::default();
1576        let mut cache = Default::default();
1577        let mut checker = SubtypeChecker::new(&mut cache);
1578
1579        log::debug!("populating implicit imports");
1580
1581        // Enumerate the instantiation nodes and populate the import types
1582        for index in self.0.graph.node_indices() {
1583            let node = &self.0.graph[index];
1584            if !matches!(node.kind, NodeKind::Instantiation(_)) {
1585                continue;
1586            }
1587
1588            let package = &self.0[node.package.unwrap()];
1589            let world = &self.0.types[package.ty()];
1590
1591            let unsatisfied_args = world
1592                .imports
1593                .iter()
1594                .enumerate()
1595                .filter(|(i, _)| !node.is_arg_satisfied(*i));
1596
1597            // Go through the unsatisfied arguments and import them
1598            for (_, (name, kind)) in unsatisfied_args {
1599                if let Some(import) = self.0.imports.get(name).copied() {
1600                    return Err(EncodeError::ImplicitImportConflict {
1601                        import: NodeId(import),
1602                        instantiation: NodeId(index),
1603                        package: PackageKey::new(package),
1604                        name: name.to_string(),
1605                    });
1606                }
1607
1608                instantiations.entry(name).or_insert(index);
1609
1610                aggregator = aggregator
1611                    .aggregate(name, &self.0.types, *kind, &mut checker)
1612                    .map_err(|e| EncodeError::ImportTypeMergeConflict {
1613                        import: name.clone(),
1614                        first: NodeId(instantiations[&name]),
1615                        second: NodeId(index),
1616                        source: e,
1617                    })?;
1618                implicit_imports.push((name, index));
1619            }
1620        }
1621
1622        log::debug!("populating explicit imports");
1623
1624        for n in import_nodes {
1625            let node = &self.0.graph[n];
1626            if let NodeKind::Import(name) = &node.kind {
1627                explicit_imports.insert(name.as_str(), n);
1628                aggregator = aggregator
1629                    .aggregate(name, self.0.types(), node.item_kind, &mut checker)
1630                    .unwrap();
1631            }
1632        }
1633        Ok(aggregator)
1634    }
1635
1636    fn definition(&self, state: &mut State, node: &Node) -> u32 {
1637        let name = node.export.as_deref().unwrap();
1638
1639        log::debug!(
1640            "encoding definition for {kind} `{name}`",
1641            kind = node.item_kind.desc(&self.0.types)
1642        );
1643
1644        // Check to see if the type is already
1645        let encoder = TypeEncoder::new(&self.0.types);
1646        let (ty, index) = match node.item_kind {
1647            ItemKind::Type(ty) => match ty {
1648                Type::Resource(_) => panic!("resources cannot be defined"),
1649                Type::Func(_) => (ty, encoder.ty(state, ty, None)),
1650                Type::Value(vt) => {
1651                    // Check for an alias and use the existing index
1652                    if let ValueType::Defined(id) = vt {
1653                        if let DefinedType::Alias(aliased @ ValueType::Defined(_)) =
1654                            &self.0.types()[id]
1655                        {
1656                            (ty, state.current.type_indexes[&Type::Value(*aliased)])
1657                        } else {
1658                            (ty, encoder.ty(state, ty, None))
1659                        }
1660                    } else {
1661                        (ty, encoder.ty(state, ty, None))
1662                    }
1663                }
1664                Type::Interface(id) => (ty, encoder.interface(state, id)),
1665                Type::World(id) => (ty, encoder.world(state, id)),
1666                Type::Module(_) => (ty, encoder.ty(state, ty, None)),
1667            },
1668            _ => panic!("only types can be defined"),
1669        };
1670
1671        let index = state
1672            .builder()
1673            .export(name, ComponentExportKind::Type, index, None);
1674
1675        // Remap to the exported index
1676        state.current.type_indexes.insert(ty, index);
1677
1678        log::debug!("definition `{name}` encoded to type index {index}");
1679        index
1680    }
1681
1682    fn import(&self, state: &mut State, name: &str, types: &Types, kind: ItemKind) -> u32 {
1683        // Check to see if this is an import of an interface that's already been
1684        // imported; this can happen based on importing of shared dependencies
1685        if let ItemKind::Instance(id) = kind {
1686            if let Some(id) = &types[id].id {
1687                if let Some(index) = state.current.instances.get(id) {
1688                    return *index;
1689                }
1690            }
1691        }
1692
1693        // Defer to special handling if the item being imported is a resource
1694        let encoder = TypeEncoder::new(types);
1695        if let ItemKind::Type(Type::Resource(id)) = kind {
1696            return encoder.import_resource(state, name, id);
1697        }
1698
1699        log::debug!(
1700            "encoding import of {kind} `{name}`",
1701            kind = kind.desc(types)
1702        );
1703
1704        // Encode the type and import
1705        let ty = encoder.ty(state, kind.ty(), None);
1706        let index = state.builder().import(
1707            name,
1708            match kind {
1709                ItemKind::Type(_) => ComponentTypeRef::Type(TypeBounds::Eq(ty)),
1710                ItemKind::Func(_) => ComponentTypeRef::Func(ty),
1711                ItemKind::Instance(_) => ComponentTypeRef::Instance(ty),
1712                ItemKind::Component(_) => ComponentTypeRef::Component(ty),
1713                ItemKind::Module(_) => ComponentTypeRef::Module(ty),
1714                ItemKind::Value(_) => ComponentTypeRef::Value(ComponentValType::Type(ty)),
1715            },
1716        );
1717
1718        log::debug!(
1719            "import `{name}` encoded to {desc} index {index}",
1720            desc = kind.desc(types)
1721        );
1722
1723        match kind {
1724            ItemKind::Type(ty) => {
1725                // Remap to the imported index
1726                state.current.type_indexes.insert(ty, index);
1727            }
1728            ItemKind::Instance(id) => {
1729                if let Some(id) = &types[id].id {
1730                    log::debug!(
1731                        "interface `{id}` is available for aliasing as instance index {index}"
1732                    );
1733                    state.current.instances.insert(id.clone(), index);
1734                }
1735            }
1736            _ => {}
1737        }
1738
1739        index
1740    }
1741
1742    fn instantiation(
1743        &self,
1744        state: &mut State,
1745        index: NodeIndex,
1746        node: &Node,
1747        options: EncodeOptions,
1748    ) -> u32 {
1749        let package_id = node.package.expect("instantiation requires a package");
1750        let package = self.0.packages[package_id.index].package.as_ref().unwrap();
1751        let imports = &self.0.types[package.ty()].imports;
1752
1753        let component_index = if let Some(index) = state.packages.get(&package_id) {
1754            *index
1755        } else {
1756            let index = if options.define_components {
1757                state.builder().component_raw(package.bytes())
1758            } else {
1759                let encoder = TypeEncoder::new(&self.0.types);
1760                let ty = encoder.component(state, package.ty());
1761                state.builder().import(
1762                    &Self::package_import_name(package),
1763                    ComponentTypeRef::Component(ty),
1764                )
1765            };
1766
1767            state.packages.insert(package_id, index);
1768            index
1769        };
1770
1771        let mut arguments = Vec::with_capacity(imports.len());
1772        arguments.extend(
1773            self.0
1774                .graph
1775                .edges_directed(index, Direction::Incoming)
1776                .map(|e| {
1777                    let kind = self.0.graph[e.source()].item_kind.into();
1778                    let index = state.node_indexes[&e.source()];
1779                    match e.weight() {
1780                        Edge::Alias(_) | Edge::Dependency => {
1781                            panic!("unexpected edge for an instantiation")
1782                        }
1783                        Edge::Argument(i) => (
1784                            Cow::Borrowed(imports.get_index(*i).unwrap().0.as_str()),
1785                            kind,
1786                            index,
1787                        ),
1788                    }
1789                }),
1790        );
1791
1792        if let Some(implicit) = state.implicit_args.remove(&index) {
1793            arguments.extend(implicit.into_iter().map(|(n, k, i)| (n.into(), k, i)));
1794        }
1795
1796        log::debug!(
1797            "instantiating package `{package}` with arguments: {arguments:?}",
1798            package = package.name(),
1799        );
1800
1801        let index = state.builder().instantiate(component_index, arguments);
1802
1803        log::debug!(
1804            "instantiation of package `{package}` encoded to instance index {index}",
1805            package = package.name(),
1806        );
1807
1808        index
1809    }
1810
1811    fn alias(&self, state: &mut State, node: NodeIndex) -> u32 {
1812        let (source, export) = self
1813            .0
1814            .get_alias_source(NodeId(node))
1815            .expect("alias should have a source");
1816
1817        let source_node = &self.0[source];
1818        let exports = match source_node.item_kind {
1819            ItemKind::Instance(id) => &self.0.types[id].exports,
1820            _ => panic!("expected the source of an alias to be an instance"),
1821        };
1822
1823        let kind = exports[export];
1824        let instance = state.node_indexes[&source.0];
1825
1826        log::debug!(
1827            "encoding alias for {kind} export `{export}` of instance index {instance}",
1828            kind = kind.desc(&self.0.types),
1829        );
1830
1831        let index = state.builder().alias(Alias::InstanceExport {
1832            instance,
1833            kind: kind.into(),
1834            name: export,
1835        });
1836
1837        log::debug!(
1838            "alias of export `{export}` encoded to {kind} index {index}",
1839            kind = kind.desc(&self.0.types)
1840        );
1841        index
1842    }
1843
1844    fn package_import_name(package: &Package) -> String {
1845        let mut name = String::new();
1846
1847        write!(&mut name, "unlocked-dep=<{name}", name = package.name()).unwrap();
1848        if let Some(version) = package.version() {
1849            write!(&mut name, "@{{>={version}}}", version = version).unwrap();
1850        }
1851
1852        write!(&mut name, ">").unwrap();
1853        name
1854    }
1855
1856    fn encode_names(&self, state: &State, encoded: &mut ComponentBuilder) {
1857        let mut names = ComponentNameSection::new();
1858        let mut types = NameMap::new();
1859        let mut funcs = NameMap::new();
1860        let mut instances = NameMap::new();
1861        let mut components = NameMap::new();
1862        let mut modules = NameMap::new();
1863        let mut values = NameMap::new();
1864
1865        for index in self.0.graph.node_indices() {
1866            let node = &self.0.graph[index];
1867            if let Some(name) = &node.name {
1868                let map = match node.item_kind {
1869                    ItemKind::Type(_) => &mut types,
1870                    ItemKind::Func(_) => &mut funcs,
1871                    ItemKind::Instance(_) => &mut instances,
1872                    ItemKind::Component(_) => &mut components,
1873                    ItemKind::Module(_) => &mut modules,
1874                    ItemKind::Value(_) => &mut values,
1875                };
1876
1877                let index = state.node_indexes[&index];
1878                map.append(index, name)
1879            }
1880        }
1881
1882        if !types.is_empty() {
1883            names.types(&types);
1884        }
1885
1886        if !funcs.is_empty() {
1887            names.funcs(&funcs);
1888        }
1889
1890        if !instances.is_empty() {
1891            names.instances(&instances);
1892        }
1893
1894        if !components.is_empty() {
1895            names.components(&components);
1896        }
1897
1898        if !modules.is_empty() {
1899            names.core_modules(&modules);
1900        }
1901
1902        if !values.is_empty() {
1903            names.values(&values);
1904        }
1905
1906        if !names.is_empty() {
1907            encoded.custom_section(&names.as_custom());
1908        }
1909    }
1910}
1911
1912#[cfg(test)]
1913mod test {
1914    use super::*;
1915    use wac_types::{DefinedType, PrimitiveType, Resource, ValueType};
1916
1917    #[test]
1918    fn it_adds_a_type_definition() {
1919        let mut graph = CompositionGraph::new();
1920        let id = graph
1921            .types_mut()
1922            .add_defined_type(DefinedType::Alias(ValueType::Primitive(
1923                PrimitiveType::Bool,
1924            )));
1925        assert!(graph
1926            .define_type("foo", Type::Value(ValueType::Defined(id)))
1927            .is_ok());
1928    }
1929
1930    #[test]
1931    fn it_cannot_define_a_resource() {
1932        let mut graph = CompositionGraph::new();
1933        let id = graph.types_mut().add_resource(Resource {
1934            name: "a".to_string(),
1935            alias: None,
1936        });
1937        assert!(matches!(
1938            graph.define_type("foo", Type::Resource(id)).unwrap_err(),
1939            DefineTypeError::CannotDefineResource
1940        ));
1941    }
1942
1943    #[test]
1944    fn it_must_export_a_type_definition() {
1945        let mut graph = CompositionGraph::new();
1946        let id = graph
1947            .types_mut()
1948            .add_defined_type(DefinedType::Alias(ValueType::Primitive(PrimitiveType::S32)));
1949        let id = graph
1950            .define_type("foo", Type::Value(ValueType::Defined(id)))
1951            .unwrap();
1952        assert!(matches!(
1953            graph.unexport(id).unwrap_err(),
1954            UnexportError::MustExportDefinition
1955        ));
1956    }
1957}