use crate::{
cilassembly::{ChangeRefRc, CilAssembly},
metadata::{
tables::{CodedIndex, CodedIndexType, MemberRefRaw, TableDataOwned, TableId},
token::Token,
},
Error, Result,
};
pub struct MemberRefBuilder {
class: Option<CodedIndex>,
name: Option<String>,
signature: Option<Vec<u8>>,
}
impl Default for MemberRefBuilder {
fn default() -> Self {
Self::new()
}
}
impl MemberRefBuilder {
#[must_use]
pub fn new() -> Self {
Self {
class: None,
name: None,
signature: None,
}
}
#[must_use]
pub fn class(mut self, class: CodedIndex) -> Self {
self.class = Some(class);
self
}
#[must_use]
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
#[must_use]
pub fn signature(mut self, signature: &[u8]) -> Self {
self.signature = Some(signature.to_vec());
self
}
pub fn build(self, assembly: &mut CilAssembly) -> Result<ChangeRefRc> {
let class = self
.class
.ok_or_else(|| Error::ModificationInvalid("MemberRef class is required".to_string()))?;
let name = self
.name
.ok_or_else(|| Error::ModificationInvalid("MemberRef name is required".to_string()))?;
let signature = self.signature.ok_or_else(|| {
Error::ModificationInvalid("MemberRef signature is required".to_string())
})?;
let valid_class_tables = CodedIndexType::MemberRefParent.tables();
if !valid_class_tables.contains(&class.tag) {
return Err(Error::ModificationInvalid(format!(
"Class must be a MemberRefParent coded index (TypeDef/TypeRef/ModuleRef/MethodDef/TypeSpec), got {:?}",
class.tag
)));
}
let name_index = assembly.string_get_or_add(&name)?.placeholder();
let signature_index = assembly.blob_add(&signature)?.placeholder();
let rid = assembly.next_rid(TableId::MemberRef)?;
let token_value = ((TableId::MemberRef as u32) << 24) | rid;
let token = Token::new(token_value);
let memberref_raw = MemberRefRaw {
rid,
token,
offset: 0, class,
name: name_index,
signature: signature_index,
};
assembly.table_row_add(TableId::MemberRef, TableDataOwned::MemberRef(memberref_raw))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{cilassembly::CilAssembly, metadata::cilassemblyview::CilAssemblyView};
use std::path::PathBuf;
#[test]
fn test_memberref_builder_basic() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let declaring_type =
CodedIndex::new(TableId::TypeRef, 1, CodedIndexType::MemberRefParent);
let method_signature = &[0x20, 0x00, 0x01];
let ref_ = MemberRefBuilder::new()
.class(declaring_type)
.name("ToString")
.signature(method_signature)
.build(&mut assembly)
.unwrap();
assert_eq!(
ref_.kind(),
crate::cilassembly::ChangeRefKind::TableRow(TableId::MemberRef)
);
}
}
#[test]
fn test_memberref_builder_field_reference() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let declaring_type =
CodedIndex::new(TableId::TypeDef, 1, CodedIndexType::MemberRefParent);
let field_signature = &[0x06, 0x08];
let ref_ = MemberRefBuilder::new()
.class(declaring_type)
.name("m_value")
.signature(field_signature)
.build(&mut assembly)
.unwrap();
assert_eq!(
ref_.kind(),
crate::cilassembly::ChangeRefKind::TableRow(TableId::MemberRef)
);
}
}
#[test]
fn test_memberref_builder_constructor_reference() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let declaring_type =
CodedIndex::new(TableId::TypeRef, 2, CodedIndexType::MemberRefParent);
let ctor_signature = &[0x20, 0x01, 0x01, 0x1C];
let ref_ = MemberRefBuilder::new()
.class(declaring_type)
.name(".ctor")
.signature(ctor_signature)
.build(&mut assembly)
.unwrap();
assert_eq!(
ref_.kind(),
crate::cilassembly::ChangeRefKind::TableRow(TableId::MemberRef)
);
}
}
#[test]
fn test_memberref_builder_module_reference() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let module_ref =
CodedIndex::new(TableId::ModuleRef, 1, CodedIndexType::MemberRefParent);
let global_method_sig = &[0x00, 0x01, 0x08, 0x08];
let ref_ = MemberRefBuilder::new()
.class(module_ref)
.name("GlobalFunction")
.signature(global_method_sig)
.build(&mut assembly)
.unwrap();
assert_eq!(
ref_.kind(),
crate::cilassembly::ChangeRefKind::TableRow(TableId::MemberRef)
);
}
}
#[test]
fn test_memberref_builder_generic_type_reference() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let generic_type =
CodedIndex::new(TableId::TypeSpec, 1, CodedIndexType::MemberRefParent);
let method_signature = &[0x20, 0x01, 0x0E, 0x1C];
let ref_ = MemberRefBuilder::new()
.class(generic_type)
.name("GetValue")
.signature(method_signature)
.build(&mut assembly)
.unwrap();
assert_eq!(
ref_.kind(),
crate::cilassembly::ChangeRefKind::TableRow(TableId::MemberRef)
);
}
}
#[test]
fn test_memberref_builder_missing_class() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let result = MemberRefBuilder::new()
.name("TestMethod")
.signature(&[0x20, 0x00, 0x01])
.build(&mut assembly);
assert!(result.is_err());
}
}
#[test]
fn test_memberref_builder_missing_name() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let declaring_type =
CodedIndex::new(TableId::TypeRef, 1, CodedIndexType::MemberRefParent);
let result = MemberRefBuilder::new()
.class(declaring_type)
.signature(&[0x20, 0x00, 0x01])
.build(&mut assembly);
assert!(result.is_err());
}
}
#[test]
fn test_memberref_builder_missing_signature() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let declaring_type =
CodedIndex::new(TableId::TypeRef, 1, CodedIndexType::MemberRefParent);
let result = MemberRefBuilder::new()
.class(declaring_type)
.name("TestMethod")
.build(&mut assembly);
assert!(result.is_err());
}
}
#[test]
fn test_memberref_builder_invalid_class_type() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let invalid_class = CodedIndex::new(TableId::Field, 1, CodedIndexType::MemberRefParent);
let result = MemberRefBuilder::new()
.class(invalid_class)
.name("TestMethod")
.signature(&[0x20, 0x00, 0x01])
.build(&mut assembly);
assert!(result.is_err());
}
}
#[test]
fn test_memberref_builder_multiple_member_refs() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let type_ref1 = CodedIndex::new(TableId::TypeRef, 1, CodedIndexType::MemberRefParent);
let type_ref2 = CodedIndex::new(TableId::TypeRef, 2, CodedIndexType::MemberRefParent);
let type_def1 = CodedIndex::new(TableId::TypeDef, 1, CodedIndexType::MemberRefParent);
let method_sig = &[0x20, 0x00, 0x01]; let field_sig = &[0x06, 0x08];
let member1 = MemberRefBuilder::new()
.class(type_ref1)
.name("Method1")
.signature(method_sig)
.build(&mut assembly)
.unwrap();
let member2 = MemberRefBuilder::new()
.class(type_ref2.clone())
.name("Field1")
.signature(field_sig)
.build(&mut assembly)
.unwrap();
let member3 = MemberRefBuilder::new()
.class(type_def1)
.name("Method2")
.signature(method_sig)
.build(&mut assembly)
.unwrap();
let member4 = MemberRefBuilder::new()
.class(type_ref2)
.name(".ctor")
.signature(&[0x20, 0x01, 0x01, 0x08]) .build(&mut assembly)
.unwrap();
assert!(!std::sync::Arc::ptr_eq(&member1, &member2));
assert!(!std::sync::Arc::ptr_eq(&member2, &member3));
assert!(!std::sync::Arc::ptr_eq(&member3, &member4));
assert_eq!(
member1.kind(),
crate::cilassembly::ChangeRefKind::TableRow(TableId::MemberRef)
);
assert_eq!(
member2.kind(),
crate::cilassembly::ChangeRefKind::TableRow(TableId::MemberRef)
);
assert_eq!(
member3.kind(),
crate::cilassembly::ChangeRefKind::TableRow(TableId::MemberRef)
);
assert_eq!(
member4.kind(),
crate::cilassembly::ChangeRefKind::TableRow(TableId::MemberRef)
);
}
}
}