use crate::metadata::token::Token;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CfgEdgeKind {
Unconditional,
ConditionalTrue,
ConditionalFalse,
Switch {
case_value: Option<i32>,
},
ExceptionHandler {
exception_type: Option<Token>,
},
Leave,
EndFinally,
}
impl CfgEdgeKind {
#[must_use]
pub const fn is_conditional(&self) -> bool {
matches!(self, Self::ConditionalTrue | Self::ConditionalFalse)
}
#[must_use]
pub const fn is_exceptional(&self) -> bool {
matches!(
self,
Self::ExceptionHandler { .. } | Self::Leave | Self::EndFinally
)
}
#[must_use]
pub const fn is_switch(&self) -> bool {
matches!(self, Self::Switch { .. })
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CfgEdge {
target: usize,
kind: CfgEdgeKind,
}
impl CfgEdge {
#[must_use]
pub const fn new(target: usize, kind: CfgEdgeKind) -> Self {
Self { target, kind }
}
#[must_use]
pub const fn target(&self) -> usize {
self.target
}
#[must_use]
pub const fn kind(&self) -> &CfgEdgeKind {
&self.kind
}
#[must_use]
pub const fn unconditional(target: usize) -> Self {
Self::new(target, CfgEdgeKind::Unconditional)
}
#[must_use]
pub const fn conditional_true(target: usize) -> Self {
Self::new(target, CfgEdgeKind::ConditionalTrue)
}
#[must_use]
pub const fn conditional_false(target: usize) -> Self {
Self::new(target, CfgEdgeKind::ConditionalFalse)
}
#[must_use]
pub const fn switch_case(target: usize, case_value: Option<i32>) -> Self {
Self::new(target, CfgEdgeKind::Switch { case_value })
}
#[must_use]
pub const fn exception_handler(target: usize, exception_type: Option<Token>) -> Self {
Self::new(target, CfgEdgeKind::ExceptionHandler { exception_type })
}
#[must_use]
pub const fn leave(target: usize) -> Self {
Self::new(target, CfgEdgeKind::Leave)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_edge_kind_is_conditional() {
assert!(!CfgEdgeKind::Unconditional.is_conditional());
assert!(CfgEdgeKind::ConditionalTrue.is_conditional());
assert!(CfgEdgeKind::ConditionalFalse.is_conditional());
assert!(!CfgEdgeKind::Switch {
case_value: Some(0)
}
.is_conditional());
assert!(!CfgEdgeKind::ExceptionHandler {
exception_type: None
}
.is_conditional());
assert!(!CfgEdgeKind::Leave.is_conditional());
assert!(!CfgEdgeKind::EndFinally.is_conditional());
}
#[test]
fn test_edge_kind_is_exceptional() {
assert!(!CfgEdgeKind::Unconditional.is_exceptional());
assert!(!CfgEdgeKind::ConditionalTrue.is_exceptional());
assert!(!CfgEdgeKind::ConditionalFalse.is_exceptional());
assert!(!CfgEdgeKind::Switch {
case_value: Some(0)
}
.is_exceptional());
assert!(CfgEdgeKind::ExceptionHandler {
exception_type: None
}
.is_exceptional());
assert!(CfgEdgeKind::Leave.is_exceptional());
assert!(CfgEdgeKind::EndFinally.is_exceptional());
}
#[test]
fn test_edge_kind_is_switch() {
assert!(!CfgEdgeKind::Unconditional.is_switch());
assert!(CfgEdgeKind::Switch {
case_value: Some(0)
}
.is_switch());
assert!(CfgEdgeKind::Switch { case_value: None }.is_switch());
}
#[test]
fn test_cfg_edge_creation() {
let edge = CfgEdge::new(5, CfgEdgeKind::Unconditional);
assert_eq!(edge.target(), 5);
assert_eq!(*edge.kind(), CfgEdgeKind::Unconditional);
}
#[test]
fn test_cfg_edge_factory_methods() {
let unconditional = CfgEdge::unconditional(1);
assert_eq!(unconditional.target(), 1);
assert_eq!(*unconditional.kind(), CfgEdgeKind::Unconditional);
let cond_true = CfgEdge::conditional_true(2);
assert_eq!(cond_true.target(), 2);
assert_eq!(*cond_true.kind(), CfgEdgeKind::ConditionalTrue);
let cond_false = CfgEdge::conditional_false(3);
assert_eq!(cond_false.target(), 3);
assert_eq!(*cond_false.kind(), CfgEdgeKind::ConditionalFalse);
let switch = CfgEdge::switch_case(4, Some(42));
assert_eq!(switch.target(), 4);
assert_eq!(
*switch.kind(),
CfgEdgeKind::Switch {
case_value: Some(42)
}
);
let exception = CfgEdge::exception_handler(5, None);
assert_eq!(exception.target(), 5);
assert_eq!(
*exception.kind(),
CfgEdgeKind::ExceptionHandler {
exception_type: None
}
);
let leave = CfgEdge::leave(6);
assert_eq!(leave.target(), 6);
assert_eq!(*leave.kind(), CfgEdgeKind::Leave);
}
}