use std::{collections::HashSet, hash::Hash};
pub use self::summary::{DEFAULT_MAX_ITERATIONS, SummaryStore, SummaryTransfer, solve};
use crate::{graph::IndexedGraph, target::Target, world::World};
mod summary;
#[derive(Debug)]
pub struct CallGraph<K>
where
K: Hash + Eq + Clone,
{
graph: IndexedGraph<K, ()>,
self_recursive: HashSet<K>,
}
impl<K> Default for CallGraph<K>
where
K: Hash + Eq + Clone,
{
fn default() -> Self {
Self::new()
}
}
impl<K> CallGraph<K>
where
K: Hash + Eq + Clone,
{
#[must_use]
pub fn new() -> Self {
Self {
graph: IndexedGraph::new(),
self_recursive: HashSet::new(),
}
}
#[must_use]
pub fn from_edges<I, C>(methods: I, callees: C) -> Self
where
I: IntoIterator<Item = K>,
C: Fn(&K) -> Vec<K>,
{
let mut graph = Self::new();
let keys: Vec<K> = methods.into_iter().collect();
let known: HashSet<K> = keys.iter().cloned().collect();
for key in &keys {
graph.add_method(key.clone());
}
for caller in &keys {
for callee in callees(caller) {
if known.contains(&callee) {
graph.add_call(caller.clone(), callee);
}
}
}
graph
}
#[must_use]
pub fn from_world<T, W>(world: &W) -> Self
where
T: Target<MethodRef = K>,
W: World<T> + ?Sized,
{
Self::from_edges(world.all_methods(), |method| world.callees(method))
}
pub fn add_method(&mut self, method: K) {
self.graph.add_node(method);
}
pub fn add_call(&mut self, caller: K, callee: K) {
if caller == callee {
self.self_recursive.insert(caller.clone());
}
let _ = self.graph.add_edge(caller, callee, ());
}
#[must_use]
pub fn len(&self) -> usize {
self.graph.node_count()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.graph.node_count() == 0
}
#[must_use]
pub fn is_self_recursive(&self, method: &K) -> bool {
self.self_recursive.contains(method)
}
#[must_use]
pub fn components_callee_first(&self) -> Vec<Vec<K>> {
self.graph
.map_sccs_to_keys(&crate::graph::algorithms::strongly_connected_components(
self.graph.inner(),
))
}
#[must_use]
pub fn is_recursive(&self, component: &[K]) -> bool {
component.len() > 1
|| component
.first()
.is_some_and(|method| self.is_self_recursive(method))
}
}