use crate::{
cilassembly::{ChangeRefRc, CilAssembly},
metadata::{
tables::{MethodPtrRaw, TableDataOwned, TableId},
token::Token,
},
Error, Result,
};
#[derive(Debug, Clone)]
pub struct MethodPtrBuilder {
method: Option<u32>,
}
impl MethodPtrBuilder {
#[must_use]
pub fn new() -> Self {
Self { method: None }
}
#[must_use]
pub fn method(mut self, method: u32) -> Self {
self.method = Some(method);
self
}
pub fn build(self, assembly: &mut CilAssembly) -> Result<ChangeRefRc> {
let method = self.method.ok_or_else(|| {
Error::ModificationInvalid("Method RID is required for MethodPtr".to_string())
})?;
let method_ptr = MethodPtrRaw {
rid: 0,
token: Token::new(0),
offset: 0,
method,
};
assembly.table_row_add(TableId::MethodPtr, TableDataOwned::MethodPtr(method_ptr))
}
}
impl Default for MethodPtrBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
cilassembly::ChangeRefKind, test::factories::table::assemblyref::get_test_assembly,
};
#[test]
fn test_methodptr_builder_new() {
let builder = MethodPtrBuilder::new();
assert!(builder.method.is_none());
}
#[test]
fn test_methodptr_builder_default() {
let builder = MethodPtrBuilder::default();
assert!(builder.method.is_none());
}
#[test]
fn test_methodptr_builder_basic() -> Result<()> {
let mut assembly = get_test_assembly()?;
let ref_ = MethodPtrBuilder::new()
.method(1)
.build(&mut assembly)
.expect("Should build successfully");
assert_eq!(ref_.kind(), ChangeRefKind::TableRow(TableId::MethodPtr));
Ok(())
}
#[test]
fn test_methodptr_builder_reordering() -> Result<()> {
let mut assembly = get_test_assembly()?;
let ref_ = MethodPtrBuilder::new()
.method(25) .build(&mut assembly)
.expect("Should build successfully");
assert_eq!(ref_.kind(), ChangeRefKind::TableRow(TableId::MethodPtr));
Ok(())
}
#[test]
fn test_methodptr_builder_missing_method() -> Result<()> {
let mut assembly = get_test_assembly()?;
let result = MethodPtrBuilder::new().build(&mut assembly);
assert!(result.is_err());
match result.unwrap_err() {
Error::ModificationInvalid(details) => {
assert!(details.contains("Method RID is required"));
}
_ => panic!("Expected ModificationInvalid error"),
}
Ok(())
}
#[test]
fn test_methodptr_builder_clone() {
let builder = MethodPtrBuilder::new().method(8);
let cloned = builder.clone();
assert_eq!(builder.method, cloned.method);
}
#[test]
fn test_methodptr_builder_debug() {
let builder = MethodPtrBuilder::new().method(12);
let debug_str = format!("{builder:?}");
assert!(debug_str.contains("MethodPtrBuilder"));
assert!(debug_str.contains("method"));
}
#[test]
fn test_methodptr_builder_fluent_interface() -> Result<()> {
let mut assembly = get_test_assembly()?;
let ref_ = MethodPtrBuilder::new()
.method(42)
.build(&mut assembly)
.expect("Should build successfully");
assert_eq!(ref_.kind(), ChangeRefKind::TableRow(TableId::MethodPtr));
Ok(())
}
#[test]
fn test_methodptr_builder_multiple_builds() -> Result<()> {
let mut assembly = get_test_assembly()?;
let ref1 = MethodPtrBuilder::new()
.method(20)
.build(&mut assembly)
.expect("Should build first pointer");
let ref2 = MethodPtrBuilder::new()
.method(10)
.build(&mut assembly)
.expect("Should build second pointer");
let ref3 = MethodPtrBuilder::new()
.method(30)
.build(&mut assembly)
.expect("Should build third pointer");
assert!(!std::sync::Arc::ptr_eq(&ref1, &ref2));
assert!(!std::sync::Arc::ptr_eq(&ref2, &ref3));
assert_eq!(ref1.kind(), ChangeRefKind::TableRow(TableId::MethodPtr));
assert_eq!(ref2.kind(), ChangeRefKind::TableRow(TableId::MethodPtr));
assert_eq!(ref3.kind(), ChangeRefKind::TableRow(TableId::MethodPtr));
Ok(())
}
#[test]
fn test_methodptr_builder_large_method_rid() -> Result<()> {
let mut assembly = get_test_assembly()?;
let ref_ = MethodPtrBuilder::new()
.method(0xFFFF) .build(&mut assembly)
.expect("Should handle large method RID");
assert_eq!(ref_.kind(), ChangeRefKind::TableRow(TableId::MethodPtr));
Ok(())
}
#[test]
fn test_methodptr_builder_method_ordering_scenario() -> Result<()> {
let mut assembly = get_test_assembly()?;
let logical_to_physical = [(1, 30), (2, 10), (3, 20)];
let mut refs = Vec::new();
for (logical_idx, physical_method) in logical_to_physical {
let ref_ = MethodPtrBuilder::new()
.method(physical_method)
.build(&mut assembly)
.expect("Should build method pointer");
refs.push((logical_idx, ref_));
}
for (i, (logical_idx, ref_)) in refs.iter().enumerate() {
assert_eq!(*logical_idx, i + 1);
assert_eq!(ref_.kind(), ChangeRefKind::TableRow(TableId::MethodPtr));
}
Ok(())
}
#[test]
fn test_methodptr_builder_zero_method() -> Result<()> {
let mut assembly = get_test_assembly()?;
let result = MethodPtrBuilder::new().method(0).build(&mut assembly);
assert!(result.is_ok());
Ok(())
}
#[test]
fn test_methodptr_builder_edit_continue_scenario() -> Result<()> {
let mut assembly = get_test_assembly()?;
let original_methods = [5, 10, 15];
let mut refs = Vec::new();
for &method_rid in &original_methods {
let ref_ = MethodPtrBuilder::new()
.method(method_rid)
.build(&mut assembly)
.expect("Should build method pointer for edit-continue");
refs.push(ref_);
}
for ref_ in refs.iter() {
assert_eq!(ref_.kind(), ChangeRefKind::TableRow(TableId::MethodPtr));
}
Ok(())
}
#[test]
fn test_methodptr_builder_hot_reload_scenario() -> Result<()> {
let mut assembly = get_test_assembly()?;
let new_method_implementations = [100, 200, 300];
let mut pointer_refs = Vec::new();
for &new_method in &new_method_implementations {
let pointer_ref = MethodPtrBuilder::new()
.method(new_method)
.build(&mut assembly)
.expect("Should build pointer for hot-reload");
pointer_refs.push(pointer_ref);
}
assert_eq!(pointer_refs.len(), 3);
for ref_ in pointer_refs.iter() {
assert_eq!(ref_.kind(), ChangeRefKind::TableRow(TableId::MethodPtr));
}
Ok(())
}
}