use std::{
fmt,
hash::{Hash, Hasher},
sync::Arc,
sync::Weak,
};
use crate::{
metadata::{
method::MethodRef,
signatures::{SignatureMethod, TypeSignature},
tables::{
AssemblyRc, AssemblyRefRc, DeclSecurityRc, EventRc, ExportedTypeRc, FieldRc, FileRc,
GenericParamConstraintRc, GenericParamRc, InterfaceImplRc, MemberRefRc, MethodSpecList,
MethodSpecRc, ModuleRc, ModuleRefRc, ParamRc, PropertyRc, StandAloneSigRc,
},
typesystem::{CilPrimitive, CilPrimitiveKind, CilType, CilTypeRc},
},
prelude::{GenericParamList, Token},
};
pub type CilTypeRefList = Arc<boxcar::Vec<CilTypeRef>>;
#[derive(Clone, Debug)]
pub struct CilTypeRef {
weak_ref: Weak<CilType>,
}
impl CilTypeRef {
pub fn new(strong_ref: &CilTypeRc) -> Self {
Self {
weak_ref: Arc::downgrade(strong_ref),
}
}
#[must_use]
pub fn upgrade(&self) -> Option<CilTypeRc> {
self.weak_ref.upgrade()
}
#[must_use]
pub fn expect(&self, msg: &str) -> CilTypeRc {
self.weak_ref.upgrade().expect(msg)
}
#[must_use]
pub fn is_valid(&self) -> bool {
self.weak_ref.strong_count() > 0
}
#[must_use]
pub fn token(&self) -> Option<Token> {
self.upgrade().map(|t| t.token)
}
#[must_use]
pub fn name(&self) -> Option<String> {
self.upgrade().map(|t| t.name.clone())
}
#[must_use]
pub fn namespace(&self) -> Option<String> {
self.upgrade().map(|t| t.namespace.clone())
}
#[must_use]
pub fn fullname(&self) -> Option<String> {
self.upgrade().map(|t| t.fullname())
}
#[must_use]
pub fn nested_types(&self) -> Option<CilTypeRefList> {
self.upgrade().map(|t| t.nested_types.clone())
}
#[must_use]
pub fn generic_params(&self) -> Option<GenericParamList> {
self.upgrade().map(|t| t.generic_params.clone())
}
#[must_use]
pub fn generic_args(&self) -> Option<MethodSpecList> {
self.upgrade().map(|t| t.generic_args.clone())
}
#[must_use]
pub fn is_compatible_with(&self, other: &CilType) -> bool {
if let Some(this_type) = self.upgrade() {
this_type.is_compatible_with(other)
} else {
false
}
}
#[must_use]
pub fn accepts_constant(&self, constant: &CilPrimitive) -> bool {
if let Some(this_type) = self.upgrade() {
this_type.accepts_constant(constant)
} else {
false
}
}
}
impl From<CilTypeRc> for CilTypeRef {
fn from(strong_ref: CilTypeRc) -> Self {
Self::new(&strong_ref)
}
}
pub struct CilTypeRefListIter<'a> {
list: &'a CilTypeRefList,
index: usize,
}
impl<'a> CilTypeRefListIter<'a> {
#[must_use]
pub fn new(list: &'a CilTypeRefList) -> Self {
Self { list, index: 0 }
}
}
impl<'a> Iterator for CilTypeRefListIter<'a> {
type Item = &'a CilTypeRef;
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.list.count() {
let result = self.list.get(self.index);
self.index += 1;
result
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.list.count().saturating_sub(self.index);
(remaining, Some(remaining))
}
}
impl ExactSizeIterator for CilTypeRefListIter<'_> {
fn len(&self) -> usize {
self.list.count().saturating_sub(self.index)
}
}
#[derive(Debug, Clone, PartialEq, Default, Hash)]
pub struct ArrayDimensions {
pub size: Option<u32>,
pub lower_bound: Option<u32>,
}
#[derive(Clone)]
pub enum CilTypeReference {
TypeRef(CilTypeRef),
TypeDef(CilTypeRef),
TypeSpec(CilTypeRef),
Field(FieldRc),
Param(ParamRc),
Property(PropertyRc),
MethodDef(MethodRef),
InterfaceImpl(InterfaceImplRc),
MemberRef(MemberRefRc),
Module(ModuleRc),
DeclSecurity(DeclSecurityRc),
Event(EventRc),
StandAloneSig(StandAloneSigRc),
ModuleRef(ModuleRefRc),
Assembly(AssemblyRc),
AssemblyRef(AssemblyRefRc),
File(FileRc),
ExportedType(ExportedTypeRc),
GenericParam(GenericParamRc),
GenericParamConstraint(GenericParamConstraintRc),
MethodSpec(MethodSpecRc),
None,
}
impl CilTypeReference {
#[must_use]
pub fn token(&self) -> Option<Token> {
match self {
Self::TypeRef(r) | Self::TypeDef(r) | Self::TypeSpec(r) => r.token(),
Self::Field(r) => Some(r.token),
Self::Param(r) => Some(r.token),
Self::Property(r) => Some(r.token),
Self::MethodDef(r) => r.token(),
Self::InterfaceImpl(r) => Some(r.token),
Self::MemberRef(r) => Some(r.token),
Self::Module(r) => Some(r.token),
Self::DeclSecurity(r) => Some(r.token),
Self::Event(r) => Some(r.token),
Self::StandAloneSig(r) => Some(r.token),
Self::ModuleRef(r) => Some(r.token),
Self::Assembly(r) => Some(r.token),
Self::AssemblyRef(r) => Some(r.token),
Self::File(r) => Some(r.token),
Self::ExportedType(r) => Some(r.token),
Self::GenericParam(r) => Some(r.token),
Self::GenericParamConstraint(r) => Some(r.token),
Self::MethodSpec(r) => Some(r.token),
Self::None => None,
}
}
#[must_use]
pub fn name(&self) -> Option<String> {
match self {
Self::TypeRef(r) | Self::TypeDef(r) | Self::TypeSpec(r) => r.name(),
Self::Field(r) => Some(r.name.clone()),
Self::Param(r) => r.name.clone(),
Self::Property(r) => Some(r.name.clone()),
Self::MethodDef(r) => r.name(),
Self::MemberRef(r) => Some(r.name.clone()),
Self::Module(r) => Some(r.name.clone()),
Self::Event(r) => Some(r.name.clone()),
Self::ModuleRef(r) => Some(r.name.clone()),
Self::Assembly(r) => Some(r.name.clone()),
Self::AssemblyRef(r) => Some(r.name.clone()),
Self::File(r) => Some(r.name.clone()),
Self::ExportedType(r) => Some(r.name.clone()),
Self::GenericParam(r) => Some(r.name.clone()),
_ => None,
}
}
#[must_use]
pub fn fullname(&self) -> Option<String> {
match self {
Self::TypeRef(r) | Self::TypeDef(r) | Self::TypeSpec(r) => r.fullname(),
_ => self.name(),
}
}
}
#[allow(non_snake_case, dead_code)]
pub mod ELEMENT_TYPE {
pub const END: u8 = 0x00;
pub const VOID: u8 = 0x01;
pub const BOOLEAN: u8 = 0x02;
pub const CHAR: u8 = 0x03;
pub const I1: u8 = 0x04;
pub const U1: u8 = 0x05;
pub const I2: u8 = 0x06;
pub const U2: u8 = 0x07;
pub const I4: u8 = 0x08;
pub const U4: u8 = 0x09;
pub const I8: u8 = 0x0a;
pub const U8: u8 = 0x0b;
pub const R4: u8 = 0x0c;
pub const R8: u8 = 0x0d;
pub const STRING: u8 = 0x0e;
pub const PTR: u8 = 0x0f;
pub const BYREF: u8 = 0x10;
pub const VALUETYPE: u8 = 0x11;
pub const CLASS: u8 = 0x12;
pub const VAR: u8 = 0x13;
pub const ARRAY: u8 = 0x14;
pub const GENERICINST: u8 = 0x15;
pub const TYPEDBYREF: u8 = 0x16;
pub const I: u8 = 0x18;
pub const U: u8 = 0x19;
pub const FNPTR: u8 = 0x1b;
pub const OBJECT: u8 = 0x1c;
pub const SZARRAY: u8 = 0x1d;
pub const MVAR: u8 = 0x1e;
pub const CMOD_REQD: u8 = 0x1f;
pub const CMOD_OPT: u8 = 0x20;
pub const INTERNAL: u8 = 0x21;
pub const MODIFIER: u8 = 0x40;
pub const SENTINEL: u8 = 0x41;
pub const PINNED: u8 = 0x45;
}
pub struct CilModifier {
pub required: bool,
pub modifier: CilTypeRef,
}
#[derive(Debug, Clone, PartialEq)]
pub enum CilFlavor {
Void,
Boolean,
Char,
I1,
U1,
I2,
U2,
I4,
U4,
I8,
U8,
R4,
R8,
I,
U,
Object,
String,
Array {
element_type: Box<CilFlavor>,
rank: u32,
dimensions: Vec<ArrayDimensions>,
},
Pointer,
ByRef,
GenericInstance,
Pinned,
FnPtr {
signature: SignatureMethod,
},
GenericParameter {
index: u32,
method: bool,
},
Class,
ValueType,
Interface,
TypedRef {
target_type: Option<Box<CilFlavor>>,
},
Unknown,
}
impl Hash for CilFlavor {
fn hash<H: Hasher>(&self, state: &mut H) {
std::mem::discriminant(self).hash(state);
match self {
CilFlavor::Array {
element_type,
rank,
dimensions,
} => {
element_type.hash(state);
rank.hash(state);
dimensions.hash(state);
}
CilFlavor::FnPtr { signature } => {
signature.param_count.hash(state);
signature.param_count_generic.hash(state);
signature.has_this.hash(state);
signature.explicit_this.hash(state);
signature.vararg.hash(state);
}
CilFlavor::GenericParameter { index, method } => {
index.hash(state);
method.hash(state);
}
_ => {}
}
}
}
impl CilFlavor {
#[must_use]
pub fn is_primitive(&self) -> bool {
matches!(
self,
CilFlavor::Void
| CilFlavor::Boolean
| CilFlavor::Char
| CilFlavor::I1
| CilFlavor::U1
| CilFlavor::I2
| CilFlavor::U2
| CilFlavor::I4
| CilFlavor::U4
| CilFlavor::I8
| CilFlavor::U8
| CilFlavor::R4
| CilFlavor::R8
| CilFlavor::I
| CilFlavor::U
| CilFlavor::Object
| CilFlavor::String
)
}
#[must_use]
pub fn is_value_type(&self) -> bool {
matches!(
self,
CilFlavor::Boolean
| CilFlavor::Char
| CilFlavor::I1
| CilFlavor::U1
| CilFlavor::I2
| CilFlavor::U2
| CilFlavor::I4
| CilFlavor::U4
| CilFlavor::I8
| CilFlavor::U8
| CilFlavor::R4
| CilFlavor::R8
| CilFlavor::I
| CilFlavor::U
| CilFlavor::ValueType
)
}
#[must_use]
pub fn is_reference_type(&self) -> bool {
matches!(
self,
CilFlavor::Object | CilFlavor::String | CilFlavor::Class | CilFlavor::Array { .. }
)
}
#[must_use]
pub fn to_primitive_kind(&self) -> Option<CilPrimitiveKind> {
match self {
CilFlavor::Void => Some(CilPrimitiveKind::Void),
CilFlavor::Boolean => Some(CilPrimitiveKind::Boolean),
CilFlavor::Char => Some(CilPrimitiveKind::Char),
CilFlavor::I1 => Some(CilPrimitiveKind::I1),
CilFlavor::U1 => Some(CilPrimitiveKind::U1),
CilFlavor::I2 => Some(CilPrimitiveKind::I2),
CilFlavor::U2 => Some(CilPrimitiveKind::U2),
CilFlavor::I4 => Some(CilPrimitiveKind::I4),
CilFlavor::U4 => Some(CilPrimitiveKind::U4),
CilFlavor::I8 => Some(CilPrimitiveKind::I8),
CilFlavor::U8 => Some(CilPrimitiveKind::U8),
CilFlavor::R4 => Some(CilPrimitiveKind::R4),
CilFlavor::R8 => Some(CilPrimitiveKind::R8),
CilFlavor::I => Some(CilPrimitiveKind::I),
CilFlavor::U => Some(CilPrimitiveKind::U),
CilFlavor::Object => Some(CilPrimitiveKind::Object),
CilFlavor::String => Some(CilPrimitiveKind::String),
CilFlavor::ValueType => Some(CilPrimitiveKind::ValueType),
CilFlavor::GenericParameter { method, .. } => {
if *method {
Some(CilPrimitiveKind::MVar)
} else {
Some(CilPrimitiveKind::Var)
}
}
_ => None,
}
}
#[must_use]
pub fn is_compatible_with(&self, target: &CilFlavor) -> bool {
if self == target {
return true;
}
#[allow(clippy::match_same_arms)]
match (self, target) {
(CilFlavor::I1, CilFlavor::U1) | (CilFlavor::U1, CilFlavor::I1) => true,
(CilFlavor::I2, CilFlavor::U2) | (CilFlavor::U2, CilFlavor::I2) => true,
(CilFlavor::I4, CilFlavor::U4) | (CilFlavor::U4, CilFlavor::I4) => true,
(CilFlavor::I8, CilFlavor::U8) | (CilFlavor::U8, CilFlavor::I8) => true,
(CilFlavor::I4, CilFlavor::I1 | CilFlavor::U1 | CilFlavor::I2 | CilFlavor::U2) => true,
(CilFlavor::I1 | CilFlavor::U1, CilFlavor::I2 | CilFlavor::I4 | CilFlavor::I8) => true,
(CilFlavor::I2, CilFlavor::I4 | CilFlavor::I8) => true,
(CilFlavor::I4, CilFlavor::I8) => true,
(CilFlavor::U1, CilFlavor::U2 | CilFlavor::U4 | CilFlavor::U8) => true,
(CilFlavor::U2, CilFlavor::U4 | CilFlavor::U8) => true,
(CilFlavor::U4, CilFlavor::U8) => true,
(CilFlavor::R4, CilFlavor::R8) => true,
(
CilFlavor::I1 | CilFlavor::U1 | CilFlavor::I2 | CilFlavor::U2 | CilFlavor::I4,
CilFlavor::R4 | CilFlavor::R8,
) => true,
(CilFlavor::I8 | CilFlavor::U4 | CilFlavor::U8, CilFlavor::R8) => true,
(source, CilFlavor::Object) if source.is_reference_type() => true,
(source, CilFlavor::ValueType) if source.is_value_type() => true,
(_, CilFlavor::GenericParameter { .. }) => true,
(CilFlavor::GenericParameter { .. }, _) => true,
_ => false,
}
}
#[must_use]
pub fn accepts_constant(&self, constant_flavor: &CilFlavor) -> bool {
if self == constant_flavor {
return true;
}
#[allow(clippy::match_same_arms)]
match (constant_flavor, self) {
(CilFlavor::I1, CilFlavor::I2 | CilFlavor::I4 | CilFlavor::I8) => true,
(CilFlavor::I2, CilFlavor::I4 | CilFlavor::I8) => true,
(CilFlavor::I4, CilFlavor::I8) => true,
(CilFlavor::U1, CilFlavor::U2 | CilFlavor::U4 | CilFlavor::U8) => true,
(CilFlavor::U2, CilFlavor::U4 | CilFlavor::U8) => true,
(CilFlavor::U4, CilFlavor::U8) => true,
(CilFlavor::R4, CilFlavor::R8) => true,
(CilFlavor::String, CilFlavor::Object) => true,
_ => false,
}
}
#[must_use]
pub fn is_stack_assignable_from(&self, value_flavor: &CilFlavor) -> bool {
if self == value_flavor {
return true;
}
match (self, value_flavor) {
(
CilFlavor::I1
| CilFlavor::U1
| CilFlavor::I2
| CilFlavor::U2
| CilFlavor::I4
| CilFlavor::U4
| CilFlavor::Boolean
| CilFlavor::Char,
CilFlavor::I4,
)
| (
CilFlavor::Object
| CilFlavor::String
| CilFlavor::Class
| CilFlavor::Interface
| CilFlavor::ByRef
| CilFlavor::Array { .. },
CilFlavor::Object,
)
| (
CilFlavor::Object,
CilFlavor::String
| CilFlavor::Class
| CilFlavor::Interface
| CilFlavor::ByRef
| CilFlavor::Array { .. },
)
| (CilFlavor::Pointer, CilFlavor::I | CilFlavor::U)
| (CilFlavor::I | CilFlavor::U, CilFlavor::Pointer)
| (CilFlavor::I, CilFlavor::U)
| (CilFlavor::U, CilFlavor::I)
| (CilFlavor::GenericParameter { .. }, _)
| (_, CilFlavor::GenericParameter { .. }) => true,
_ => false,
}
}
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
CilFlavor::Void => "void",
CilFlavor::Boolean => "bool",
CilFlavor::Char => "char",
CilFlavor::I1 => "int8",
CilFlavor::U1 => "uint8",
CilFlavor::I2 => "int16",
CilFlavor::U2 => "uint16",
CilFlavor::I4 => "int32",
CilFlavor::U4 => "uint32",
CilFlavor::I8 => "int64",
CilFlavor::U8 => "uint64",
CilFlavor::R4 => "float32",
CilFlavor::R8 => "float64",
CilFlavor::I => "native int",
CilFlavor::U => "native uint",
CilFlavor::Object => "object",
CilFlavor::String => "string",
CilFlavor::Pointer => "ptr",
CilFlavor::ByRef => "byref",
CilFlavor::ValueType => "valuetype",
CilFlavor::Class => "class",
CilFlavor::Interface => "interface",
CilFlavor::Array { .. } => "array",
CilFlavor::GenericInstance => "generic",
CilFlavor::GenericParameter { .. } => "typeparam",
CilFlavor::TypedRef { .. } => "typedref",
CilFlavor::FnPtr { .. } => "fnptr",
CilFlavor::Pinned => "pinned",
CilFlavor::Unknown => "unknown",
}
}
#[must_use]
pub fn element_size(&self, ptr_size: PointerSize) -> Option<usize> {
match self {
CilFlavor::Boolean | CilFlavor::I1 | CilFlavor::U1 => Some(1),
CilFlavor::Char | CilFlavor::I2 | CilFlavor::U2 => Some(2),
CilFlavor::I4 | CilFlavor::U4 | CilFlavor::R4 => Some(4),
CilFlavor::I8 | CilFlavor::U8 | CilFlavor::R8 => Some(8),
CilFlavor::I | CilFlavor::U => Some(ptr_size.bytes()),
_ => None,
}
}
}
impl fmt::Display for CilFlavor {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl From<&TypeSignature> for CilFlavor {
fn from(sig: &TypeSignature) -> Self {
match sig {
TypeSignature::Void | TypeSignature::Sentinel => CilFlavor::Void,
TypeSignature::Boolean => CilFlavor::Boolean,
TypeSignature::Char => CilFlavor::Char,
TypeSignature::I1 => CilFlavor::I1,
TypeSignature::U1 => CilFlavor::U1,
TypeSignature::I2 => CilFlavor::I2,
TypeSignature::U2 => CilFlavor::U2,
TypeSignature::I4 => CilFlavor::I4,
TypeSignature::U4 => CilFlavor::U4,
TypeSignature::I8 => CilFlavor::I8,
TypeSignature::U8 => CilFlavor::U8,
TypeSignature::R4 => CilFlavor::R4,
TypeSignature::R8 => CilFlavor::R8,
TypeSignature::I => CilFlavor::I,
TypeSignature::U => CilFlavor::U,
TypeSignature::String => CilFlavor::String,
TypeSignature::Object | TypeSignature::Boxed => CilFlavor::Object,
TypeSignature::Class(_) | TypeSignature::Type => CilFlavor::Class,
TypeSignature::ValueType(_) => CilFlavor::ValueType,
TypeSignature::Ptr(_) => CilFlavor::Pointer,
TypeSignature::ByRef(_) => CilFlavor::ByRef,
TypeSignature::TypedByRef => CilFlavor::TypedRef { target_type: None },
TypeSignature::FnPtr(method_sig) => CilFlavor::FnPtr {
signature: (**method_sig).clone(),
},
TypeSignature::SzArray(sz_arr) => CilFlavor::Array {
element_type: Box::new(CilFlavor::from(sz_arr.base.as_ref())),
rank: 1,
dimensions: vec![],
},
TypeSignature::Array(arr) => CilFlavor::Array {
element_type: Box::new(CilFlavor::from(arr.base.as_ref())),
rank: arr.rank,
dimensions: arr
.dimensions
.iter()
.map(|dim| ArrayDimensions {
size: dim.size,
lower_bound: dim.lower_bound,
})
.collect(),
},
TypeSignature::GenericInst(_, _) => CilFlavor::GenericInstance,
TypeSignature::GenericParamType(index) => CilFlavor::GenericParameter {
method: false,
index: *index,
},
TypeSignature::GenericParamMethod(index) => CilFlavor::GenericParameter {
method: true,
index: *index,
},
TypeSignature::Pinned(inner) => {
let inner_flavor = CilFlavor::from(inner.as_ref());
match inner_flavor {
CilFlavor::Unknown => CilFlavor::Pinned,
_ => inner_flavor,
}
}
TypeSignature::ModifiedRequired(_) | TypeSignature::ModifiedOptional(_) => {
CilFlavor::Unknown
}
TypeSignature::Unknown
| TypeSignature::Internal
| TypeSignature::Modifier
| TypeSignature::Reserved
| TypeSignature::Field => CilFlavor::Unknown,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PointerSize {
Bit32,
Bit64,
}
impl PointerSize {
#[must_use]
pub fn from_pe(is_64bit: bool) -> Self {
if is_64bit {
Self::Bit64
} else {
Self::Bit32
}
}
#[must_use]
pub fn bytes(self) -> usize {
match self {
Self::Bit32 => 4,
Self::Bit64 => 8,
}
}
#[must_use]
pub fn bits(self) -> u32 {
match self {
Self::Bit32 => 32,
Self::Bit64 => 64,
}
}
#[must_use]
pub fn mask_signed(self, value: i64) -> i64 {
match self {
Self::Bit32 => {
#[allow(clippy::cast_possible_truncation)]
let truncated = value as i32;
i64::from(truncated)
}
Self::Bit64 => value,
}
}
#[must_use]
pub fn mask_unsigned(self, value: u64) -> u64 {
match self {
Self::Bit32 => {
#[allow(clippy::cast_possible_truncation)]
let truncated = value as u32;
u64::from(truncated)
}
Self::Bit64 => value,
}
}
}