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 {
1229                component_model: true,
1230                ..Default::default()
1231            })
1232            .validate_all(&bytes)
1233            .map_err(|e| EncodeError::ValidationFailure { source: e })?;
1234        }
1235
1236        Ok(bytes)
1237    }
1238
1239    /// Decodes a composition graph from the bytes of a WebAssembly component.
1240    pub fn decode(_data: &[u8]) -> Result<CompositionGraph, DecodeError> {
1241        todo!("decoding of composition graphs is not yet implemented")
1242    }
1243
1244    fn alloc_package(&mut self, package: wac_types::Package) -> PackageId {
1245        let (index, entry) = if let Some(index) = self.free_packages.pop() {
1246            let entry = &mut self.packages[index];
1247            assert!(entry.package.is_none());
1248            (index, entry)
1249        } else {
1250            let index = self.packages.len();
1251            self.packages.push(RegisteredPackage::new(0));
1252            (index, &mut self.packages[index])
1253        };
1254
1255        entry.package = Some(package);
1256
1257        PackageId {
1258            index,
1259            generation: entry.generation,
1260        }
1261    }
1262
1263    /// Yields an iterator over the resolved imports (both implicit and explicit) of the graph.
1264    ///
1265    /// The iterator returns the name, the `ItemKind`, and an optional node id for explicit imports.
1266    pub fn imports(&self) -> impl Iterator<Item = (&str, ItemKind, Option<NodeId>)> {
1267        let mut imports = Vec::new();
1268        for index in self.graph.node_indices() {
1269            let node = &self.graph[index];
1270            if !matches!(node.kind, NodeKind::Instantiation(_)) {
1271                continue;
1272            }
1273
1274            let package = &self[node.package.unwrap()];
1275            let world = &self.types[package.ty()];
1276
1277            let unsatisfied_args = world
1278                .imports
1279                .iter()
1280                .enumerate()
1281                .filter(|(i, _)| !node.is_arg_satisfied(*i));
1282
1283            // Go through the unsatisfied arguments and import them
1284            for (_, (name, item_kind)) in unsatisfied_args {
1285                imports.push((name.as_str(), *item_kind, None));
1286            }
1287        }
1288
1289        for n in self.node_ids() {
1290            let node = &self.graph[n.0];
1291            if let NodeKind::Import(name) = &node.kind {
1292                imports.push((name.as_str(), node.item_kind, Some(n)));
1293            }
1294        }
1295        imports.into_iter()
1296    }
1297}
1298
1299impl Index<NodeId> for CompositionGraph {
1300    type Output = Node;
1301
1302    fn index(&self, index: NodeId) -> &Self::Output {
1303        &self.graph[index.0]
1304    }
1305}
1306
1307impl Index<PackageId> for CompositionGraph {
1308    type Output = Package;
1309
1310    fn index(&self, index: PackageId) -> &Self::Output {
1311        let PackageId { index, generation } = index;
1312        let entry = self.packages.get(index).expect("invalid package id");
1313        if entry.generation != generation {
1314            panic!("invalid package id");
1315        }
1316
1317        entry.package.as_ref().unwrap()
1318    }
1319}
1320
1321impl fmt::Debug for CompositionGraph {
1322    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1323        let node_attr = |_, (i, node): (_, &Node)| {
1324            let label = match &node.kind {
1325                NodeKind::Definition => format!(
1326                    r#"type definition \"{name}\""#,
1327                    name = node.export.as_ref().unwrap()
1328                ),
1329                NodeKind::Import(name) => format!(r#"import \"{name}\""#),
1330                NodeKind::Instantiation(_) => {
1331                    let package = &self[node.package.unwrap()];
1332                    format!(r#"instantiation of package \"{key}\""#, key = package.key())
1333                }
1334                NodeKind::Alias => {
1335                    let (_, source) = self.get_alias_source(NodeId(i)).unwrap();
1336                    format!(r#"alias of export \"{source}\""#)
1337                }
1338            };
1339
1340            let mut desc = String::new();
1341            write!(
1342                &mut desc,
1343                r#"label = "{label}"; kind = "{kind}""#,
1344                kind = node.item_kind.desc(&self.types)
1345            )
1346            .unwrap();
1347
1348            if let Some(export) = &node.export {
1349                write!(&mut desc, r#"; export = "{export}""#).unwrap();
1350            }
1351
1352            desc
1353        };
1354
1355        let dot = Dot::with_attr_getters(
1356            &self.graph,
1357            &[Config::NodeNoLabel],
1358            &|_, _| String::new(),
1359            &node_attr,
1360        );
1361
1362        write!(f, "{:?}", dot)
1363    }
1364}
1365
1366#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1367/// Information about the tool that processed the graph.
1368pub struct Processor<'a> {
1369    /// The name of the tool that processed the graph.
1370    pub name: &'a str,
1371    /// The version of the tool that processed the graph.
1372    pub version: &'a str,
1373}
1374
1375/// The options for encoding a composition graph.
1376#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1377pub struct EncodeOptions<'a> {
1378    /// Whether or not to define instantiated components.
1379    ///
1380    /// If `false`, components will be imported instead.
1381    ///
1382    /// Defaults to `true`.
1383    pub define_components: bool,
1384
1385    /// Whether or not to validate the encoded output.
1386    ///
1387    /// Defaults to `true`.
1388    pub validate: bool,
1389
1390    /// Information about the processor of the composition graph.
1391    pub processor: Option<Processor<'a>>,
1392}
1393
1394impl Default for EncodeOptions<'_> {
1395    fn default() -> Self {
1396        Self {
1397            define_components: true,
1398            validate: true,
1399            processor: None,
1400        }
1401    }
1402}
1403
1404/// Used to encode a composition graph as a new WebAssembly component.
1405struct CompositionGraphEncoder<'a>(&'a CompositionGraph);
1406
1407impl<'a> CompositionGraphEncoder<'a> {
1408    fn new(graph: &'a CompositionGraph) -> Self {
1409        Self(graph)
1410    }
1411
1412    fn encode(self, options: EncodeOptions) -> Result<Vec<u8>, EncodeError> {
1413        let mut state = State::new();
1414
1415        // Separate import nodes from other nodes keeping topological order
1416        let (import_nodes, other_nodes) = self
1417            .toposort()
1418            .map_err(|n| EncodeError::GraphContainsCycle { node: NodeId(n) })?
1419            .into_iter()
1420            .partition::<Vec<_>, _>(|index| {
1421                let node = &self.0.graph[*index];
1422                matches!(node.kind, NodeKind::Import(_))
1423            });
1424
1425        // First populate the state with both implicit instantiation arguments and explicit imports
1426        self.encode_imports(&mut state, import_nodes)?;
1427
1428        // Encode non-import nodes in the graph in topographical order
1429        for n in other_nodes {
1430            let node = &self.0.graph[n];
1431            let index = match &node.kind {
1432                NodeKind::Definition => self.definition(&mut state, node),
1433                NodeKind::Instantiation(_) => self.instantiation(&mut state, n, node, options),
1434                NodeKind::Alias => self.alias(&mut state, n),
1435                // `other_nodes` does not contain any import nodes
1436                NodeKind::Import(_) => unreachable!(),
1437            };
1438
1439            let prev = state.node_indexes.insert(n, index);
1440            assert!(prev.is_none());
1441        }
1442
1443        // Encode the exports, skipping any definitions as they've
1444        // already been exported
1445        for (name, node) in self
1446            .0
1447            .exports
1448            .iter()
1449            .filter(|(_, n)| !matches!(self.0.graph[**n].kind, NodeKind::Definition))
1450        {
1451            let index = state.node_indexes[node];
1452            let node = &self.0.graph[*node];
1453            state
1454                .builder()
1455                .export(name, node.item_kind.into(), index, None);
1456        }
1457
1458        let mut builder = std::mem::take(state.builder());
1459        self.encode_names(&state, &mut builder);
1460
1461        if let Some(processor) = &options.processor {
1462            let mut section = wasm_metadata::Producers::empty();
1463            section.add("processed-by", processor.name, processor.version);
1464            builder.raw_custom_section(&section.raw_custom_section());
1465        }
1466
1467        Ok(builder.finish())
1468    }
1469
1470    /// Performs a toposort of the composition graph.
1471    ///
1472    /// This differs from `toposort` in `petgraph` in that the
1473    /// nodes are iterated in *reverse order*, resulting in the
1474    /// returned topologically-sorted set to be in index order for
1475    /// independent nodes.
1476    fn toposort(&self) -> Result<Vec<NodeIndex>, NodeIndex> {
1477        let graph = &self.0.graph;
1478        let mut dfs = Dfs::empty(graph);
1479        dfs.reset(graph);
1480        let mut finished = graph.visit_map();
1481
1482        let mut finish_stack = Vec::new();
1483        for i in graph.node_identifiers().rev() {
1484            if dfs.discovered.is_visited(&i) {
1485                continue;
1486            }
1487            dfs.stack.push(i);
1488            while let Some(&nx) = dfs.stack.last() {
1489                if dfs.discovered.visit(nx) {
1490                    // First time visiting `nx`: Push neighbors, don't pop `nx`
1491                    for succ in graph.neighbors(nx) {
1492                        if succ == nx {
1493                            // self cycle
1494                            return Err(nx);
1495                        }
1496                        if !dfs.discovered.is_visited(&succ) {
1497                            dfs.stack.push(succ);
1498                        }
1499                    }
1500                } else {
1501                    dfs.stack.pop();
1502                    if finished.visit(nx) {
1503                        // Second time: All reachable nodes must have been finished
1504                        finish_stack.push(nx);
1505                    }
1506                }
1507            }
1508        }
1509        finish_stack.reverse();
1510
1511        dfs.reset(graph);
1512        for &i in &finish_stack {
1513            dfs.move_to(i);
1514            let mut cycle = false;
1515            while let Some(j) = dfs.next(Reversed(graph)) {
1516                if cycle {
1517                    return Err(j);
1518                }
1519                cycle = true;
1520            }
1521        }
1522
1523        Ok(finish_stack)
1524    }
1525
1526    /// Encode both implicit and explicit imports.
1527    ///
1528    /// `import_nodes` are expected to only be `NodeKind::Import` nodes.
1529    fn encode_imports(
1530        &self,
1531        state: &mut State,
1532        import_nodes: Vec<NodeIndex>,
1533    ) -> Result<(), EncodeError> {
1534        let mut explicit_imports = HashMap::new();
1535        let mut implicit_imports = Vec::new();
1536        let aggregator =
1537            self.resolve_imports(import_nodes, &mut implicit_imports, &mut explicit_imports)?;
1538
1539        let mut encoded = HashMap::new();
1540
1541        // Next encode the imports
1542        for (name, kind) in aggregator.imports() {
1543            log::debug!("import `{name}` is being imported");
1544            let index = self.import(state, name, aggregator.types(), kind);
1545            encoded.insert(name, (kind.into(), index));
1546        }
1547
1548        // Populate the implicit argument map
1549        for (name, node) in implicit_imports {
1550            let (kind, index) = encoded[name];
1551            state
1552                .implicit_args
1553                .entry(node)
1554                .or_default()
1555                .push((name.to_owned(), kind, index));
1556        }
1557
1558        // Finally, populate the node indexes with the encoded explicit imports
1559        for (name, node_index) in explicit_imports {
1560            let (_, encoded_index) = encoded[name];
1561            state.node_indexes.insert(node_index, encoded_index);
1562        }
1563
1564        Ok(())
1565    }
1566
1567    /// Resolves the imports (both implicit and explicit) of the given nodes.
1568    ///
1569    /// Populates hashmaps that map the implicit and explicit import nodes to their import names.
1570    /// Returns a type aggregator that contains the resolved types of the imports.
1571    fn resolve_imports(
1572        &'a self,
1573        import_nodes: Vec<NodeIndex>,
1574        implicit_imports: &mut Vec<(&'a str, NodeIndex)>,
1575        explicit_imports: &mut HashMap<&'a str, NodeIndex>,
1576    ) -> Result<TypeAggregator, EncodeError> {
1577        let mut instantiations = HashMap::new();
1578        let mut aggregator = TypeAggregator::default();
1579        let mut cache = Default::default();
1580        let mut checker = SubtypeChecker::new(&mut cache);
1581
1582        log::debug!("populating implicit imports");
1583
1584        // Enumerate the instantiation nodes and populate the import types
1585        for index in self.0.graph.node_indices() {
1586            let node = &self.0.graph[index];
1587            if !matches!(node.kind, NodeKind::Instantiation(_)) {
1588                continue;
1589            }
1590
1591            let package = &self.0[node.package.unwrap()];
1592            let world = &self.0.types[package.ty()];
1593
1594            let unsatisfied_args = world
1595                .imports
1596                .iter()
1597                .enumerate()
1598                .filter(|(i, _)| !node.is_arg_satisfied(*i));
1599
1600            // Go through the unsatisfied arguments and import them
1601            for (_, (name, kind)) in unsatisfied_args {
1602                if let Some(import) = self.0.imports.get(name).copied() {
1603                    return Err(EncodeError::ImplicitImportConflict {
1604                        import: NodeId(import),
1605                        instantiation: NodeId(index),
1606                        package: PackageKey::new(package),
1607                        name: name.to_string(),
1608                    });
1609                }
1610
1611                instantiations.entry(name).or_insert(index);
1612
1613                aggregator = aggregator
1614                    .aggregate(name, &self.0.types, *kind, &mut checker)
1615                    .map_err(|e| EncodeError::ImportTypeMergeConflict {
1616                        import: name.clone(),
1617                        first: NodeId(instantiations[&name]),
1618                        second: NodeId(index),
1619                        source: e,
1620                    })?;
1621                implicit_imports.push((name, index));
1622            }
1623        }
1624
1625        log::debug!("populating explicit imports");
1626
1627        for n in import_nodes {
1628            let node = &self.0.graph[n];
1629            if let NodeKind::Import(name) = &node.kind {
1630                explicit_imports.insert(name.as_str(), n);
1631                aggregator = aggregator
1632                    .aggregate(name, self.0.types(), node.item_kind, &mut checker)
1633                    .unwrap();
1634            }
1635        }
1636        Ok(aggregator)
1637    }
1638
1639    fn definition(&self, state: &mut State, node: &Node) -> u32 {
1640        let name = node.export.as_deref().unwrap();
1641
1642        log::debug!(
1643            "encoding definition for {kind} `{name}`",
1644            kind = node.item_kind.desc(&self.0.types)
1645        );
1646
1647        // Check to see if the type is already
1648        let encoder = TypeEncoder::new(&self.0.types);
1649        let (ty, index) = match node.item_kind {
1650            ItemKind::Type(ty) => match ty {
1651                Type::Resource(_) => panic!("resources cannot be defined"),
1652                Type::Func(_) => (ty, encoder.ty(state, ty, None)),
1653                Type::Value(vt) => {
1654                    // Check for an alias and use the existing index
1655                    if let ValueType::Defined(id) = vt {
1656                        if let DefinedType::Alias(aliased @ ValueType::Defined(_)) =
1657                            &self.0.types()[id]
1658                        {
1659                            (ty, state.current.type_indexes[&Type::Value(*aliased)])
1660                        } else {
1661                            (ty, encoder.ty(state, ty, None))
1662                        }
1663                    } else {
1664                        (ty, encoder.ty(state, ty, None))
1665                    }
1666                }
1667                Type::Interface(id) => (ty, encoder.interface(state, id)),
1668                Type::World(id) => (ty, encoder.world(state, id)),
1669                Type::Module(_) => (ty, encoder.ty(state, ty, None)),
1670            },
1671            _ => panic!("only types can be defined"),
1672        };
1673
1674        let index = state
1675            .builder()
1676            .export(name, ComponentExportKind::Type, index, None);
1677
1678        // Remap to the exported index
1679        state.current.type_indexes.insert(ty, index);
1680
1681        log::debug!("definition `{name}` encoded to type index {index}");
1682        index
1683    }
1684
1685    fn import(&self, state: &mut State, name: &str, types: &Types, kind: ItemKind) -> u32 {
1686        // Check to see if this is an import of an interface that's already been
1687        // imported; this can happen based on importing of shared dependencies
1688        if let ItemKind::Instance(id) = kind {
1689            if let Some(id) = &types[id].id {
1690                if let Some(index) = state.current.instances.get(id) {
1691                    return *index;
1692                }
1693            }
1694        }
1695
1696        // Defer to special handling if the item being imported is a resource
1697        let encoder = TypeEncoder::new(types);
1698        if let ItemKind::Type(Type::Resource(id)) = kind {
1699            return encoder.import_resource(state, name, id);
1700        }
1701
1702        log::debug!(
1703            "encoding import of {kind} `{name}`",
1704            kind = kind.desc(types)
1705        );
1706
1707        // Encode the type and import
1708        let ty = encoder.ty(state, kind.ty(), None);
1709        let index = state.builder().import(
1710            name,
1711            match kind {
1712                ItemKind::Type(_) => ComponentTypeRef::Type(TypeBounds::Eq(ty)),
1713                ItemKind::Func(_) => ComponentTypeRef::Func(ty),
1714                ItemKind::Instance(_) => ComponentTypeRef::Instance(ty),
1715                ItemKind::Component(_) => ComponentTypeRef::Component(ty),
1716                ItemKind::Module(_) => ComponentTypeRef::Module(ty),
1717                ItemKind::Value(_) => ComponentTypeRef::Value(ComponentValType::Type(ty)),
1718            },
1719        );
1720
1721        log::debug!(
1722            "import `{name}` encoded to {desc} index {index}",
1723            desc = kind.desc(types)
1724        );
1725
1726        match kind {
1727            ItemKind::Type(ty) => {
1728                // Remap to the imported index
1729                state.current.type_indexes.insert(ty, index);
1730            }
1731            ItemKind::Instance(id) => {
1732                if let Some(id) = &types[id].id {
1733                    log::debug!(
1734                        "interface `{id}` is available for aliasing as instance index {index}"
1735                    );
1736                    state.current.instances.insert(id.clone(), index);
1737                }
1738            }
1739            _ => {}
1740        }
1741
1742        index
1743    }
1744
1745    fn instantiation(
1746        &self,
1747        state: &mut State,
1748        index: NodeIndex,
1749        node: &Node,
1750        options: EncodeOptions,
1751    ) -> u32 {
1752        let package_id = node.package.expect("instantiation requires a package");
1753        let package = self.0.packages[package_id.index].package.as_ref().unwrap();
1754        let imports = &self.0.types[package.ty()].imports;
1755
1756        let component_index = if let Some(index) = state.packages.get(&package_id) {
1757            *index
1758        } else {
1759            let index = if options.define_components {
1760                state.builder().component_raw(package.bytes())
1761            } else {
1762                let encoder = TypeEncoder::new(&self.0.types);
1763                let ty = encoder.component(state, package.ty());
1764                state.builder().import(
1765                    &Self::package_import_name(package),
1766                    ComponentTypeRef::Component(ty),
1767                )
1768            };
1769
1770            state.packages.insert(package_id, index);
1771            index
1772        };
1773
1774        let mut arguments = Vec::with_capacity(imports.len());
1775        arguments.extend(
1776            self.0
1777                .graph
1778                .edges_directed(index, Direction::Incoming)
1779                .map(|e| {
1780                    let kind = self.0.graph[e.source()].item_kind.into();
1781                    let index = state.node_indexes[&e.source()];
1782                    match e.weight() {
1783                        Edge::Alias(_) | Edge::Dependency => {
1784                            panic!("unexpected edge for an instantiation")
1785                        }
1786                        Edge::Argument(i) => (
1787                            Cow::Borrowed(imports.get_index(*i).unwrap().0.as_str()),
1788                            kind,
1789                            index,
1790                        ),
1791                    }
1792                }),
1793        );
1794
1795        if let Some(implicit) = state.implicit_args.remove(&index) {
1796            arguments.extend(implicit.into_iter().map(|(n, k, i)| (n.into(), k, i)));
1797        }
1798
1799        log::debug!(
1800            "instantiating package `{package}` with arguments: {arguments:?}",
1801            package = package.name(),
1802        );
1803
1804        let index = state.builder().instantiate(component_index, arguments);
1805
1806        log::debug!(
1807            "instantiation of package `{package}` encoded to instance index {index}",
1808            package = package.name(),
1809        );
1810
1811        index
1812    }
1813
1814    fn alias(&self, state: &mut State, node: NodeIndex) -> u32 {
1815        let (source, export) = self
1816            .0
1817            .get_alias_source(NodeId(node))
1818            .expect("alias should have a source");
1819
1820        let source_node = &self.0[source];
1821        let exports = match source_node.item_kind {
1822            ItemKind::Instance(id) => &self.0.types[id].exports,
1823            _ => panic!("expected the source of an alias to be an instance"),
1824        };
1825
1826        let kind = exports[export];
1827        let instance = state.node_indexes[&source.0];
1828
1829        log::debug!(
1830            "encoding alias for {kind} export `{export}` of instance index {instance}",
1831            kind = kind.desc(&self.0.types),
1832        );
1833
1834        let index = state.builder().alias(Alias::InstanceExport {
1835            instance,
1836            kind: kind.into(),
1837            name: export,
1838        });
1839
1840        log::debug!(
1841            "alias of export `{export}` encoded to {kind} index {index}",
1842            kind = kind.desc(&self.0.types)
1843        );
1844        index
1845    }
1846
1847    fn package_import_name(package: &Package) -> String {
1848        let mut name = String::new();
1849
1850        write!(&mut name, "unlocked-dep=<{name}", name = package.name()).unwrap();
1851        if let Some(version) = package.version() {
1852            write!(&mut name, "@{{>={version}}}", version = version).unwrap();
1853        }
1854
1855        write!(&mut name, ">").unwrap();
1856        name
1857    }
1858
1859    fn encode_names(&self, state: &State, encoded: &mut ComponentBuilder) {
1860        let mut names = ComponentNameSection::new();
1861        let mut types = NameMap::new();
1862        let mut funcs = NameMap::new();
1863        let mut instances = NameMap::new();
1864        let mut components = NameMap::new();
1865        let mut modules = NameMap::new();
1866        let mut values = NameMap::new();
1867
1868        for index in self.0.graph.node_indices() {
1869            let node = &self.0.graph[index];
1870            if let Some(name) = &node.name {
1871                let map = match node.item_kind {
1872                    ItemKind::Type(_) => &mut types,
1873                    ItemKind::Func(_) => &mut funcs,
1874                    ItemKind::Instance(_) => &mut instances,
1875                    ItemKind::Component(_) => &mut components,
1876                    ItemKind::Module(_) => &mut modules,
1877                    ItemKind::Value(_) => &mut values,
1878                };
1879
1880                let index = state.node_indexes[&index];
1881                map.append(index, name)
1882            }
1883        }
1884
1885        if !types.is_empty() {
1886            names.types(&types);
1887        }
1888
1889        if !funcs.is_empty() {
1890            names.funcs(&funcs);
1891        }
1892
1893        if !instances.is_empty() {
1894            names.instances(&instances);
1895        }
1896
1897        if !components.is_empty() {
1898            names.components(&components);
1899        }
1900
1901        if !modules.is_empty() {
1902            names.core_modules(&modules);
1903        }
1904
1905        if !values.is_empty() {
1906            names.values(&values);
1907        }
1908
1909        if !names.is_empty() {
1910            encoded.custom_section(&names.as_custom());
1911        }
1912    }
1913}
1914
1915#[cfg(test)]
1916mod test {
1917    use super::*;
1918    use wac_types::{DefinedType, PrimitiveType, Resource, ValueType};
1919
1920    #[test]
1921    fn it_adds_a_type_definition() {
1922        let mut graph = CompositionGraph::new();
1923        let id = graph
1924            .types_mut()
1925            .add_defined_type(DefinedType::Alias(ValueType::Primitive(
1926                PrimitiveType::Bool,
1927            )));
1928        assert!(graph
1929            .define_type("foo", Type::Value(ValueType::Defined(id)))
1930            .is_ok());
1931    }
1932
1933    #[test]
1934    fn it_cannot_define_a_resource() {
1935        let mut graph = CompositionGraph::new();
1936        let id = graph.types_mut().add_resource(Resource {
1937            name: "a".to_string(),
1938            alias: None,
1939        });
1940        assert!(matches!(
1941            graph.define_type("foo", Type::Resource(id)).unwrap_err(),
1942            DefineTypeError::CannotDefineResource
1943        ));
1944    }
1945
1946    #[test]
1947    fn it_must_export_a_type_definition() {
1948        let mut graph = CompositionGraph::new();
1949        let id = graph
1950            .types_mut()
1951            .add_defined_type(DefinedType::Alias(ValueType::Primitive(PrimitiveType::S32)));
1952        let id = graph
1953            .define_type("foo", Type::Value(ValueType::Defined(id)))
1954            .unwrap();
1955        assert!(matches!(
1956            graph.unexport(id).unwrap_err(),
1957            UnexportError::MustExportDefinition
1958        ));
1959    }
1960}