#[derive(Debug, Clone)]
pub struct Block {
pub id: usize,
pub start: u32,
pub end: u32,
pub ins_len: u32,
pub succ: Vec<usize>,
pub pred: Vec<usize>,
pub handlers: Vec<u32>,
}
impl Block {
pub fn ends_cond(&self) -> bool {
matches!(self.succ.len(), 2)
}
pub fn is_exit(&self) -> bool {
self.succ.is_empty()
}
}
#[derive(Debug, Clone)]
pub struct ExcRange {
pub start: u32,
pub end: u32,
pub handler: u32,
pub catch_type: Option<String>,
}
#[derive(Debug, Clone)]
pub struct ExcEdge {
pub range: usize,
pub from: usize,
pub to: usize,
}
pub struct Cfg {
pub blocks: Vec<Block>,
pub entry: usize,
pub exc_edges: Vec<ExcEdge>,
pub exc_ranges: Vec<ExcRange>,
starts: Vec<u32>,
}
impl Cfg {
pub fn from_blocks(blocks: Vec<Block>, entry: usize, exc_ranges: Vec<ExcRange>) -> Cfg {
let mut blocks = blocks;
for b in blocks.iter_mut() {
b.pred.clear();
b.handlers.clear();
}
for i in 0..blocks.len() {
let succ = blocks[i].succ.clone();
for s in succ {
if s < blocks.len() {
blocks[s].pred.push(i);
}
}
}
let mut exc_edges = Vec::new();
for (ri, r) in exc_ranges.iter().enumerate() {
let handler = blocks.iter().position(|b| b.start == r.handler);
if let Some(h) = handler {
blocks[h].handlers.push(ri as u32);
}
for b in blocks.iter() {
if b.start >= r.start && b.start < r.end && b.ins_len != 0 {
if let Some(h) = handler {
exc_edges.push(ExcEdge { range: ri, from: b.id, to: h });
}
}
}
}
for b in blocks.iter_mut() {
b.pred.sort_unstable();
b.pred.dedup();
b.handlers.sort_unstable();
b.handlers.dedup();
}
let mut starts: Vec<u32> = blocks.iter().map(|b| b.start).collect();
starts.sort_unstable();
if crate::dbg_flag!("JCDC_DBG_CORE_CFG") {
for b in &blocks {
eprintln!(
"CORE_CFG b={} start={} end={} ins_len={} succ={:?} pred={:?} handlers={:?}",
b.id, b.start, b.end, b.ins_len, b.succ, b.pred, b.handlers
);
}
for (i, r) in exc_ranges.iter().enumerate() {
eprintln!("CORE_EXC r={} {}..{} -> {} type={:?}", i, r.start, r.end, r.handler, r.catch_type);
}
for e in &exc_edges {
eprintln!("CORE_EDGE r={} from={} to={}", e.range, e.from, e.to);
}
}
Cfg { blocks, entry, exc_edges, exc_ranges, starts }
}
pub fn from_parts(
blocks: Vec<Block>,
entry: usize,
exc_ranges: Vec<ExcRange>,
exc_edges: Vec<ExcEdge>,
) -> Cfg {
let mut starts: Vec<u32> = blocks.iter().map(|b| b.start).collect();
starts.sort_unstable();
Cfg { blocks, entry, exc_edges, exc_ranges, starts }
}
pub fn block_at(&self, p: u32) -> Option<usize> {
let i = self.starts.partition_point(|&l| l <= p);
if i == 0 {
None
} else {
Some(i - 1)
}
}
pub fn len(&self) -> usize {
self.blocks.len()
}
pub fn is_empty(&self) -> bool {
self.blocks.is_empty()
}
pub fn is_block_start(&self, p: u32) -> bool {
self.starts.binary_search(&p).is_ok()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn blk(id: usize, start: u32, len: u32, succ: Vec<usize>) -> Block {
Block {
id,
start,
end: start + len,
ins_len: len,
succ,
pred: Vec::new(),
handlers: Vec::new(),
}
}
#[test]
fn preds_and_handlers_are_derived() {
let cfg = Cfg::from_blocks(
vec![blk(0, 0, 2, vec![1]), blk(1, 2, 2, vec![2]), blk(2, 4, 2, vec![]), blk(3, 6, 1, vec![])],
0,
vec![ExcRange { start: 0, end: 6, handler: 6, catch_type: Some("java/lang/Throwable".into()) }],
);
assert_eq!(cfg.blocks[1].pred, vec![0]);
assert_eq!(cfg.blocks[2].pred, vec![1]);
assert_eq!(cfg.blocks[3].pred, Vec::<usize>::new());
assert_eq!(cfg.blocks[3].handlers, vec![0]);
assert_eq!(cfg.exc_edges.len(), 3);
assert!(cfg.exc_edges.iter().all(|e| e.range == 0 && e.to == 3));
assert!(cfg.block_at(3) == Some(1));
assert!(cfg.block_at(6) == Some(3));
assert!(cfg.is_block_start(4) && !cfg.is_block_start(5));
}
}