use std::{collections::HashMap, hash::Hash};
use crate::interproc::CallGraph;
pub const DEFAULT_MAX_ITERATIONS: usize = 8;
#[derive(Debug, Clone)]
pub struct SummaryStore<K, S>
where
K: Hash + Eq + Clone,
{
summaries: HashMap<K, S>,
}
impl<K, S> Default for SummaryStore<K, S>
where
K: Hash + Eq + Clone,
{
fn default() -> Self {
Self {
summaries: HashMap::new(),
}
}
}
impl<K, S> SummaryStore<K, S>
where
K: Hash + Eq + Clone,
{
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn get(&self, method: &K) -> Option<&S> {
self.summaries.get(method)
}
pub fn entry(&mut self, method: K) -> &mut S
where
S: Default,
{
self.summaries.entry(method).or_default()
}
pub fn insert(&mut self, method: K, summary: S) {
self.summaries.insert(method, summary);
}
pub fn remove(&mut self, method: &K) -> Option<S> {
self.summaries.remove(method)
}
#[must_use]
pub fn len(&self) -> usize {
self.summaries.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.summaries.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = (&K, &S)> {
self.summaries.iter()
}
}
pub trait SummaryTransfer<K, S>
where
K: Hash + Eq + Clone,
{
fn visit(&mut self, method: &K, summaries: &mut SummaryStore<K, S>) -> bool;
}
pub fn solve<K, S, F>(
graph: &CallGraph<K>,
transfer: &mut F,
max_iterations: usize,
) -> SummaryStore<K, S>
where
K: Hash + Eq + Clone,
F: SummaryTransfer<K, S> + ?Sized,
{
let mut summaries = SummaryStore::new();
for component in graph.components_callee_first() {
let iterations = if graph.is_recursive(&component) {
max_iterations
} else {
1
};
for _ in 0..iterations {
let mut changed = false;
for method in &component {
changed |= transfer.visit(method, &mut summaries);
}
if !changed {
break;
}
}
}
summaries
}