use serde::{Deserialize, Serialize};
use tsz_parser::NodeIndex;
pub mod flow_flags {
pub const UNREACHABLE: u32 = 1 << 0; pub const START: u32 = 1 << 1; pub const BRANCH_LABEL: u32 = 1 << 2; pub const LOOP_LABEL: u32 = 1 << 3; pub const ASSIGNMENT: u32 = 1 << 4; pub const TRUE_CONDITION: u32 = 1 << 5; pub const FALSE_CONDITION: u32 = 1 << 6; pub const SWITCH_CLAUSE: u32 = 1 << 7; pub const ARRAY_MUTATION: u32 = 1 << 8; pub const CALL: u32 = 1 << 9; pub const REDUCE_LABEL: u32 = 1 << 10; pub const REFERENCED: u32 = 1 << 11; pub const AWAIT_POINT: u32 = 1 << 12; pub const YIELD_POINT: u32 = 1 << 13;
pub const LABEL: u32 = BRANCH_LABEL | LOOP_LABEL;
pub const CONDITION: u32 = TRUE_CONDITION | FALSE_CONDITION;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct FlowNodeId(pub u32);
impl FlowNodeId {
pub const NONE: Self = Self(u32::MAX);
#[must_use]
pub const fn is_none(&self) -> bool {
self.0 == u32::MAX
}
#[must_use]
pub const fn is_some(&self) -> bool {
self.0 != u32::MAX
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct FlowNode {
pub flags: u32,
pub id: FlowNodeId,
pub antecedent: Vec<FlowNodeId>,
pub node: NodeIndex,
}
impl FlowNode {
#[must_use]
pub const fn new(id: FlowNodeId, flags: u32) -> Self {
Self {
flags,
id,
antecedent: Vec::new(),
node: NodeIndex::NONE,
}
}
#[must_use]
pub const fn has_flags(&self, flags: u32) -> bool {
(self.flags & flags) == flags
}
#[must_use]
pub const fn has_any_flags(&self, flags: u32) -> bool {
(self.flags & flags) != 0
}
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct FlowNodeArena {
nodes: Vec<FlowNode>,
}
impl FlowNodeArena {
#[must_use]
pub const fn new() -> Self {
Self { nodes: Vec::new() }
}
pub fn alloc(&mut self, flags: u32) -> FlowNodeId {
let id = FlowNodeId(
u32::try_from(self.nodes.len()).expect("flow node arena length exceeds u32"),
);
self.nodes.push(FlowNode::new(id, flags));
id
}
#[must_use]
pub fn get(&self, id: FlowNodeId) -> Option<&FlowNode> {
if id.is_none() {
None
} else {
self.nodes.get(id.0 as usize)
}
}
pub fn get_mut(&mut self, id: FlowNodeId) -> Option<&mut FlowNode> {
if id.is_none() {
None
} else {
self.nodes.get_mut(id.0 as usize)
}
}
#[must_use]
pub const fn len(&self) -> usize {
self.nodes.len()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
pub fn clear(&mut self) {
self.nodes.clear();
}
#[must_use]
pub fn find_unreachable(&self) -> Option<FlowNodeId> {
for (idx, node) in self.nodes.iter().enumerate() {
if node.has_any_flags(flow_flags::UNREACHABLE) {
return Some(FlowNodeId(
u32::try_from(idx).expect("flow node index exceeds u32"),
));
}
}
None
}
}