use serde::{Deserialize, Serialize};
use crate::cfg::types::{BlockId, CFGInfo, EdgeType};
use crate::dfg::types::DFGInfo;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ControlDependence {
pub condition_line: usize,
pub dependent_line: usize,
pub branch_type: BranchType,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum BranchType {
True,
False,
Loop,
Exception,
Unconditional,
}
impl From<EdgeType> for BranchType {
fn from(edge_type: EdgeType) -> Self {
match edge_type {
EdgeType::True => BranchType::True,
EdgeType::False | EdgeType::Else => BranchType::False,
EdgeType::BackEdge | EdgeType::Loop | EdgeType::Iterate | EdgeType::Next => {
BranchType::Loop
}
EdgeType::Exception | EdgeType::ExceptionGroup => BranchType::Exception,
_ => BranchType::Unconditional,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PDGInfo {
pub function_name: String,
pub cfg: CFGInfo,
pub dfg: DFGInfo,
pub control_deps: Vec<ControlDependence>,
}
impl PDGInfo {
pub fn new(cfg: CFGInfo, dfg: DFGInfo, control_deps: Vec<ControlDependence>) -> Self {
let function_name = cfg.function_name.clone();
Self {
function_name,
cfg,
dfg,
control_deps,
}
}
pub fn control_deps_for(&self, line: usize) -> Vec<usize> {
self.control_deps
.iter()
.filter(|dep| dep.dependent_line == line)
.map(|dep| dep.condition_line)
.collect()
}
pub fn lines_controlled_by(&self, condition_line: usize) -> Vec<usize> {
self.control_deps
.iter()
.filter(|dep| dep.condition_line == condition_line)
.map(|dep| dep.dependent_line)
.collect()
}
pub fn is_control_dependent(&self, line_a: usize, line_b: usize) -> bool {
self.control_deps
.iter()
.any(|dep| dep.dependent_line == line_a && dep.condition_line == line_b)
}
pub fn control_dep_count(&self) -> usize {
self.control_deps.len()
}
pub fn data_edge_count(&self) -> usize {
self.dfg.edges.len()
}
pub fn variables(&self) -> Vec<&str> {
self.dfg.variables()
}
pub fn block_for_line(&self, line: usize) -> Option<BlockId> {
self.cfg.block_for_line(line)
}
pub fn to_dict(&self) -> serde_json::Value {
serde_json::json!({
"function_name": self.function_name,
"control_deps": self.control_deps.len(),
"data_edges": self.dfg.edges.len(),
"cfg_blocks": self.cfg.blocks.len(),
"cfg_edges": self.cfg.edges.len(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
fn create_test_control_dep() -> ControlDependence {
ControlDependence {
condition_line: 2,
dependent_line: 3,
branch_type: BranchType::True,
}
}
#[test]
fn test_branch_type_from_edge_type() {
assert_eq!(BranchType::from(EdgeType::True), BranchType::True);
assert_eq!(BranchType::from(EdgeType::False), BranchType::False);
assert_eq!(BranchType::from(EdgeType::Else), BranchType::False);
assert_eq!(BranchType::from(EdgeType::BackEdge), BranchType::Loop);
assert_eq!(BranchType::from(EdgeType::Loop), BranchType::Loop);
assert_eq!(BranchType::from(EdgeType::Exception), BranchType::Exception);
assert_eq!(
BranchType::from(EdgeType::Unconditional),
BranchType::Unconditional
);
}
#[test]
fn test_control_dependence_equality() {
let dep1 = create_test_control_dep();
let dep2 = create_test_control_dep();
assert_eq!(dep1, dep2);
let dep3 = ControlDependence {
condition_line: 2,
dependent_line: 4, branch_type: BranchType::True,
};
assert_ne!(dep1, dep3);
}
}