use crate::metadata::{
method::{ExceptionHandler, ExceptionHandlerFlags},
token::Token,
};
#[derive(Debug, Clone)]
pub struct SsaExceptionHandler {
pub flags: ExceptionHandlerFlags,
pub try_offset: u32,
pub try_length: u32,
pub handler_offset: u32,
pub handler_length: u32,
pub class_token_or_filter: u32,
pub try_start_block: Option<usize>,
pub try_end_block: Option<usize>,
pub handler_start_block: Option<usize>,
pub handler_end_block: Option<usize>,
pub filter_start_block: Option<usize>,
}
impl SsaExceptionHandler {
#[must_use]
pub fn from_exception_handler(handler: &ExceptionHandler) -> Self {
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
};
Self {
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,
try_start_block: None,
try_end_block: None,
handler_start_block: None,
handler_end_block: None,
filter_start_block: None,
}
}
#[must_use]
pub fn class_token(&self) -> Option<Token> {
if self.flags == ExceptionHandlerFlags::EXCEPTION {
Some(Token::new(self.class_token_or_filter))
} else {
None
}
}
#[must_use]
pub fn filter_offset(&self) -> Option<u32> {
if self.flags == ExceptionHandlerFlags::FILTER {
Some(self.class_token_or_filter)
} else {
None
}
}
#[must_use]
pub fn has_block_mapping(&self) -> bool {
self.try_start_block.is_some() && self.handler_start_block.is_some()
}
pub fn remap_block_indices(&mut self, block_remap: &[Option<usize>]) {
self.try_start_block = self
.try_start_block
.and_then(|idx| block_remap.get(idx).copied().flatten());
self.try_end_block = self
.try_end_block
.and_then(|idx| block_remap.get(idx).copied().flatten());
self.handler_start_block = self
.handler_start_block
.and_then(|idx| block_remap.get(idx).copied().flatten());
self.handler_end_block = self
.handler_end_block
.and_then(|idx| block_remap.get(idx).copied().flatten());
self.filter_start_block = self
.filter_start_block
.and_then(|idx| block_remap.get(idx).copied().flatten());
}
}
#[cfg(test)]
mod tests {
use crate::metadata::method::ExceptionHandlerFlags;
use super::*;
#[test]
fn test_remap_block_indices_basic() {
let mut handler = SsaExceptionHandler {
flags: ExceptionHandlerFlags::EXCEPTION,
try_offset: 0,
try_length: 10,
handler_offset: 10,
handler_length: 5,
class_token_or_filter: 0x01000001,
try_start_block: Some(0),
try_end_block: Some(2),
handler_start_block: Some(3),
handler_end_block: Some(4),
filter_start_block: None,
};
let block_remap = vec![Some(0), None, Some(1), Some(2), Some(3)];
handler.remap_block_indices(&block_remap);
assert_eq!(handler.try_start_block, Some(0)); assert_eq!(handler.try_end_block, Some(1)); assert_eq!(handler.handler_start_block, Some(2)); assert_eq!(handler.handler_end_block, Some(3)); }
#[test]
fn test_remap_block_indices_removed_block() {
let mut handler = SsaExceptionHandler {
flags: ExceptionHandlerFlags::EXCEPTION,
try_offset: 0,
try_length: 10,
handler_offset: 10,
handler_length: 5,
class_token_or_filter: 0x01000001,
try_start_block: Some(1), try_end_block: Some(2),
handler_start_block: Some(3),
handler_end_block: None,
filter_start_block: None,
};
let block_remap = vec![Some(0), None, Some(1), Some(2)];
handler.remap_block_indices(&block_remap);
assert_eq!(handler.try_start_block, None); assert_eq!(handler.try_end_block, Some(1)); assert_eq!(handler.handler_start_block, Some(2)); }
#[test]
fn test_remap_block_indices_filter_handler() {
let mut handler = SsaExceptionHandler {
flags: ExceptionHandlerFlags::FILTER,
try_offset: 0,
try_length: 10,
handler_offset: 15,
handler_length: 5,
class_token_or_filter: 10, try_start_block: Some(0),
try_end_block: Some(1),
handler_start_block: Some(3),
handler_end_block: Some(4),
filter_start_block: Some(2), };
let block_remap = vec![Some(0), Some(1), Some(2), Some(3), Some(4)];
handler.remap_block_indices(&block_remap);
assert_eq!(handler.try_start_block, Some(0));
assert_eq!(handler.filter_start_block, Some(2));
assert_eq!(handler.handler_start_block, Some(3));
}
}