use crate::{
emulation::{runtime::hook::PreHookResult, thread::EmulationThread, EmValue, HeapObject},
metadata::{
method::MethodRc,
token::Token,
typesystem::{CilFlavor, CilPrimitiveKind, CilTypeReference},
},
CilObject,
};
pub(crate) fn extract_type_token(thread: &EmulationThread, val: &EmValue) -> Option<Token> {
match val {
EmValue::ObjectRef(href) => thread
.heap()
.get_reflection_type_token(*href)
.unwrap_or_default(),
EmValue::Null => None,
_ => None,
}
}
pub(crate) fn normalize_type_token(thread: &EmulationThread, token: Token) -> Token {
if token.value() & 0xFF00_0000 == 0xF000_0000 {
return token;
}
if matches!(token.table(), 0x01 | 0x02) {
if let Some(asm) = thread.assembly().cloned() {
if let Some(cil_type) = asm.types().resolve(&token) {
if cil_type.namespace == "System" {
if let Some(primitive_token) = bcl_name_to_primitive_token(&cil_type.name) {
return primitive_token;
}
}
}
}
}
token
}
pub(crate) fn bcl_name_to_primitive_token(name: &str) -> Option<Token> {
let kind = match name {
"Boolean" => CilPrimitiveKind::Boolean,
"Char" => CilPrimitiveKind::Char,
"SByte" => CilPrimitiveKind::I1,
"Byte" => CilPrimitiveKind::U1,
"Int16" => CilPrimitiveKind::I2,
"UInt16" => CilPrimitiveKind::U2,
"Int32" => CilPrimitiveKind::I4,
"UInt32" => CilPrimitiveKind::U4,
"Int64" => CilPrimitiveKind::I8,
"UInt64" => CilPrimitiveKind::U8,
"Single" => CilPrimitiveKind::R4,
"Double" => CilPrimitiveKind::R8,
"IntPtr" => CilPrimitiveKind::I,
"UIntPtr" => CilPrimitiveKind::U,
"String" => CilPrimitiveKind::String,
"Object" => CilPrimitiveKind::Object,
"Void" => CilPrimitiveKind::Void,
_ => return None,
};
Some(kind.token())
}
pub(crate) fn find_method_by_name(asm: &CilObject, type_token: Token, name: &str) -> Option<Token> {
if let Some(cil_type) = asm.types().resolve(&type_token) {
let mut best: Option<(Token, usize)> = None;
for (_, method_weak) in cil_type.methods.iter() {
if let Some(method) = method_weak.upgrade() {
if method.name == name {
let param_count = method.signature.params.len();
if best.is_none() || param_count < best.unwrap().1 {
best = Some((method.token, param_count));
}
}
}
}
if let Some((token, _)) = best {
return Some(token);
}
if let Some(base_rc) = cil_type.base() {
return find_method_by_name(asm, base_rc.token, name);
}
}
None
}
pub(crate) fn resolve_method_from_token(method_token: Token, asm: &CilObject) -> Option<MethodRc> {
if let Some(method) = asm.methods().get(&method_token).map(|e| e.value().clone()) {
return Some(method);
}
if let Some(resolved) = asm.resolver().resolve_method(method_token) {
if resolved != method_token {
return asm.methods().get(&resolved).map(|e| e.value().clone());
}
}
None
}
pub(crate) fn alloc_type_array_from_tokens(
thread: &mut EmulationThread,
tokens: &[Token],
) -> PreHookResult {
match thread
.heap_mut()
.alloc_array(CilFlavor::Object, tokens.len())
{
Ok(arr_ref) => {
for (i, token) in tokens.iter().enumerate() {
if let Ok(elem_ref) = thread.heap_mut().alloc_reflection_type(*token, None) {
try_hook!(thread.heap().set_array_element(
arr_ref,
i,
EmValue::ObjectRef(elem_ref)
));
}
}
PreHookResult::Bypass(Some(EmValue::ObjectRef(arr_ref)))
}
Err(e) => PreHookResult::Error(format!("heap allocation failed: {e}")),
}
}
pub(crate) fn unbox_value(thread: &EmulationThread, value: &EmValue) -> EmValue {
if let EmValue::ObjectRef(href) = value {
if let Ok(HeapObject::BoxedValue { value: inner, .. }) = thread.heap().get(*href) {
return *inner;
}
}
value.clone()
}
pub(crate) fn box_value_if_needed(thread: &EmulationThread, value: EmValue) -> EmValue {
match &value {
EmValue::ObjectRef(_) | EmValue::Null => value,
EmValue::I32(_) => {
match thread
.heap()
.alloc_boxed(CilPrimitiveKind::I4.token(), value)
{
Ok(href) => EmValue::ObjectRef(href),
Err(_) => EmValue::Null,
}
}
EmValue::I64(_) => {
match thread
.heap()
.alloc_boxed(CilPrimitiveKind::I8.token(), value)
{
Ok(href) => EmValue::ObjectRef(href),
Err(_) => EmValue::Null,
}
}
_ => value,
}
}
pub(crate) fn resolve_attribute_type_token(
asm: &CilObject,
constructor: &CilTypeReference,
) -> Option<Token> {
match constructor {
CilTypeReference::MethodDef(method_weak) => {
let method = method_weak.upgrade()?;
asm.resolver().declaring_type(method.token).map(|t| t.token)
}
CilTypeReference::MemberRef(member_rc) => match &member_rc.declaredby {
CilTypeReference::TypeDef(r) | CilTypeReference::TypeRef(r) => r.token(),
_ => None,
},
_ => None,
}
}