use std::{
fmt,
sync::{Arc, OnceLock},
};
use crate::{
metadata::{
customattributes::CustomAttributeValueList,
tables::GenericParamAttributes,
token::Token,
typesystem::{CilTypeRefList, CilTypeReference},
},
Result,
};
pub struct GenericParam {
pub rid: u32,
pub token: Token,
pub offset: usize,
pub number: u32,
pub flags: GenericParamAttributes,
pub owner: OnceLock<CilTypeReference>,
pub constraints: CilTypeRefList,
pub name: String,
pub custom_attributes: CustomAttributeValueList,
}
impl GenericParam {
pub fn apply(self: &Arc<Self>) -> Result<()> {
match self.owner.get() {
Some(owner) => match owner {
CilTypeReference::TypeDef(cil_type) => {
if let Some(generic_params) = cil_type.generic_params() {
let mut already_exists = false;
for (_, existing_param) in generic_params.iter() {
if existing_param.name == self.name
&& existing_param.number == self.number
{
already_exists = true;
break;
}
}
if !already_exists {
generic_params.push(self.clone());
}
}
Ok(())
}
CilTypeReference::MethodDef(method) => {
if let Some(method) = method.upgrade() {
let mut already_exists = false;
for (_, existing_param) in method.generic_params.iter() {
if existing_param.name == self.name
&& existing_param.number == self.number
{
already_exists = true;
break;
}
}
if !already_exists {
method.generic_params.push(self.clone());
}
}
Ok(())
}
_ => Err(malformed_error!("Invalid owner type reference")),
},
None => Err(malformed_error!("No owner type reference")),
}
}
}
impl fmt::Display for GenericParam {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let vk = self.flags.variance_keyword();
if !vk.is_empty() {
write!(f, "{vk}")?;
}
let mut constraints = Vec::new();
if self
.flags
.contains(GenericParamAttributes::REFERENCE_TYPE_CONSTRAINT)
{
constraints.push("class".to_string());
}
if self
.flags
.contains(GenericParamAttributes::NOT_NULLABLE_VALUE_TYPE_CONSTRAINT)
{
constraints.push("valuetype".to_string());
}
if self
.flags
.contains(GenericParamAttributes::DEFAULT_CONSTRUCTOR_CONSTRAINT)
{
constraints.push(".ctor".to_string());
}
for (_, constraint_ref) in self.constraints.iter() {
if let Some(constraint_type) = constraint_ref.upgrade() {
constraints.push(constraint_type.fullname());
}
}
if !constraints.is_empty() {
write!(f, "({}) ", constraints.join(", "))?;
}
match self.owner.get() {
Some(CilTypeReference::TypeDef(_)) => write!(f, "!{}", self.number),
Some(CilTypeReference::MethodDef(_)) => write!(f, "!!{}", self.number),
_ => write!(f, "!{}", self.number),
}
}
}