use crate::{
metadata::tables::{
methoddef::MethodDefRaw,
types::{RowWritable, TableId, TableInfoRef},
},
utils::{write_le_at, write_le_at_dyn},
Error, Result,
};
impl RowWritable for MethodDefRaw {
fn row_write(
&self,
data: &mut [u8],
offset: &mut usize,
_rid: u32,
sizes: &TableInfoRef,
) -> Result<()> {
write_le_at(data, offset, self.rva)?;
let impl_flags_u16 = u16::try_from(self.impl_flags).map_err(|_| {
Error::LayoutFailed("Method implementation flags value exceeds u16 range".to_string())
})?;
write_le_at(data, offset, impl_flags_u16)?;
let flags_u16 = u16::try_from(self.flags)
.map_err(|_| Error::LayoutFailed("Method flags value exceeds u16 range".to_string()))?;
write_le_at(data, offset, flags_u16)?;
write_le_at_dyn(data, offset, self.name, sizes.is_large_str())?;
write_le_at_dyn(data, offset, self.signature, sizes.is_large_blob())?;
write_le_at_dyn(
data,
offset,
self.param_list,
sizes.is_large(TableId::Param),
)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
metadata::tables::{
types::{RowReadable, TableInfo, TableRow},
TableId,
},
metadata::token::Token,
};
use std::sync::Arc;
#[test]
fn test_row_size() {
let table_info = Arc::new(TableInfo::new_test(
&[(TableId::Param, 100)], false, false, false, ));
let size = <MethodDefRaw as TableRow>::row_size(&table_info);
assert_eq!(size, 14);
let table_info_large = Arc::new(TableInfo::new_test(
&[(TableId::Param, 70000)], true, true, false, ));
let size_large = <MethodDefRaw as TableRow>::row_size(&table_info_large);
assert_eq!(size_large, 20);
}
#[test]
fn test_round_trip_serialization() {
let original_row = MethodDefRaw {
rid: 1,
token: Token::new(0x06000001),
offset: 0,
rva: 0x2048,
impl_flags: 0x0000, flags: 0x0006, name: 0x1234,
signature: 0x5678,
param_list: 1,
};
let table_info = Arc::new(TableInfo::new_test(
&[(TableId::Param, 100)], false, false, false, ));
let row_size = <MethodDefRaw as TableRow>::row_size(&table_info) as usize;
let mut buffer = vec![0u8; row_size];
let mut offset = 0;
original_row
.row_write(&mut buffer, &mut offset, 1, &table_info)
.expect("Serialization should succeed");
let mut read_offset = 0;
let deserialized_row = MethodDefRaw::row_read(&buffer, &mut read_offset, 1, &table_info)
.expect("Deserialization should succeed");
assert_eq!(deserialized_row.rid, original_row.rid);
assert_eq!(deserialized_row.rva, original_row.rva);
assert_eq!(deserialized_row.impl_flags, original_row.impl_flags);
assert_eq!(deserialized_row.flags, original_row.flags);
assert_eq!(deserialized_row.name, original_row.name);
assert_eq!(deserialized_row.signature, original_row.signature);
assert_eq!(deserialized_row.param_list, original_row.param_list);
}
#[test]
fn test_known_binary_format() {
let data = vec![
0x48, 0x20, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x34, 0x12, 0x78, 0x56, 0x01, 0x00, ];
let table_info = Arc::new(TableInfo::new_test(
&[(TableId::Param, 100)],
false,
false,
false,
));
let mut read_offset = 0;
let reference_row = MethodDefRaw::row_read(&data, &mut read_offset, 1, &table_info)
.expect("Reading reference data should succeed");
let mut buffer = vec![0u8; data.len()];
let mut write_offset = 0;
reference_row
.row_write(&mut buffer, &mut write_offset, 1, &table_info)
.expect("Serialization should succeed");
assert_eq!(
buffer, data,
"Serialized data should match original binary format"
);
}
#[test]
fn test_method_attributes() {
let test_cases = vec![
(0x0001, "CompilerControlled"),
(0x0002, "Private"),
(0x0006, "Public"),
(0x0010, "Static"),
(0x0020, "Final"),
(0x0040, "Virtual"),
(0x0080, "HideBySig"),
(0x0100, "CheckAccessOnOverride"),
(0x0200, "Abstract"),
(0x0400, "SpecialName"),
(0x0800, "RTSpecialName"),
(0x1000, "PinvokeImpl"),
(0x0056, "Public|Virtual|HideBySig"), ];
let table_info = Arc::new(TableInfo::new_test(
&[(TableId::Param, 100)],
false,
false,
false,
));
for (flags, description) in test_cases {
let method_row = MethodDefRaw {
rid: 1,
token: Token::new(0x06000001),
offset: 0,
rva: 0x2000,
impl_flags: 0,
flags,
name: 0x100,
signature: 0x200,
param_list: 1,
};
let row_size = <MethodDefRaw as TableRow>::row_size(&table_info) as usize;
let mut buffer = vec![0u8; row_size];
let mut offset = 0;
method_row
.row_write(&mut buffer, &mut offset, 1, &table_info)
.unwrap_or_else(|_| panic!("Serialization should succeed for {description}"));
let mut read_offset = 0;
let deserialized_row =
MethodDefRaw::row_read(&buffer, &mut read_offset, 1, &table_info)
.unwrap_or_else(|_| panic!("Deserialization should succeed for {description}"));
assert_eq!(
deserialized_row.flags, method_row.flags,
"Flags should match for {description}"
);
}
}
#[test]
fn test_implementation_flags() {
let test_cases = vec![
(0x0000, "IL"),
(0x0001, "Native"),
(0x0002, "OPTIL"),
(0x0003, "Runtime"),
(0x0004, "Unmanaged"),
(0x0008, "ForwardRef"),
(0x0010, "PreserveSig"),
(0x0020, "InternalCall"),
(0x0040, "Synchronized"),
(0x0080, "NoInlining"),
(0x0100, "MaxMethodImplVal"),
];
let table_info = Arc::new(TableInfo::new_test(
&[(TableId::Param, 100)],
false,
false,
false,
));
for (impl_flags, description) in test_cases {
let method_row = MethodDefRaw {
rid: 1,
token: Token::new(0x06000001),
offset: 0,
rva: 0x2000,
impl_flags,
flags: 0x0006, name: 0x100,
signature: 0x200,
param_list: 1,
};
let row_size = <MethodDefRaw as TableRow>::row_size(&table_info) as usize;
let mut buffer = vec![0u8; row_size];
let mut offset = 0;
method_row
.row_write(&mut buffer, &mut offset, 1, &table_info)
.unwrap_or_else(|_| panic!("Serialization should succeed for {description}"));
let mut read_offset = 0;
let deserialized_row =
MethodDefRaw::row_read(&buffer, &mut read_offset, 1, &table_info)
.unwrap_or_else(|_| panic!("Deserialization should succeed for {description}"));
assert_eq!(
deserialized_row.impl_flags, method_row.impl_flags,
"Implementation flags should match for {description}"
);
}
}
#[test]
fn test_large_heap_serialization() {
let original_row = MethodDefRaw {
rid: 1,
token: Token::new(0x06000001),
offset: 0,
rva: 0x12345678,
impl_flags: 0x0040, flags: 0x0056, name: 0x123456,
signature: 0x789ABC,
param_list: 0x8000,
};
let table_info = Arc::new(TableInfo::new_test(
&[(TableId::Param, 70000)], true, true, false, ));
let row_size = <MethodDefRaw as TableRow>::row_size(&table_info) as usize;
let mut buffer = vec![0u8; row_size];
let mut offset = 0;
original_row
.row_write(&mut buffer, &mut offset, 1, &table_info)
.expect("Large heap serialization should succeed");
let mut read_offset = 0;
let deserialized_row = MethodDefRaw::row_read(&buffer, &mut read_offset, 1, &table_info)
.expect("Large heap deserialization should succeed");
assert_eq!(deserialized_row.rva, original_row.rva);
assert_eq!(deserialized_row.impl_flags, original_row.impl_flags);
assert_eq!(deserialized_row.flags, original_row.flags);
assert_eq!(deserialized_row.name, original_row.name);
assert_eq!(deserialized_row.signature, original_row.signature);
assert_eq!(deserialized_row.param_list, original_row.param_list);
}
#[test]
fn test_edge_cases() {
let abstract_method = MethodDefRaw {
rid: 1,
token: Token::new(0x06000001),
offset: 0,
rva: 0, impl_flags: 0,
flags: 0x0206, name: 0,
signature: 0,
param_list: 0,
};
let table_info = Arc::new(TableInfo::new_test(
&[(TableId::Param, 100)],
false,
false,
false,
));
let row_size = <MethodDefRaw as TableRow>::row_size(&table_info) as usize;
let mut buffer = vec![0u8; row_size];
let mut offset = 0;
abstract_method
.row_write(&mut buffer, &mut offset, 1, &table_info)
.expect("Abstract method serialization should succeed");
let mut read_offset = 0;
let deserialized_row = MethodDefRaw::row_read(&buffer, &mut read_offset, 1, &table_info)
.expect("Abstract method deserialization should succeed");
assert_eq!(deserialized_row.rva, abstract_method.rva);
assert_eq!(deserialized_row.impl_flags, abstract_method.impl_flags);
assert_eq!(deserialized_row.flags, abstract_method.flags);
assert_eq!(deserialized_row.name, abstract_method.name);
assert_eq!(deserialized_row.signature, abstract_method.signature);
assert_eq!(deserialized_row.param_list, abstract_method.param_list);
}
#[test]
fn test_flags_range_validation() {
let large_flags_row = MethodDefRaw {
rid: 1,
token: Token::new(0x06000001),
offset: 0,
rva: 0x2000,
impl_flags: 0x12345678, flags: 0x87654321, name: 0x100,
signature: 0x200,
param_list: 1,
};
let table_info = Arc::new(TableInfo::new_test(
&[(TableId::Param, 100)],
false,
false,
false,
));
let row_size = <MethodDefRaw as TableRow>::row_size(&table_info) as usize;
let mut buffer = vec![0u8; row_size];
let mut offset = 0;
let result = large_flags_row.row_write(&mut buffer, &mut offset, 1, &table_info);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Method implementation flags value exceeds u16 range"));
}
}