swc_bundler/modules/sort/
graph.rs1use std::{collections::VecDeque, iter::repeat};
2
3use petgraph::{
4 prelude::GraphMap,
5 Directed,
6 EdgeDirection::{self, Incoming, Outgoing},
7};
8use rustc_hash::{FxBuildHasher, FxHashSet};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub(super) enum Required {
13 Always,
15
16 Maybe,
18}
19
20#[derive(Debug, Default)]
22pub(super) struct StmtDepGraph {
23 inner: GraphMap<usize, Required, Directed, FxBuildHasher>,
24 paths: Vec<FxHashSet<usize>>,
27}
28
29impl StmtDepGraph {
30 pub fn node_count(&self) -> usize {
31 self.inner.node_count()
32 }
33
34 pub fn add_node(&mut self, node: usize) {
35 self.inner.add_node(node);
36 }
37
38 pub fn remove_node(&mut self, node: usize) {
39 self.inner.add_node(node);
40 }
41
42 pub fn add_edge(&mut self, a: usize, b: usize, required: Required) {
43 self.inner.add_edge(a, b, required);
44
45 self.insert_transitives(a, b);
46 }
47
48 fn calc_transitives(&self, id: usize, dir: EdgeDirection) -> FxHashSet<usize> {
49 let mut set = FxHashSet::default();
50
51 let mut queue = VecDeque::default();
52 queue.push_front(id);
53
54 while let Some(id) = queue.pop_front() {
55 if !set.insert(id) {
57 continue;
58 }
59
60 for transitive in self.inner.neighbors_directed(id, dir) {
61 queue.push_back(transitive);
62 }
63 }
64
65 set
66 }
67
68 fn insert_transitives(&mut self, from: usize, to: usize) {
69 if self.paths.len() <= from {
70 self.paths
71 .extend(repeat(Default::default()).take(from + 1 - self.paths.len()))
72 }
73 if !self.paths[from].insert(to) {
74 return;
75 }
76
77 for transitive in self.calc_transitives(to, Outgoing) {
78 if from == transitive {
79 continue;
80 }
81 self.paths[from].insert(transitive);
82 }
83
84 for transitive in self.calc_transitives(from, Incoming) {
85 if to == transitive {
86 continue;
87 }
88
89 self.paths[transitive].insert(to);
90 }
91 }
92
93 pub fn edge_weight(&self, a: usize, b: usize) -> Option<Required> {
94 self.inner.edge_weight(a, b).cloned()
95 }
96
97 pub fn neighbors_directed(
98 &self,
99 start: usize,
100 dir: EdgeDirection,
101 ) -> impl '_ + Iterator<Item = usize> {
102 self.inner.neighbors_directed(start, dir)
103 }
104
105 pub fn has_a_path(&self, from: usize, to: usize) -> bool {
106 match self.paths.get(from) {
107 Some(ps) => ps.contains(&to),
108 None => false,
109 }
110 }
111}