use crate::metadata::{
method::{ExceptionHandler as MetadataExceptionHandler, ExceptionHandlerFlags},
token::Token,
};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ExceptionClause {
Catch {
try_offset: u32,
try_length: u32,
handler_offset: u32,
handler_length: u32,
catch_type: Token,
},
Filter {
try_offset: u32,
try_length: u32,
handler_offset: u32,
handler_length: u32,
filter_offset: u32,
},
Finally {
try_offset: u32,
try_length: u32,
handler_offset: u32,
handler_length: u32,
},
Fault {
try_offset: u32,
try_length: u32,
handler_offset: u32,
handler_length: u32,
},
}
impl ExceptionClause {
#[must_use]
pub fn try_offset(&self) -> u32 {
match self {
Self::Catch { try_offset, .. }
| Self::Filter { try_offset, .. }
| Self::Finally { try_offset, .. }
| Self::Fault { try_offset, .. } => *try_offset,
}
}
#[must_use]
pub fn try_length(&self) -> u32 {
match self {
Self::Catch { try_length, .. }
| Self::Filter { try_length, .. }
| Self::Finally { try_length, .. }
| Self::Fault { try_length, .. } => *try_length,
}
}
#[must_use]
pub fn try_end(&self) -> u32 {
self.try_offset() + self.try_length()
}
#[must_use]
pub fn handler_offset(&self) -> u32 {
match self {
Self::Catch { handler_offset, .. }
| Self::Filter { handler_offset, .. }
| Self::Finally { handler_offset, .. }
| Self::Fault { handler_offset, .. } => *handler_offset,
}
}
#[must_use]
pub fn handler_length(&self) -> u32 {
match self {
Self::Catch { handler_length, .. }
| Self::Filter { handler_length, .. }
| Self::Finally { handler_length, .. }
| Self::Fault { handler_length, .. } => *handler_length,
}
}
#[must_use]
pub fn handler_end(&self) -> u32 {
self.handler_offset() + self.handler_length()
}
#[must_use]
pub fn is_in_try(&self, offset: u32) -> bool {
offset >= self.try_offset() && offset < self.try_end()
}
#[must_use]
pub fn is_in_handler(&self, offset: u32) -> bool {
offset >= self.handler_offset() && offset < self.handler_end()
}
#[must_use]
pub fn is_catch(&self) -> bool {
matches!(self, Self::Catch { .. })
}
#[must_use]
pub fn is_filter(&self) -> bool {
matches!(self, Self::Filter { .. })
}
#[must_use]
pub fn is_finally(&self) -> bool {
matches!(self, Self::Finally { .. })
}
#[must_use]
pub fn is_fault(&self) -> bool {
matches!(self, Self::Fault { .. })
}
#[must_use]
pub fn catch_type(&self) -> Option<Token> {
match self {
Self::Catch { catch_type, .. } => Some(*catch_type),
_ => None,
}
}
#[must_use]
pub fn filter_offset(&self) -> Option<u32> {
match self {
Self::Filter { filter_offset, .. } => Some(*filter_offset),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct InstructionLocation {
pub method: Token,
pub offset: u32,
}
impl InstructionLocation {
#[must_use]
pub fn new(method: Token, offset: u32) -> Self {
Self { method, offset }
}
}
impl std::fmt::Display for InstructionLocation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}+0x{:04X}", self.method, self.offset)
}
}
#[derive(Clone, Debug)]
pub enum HandlerMatch {
Catch {
method: Token,
handler_offset: u32,
},
Filter {
method: Token,
filter_offset: u32,
handler_offset: u32,
},
Finally {
method: Token,
handler_offset: u32,
handler_length: u32,
continue_search_after: bool,
},
Fault {
method: Token,
handler_offset: u32,
handler_length: u32,
},
}
impl ExceptionClause {
#[must_use]
pub fn from_metadata_handler(handler: &MetadataExceptionHandler) -> Self {
if handler.flags.contains(ExceptionHandlerFlags::FILTER) {
ExceptionClause::Filter {
try_offset: handler.try_offset,
try_length: handler.try_length,
handler_offset: handler.handler_offset,
handler_length: handler.handler_length,
filter_offset: handler.filter_offset,
}
} else if handler.flags.contains(ExceptionHandlerFlags::FINALLY) {
ExceptionClause::Finally {
try_offset: handler.try_offset,
try_length: handler.try_length,
handler_offset: handler.handler_offset,
handler_length: handler.handler_length,
}
} else if handler.flags.contains(ExceptionHandlerFlags::FAULT) {
ExceptionClause::Fault {
try_offset: handler.try_offset,
try_length: handler.try_length,
handler_offset: handler.handler_offset,
handler_length: handler.handler_length,
}
} else {
let catch_type = handler
.handler
.as_ref()
.map_or_else(|| Token::new(0), |t| t.token);
ExceptionClause::Catch {
try_offset: handler.try_offset,
try_length: handler.try_length,
handler_offset: handler.handler_offset,
handler_length: handler.handler_length,
catch_type,
}
}
}
#[must_use]
pub fn from_metadata_handlers(handlers: &[MetadataExceptionHandler]) -> Vec<Self> {
handlers.iter().map(Self::from_metadata_handler).collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_exception_clause_catch() {
let clause = ExceptionClause::Catch {
try_offset: 0x10,
try_length: 0x20,
handler_offset: 0x30,
handler_length: 0x10,
catch_type: Token::new(0x01000001),
};
assert!(clause.is_catch());
assert!(!clause.is_finally());
assert_eq!(clause.try_offset(), 0x10);
assert_eq!(clause.try_end(), 0x30);
assert_eq!(clause.handler_offset(), 0x30);
assert!(clause.is_in_try(0x15));
assert!(!clause.is_in_try(0x30));
assert!(clause.is_in_handler(0x35));
}
#[test]
fn test_exception_clause_finally() {
let clause = ExceptionClause::Finally {
try_offset: 0x00,
try_length: 0x50,
handler_offset: 0x50,
handler_length: 0x10,
};
assert!(clause.is_finally());
assert!(!clause.is_catch());
assert!(clause.catch_type().is_none());
}
#[test]
fn test_instruction_location() {
let loc = InstructionLocation::new(Token::new(0x06000001), 0x0042);
assert_eq!(loc.method, Token::new(0x06000001));
assert_eq!(loc.offset, 0x0042);
}
}