1use std::{
2 borrow::Cow,
3 fmt::{self, Debug, Display},
4};
5
6use itertools::Itertools as _;
7use petgraph::{
8 dot::{Config, Dot},
9 visit::EdgeRef,
10};
11use rustc_hash::FxHashMap;
12
13use super::IterationInstructionKind;
14use crate::{
15 BasicBlock, ControlFlowGraph, EdgeType, Instruction, InstructionKind, LabeledInstruction,
16 ReturnInstructionKind,
17};
18
19pub trait DisplayDot {
20 fn display_dot(&self) -> String;
21}
22
23impl DisplayDot for ControlFlowGraph {
24 fn display_dot(&self) -> String {
25 format!(
26 "{:?}",
27 Dot::with_attr_getters(
28 &self.graph,
29 &[Config::EdgeNoLabel, Config::NodeNoLabel],
30 &|_graph, edge| {
31 let weight = edge.weight();
32 let mut attrs = Attrs::default().with("label", format!("{weight:?}"));
33
34 if matches!(weight, EdgeType::Unreachable)
35 || self.basic_block(edge.source()).is_unreachable()
36 {
37 attrs += ("style", "dotted");
38 } else if matches!(weight, EdgeType::Error(_)) {
39 attrs += ("color", "red");
40 }
41
42 format!("{attrs:?}")
43 },
44 &|_graph, node| {
45 let block = &self.basic_blocks[*node.1];
46 let mut attrs = Attrs::default().with("label", block.display_dot());
47
48 if node.1.index() == 0 {
49 attrs += ("color", "green");
50 }
51 if block.is_unreachable() {
52 attrs += ("style", "dotted");
53 }
54
55 format!("{attrs:?}")
56 },
57 )
58 )
59 }
60}
61
62impl DisplayDot for BasicBlock {
63 fn display_dot(&self) -> String {
64 self.instructions().iter().map(DisplayDot::display_dot).join("\n")
65 }
66}
67
68impl DisplayDot for Instruction {
69 fn display_dot(&self) -> String {
70 match self.kind {
71 InstructionKind::Statement => "statement",
72 InstructionKind::Unreachable => "unreachable",
73 InstructionKind::Throw => "throw",
74 InstructionKind::Condition => "condition",
75 InstructionKind::Iteration(IterationInstructionKind::Of) => "iteration <of>",
76 InstructionKind::Iteration(IterationInstructionKind::In) => "iteration <in>",
77 InstructionKind::Break(LabeledInstruction::Labeled) => "break <label>",
78 InstructionKind::Break(LabeledInstruction::Unlabeled) => "break",
79 InstructionKind::Continue(LabeledInstruction::Labeled) => "continue <label>",
80 InstructionKind::Continue(LabeledInstruction::Unlabeled) => "continue",
81 InstructionKind::Return(ReturnInstructionKind::ImplicitUndefined) => {
82 "return <implicit undefined>"
83 }
84 InstructionKind::ImplicitReturn => "return",
85 InstructionKind::Return(ReturnInstructionKind::NotImplicitUndefined) => {
86 "return <value>"
87 }
88 }
89 .to_string()
90 }
91}
92
93#[derive(Clone)]
94pub enum Attr<'a> {
95 String(Cow<'a, str>),
96 Identifier(Cow<'a, str>),
97 Int(i64),
98}
99impl<'a> Attr<'a> {
100 #[inline]
101 #[must_use]
102 pub fn ident<S>(identifier: S) -> Self
103 where
104 S: Into<Cow<'a, str>>,
105 {
106 Self::Identifier(identifier.into())
107 }
108}
109
110impl Debug for Attr<'_> {
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 match self {
113 Self::Int(i) => Display::fmt(i, f),
114 Self::String(s) => Debug::fmt(s, f),
115 Self::Identifier(ident) => Display::fmt(ident, f), }
117 }
118}
119
120impl<'a> From<&'a str> for Attr<'a> {
121 fn from(value: &'a str) -> Self {
122 Self::String(Cow::Borrowed(value))
123 }
124}
125
126impl From<String> for Attr<'static> {
127 fn from(value: String) -> Self {
128 Self::String(Cow::Owned(value))
129 }
130}
131
132impl From<i64> for Attr<'_> {
133 fn from(value: i64) -> Self {
134 Self::Int(value)
135 }
136}
137
138#[derive(Default)]
139pub struct Attrs<'a>(FxHashMap<Cow<'a, str>, Attr<'a>>);
140impl<'a> Attrs<'a> {
141 #[must_use]
142 #[inline]
143 pub fn with<K, V>(mut self, key: K, value: V) -> Self
144 where
145 K: Into<Cow<'static, str>>,
146 V: Into<Attr<'a>>,
147 {
148 self += (key, value);
149 self
150 }
151}
152
153impl<'a, K, V> FromIterator<(K, V)> for Attrs<'a>
154where
155 K: Into<Cow<'static, str>>,
156 V: Into<Attr<'a>>,
157{
158 fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
159 Self(iter.into_iter().map(|(k, v)| (k.into(), v.into())).collect())
160 }
161}
162
163impl<'a, K, V> std::ops::AddAssign<(K, V)> for Attrs<'a>
164where
165 K: Into<Cow<'static, str>>,
166 V: Into<Attr<'a>>,
167{
168 fn add_assign(&mut self, (key, value): (K, V)) {
169 self.0.insert(key.into(), value.into());
170 }
171}
172
173impl Debug for Attrs<'_> {
174 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175 if self.0.is_empty() {
176 return Ok(());
177 }
178
179 for (i, (k, v)) in self.0.iter().enumerate() {
180 if i == 0 {
181 write!(f, "{k}={v:?}")?;
182 } else {
183 write!(f, ", {k}={v:?}")?;
184 }
185 }
186
187 Ok(())
188 }
189}