use std::sync::{atomic::AtomicBool, Arc, OnceLock};
use crate::{
metadata::{
signatures::{
parse_field_signature, parse_method_signature, SignatureMethod, TypeSignature,
},
streams::{Blob, Strings},
tables::{
CodedIndex, CodedIndexType, MemberRef, MemberRefRc, MemberRefSignature, Param, ParamRc,
TableInfoRef, TableRow,
},
token::Token,
typesystem::{CilTypeReference, TypeRegistry},
},
Result,
};
#[derive(Clone, Debug)]
pub struct MemberRefRaw {
pub rid: u32,
pub token: Token,
pub offset: usize,
pub class: CodedIndex,
pub name: u32,
pub signature: u32,
}
impl MemberRefRaw {
fn create_params_from_signature(
method_sig: &SignatureMethod,
_strings: &Strings,
) -> Arc<boxcar::Vec<ParamRc>> {
let params = Arc::new(boxcar::Vec::with_capacity(method_sig.params.len() + 1));
let return_param = Arc::new(Param {
rid: 0, token: Token::new(0), offset: 0,
flags: 0,
sequence: 0, name: None, default: OnceLock::new(),
marshal: OnceLock::new(),
modifiers: Arc::new(boxcar::Vec::new()),
base: OnceLock::new(),
is_by_ref: AtomicBool::new(method_sig.return_type.by_ref),
custom_attributes: Arc::new(boxcar::Vec::new()),
});
params.push(return_param);
for (index, param_sig) in method_sig.params.iter().enumerate() {
let param = Arc::new(Param {
rid: 0, token: Token::new(0), offset: 0,
flags: 0,
#[allow(clippy::cast_possible_truncation)]
sequence: (index + 1) as u32, name: None, default: OnceLock::new(),
marshal: OnceLock::new(),
modifiers: Arc::new(boxcar::Vec::new()),
base: OnceLock::new(),
is_by_ref: AtomicBool::new(param_sig.by_ref),
custom_attributes: Arc::new(boxcar::Vec::new()),
});
params.push(param);
}
params
}
pub fn apply(&self) -> Result<()> {
Ok(())
}
pub fn to_owned<F>(
&self,
strings: &Strings,
blob: &Blob,
types: &Arc<TypeRegistry>,
get_ref: F,
) -> Result<MemberRefRc>
where
F: Fn(&CodedIndex) -> CilTypeReference,
{
let signature_data = blob.get(self.signature as usize)?;
if signature_data.is_empty() {
return Err(malformed_error!("Invalid signature data"));
}
let (signature, params) = if signature_data[0] == 0x6 {
(
MemberRefSignature::Field(parse_field_signature(signature_data)?),
Arc::new(boxcar::Vec::new()),
)
} else {
let method_sig = parse_method_signature(signature_data)?;
let params = Self::create_params_from_signature(&method_sig, strings);
let method_param_count = Some(method_sig.params.len());
for (_, param) in params.iter() {
if param.sequence == 0 {
param.apply_signature(
&method_sig.return_type,
types.clone(),
method_param_count,
)?;
} else {
let index = (param.sequence - 1) as usize;
if let Some(param_signature) = method_sig.params.get(index) {
param.apply_signature(
param_signature,
types.clone(),
method_param_count,
)?;
}
}
}
(MemberRefSignature::Method(method_sig), params)
};
let declaredby = get_ref(&self.class);
if matches!(declaredby, CilTypeReference::None) {
return Err(malformed_error!(
"Failed to resolve class token - {}",
self.class.token.value()
));
}
match (&signature, &declaredby) {
(
MemberRefSignature::Method(method_sig),
CilTypeReference::TypeDef(_parent_type) | CilTypeReference::TypeRef(_parent_type),
) => {
if strings.get(self.name as usize)?.is_empty() {
return Err(malformed_error!(
"Method name cannot be empty for MemberRef token {}",
self.token.value()
));
}
if method_sig.params.len() > 255 {
return Err(malformed_error!(
"Method signature has too many parameters ({}) for MemberRef token {}",
method_sig.params.len(),
self.token.value()
));
}
}
(
MemberRefSignature::Field(field_sig),
CilTypeReference::TypeDef(_parent_type) | CilTypeReference::TypeRef(_parent_type),
) => {
if strings.get(self.name as usize)?.is_empty() {
return Err(malformed_error!(
"Field name cannot be empty for MemberRef token {}",
self.token.value()
));
}
if matches!(field_sig.base, TypeSignature::Unknown) {
return Err(malformed_error!(
"Field signature has unknown type for MemberRef token {}",
self.token.value()
));
}
}
_ => {}
}
let member_ref = Arc::new(MemberRef {
rid: self.rid,
token: self.token,
offset: self.offset,
declaredby,
name: strings.get(self.name as usize)?.to_string(),
signature,
params,
custom_attributes: Arc::new(boxcar::Vec::new()),
});
Ok(member_ref)
}
}
impl TableRow for MemberRefRaw {
#[rustfmt::skip]
fn row_size(sizes: &TableInfoRef) -> u32 {
u32::from(
sizes.coded_index_bytes(CodedIndexType::MemberRefParent) +
sizes.str_bytes() +
sizes.blob_bytes()
)
}
}