use super::{
attribute::{Attribute, SecurityDeclaration},
body,
generic::{self, show_constraints},
signature,
types::{CustomTypeModifier, MemberType, MethodType},
ResolvedDebug,
};
use crate::resolution::*;
use dotnetdll_macros::From;
use std::borrow::Cow;
use std::fmt::{Display, Formatter, Write};
pub use crate::binary::signature::{encoded::NativeIntrinsic, kinds::MarshalSpec};
pub use dotnetdll_macros::{field_ref, method_ref};
macro_rules! name_display {
($i:ty) => {
impl Display for $i {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name)
}
}
};
}
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, From)]
pub enum Accessibility {
CompilerControlled,
Access(super::Accessibility),
}
impl Display for Accessibility {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
use Accessibility::*;
match self {
CompilerControlled => write!(f, "[compiler controlled]"),
Access(a) => write!(f, "{}", a),
}
}
}
impl Accessibility {
pub fn to_mask(self) -> u16 {
match self {
Accessibility::CompilerControlled => 0x0,
Accessibility::Access(super::Accessibility::Private) => 0x1,
Accessibility::Access(super::Accessibility::FamilyANDAssembly) => 0x2,
Accessibility::Access(super::Accessibility::Assembly) => 0x3,
Accessibility::Access(super::Accessibility::Family) => 0x4,
Accessibility::Access(super::Accessibility::FamilyORAssembly) => 0x5,
Accessibility::Access(super::Accessibility::Public) => 0x6,
}
}
}
#[derive(Debug, Clone)]
pub struct Field<'a> {
pub attributes: Vec<Attribute<'a>>,
pub name: Cow<'a, str>,
pub type_modifiers: Vec<CustomTypeModifier>,
pub by_ref: bool,
pub return_type: MemberType,
pub accessibility: Accessibility,
pub static_member: bool,
pub init_only: bool,
pub literal: bool,
pub default: Option<Constant>,
pub not_serialized: bool,
pub special_name: bool,
pub pinvoke: Option<PInvoke<'a>>,
pub runtime_special_name: bool,
pub offset: Option<usize>,
pub marshal: Option<MarshalSpec>,
pub initial_value: Option<Cow<'a, [u8]>>,
}
name_display!(Field<'_>);
impl ResolvedDebug for Field<'_> {
fn show(&self, res: &Resolution) -> String {
let mut buf = format!("{} ", self.accessibility);
if self.static_member {
buf.push_str("static ");
}
write!(buf, "{} {}", self.return_type.show(res), self.name).unwrap();
if let Some(c) = &self.default {
write!(buf, " = {:?}", c).unwrap();
}
buf
}
}
impl<'a> Field<'a> {
pub fn new(
static_member: bool,
access: super::Accessibility,
name: impl Into<Cow<'a, str>>,
return_type: MemberType,
) -> Self {
Self {
attributes: vec![],
name: name.into(),
type_modifiers: vec![],
by_ref: false,
return_type,
accessibility: Accessibility::Access(access),
static_member,
init_only: false,
literal: false,
default: None,
not_serialized: false,
special_name: false,
pinvoke: None,
runtime_special_name: false,
offset: None,
marshal: None,
initial_value: None,
}
}
pub fn instance(access: super::Accessibility, name: impl Into<Cow<'a, str>>, return_type: MemberType) -> Self {
Self::new(false, access, name, return_type)
}
pub fn static_member(access: super::Accessibility, name: impl Into<Cow<'a, str>>, return_type: MemberType) -> Self {
Self::new(true, access, name, return_type)
}
}
#[derive(Debug, Clone, From, Eq, PartialEq)]
pub enum FieldReferenceParent {
Type(MethodType),
Module(ModuleRefIndex),
}
#[derive(Debug, Clone)]
pub struct ExternalFieldReference<'a> {
pub attributes: Vec<Attribute<'a>>,
pub parent: FieldReferenceParent,
pub name: Cow<'a, str>,
pub custom_modifiers: Vec<CustomTypeModifier>,
pub field_type: MemberType,
}
name_display!(ExternalFieldReference<'_>);
impl<'a> ExternalFieldReference<'a> {
pub const fn new(parent: FieldReferenceParent, field_type: MemberType, name: Cow<'a, str>) -> Self {
Self {
attributes: vec![],
parent,
name,
custom_modifiers: vec![],
field_type,
}
}
}
#[derive(Debug, Copy, Clone, From, Eq, PartialEq)]
pub enum FieldSource {
Definition(FieldIndex),
Reference(FieldRefIndex),
}
impl ResolvedDebug for FieldSource {
fn show(&self, res: &Resolution) -> String {
use FieldSource::*;
match self {
Definition(i) => {
format!("{}.{}", res[i.parent_type].nested_type_name(res), res[*i].name)
}
Reference(i) => {
use FieldReferenceParent::*;
let f = &res[*i];
format!(
"{}.{}",
match &f.parent {
Type(t) => t.show(res),
Module(m) => res[*m].name.to_string(),
},
f.name
)
}
}
}
}
#[derive(Debug, Clone)]
pub struct Property<'a> {
pub attributes: Vec<Attribute<'a>>,
pub name: Cow<'a, str>,
pub getter: Option<Method<'a>>,
pub setter: Option<Method<'a>>,
pub other: Vec<Method<'a>>,
pub static_member: bool,
pub property_type: signature::Parameter, pub parameters: Vec<signature::Parameter>,
pub special_name: bool,
pub runtime_special_name: bool,
pub default: Option<Constant>,
}
name_display!(Property<'_>);
impl ResolvedDebug for Property<'_> {
fn show(&self, res: &Resolution) -> String {
let mut buf = String::new();
let accessors: Vec<_> = [self.getter.as_ref(), self.setter.as_ref()]
.into_iter()
.flatten()
.collect();
let least_restrictive = accessors.iter().map(|m| m.accessibility).max();
if let Some(access) = least_restrictive {
write!(buf, "{} ", access).unwrap();
}
if accessors.iter().any(|m| m.is_static()) {
buf.push_str("static ");
}
if accessors.iter().any(|m| m.abstract_member) {
buf.push_str("abstract ");
} else if accessors.iter().any(|m| m.virtual_member) {
buf.push_str("virtual ");
}
write!(buf, "{} {} {{ ", self.property_type.show(res), self.name).unwrap();
if let Some(method) = &self.getter {
if matches!(least_restrictive, Some(a) if method.accessibility < a) {
write!(buf, "{} ", method.accessibility).unwrap();
}
buf.push_str("get; ");
}
if let Some(method) = &self.setter {
if matches!(least_restrictive, Some(a) if method.accessibility < a) {
write!(buf, "{} ", method.accessibility).unwrap();
}
buf.push_str("set; ");
}
buf.push('}');
if let Some(c) = &self.default {
write!(buf, " = {:?}", c).unwrap();
}
buf
}
}
impl<'a> Property<'a> {
pub fn new(static_member: bool, name: impl Into<Cow<'a, str>>, property_type: signature::Parameter) -> Self {
Self {
attributes: vec![],
name: name.into(),
getter: None,
setter: None,
other: vec![],
static_member,
property_type,
parameters: vec![],
special_name: false,
runtime_special_name: false,
default: None,
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum VtableLayout {
ReuseSlot,
NewSlot,
}
#[derive(Debug, Clone, Default)]
pub struct ParameterMetadata<'a> {
pub attributes: Vec<Attribute<'a>>,
pub name: Option<Cow<'a, str>>,
pub is_in: bool,
pub is_out: bool,
pub optional: bool,
pub default: Option<Constant>,
pub marshal: Option<MarshalSpec>,
}
impl Display for ParameterMetadata<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name.as_deref().unwrap_or(""))
}
}
impl<'a> ParameterMetadata<'a> {
pub fn name(name: impl Into<Cow<'a, str>>) -> Self {
Self {
name: Some(name.into()),
..Self::default()
}
}
pub fn marshal(marshal: MarshalSpec) -> Self {
Self {
marshal: Some(marshal),
..Self::default()
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum BodyFormat {
IL,
Native,
Runtime,
}
#[derive(Debug, Copy, Clone)]
pub enum BodyManagement {
Unmanaged,
Managed,
}
#[derive(Debug, Clone)]
pub struct Method<'a> {
pub attributes: Vec<Attribute<'a>>,
pub name: Cow<'a, str>,
pub body: Option<body::Method>,
pub signature: signature::ManagedMethod,
pub accessibility: Accessibility,
pub generic_parameters: Vec<generic::Method<'a>>,
pub return_type_metadata: Option<ParameterMetadata<'a>>,
pub parameter_metadata: Vec<Option<ParameterMetadata<'a>>>,
pub sealed: bool,
pub virtual_member: bool,
pub hide_by_sig: bool,
pub vtable_layout: VtableLayout,
pub strict: bool,
pub abstract_member: bool,
pub special_name: bool,
pub pinvoke: Option<PInvoke<'a>>,
pub runtime_special_name: bool,
pub security: Option<SecurityDeclaration<'a>>,
pub require_sec_object: bool,
pub body_format: BodyFormat,
pub body_management: BodyManagement,
pub forward_ref: bool,
pub preserve_sig: bool,
pub synchronized: bool,
pub no_inlining: bool,
pub no_optimization: bool,
}
name_display!(Method<'_>);
impl ResolvedDebug for Method<'_> {
fn show(&self, res: &Resolution) -> String {
let mut buf = format!("{} ", self.accessibility);
if self.is_static() {
buf.push_str("static ");
}
if self.abstract_member {
buf.push_str("abstract ");
} else if self.virtual_member {
buf.push_str("virtual ");
}
if self.pinvoke.is_some() {
buf.push_str("extern ");
}
match &self.signature.return_type.1 {
None => buf.push_str("void "),
Some(t) => write!(buf, "{} ", t.show(res)).unwrap(),
}
write!(
buf,
"{}{}({})",
self.name,
self.generic_parameters.show(res),
self.signature
.parameters
.iter()
.map(|p| p.1.show(res))
.collect::<Vec<_>>()
.join(", "),
)
.unwrap();
if let Some(constraints) = show_constraints(&self.generic_parameters, res) {
write!(buf, " {}", constraints).unwrap();
}
buf
}
}
impl<'a> Method<'a> {
pub fn new(
access: super::Accessibility,
signature: signature::ManagedMethod,
name: impl Into<Cow<'a, str>>,
body: Option<body::Method>,
) -> Self {
Self {
attributes: vec![],
name: name.into(),
body,
signature,
accessibility: Accessibility::Access(access),
generic_parameters: vec![],
return_type_metadata: None,
parameter_metadata: vec![],
sealed: false,
virtual_member: false,
hide_by_sig: true,
vtable_layout: VtableLayout::ReuseSlot,
strict: false,
abstract_member: false,
special_name: false,
pinvoke: None,
runtime_special_name: false,
security: None,
require_sec_object: false,
body_format: BodyFormat::IL,
body_management: BodyManagement::Managed,
forward_ref: false,
preserve_sig: false,
synchronized: false,
no_inlining: false,
no_optimization: false,
}
}
pub fn is_static(&self) -> bool {
!self.signature.instance
}
pub fn constructor(
access: super::Accessibility,
parameters: Vec<signature::Parameter>,
body: Option<body::Method>,
) -> Self {
Self {
special_name: true,
runtime_special_name: true,
..Self::new(
access,
signature::MethodSignature::new(true, signature::ReturnType::VOID, parameters),
".ctor",
body,
)
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum CharacterSet {
NotSpecified,
Ansi,
Unicode,
Auto,
}
#[derive(Debug, Copy, Clone)]
pub enum UnmanagedCallingConvention {
Platformapi,
Cdecl,
Stdcall,
Thiscall,
Fastcall,
}
#[derive(Debug, Clone)]
pub struct PInvoke<'a> {
pub no_mangle: bool,
pub character_set: CharacterSet,
pub supports_last_error: bool,
pub calling_convention: UnmanagedCallingConvention,
pub import_name: Cow<'a, str>,
pub import_scope: ModuleRefIndex,
}
impl<'a> PInvoke<'a> {
pub fn new(import_scope: ModuleRefIndex, import_name: impl Into<Cow<'a, str>>) -> Self {
Self {
no_mangle: false,
character_set: CharacterSet::NotSpecified,
supports_last_error: false,
calling_convention: UnmanagedCallingConvention::Platformapi,
import_name: import_name.into(),
import_scope,
}
}
}
#[derive(Debug, Clone, From)]
pub enum MethodReferenceParent {
Type(MethodType),
Module(ModuleRefIndex),
VarargMethod(MethodIndex),
}
#[derive(Debug, Clone)]
pub struct ExternalMethodReference<'a> {
pub attributes: Vec<Attribute<'a>>,
pub parent: MethodReferenceParent,
pub name: Cow<'a, str>,
pub signature: signature::ManagedMethod,
}
name_display!(ExternalMethodReference<'_>);
impl<'a> ExternalMethodReference<'a> {
pub fn new(
parent: MethodReferenceParent,
name: impl Into<Cow<'a, str>>,
signature: signature::ManagedMethod,
) -> Self {
Self {
attributes: vec![],
parent,
name: name.into(),
signature,
}
}
}
#[derive(Debug, Copy, Clone, From, Eq, PartialEq)]
pub enum UserMethod {
Definition(MethodIndex),
Reference(MethodRefIndex),
}
impl ResolvedDebug for UserMethod {
fn show(&self, res: &Resolution) -> String {
let signature;
let parent_name;
let method_name: &str;
match self {
UserMethod::Definition(i) => {
let method = &res[*i];
signature = &method.signature;
parent_name = res[i.parent_type].nested_type_name(res);
method_name = &method.name;
}
UserMethod::Reference(i) => {
let r = &res[*i];
signature = &r.signature;
method_name = &r.name;
use MethodReferenceParent::*;
parent_name = match &r.parent {
Type(t) => t.show(res),
Module(m) => res[*m].name.to_string(),
VarargMethod(i) => res[i.parent_type].nested_type_name(res),
}
}
}
let mut buf = if signature.instance {
String::new()
} else {
String::from("static ")
};
let ret_type = signature.return_type.show(res);
match &signature.varargs {
Some(v) => write!(
buf,
"vararg {} {}.{}({})",
ret_type,
parent_name,
method_name,
signature
.parameters
.iter()
.map(|p| p.show(res))
.chain(std::iter::once("...".to_string()))
.chain(v.iter().map(|p| p.show(res)))
.collect::<Vec<_>>()
.join(", ")
)
.unwrap(),
None => buf.push_str(&signature.show_with_name(res, format!("{}.{}", parent_name, method_name))),
};
buf
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct GenericMethodInstantiation {
pub base: UserMethod,
pub parameters: Vec<MethodType>,
}
#[derive(Debug, Clone, From, Eq, PartialEq)]
pub enum MethodSource {
User(#[nested(MethodIndex, MethodRefIndex)] UserMethod),
Generic(GenericMethodInstantiation),
}
impl ResolvedDebug for MethodSource {
fn show(&self, res: &Resolution) -> String {
use MethodSource::*;
match self {
User(u) => u.show(res),
Generic(g) => format!(
"({})<{}>",
g.base.show(res),
g.parameters.iter().map(|p| p.show(res)).collect::<Vec<_>>().join(", ")
),
}
}
}
#[derive(Debug, Clone)]
pub enum Constant {
Boolean(bool),
Char(u16), Int8(i8),
UInt8(u8),
Int16(i16),
UInt16(u16),
Int32(i32),
UInt32(u32),
Int64(i64),
UInt64(u64),
Float32(f32),
Float64(f64),
String(Vec<u16>), Null,
}
#[derive(Debug, Clone)]
pub struct Event<'a> {
pub attributes: Vec<Attribute<'a>>,
pub name: Cow<'a, str>,
pub delegate_type: MemberType, pub add_listener: Method<'a>,
pub remove_listener: Method<'a>,
pub raise_event: Option<Method<'a>>,
pub other: Vec<Method<'a>>,
pub special_name: bool,
pub runtime_special_name: bool,
}
name_display!(Event<'_>);
impl ResolvedDebug for Event<'_> {
fn show(&self, res: &Resolution) -> String {
format!(
"{} {}{}event {} {}",
self.add_listener.accessibility,
if self.add_listener.is_static() { "static " } else { "" },
if self.add_listener.abstract_member {
"abstract "
} else if self.add_listener.virtual_member {
"virtual "
} else {
""
},
self.delegate_type.show(res),
self.name
)
}
}
impl<'a> Event<'a> {
pub fn new(
name: impl Into<Cow<'a, str>>,
delegate_type: MemberType,
add_listener: Method<'a>,
remove_listener: Method<'a>,
) -> Self {
Self {
attributes: vec![],
name: name.into(),
delegate_type,
add_listener,
remove_listener,
raise_event: None,
other: vec![],
special_name: false,
runtime_special_name: false,
}
}
}