use crate::{emulation::engine::error::synthetic_exception, metadata::token::Token};
#[must_use]
pub fn parent(token: Token) -> Option<Token> {
match token {
t if t == synthetic_exception::DIVIDE_BY_ZERO => Some(synthetic_exception::ARITHMETIC),
t if t == synthetic_exception::OVERFLOW => Some(synthetic_exception::ARITHMETIC),
t if t == synthetic_exception::END_OF_STREAM => Some(synthetic_exception::IO_EXCEPTION),
t if t == synthetic_exception::FILE_NOT_FOUND => Some(synthetic_exception::IO_EXCEPTION),
t if t == synthetic_exception::DIRECTORY_NOT_FOUND => {
Some(synthetic_exception::IO_EXCEPTION)
}
t if t == synthetic_exception::OBJECT_DISPOSED => {
Some(synthetic_exception::INVALID_OPERATION)
}
t if t == synthetic_exception::ARGUMENT_NULL => {
Some(synthetic_exception::ARGUMENT_EXCEPTION)
}
t if t == synthetic_exception::ARITHMETIC => Some(synthetic_exception::SYSTEM_EXCEPTION),
t if t == synthetic_exception::IO_EXCEPTION => Some(synthetic_exception::SYSTEM_EXCEPTION),
t if t == synthetic_exception::INVALID_OPERATION => {
Some(synthetic_exception::SYSTEM_EXCEPTION)
}
t if t == synthetic_exception::ARGUMENT_EXCEPTION => {
Some(synthetic_exception::SYSTEM_EXCEPTION)
}
t if t == synthetic_exception::INDEX_OUT_OF_RANGE => {
Some(synthetic_exception::SYSTEM_EXCEPTION)
}
t if t == synthetic_exception::NULL_REFERENCE => {
Some(synthetic_exception::SYSTEM_EXCEPTION)
}
t if t == synthetic_exception::INVALID_CAST => Some(synthetic_exception::SYSTEM_EXCEPTION),
t if t == synthetic_exception::FORMAT_EXCEPTION => {
Some(synthetic_exception::SYSTEM_EXCEPTION)
}
t if t == synthetic_exception::NOT_SUPPORTED => Some(synthetic_exception::SYSTEM_EXCEPTION),
t if t == synthetic_exception::KEY_NOT_FOUND => Some(synthetic_exception::SYSTEM_EXCEPTION),
t if t == synthetic_exception::TARGET_INVOCATION => {
Some(synthetic_exception::SYSTEM_EXCEPTION)
}
t if t == synthetic_exception::TYPE_INITIALIZATION => {
Some(synthetic_exception::SYSTEM_EXCEPTION)
}
t if t == synthetic_exception::TYPE_LOAD => Some(synthetic_exception::SYSTEM_EXCEPTION),
t if t == synthetic_exception::MISSING_METHOD => {
Some(synthetic_exception::SYSTEM_EXCEPTION)
}
t if t == synthetic_exception::MISSING_FIELD => Some(synthetic_exception::SYSTEM_EXCEPTION),
t if t == synthetic_exception::NOT_IMPLEMENTED => {
Some(synthetic_exception::SYSTEM_EXCEPTION)
}
t if t == synthetic_exception::SYSTEM_EXCEPTION => {
Some(synthetic_exception::BASE_EXCEPTION)
}
t if t == synthetic_exception::BASE_EXCEPTION => None,
_ => Some(synthetic_exception::BASE_EXCEPTION),
}
}
#[must_use]
pub fn is_synthetic(token: Token) -> bool {
token.value() & 0xFFFF_FF00 == 0x7F01_0000
}
#[must_use]
pub fn is_subtype_of(source: Token, target: Token) -> bool {
if source == target {
return true;
}
let mut current = source;
for _ in 0..5 {
match parent(current) {
Some(p) if p == target => return true,
Some(p) => current = p,
None => return false,
}
}
false
}
#[must_use]
pub fn token_from_fullname(fullname: &str) -> Option<Token> {
match fullname {
"System.Exception" => Some(synthetic_exception::BASE_EXCEPTION),
"System.SystemException" => Some(synthetic_exception::SYSTEM_EXCEPTION),
"System.IndexOutOfRangeException" => Some(synthetic_exception::INDEX_OUT_OF_RANGE),
"System.NullReferenceException" => Some(synthetic_exception::NULL_REFERENCE),
"System.DivideByZeroException" => Some(synthetic_exception::DIVIDE_BY_ZERO),
"System.OverflowException" => Some(synthetic_exception::OVERFLOW),
"System.InvalidCastException" => Some(synthetic_exception::INVALID_CAST),
"System.IO.EndOfStreamException" => Some(synthetic_exception::END_OF_STREAM),
"System.ObjectDisposedException" => Some(synthetic_exception::OBJECT_DISPOSED),
"System.InvalidOperationException" => Some(synthetic_exception::INVALID_OPERATION),
"System.FormatException" => Some(synthetic_exception::FORMAT_EXCEPTION),
"System.ArgumentException" => Some(synthetic_exception::ARGUMENT_EXCEPTION),
"System.ArgumentNullException" => Some(synthetic_exception::ARGUMENT_NULL),
"System.NotSupportedException" => Some(synthetic_exception::NOT_SUPPORTED),
"System.Collections.Generic.KeyNotFoundException" => {
Some(synthetic_exception::KEY_NOT_FOUND)
}
"System.IO.FileNotFoundException" => Some(synthetic_exception::FILE_NOT_FOUND),
"System.IO.DirectoryNotFoundException" => Some(synthetic_exception::DIRECTORY_NOT_FOUND),
"System.Reflection.TargetInvocationException" => {
Some(synthetic_exception::TARGET_INVOCATION)
}
"System.ArithmeticException" => Some(synthetic_exception::ARITHMETIC),
"System.IO.IOException" => Some(synthetic_exception::IO_EXCEPTION),
"System.TypeInitializationException" => Some(synthetic_exception::TYPE_INITIALIZATION),
"System.TypeLoadException" => Some(synthetic_exception::TYPE_LOAD),
"System.MissingMethodException" => Some(synthetic_exception::MISSING_METHOD),
"System.MissingFieldException" => Some(synthetic_exception::MISSING_FIELD),
"System.NotImplementedException" => Some(synthetic_exception::NOT_IMPLEMENTED),
_ => None,
}
}
#[cfg(test)]
mod tests {
use crate::{
emulation::engine::{
error::synthetic_exception,
exceptions::{is_subtype_of, is_synthetic, token_from_fullname},
},
metadata::token::Token,
};
#[test]
fn test_is_synthetic() {
assert!(is_synthetic(synthetic_exception::BASE_EXCEPTION));
assert!(is_synthetic(synthetic_exception::DIVIDE_BY_ZERO));
assert!(is_synthetic(synthetic_exception::ARITHMETIC));
assert!(is_synthetic(synthetic_exception::IO_EXCEPTION));
assert!(!is_synthetic(Token::new(0x0200_0001))); assert!(!is_synthetic(Token::new(0x0600_0001))); }
#[test]
fn test_divide_by_zero_hierarchy() {
assert!(is_subtype_of(
synthetic_exception::DIVIDE_BY_ZERO,
synthetic_exception::DIVIDE_BY_ZERO
));
assert!(is_subtype_of(
synthetic_exception::DIVIDE_BY_ZERO,
synthetic_exception::ARITHMETIC
));
assert!(is_subtype_of(
synthetic_exception::DIVIDE_BY_ZERO,
synthetic_exception::SYSTEM_EXCEPTION
));
assert!(is_subtype_of(
synthetic_exception::DIVIDE_BY_ZERO,
synthetic_exception::BASE_EXCEPTION
));
assert!(!is_subtype_of(
synthetic_exception::DIVIDE_BY_ZERO,
synthetic_exception::IO_EXCEPTION
));
assert!(!is_subtype_of(
synthetic_exception::DIVIDE_BY_ZERO,
synthetic_exception::NULL_REFERENCE
));
}
#[test]
fn test_argument_null_hierarchy() {
assert!(is_subtype_of(
synthetic_exception::ARGUMENT_NULL,
synthetic_exception::ARGUMENT_EXCEPTION
));
assert!(is_subtype_of(
synthetic_exception::ARGUMENT_NULL,
synthetic_exception::SYSTEM_EXCEPTION
));
assert!(is_subtype_of(
synthetic_exception::ARGUMENT_NULL,
synthetic_exception::BASE_EXCEPTION
));
assert!(!is_subtype_of(
synthetic_exception::ARGUMENT_NULL,
synthetic_exception::FORMAT_EXCEPTION
));
}
#[test]
fn test_object_disposed_hierarchy() {
assert!(is_subtype_of(
synthetic_exception::OBJECT_DISPOSED,
synthetic_exception::INVALID_OPERATION
));
assert!(is_subtype_of(
synthetic_exception::OBJECT_DISPOSED,
synthetic_exception::BASE_EXCEPTION
));
}
#[test]
fn test_io_exception_hierarchy() {
assert!(is_subtype_of(
synthetic_exception::END_OF_STREAM,
synthetic_exception::IO_EXCEPTION
));
assert!(is_subtype_of(
synthetic_exception::END_OF_STREAM,
synthetic_exception::BASE_EXCEPTION
));
assert!(is_subtype_of(
synthetic_exception::FILE_NOT_FOUND,
synthetic_exception::IO_EXCEPTION
));
assert!(is_subtype_of(
synthetic_exception::DIRECTORY_NOT_FOUND,
synthetic_exception::IO_EXCEPTION
));
assert!(!is_subtype_of(
synthetic_exception::ARITHMETIC,
synthetic_exception::IO_EXCEPTION
));
}
#[test]
fn test_base_exception_catches_all_synthetic() {
let all_exceptions = [
synthetic_exception::INDEX_OUT_OF_RANGE,
synthetic_exception::NULL_REFERENCE,
synthetic_exception::DIVIDE_BY_ZERO,
synthetic_exception::OVERFLOW,
synthetic_exception::INVALID_CAST,
synthetic_exception::END_OF_STREAM,
synthetic_exception::OBJECT_DISPOSED,
synthetic_exception::INVALID_OPERATION,
synthetic_exception::FORMAT_EXCEPTION,
synthetic_exception::ARGUMENT_EXCEPTION,
synthetic_exception::ARGUMENT_NULL,
synthetic_exception::NOT_SUPPORTED,
synthetic_exception::KEY_NOT_FOUND,
synthetic_exception::FILE_NOT_FOUND,
synthetic_exception::DIRECTORY_NOT_FOUND,
synthetic_exception::TARGET_INVOCATION,
synthetic_exception::ARITHMETIC,
synthetic_exception::IO_EXCEPTION,
synthetic_exception::TYPE_INITIALIZATION,
synthetic_exception::SYSTEM_EXCEPTION,
synthetic_exception::TYPE_LOAD,
synthetic_exception::MISSING_METHOD,
synthetic_exception::MISSING_FIELD,
synthetic_exception::NOT_IMPLEMENTED,
];
for exc in &all_exceptions {
assert!(
is_subtype_of(*exc, synthetic_exception::BASE_EXCEPTION),
"Expected {:08X} to be subtype of BASE_EXCEPTION",
exc.value()
);
}
}
#[test]
fn test_not_cross_hierarchy() {
assert!(!is_subtype_of(
synthetic_exception::DIVIDE_BY_ZERO,
synthetic_exception::IO_EXCEPTION
));
assert!(!is_subtype_of(
synthetic_exception::END_OF_STREAM,
synthetic_exception::ARITHMETIC
));
assert!(!is_subtype_of(
synthetic_exception::INDEX_OUT_OF_RANGE,
synthetic_exception::NULL_REFERENCE
));
}
#[test]
fn test_token_from_fullname() {
assert_eq!(
token_from_fullname("System.DivideByZeroException"),
Some(synthetic_exception::DIVIDE_BY_ZERO)
);
assert_eq!(
token_from_fullname("System.ArithmeticException"),
Some(synthetic_exception::ARITHMETIC)
);
assert_eq!(
token_from_fullname("System.IO.IOException"),
Some(synthetic_exception::IO_EXCEPTION)
);
assert_eq!(token_from_fullname("SomeOther.Type"), None);
}
}