use crate::metadata::{
signatures::{CustomModifier, SignatureLocalVariable, TypeSignature},
typesystem::{CilFlavor, CilModifier, CilTypeRef, CilTypeRefList},
};
pub const METHOD_IMPL_CODE_TYPE_MASK: u32 = 0x0003;
pub const METHOD_IMPL_MANAGED_MASK: u32 = 0x0004;
pub const METHOD_ACCESS_MASK: u32 = 0x0007;
pub const METHOD_VTABLE_LAYOUT_MASK: u32 = 0x0100;
metadata_flags! {
pub struct MethodImplCodeType(u32);
}
impl MethodImplCodeType {
pub const IL: Self = Self(0x0000);
pub const NATIVE: Self = Self(0x0001);
pub const OPTIL: Self = Self(0x0002);
pub const RUNTIME: Self = Self(0x0003);
}
impl MethodImplCodeType {
#[must_use]
pub fn from_impl_flags(flags: u32) -> Self {
let code_type = flags & METHOD_IMPL_CODE_TYPE_MASK;
Self::new(code_type)
}
}
metadata_flags! {
pub struct MethodImplManagement(u32);
}
impl MethodImplManagement {
pub const UNMANAGED: Self = Self(0x0004);
}
impl MethodImplManagement {
#[must_use]
pub fn from_impl_flags(flags: u32) -> Self {
let management = flags & METHOD_IMPL_MANAGED_MASK;
Self::new(management)
}
}
metadata_flags! {
pub struct MethodImplOptions(u32);
}
impl MethodImplOptions {
pub const NO_INLINING: Self = Self(0x0008);
pub const FORWARD_REF: Self = Self(0x0010);
pub const SYNCHRONIZED: Self = Self(0x0020);
pub const NO_OPTIMIZATION: Self = Self(0x0040);
pub const PRESERVE_SIG: Self = Self(0x0080);
pub const AGGRESSIVE_INLINING: Self = Self(0x0100);
pub const AGGRESSIVE_OPTIMIZATION: Self = Self(0x0200);
pub const INTERNAL_CALL: Self = Self(0x1000);
pub const MAX_METHOD_IMPL_VAL: Self = Self(0xFFFF);
}
impl MethodImplOptions {
#[must_use]
pub fn from_impl_flags(flags: u32) -> Self {
let options = flags & !(METHOD_IMPL_CODE_TYPE_MASK | METHOD_IMPL_MANAGED_MASK);
Self::new(options)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct MethodAccessFlags(u32);
impl MethodAccessFlags {
pub const COMPILER_CONTROLLED: Self = Self(0x0000);
pub const PRIVATE: Self = Self(0x0001);
pub const FAMILY_AND_ASSEMBLY: Self = Self(0x0002);
pub const ASSEMBLY: Self = Self(0x0003);
pub const FAMILY: Self = Self(0x0004);
pub const FAMILY_OR_ASSEMBLY: Self = Self(0x0005);
pub const PUBLIC: Self = Self(0x0006);
#[must_use]
pub const fn bits(self) -> u32 {
self.0
}
#[must_use]
pub fn ilasm_keyword(self) -> &'static str {
match self {
Self::COMPILER_CONTROLLED => "privatescope",
Self::PRIVATE => "private",
Self::FAMILY_AND_ASSEMBLY => "famandassem",
Self::ASSEMBLY => "assembly",
Self::FAMILY => "family",
Self::FAMILY_OR_ASSEMBLY => "famorassem",
Self::PUBLIC => "public",
_ => "private",
}
}
}
impl MethodAccessFlags {
#[must_use]
pub fn from_method_flags(flags: u32) -> Self {
let access = flags & METHOD_ACCESS_MASK;
match access {
0x0000 => Self::COMPILER_CONTROLLED,
0x0001 => Self::PRIVATE,
0x0002 => Self::FAMILY_AND_ASSEMBLY,
0x0003 => Self::ASSEMBLY,
0x0004 => Self::FAMILY,
0x0005 => Self::FAMILY_OR_ASSEMBLY,
0x0006 => Self::PUBLIC,
_ => {
Self(access)
}
}
}
#[must_use]
pub const fn access_level(self) -> MethodAccessLevel {
match self.0 {
0x0001 => MethodAccessLevel::Private,
0x0002 => MethodAccessLevel::FamilyAndAssembly,
0x0003 => MethodAccessLevel::Assembly,
0x0004 => MethodAccessLevel::Family,
0x0005 => MethodAccessLevel::FamilyOrAssembly,
0x0006 => MethodAccessLevel::Public,
_ => MethodAccessLevel::CompilerControlled,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum MethodAccessLevel {
CompilerControlled,
Private,
FamilyAndAssembly,
Assembly,
Family,
FamilyOrAssembly,
Public,
}
impl MethodAccessLevel {
#[must_use]
pub const fn as_str(&self) -> &'static str {
match self {
MethodAccessLevel::CompilerControlled => "compilercontrolled",
MethodAccessLevel::Private => "private",
MethodAccessLevel::FamilyAndAssembly => "private protected",
MethodAccessLevel::Assembly => "internal",
MethodAccessLevel::Family => "protected",
MethodAccessLevel::FamilyOrAssembly => "protected internal",
MethodAccessLevel::Public => "public",
}
}
}
impl std::fmt::Display for MethodAccessLevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl PartialOrd for MethodAccessFlags {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for MethodAccessFlags {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.bits().cmp(&other.bits())
}
}
impl std::fmt::Display for MethodAccessFlags {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match *self {
Self::COMPILER_CONTROLLED => "compilercontrolled",
Self::PRIVATE => "private",
Self::FAMILY_AND_ASSEMBLY => "private protected",
Self::ASSEMBLY => "internal",
Self::FAMILY => "protected",
Self::FAMILY_OR_ASSEMBLY => "protected internal",
Self::PUBLIC => "public",
_ => "unknown",
};
f.write_str(s)
}
}
impl std::fmt::Debug for MethodAccessFlags {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = match self.bits() {
0x0000 => "COMPILER_CONTROLLED",
0x0001 => "PRIVATE",
0x0002 => "FAMILY_AND_ASSEMBLY",
0x0003 => "ASSEMBLY",
0x0004 => "FAMILY",
0x0005 => "FAMILY_OR_ASSEMBLY",
0x0006 => "PUBLIC",
_ => return write!(f, "MethodAccessFlags(0x{:04X})", self.bits()),
};
write!(f, "MethodAccessFlags({name})")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_method_access_flags_ordering() {
assert!(MethodAccessFlags::COMPILER_CONTROLLED < MethodAccessFlags::PRIVATE);
assert!(MethodAccessFlags::PRIVATE < MethodAccessFlags::FAMILY_AND_ASSEMBLY);
assert!(MethodAccessFlags::FAMILY_AND_ASSEMBLY < MethodAccessFlags::ASSEMBLY);
assert!(MethodAccessFlags::ASSEMBLY < MethodAccessFlags::FAMILY);
assert!(MethodAccessFlags::FAMILY < MethodAccessFlags::FAMILY_OR_ASSEMBLY);
assert!(MethodAccessFlags::FAMILY_OR_ASSEMBLY < MethodAccessFlags::PUBLIC);
assert!(MethodAccessFlags::PUBLIC >= MethodAccessFlags::PRIVATE);
assert!(MethodAccessFlags::FAMILY >= MethodAccessFlags::PRIVATE);
assert!(MethodAccessFlags::PRIVATE < MethodAccessFlags::PUBLIC);
assert_eq!(MethodAccessFlags::PUBLIC, MethodAccessFlags::PUBLIC);
assert!(MethodAccessFlags::PUBLIC >= MethodAccessFlags::PUBLIC);
}
#[test]
fn test_method_access_flags_access_level() {
assert_eq!(
MethodAccessFlags::COMPILER_CONTROLLED.access_level(),
MethodAccessLevel::CompilerControlled
);
assert_eq!(
MethodAccessFlags::PRIVATE.access_level(),
MethodAccessLevel::Private
);
assert_eq!(
MethodAccessFlags::FAMILY_AND_ASSEMBLY.access_level(),
MethodAccessLevel::FamilyAndAssembly
);
assert_eq!(
MethodAccessFlags::ASSEMBLY.access_level(),
MethodAccessLevel::Assembly
);
assert_eq!(
MethodAccessFlags::FAMILY.access_level(),
MethodAccessLevel::Family
);
assert_eq!(
MethodAccessFlags::FAMILY_OR_ASSEMBLY.access_level(),
MethodAccessLevel::FamilyOrAssembly
);
assert_eq!(
MethodAccessFlags::PUBLIC.access_level(),
MethodAccessLevel::Public
);
assert_eq!(
MethodAccessFlags::from_method_flags(0x0091).access_level(),
MethodAccessLevel::Private
);
}
#[test]
fn test_method_access_level_stable_strings() {
assert_eq!(
MethodAccessLevel::CompilerControlled.as_str(),
"compilercontrolled"
);
assert_eq!(MethodAccessLevel::Private.as_str(), "private");
assert_eq!(
MethodAccessLevel::FamilyAndAssembly.as_str(),
"private protected"
);
assert_eq!(MethodAccessLevel::Assembly.as_str(), "internal");
assert_eq!(MethodAccessLevel::Family.as_str(), "protected");
assert_eq!(
MethodAccessLevel::FamilyOrAssembly.as_str(),
"protected internal"
);
assert_eq!(MethodAccessLevel::Public.as_str(), "public");
assert_eq!(format!("{}", MethodAccessLevel::Public), "public");
assert_eq!(
format!("{}", MethodAccessLevel::Public),
format!("{}", MethodAccessFlags::PUBLIC)
);
assert_eq!(
format!("{}", MethodAccessLevel::FamilyAndAssembly),
format!("{}", MethodAccessFlags::FAMILY_AND_ASSEMBLY)
);
}
}
metadata_flags! {
pub struct MethodVtableFlags(u32);
}
impl MethodVtableFlags {
pub const REUSE_SLOT: Self = Self(0x0000);
pub const NEW_SLOT: Self = Self(0x0100);
}
impl MethodVtableFlags {
#[must_use]
pub fn from_method_flags(flags: u32) -> Self {
let vtable = flags & METHOD_VTABLE_LAYOUT_MASK;
Self::new(vtable)
}
}
metadata_flags! {
pub struct MethodModifiers(u32);
}
impl MethodModifiers {
pub const STATIC: Self = Self(0x0010);
pub const FINAL: Self = Self(0x0020);
pub const VIRTUAL: Self = Self(0x0040);
pub const HIDE_BY_SIG: Self = Self(0x0080);
pub const STRICT: Self = Self(0x0200);
pub const ABSTRACT: Self = Self(0x0400);
pub const SPECIAL_NAME: Self = Self(0x0800);
pub const RTSPECIAL_NAME: Self = Self(0x1000);
pub const PINVOKE_IMPL: Self = Self(0x2000);
pub const HAS_SECURITY: Self = Self(0x4000);
pub const REQUIRE_SEC_OBJECT: Self = Self(0x8000);
pub const UNMANAGED_EXPORT: Self = Self(0x0008);
}
impl MethodModifiers {
#[must_use]
pub fn from_method_flags(flags: u32) -> Self {
let modifiers = flags & !METHOD_ACCESS_MASK & !METHOD_VTABLE_LAYOUT_MASK;
Self::new(modifiers)
}
}
metadata_flags! {
pub struct MethodBodyFlags(u16);
}
impl MethodBodyFlags {
pub const TINY_FORMAT: Self = Self(0x2);
pub const FAT_FORMAT: Self = Self(0x3);
pub const MORE_SECTS: Self = Self(0x8);
pub const INIT_LOCALS: Self = Self(0x10);
}
metadata_flags! {
pub struct SectionFlags(u8);
}
impl SectionFlags {
pub const EHTABLE: Self = Self(0x1);
pub const OPT_ILTABLE: Self = Self(0x2);
pub const FAT_FORMAT: Self = Self(0x40);
pub const MORE_SECTS: Self = Self(0x80);
}
pub struct LocalVariable {
pub modifiers: Vec<CilModifier>,
pub is_byref: bool,
pub is_pinned: bool,
pub base: CilTypeRef,
}
impl LocalVariable {
#[must_use]
pub fn to_signature_local(&self) -> Option<SignatureLocalVariable> {
let modifiers: Vec<CustomModifier> = self
.modifiers
.iter()
.filter_map(|m| {
m.modifier.token().map(|token| CustomModifier {
is_required: m.required,
modifier_type: token,
})
})
.collect();
let base = Self::type_ref_to_signature(&self.base)?;
Some(SignatureLocalVariable {
modifiers,
is_byref: self.is_byref,
is_pinned: self.is_pinned,
base,
})
}
fn type_ref_to_signature(type_ref: &CilTypeRef) -> Option<TypeSignature> {
let cil_type = type_ref.upgrade()?;
let token = cil_type.token;
let flavor = cil_type.flavor();
Some(match flavor {
CilFlavor::Void => TypeSignature::Void,
CilFlavor::Boolean => TypeSignature::Boolean,
CilFlavor::Char => TypeSignature::Char,
CilFlavor::I1 => TypeSignature::I1,
CilFlavor::U1 => TypeSignature::U1,
CilFlavor::I2 => TypeSignature::I2,
CilFlavor::U2 => TypeSignature::U2,
CilFlavor::I4 => TypeSignature::I4,
CilFlavor::U4 => TypeSignature::U4,
CilFlavor::I8 => TypeSignature::I8,
CilFlavor::U8 => TypeSignature::U8,
CilFlavor::R4 => TypeSignature::R4,
CilFlavor::R8 => TypeSignature::R8,
CilFlavor::I => TypeSignature::I,
CilFlavor::U => TypeSignature::U,
CilFlavor::String => TypeSignature::String,
CilFlavor::Class | CilFlavor::Interface | CilFlavor::GenericInstance => {
TypeSignature::Class(token)
}
CilFlavor::ValueType => TypeSignature::ValueType(token),
CilFlavor::GenericParameter { index, method } => {
if *method {
TypeSignature::GenericParamMethod(*index)
} else {
TypeSignature::GenericParamType(*index)
}
}
CilFlavor::TypedRef { .. } => TypeSignature::TypedByRef,
CilFlavor::Object
| CilFlavor::Array { .. }
| CilFlavor::Pointer
| CilFlavor::ByRef
| CilFlavor::Pinned
| CilFlavor::FnPtr { .. }
| CilFlavor::Unknown => TypeSignature::Object,
})
}
}
pub struct VarArg {
pub modifiers: CilTypeRefList,
pub by_ref: bool,
pub base: CilTypeRef,
}