use analyssa::ir::exception::SsaExceptionHandler as AnalyssaSsaExceptionHandler;
pub use analyssa::ir::exception::{
BlockRange, ClauseLayout, ClausePart, ExceptionBlocks, ExceptionTableError, HandlerKind,
LaidOutHandler,
};
use crate::{
analysis::ssa::target::CilTarget,
metadata::{
method::{ExceptionHandler, ExceptionHandlerFlags},
token::Token,
},
};
pub type SsaExceptionHandler<T = CilTarget> = AnalyssaSsaExceptionHandler<T>;
#[must_use]
pub fn from_exception_handler(handler: &ExceptionHandler) -> SsaExceptionHandler {
let class_token_or_filter = if handler.flags == ExceptionHandlerFlags::EXCEPTION {
handler
.handler
.as_ref()
.map_or(handler.filter_offset, |t| t.token.value())
} else {
handler.filter_offset
};
SsaExceptionHandler {
flags: handler.flags,
try_offset: handler.try_offset,
try_length: handler.try_length,
handler_offset: handler.handler_offset,
handler_length: handler.handler_length,
class_token_or_filter,
protected_range: None,
handler_range: None,
filter_range: None,
}
}
pub trait SsaExceptionHandlerCilExt {
fn class_token(&self) -> Option<Token>;
}
impl SsaExceptionHandlerCilExt for AnalyssaSsaExceptionHandler<CilTarget> {
fn class_token(&self) -> Option<Token> {
if self.flags == ExceptionHandlerFlags::EXCEPTION {
Some(Token::new(self.class_token_or_filter))
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use analyssa::ir::HandlerKind;
fn cil_handler(flags: ExceptionHandlerFlags, filter_offset: u32) -> ExceptionHandler {
ExceptionHandler {
flags,
try_offset: 0,
try_length: 10,
handler_offset: 10,
handler_length: 5,
filter_offset,
handler: None,
}
}
#[test]
fn from_exception_handler_maps_no_block_yet() {
let converted =
from_exception_handler(&cil_handler(ExceptionHandlerFlags::EXCEPTION, 0x0100_0001));
assert_eq!(converted.protected_range, None);
assert_eq!(converted.handler_range, None);
assert_eq!(converted.filter_range, None);
assert!(!converted.has_block_mapping());
}
#[test]
fn catch_carries_its_class_token() {
let converted =
from_exception_handler(&cil_handler(ExceptionHandlerFlags::EXCEPTION, 0x0100_0001));
assert_eq!(converted.kind(), HandlerKind::Catch);
assert_eq!(converted.class_token(), Some(Token::new(0x0100_0001)));
assert_eq!(
converted.filter_offset(),
None,
"a catch clause has no filter offset to read"
);
}
#[test]
fn filter_carries_its_offset() {
let converted = from_exception_handler(&cil_handler(ExceptionHandlerFlags::FILTER, 0x20));
assert_eq!(converted.kind(), HandlerKind::Filter);
assert_eq!(converted.class_token(), None);
assert_eq!(converted.filter_offset(), Some(0x20));
}
#[test]
fn finally_and_fault_are_neither() {
for (flags, expected) in [
(ExceptionHandlerFlags::FINALLY, HandlerKind::Finally),
(ExceptionHandlerFlags::FAULT, HandlerKind::Fault),
] {
let converted = from_exception_handler(&cil_handler(flags, 0x20));
assert_eq!(converted.kind(), expected);
assert_eq!(converted.class_token(), None);
assert_eq!(
converted.filter_offset(),
None,
"only a filter clause reads the dual-purpose field as an offset"
);
}
}
}