use crate::{
container::{Container, SectionKind},
util,
};
#[derive(Debug, Clone)]
pub struct ExceptionRef {
pub type_name: String,
}
const KNOWN_EXCEPTION_TYPES: &[&str] = &[
"AssertionDefect",
"ArithmeticDefect",
"DivByZeroDefect",
"OverflowDefect",
"RangeDefect",
"IndexDefect",
"FieldDefect",
"ObjectConversionDefect",
"ReraiseDefect",
"AccessViolationDefect",
"DeadThreadDefect",
"NilAccessDefect",
"OutOfMemDefect",
"StackOverflowDefect",
"ValueError",
"IOError",
"EOFError",
"OSError",
"KeyError",
"CatchableError",
"Exception",
"Defect",
"FloatingPointDefect",
"FloatInvalidOpDefect",
"FloatDivByZeroDefect",
"FloatOverflowDefect",
"FloatUnderflowDefect",
"FloatInexactDefect",
"ResourceExhaustedError",
"ObjectAssignmentDefect",
];
pub fn scan(container: &Container<'_>) -> Vec<ExceptionRef> {
let mut results = Vec::new();
let mut seen = std::collections::HashSet::new();
for section in container.sections() {
if section.kind != SectionKind::RoData {
continue;
}
scan_section(section.data, &mut results, &mut seen);
}
results
}
fn scan_section(
data: &[u8],
out: &mut Vec<ExceptionRef>,
seen: &mut std::collections::HashSet<String>,
) {
let mut offset = 0;
while offset < data.len() {
let Some(cstr) = util::slice_cstring(data, offset, 256) else {
offset = offset.saturating_add(1);
continue;
};
if cstr.is_empty() {
offset = offset.saturating_add(1);
continue;
}
let advance = cstr.len().saturating_add(1);
if let Ok(s) = std::str::from_utf8(cstr)
&& is_exception_type_name(s)
&& seen.insert(s.to_owned())
{
out.push(ExceptionRef {
type_name: s.to_owned(),
});
}
offset = offset.saturating_add(advance);
}
}
fn is_exception_type_name(s: &str) -> bool {
if s.len() < 3 || !s.as_bytes().first().is_some_and(u8::is_ascii_uppercase) {
return false;
}
if !s.bytes().all(|b| b.is_ascii_alphanumeric()) {
return false;
}
if KNOWN_EXCEPTION_TYPES.contains(&s) {
return true;
}
s.ends_with("Error") || s.ends_with("Defect") || s.ends_with("Exception")
}