use crate::{
metadata::{
tables::TableId,
token::Token,
typesystem::{CilTypeRc, CilTypeReference},
},
CilObject,
};
pub struct TokenResolver<'a> {
assembly: &'a CilObject,
}
impl<'a> TokenResolver<'a> {
pub(crate) fn new(assembly: &'a CilObject) -> Self {
Self { assembly }
}
#[must_use]
pub fn resolve_type(&self, token: Token) -> Option<CilTypeRc> {
self.assembly.types().resolve(&token)
}
#[must_use]
pub fn resolve_method(&self, token: Token) -> Option<Token> {
match token.table() {
0x06 => Some(token),
0x0A => {
let member_ref = self.assembly.member_ref(&token)?;
let declaring_type = self.resolve_declaring_type(&member_ref.declaredby)?;
declaring_type.methods.iter().find_map(|(_, method_ref)| {
let method = method_ref.upgrade()?;
if method.name == member_ref.name {
Some(method.token)
} else {
None
}
})
}
0x2B => {
let method_spec = self.assembly.method_spec(&token)?;
let underlying = Self::extract_methodspec_token(&method_spec.method)?;
self.resolve_method(underlying)
}
_ => None,
}
}
#[must_use]
pub fn resolve_field(&self, token: Token) -> Option<Token> {
match token.table() {
0x04 => Some(token),
0x0A => {
let member_ref = self.assembly.member_ref(&token)?;
let declaring_type = self.resolve_declaring_type(&member_ref.declaredby)?;
declaring_type.fields.iter().find_map(|(_, field)| {
if field.name == member_ref.name {
Some(field.token)
} else {
None
}
})
}
_ => None,
}
}
#[must_use]
pub fn resolve_memberref_method(&self, token: Token) -> Option<Token> {
if !token.is_table(TableId::MemberRef) {
return None;
}
let member_ref = self.assembly.member_ref(&token)?;
let declaring_type = self.resolve_declaring_type(&member_ref.declaredby)?;
declaring_type.methods.iter().find_map(|(_, method_ref)| {
let method = method_ref.upgrade()?;
if method.name == member_ref.name {
Some(method.token)
} else {
None
}
})
}
#[must_use]
pub fn resolve_memberref_field(&self, token: Token) -> Option<Token> {
if !token.is_table(TableId::MemberRef) {
return None;
}
let member_ref = self.assembly.member_ref(&token)?;
let declaring_type = self.resolve_declaring_type(&member_ref.declaredby)?;
declaring_type.fields.iter().find_map(|(_, field)| {
if field.name == member_ref.name {
Some(field.token)
} else {
None
}
})
}
#[must_use]
pub fn declaring_type(&self, method_token: Token) -> Option<CilTypeRc> {
match method_token.table() {
0x0A => {
let member = self.assembly.member_ref(&method_token)?;
let type_token = match &member.declaredby {
CilTypeReference::TypeDef(r)
| CilTypeReference::TypeRef(r)
| CilTypeReference::TypeSpec(r) => r.upgrade().map(|t| t.token),
_ => member.declaredby.token(),
}?;
self.assembly.types().resolve(&type_token)
}
0x06 => {
for type_info in self.assembly.types().all_types() {
for (_, method_ref) in type_info.methods.iter() {
if let Some(method) = method_ref.upgrade() {
if method.token == method_token {
return self.assembly.types().get(&type_info.token);
}
}
}
}
None
}
0x2B => {
let method_spec = self.assembly.method_spec(&method_token)?;
let underlying = Self::extract_methodspec_token(&method_spec.method)?;
self.declaring_type(underlying)
}
_ => None,
}
}
#[must_use]
pub fn declaring_type_of_field(&self, field_token: Token) -> Option<CilTypeRc> {
match field_token.table() {
0x0A => {
let member = self.assembly.member_ref(&field_token)?;
self.resolve_declaring_type(&member.declaredby)
}
0x04 => {
for type_info in self.assembly.types().all_types() {
for (_, field_rc) in type_info.fields.iter() {
if field_rc.token == field_token {
return self.assembly.types().get(&type_info.token);
}
}
}
None
}
_ => None,
}
}
fn resolve_declaring_type(&self, type_ref: &CilTypeReference) -> Option<CilTypeRc> {
match type_ref {
CilTypeReference::TypeDef(r)
| CilTypeReference::TypeRef(r)
| CilTypeReference::TypeSpec(r) => {
let t = r.upgrade()?;
if t.token.is_table(TableId::TypeRef) {
self.assembly.types().resolve(&t.token)
} else {
Some(t)
}
}
_ => None,
}
}
fn extract_methodspec_token(method_ref: &CilTypeReference) -> Option<Token> {
match method_ref {
CilTypeReference::MethodDef(r) => r.token(),
CilTypeReference::MemberRef(r) => Some(r.token),
_ => None,
}
}
}