use crate::{
formatting::helpers::{
assembly_scoped_name, format_method_call_sig, format_type_sig, format_typespec_from_blob,
quote_identifier,
},
metadata::{
method::Method,
tables::{MemberRef, MemberRefSignature},
token::Token,
typesystem::{CilFlavor, CilTypeReference},
},
CilObject,
};
pub(super) fn resolve_token(assembly: &CilObject, token: Token) -> Option<String> {
match token.table() {
0x06 => {
assembly.methods().get(&token).map(|entry| {
let method = entry.value();
format_methoddef_ref(method, assembly)
})
}
0x0A => {
assembly
.member_ref(&token)
.map(|mref| format_memberref(&mref, assembly))
}
0x01 | 0x02 => {
assembly
.types()
.get(&token)
.map(|t| assembly_scoped_name(&t, assembly))
}
0x04 => {
assembly.types().iter().find_map(|entry| {
let cil_type = entry.value();
cil_type.fields.iter().find_map(|(_, field)| {
if field.token == token {
let type_name = assembly_scoped_name(cil_type, assembly);
let field_type = format_type_sig(&field.signature.base, assembly);
Some(format!(
"{field_type} {type_name}::{}",
quote_identifier(&field.name)
))
} else {
None
}
})
})
}
0x1B => {
if let Some(formatted) = format_typespec_from_blob(assembly, &token) {
return Some(formatted);
}
assembly.types().get(&token).map(|t| match t.flavor() {
CilFlavor::GenericParameter { index, method } => {
if *method {
format!("!!{index}")
} else {
format!("!{index}")
}
}
_ => assembly_scoped_name(&t, assembly),
})
}
0x2B => {
format_methodspec(assembly, &token)
}
0x11 => {
Some(format!("(0x{:08X})", token.value()))
}
0x70 => {
assembly
.userstrings()
.and_then(|us| us.get(token.row() as usize).ok())
.map(|s| {
let s = s.to_string_lossy();
let mut escaped = String::with_capacity(s.len() + 2);
escaped.push('"');
for ch in s.chars() {
match ch {
'"' => escaped.push_str("\\\""),
'\\' => escaped.push_str("\\\\"),
'\n' => escaped.push_str("\\n"),
'\r' => escaped.push_str("\\r"),
'\t' => escaped.push_str("\\t"),
'\0' => escaped.push_str("\\0"),
c => escaped.push(c),
}
}
escaped.push('"');
escaped
})
}
_ => None,
}
}
fn format_methoddef_ref(method: &Method, asm: &CilObject) -> String {
let declaring_type = method
.declaring_type_rc()
.map(|t| assembly_scoped_name(&t, asm));
format_method_call_sig(
method.signature.has_this && !method.is_static(),
&method.signature.return_type,
declaring_type.as_deref(),
&method.name,
&method.signature.params,
asm,
)
}
fn format_memberref(mref: &MemberRef, asm: &CilObject) -> String {
match &mref.signature {
MemberRefSignature::Method(sig) => {
let declaring = resolve_declaring_type(&mref.declaredby, asm);
format_method_call_sig(
sig.has_this,
&sig.return_type,
Some(&declaring),
&mref.name,
&sig.params,
asm,
)
}
MemberRefSignature::Field(sig) => {
let field_type = format_type_sig(&sig.base, asm);
let declaring = resolve_declaring_type(&mref.declaredby, asm);
format!("{field_type} {declaring}::{}", quote_identifier(&mref.name))
}
}
}
fn format_methodspec(assembly: &CilObject, token: &Token) -> Option<String> {
let spec = assembly.method_spec(token)?;
let base_ref = match &spec.method {
CilTypeReference::MethodDef(method_ref) => {
let method = method_ref.upgrade()?;
Some(format_methoddef_ref(&method, assembly))
}
CilTypeReference::MemberRef(mref) => Some(format_memberref(mref, assembly)),
_ => None,
}?;
if spec.instantiation.generic_args.is_empty() {
return Some(base_ref);
}
let mut result = base_ref;
if let Some(paren_pos) = result.rfind('(') {
let generic_args: Vec<String> = spec
.instantiation
.generic_args
.iter()
.map(|arg| format_type_sig(arg, assembly))
.collect();
let generic_str = format!("<{}>", generic_args.join(", "));
result.insert_str(paren_pos, &generic_str);
}
Some(result)
}
pub(super) fn resolve_declaring_type(declaredby: &CilTypeReference, asm: &CilObject) -> String {
match declaredby {
CilTypeReference::TypeSpec(r) => {
if let Some(token) = r.upgrade().map(|t| t.token) {
if let Some(formatted) = format_typespec_from_blob(asm, &token) {
return formatted;
}
}
r.upgrade()
.map(|t| assembly_scoped_name(&t, asm))
.unwrap_or_else(|| "[?]".to_string())
}
CilTypeReference::TypeRef(r) | CilTypeReference::TypeDef(r) => r
.upgrade()
.map(|t| assembly_scoped_name(&t, asm))
.unwrap_or_else(|| "[?]".to_string()),
_ => {
if let Some(token) = declaredby.token() {
asm.types()
.get(&token)
.map(|t| assembly_scoped_name(&t, asm))
.unwrap_or_else(|| declaredby.fullname().unwrap_or_else(|| "[?]".to_string()))
} else {
declaredby.fullname().unwrap_or_else(|| "[?]".to_string())
}
}
}
}