use std::sync::Arc;
use crate::{
metadata::{
customattributes::{
parse_custom_attribute_blob, parse_custom_attribute_blob_with_registry,
CustomAttributeValue,
},
streams::Blob,
tables::{
CodedIndex, CodedIndexType, CustomAttribute, CustomAttributeRc, MemberRefSignature,
TableInfoRef, TableRow,
},
token::Token,
typesystem::{CilTypeReference, TypeRegistry},
},
Result,
};
#[derive(Clone, Debug)]
pub struct CustomAttributeRaw {
pub rid: u32,
pub token: Token,
pub offset: usize,
pub parent: CodedIndex,
pub constructor: CodedIndex,
pub value: u32,
}
impl CustomAttributeRaw {
pub fn to_owned<F>(
&self,
get_ref: F,
blob: &Blob,
type_registry: Option<&Arc<TypeRegistry>>,
) -> Result<CustomAttributeRc>
where
F: Fn(&CodedIndex) -> CilTypeReference,
{
let constructor_ref = get_ref(&self.constructor);
match &constructor_ref {
CilTypeReference::MethodDef(method_ref) => {
if let Some(constructor) = method_ref.upgrade() {
if !constructor.is_constructor() {
return Err(malformed_error!(
"CustomAttribute constructor must be a .ctor or .cctor method, found '{}' (token: {})",
constructor.name,
self.token.value()
));
}
if constructor.name.is_empty() {
return Err(malformed_error!(
"Constructor name cannot be empty for CustomAttribute token {}",
self.token.value()
));
}
} else {
return Err(malformed_error!(
"CustomAttribute constructor method reference is no longer valid (token: {})",
self.token.value()
));
}
}
CilTypeReference::MemberRef(member_ref) => {
if !member_ref.is_constructor() {
return Err(malformed_error!(
"CustomAttribute constructor must be a .ctor or .cctor method, found '{}' (token: {})",
member_ref.name,
self.token.value()
));
}
if member_ref.name.is_empty() {
return Err(malformed_error!(
"Constructor name cannot be empty for CustomAttribute token {}",
self.token.value()
));
}
}
CilTypeReference::None => {
return Err(malformed_error!(
"CustomAttribute constructor reference cannot be None (token: {})",
self.token.value()
));
}
_ => {
return Err(malformed_error!(
"CustomAttribute constructor must be MethodDef or MemberRef (token: {})",
self.token.value()
));
}
}
let value = if self.value == 0 {
CustomAttributeValue {
fixed_args: vec![],
named_args: vec![],
}
} else {
match &constructor_ref {
CilTypeReference::MethodDef(method_ref) => match method_ref.upgrade() {
Some(constructor) => {
if let Some(registry) = type_registry {
parse_custom_attribute_blob_with_registry(
blob,
self.value,
&constructor.params,
registry,
)?
} else {
parse_custom_attribute_blob(blob, self.value, &constructor.params)?
}
}
None => CustomAttributeValue {
fixed_args: vec![],
named_args: vec![],
},
},
CilTypeReference::MemberRef(member_ref) => match &member_ref.signature {
MemberRefSignature::Method(_method_sig) => {
if let Some(registry) = type_registry {
parse_custom_attribute_blob_with_registry(
blob,
self.value,
&member_ref.params,
registry,
)?
} else {
parse_custom_attribute_blob(blob, self.value, &member_ref.params)?
}
}
MemberRefSignature::Field(_) => CustomAttributeValue {
fixed_args: vec![],
named_args: vec![],
},
},
_ => CustomAttributeValue {
fixed_args: vec![],
named_args: vec![],
},
}
};
Ok(Arc::new(CustomAttribute {
rid: self.rid,
token: self.token,
offset: self.offset,
parent: get_ref(&self.parent),
constructor: constructor_ref,
value,
}))
}
}
impl TableRow for CustomAttributeRaw {
#[rustfmt::skip]
fn row_size(sizes: &TableInfoRef) -> u32 {
u32::from(
sizes.coded_index_bytes(CodedIndexType::HasCustomAttribute) +
sizes.coded_index_bytes(CodedIndexType::CustomAttributeType) +
sizes.blob_bytes()
)
}
}