use super::*;
use core::{
fmt::{self, Debug},
ops::Range,
};
use wasmtime_environ::{EntityRef, SecondaryMap};
pub struct CallGraph<Node>
where
Node: EntityRef + Debug,
{
edges: SecondaryMap<Node, Range<u32>>,
edge_elems: Vec<Node>,
}
impl<Node> Debug for CallGraph<Node>
where
Node: EntityRef + Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
struct Edges<'a, Node: EntityRef + Debug>(&'a CallGraph<Node>);
impl<'a, Node: EntityRef + Debug> Debug for Edges<'a, Node> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_map()
.entries(self.0.nodes().map(|n| (n, self.0.edges(n))))
.finish()
}
}
f.debug_struct("CallGraph")
.field("edges", &Edges(self))
.finish()
}
}
impl<Node> CallGraph<Node>
where
Node: EntityRef + Debug,
{
pub fn new(
funcs: impl IntoIterator<Item = Node>,
mut get_calls: impl FnMut(Node, &mut Vec<Node>) -> Result<()>,
) -> Result<Self> {
let funcs = funcs.into_iter();
let (min, max) = funcs.size_hint();
let capacity = max.unwrap_or_else(|| 2 * min);
let mut edges = SecondaryMap::with_capacity(capacity);
let mut edge_elems = vec![];
let mut calls = vec![];
for caller in funcs {
debug_assert!(calls.is_empty());
get_calls(caller, &mut calls)?;
debug_assert_eq!(edges[caller], Range::default());
edges[caller] = extend_with_range(&mut edge_elems, calls.drain(..));
}
Ok(CallGraph { edges, edge_elems })
}
pub fn nodes(&self) -> impl ExactSizeIterator<Item = Node> {
self.edges.keys()
}
pub fn edges(&self, node: Node) -> &[Node] {
let Range { start, end } = self.edges[node].clone();
let start = usize::try_from(start).unwrap();
let end = usize::try_from(end).unwrap();
&self.edge_elems[start..end]
}
}