use crate::{
cilassembly::{ChangeRefRc, CilAssembly},
metadata::{
tables::{CodedIndex, CodedIndexType, EventRaw, TableDataOwned, TableId},
token::Token,
},
Error, Result,
};
pub struct EventBuilder {
name: Option<String>,
flags: Option<u32>,
event_type: Option<CodedIndex>,
}
impl Default for EventBuilder {
fn default() -> Self {
Self::new()
}
}
impl EventBuilder {
#[must_use]
pub fn new() -> Self {
Self {
name: None,
flags: None,
event_type: None,
}
}
#[must_use]
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
#[must_use]
pub fn flags(mut self, flags: u32) -> Self {
self.flags = Some(flags);
self
}
#[must_use]
pub fn event_type(mut self, event_type: CodedIndex) -> Self {
self.event_type = Some(event_type);
self
}
pub fn build(self, assembly: &mut CilAssembly) -> Result<ChangeRefRc> {
let name = self
.name
.ok_or_else(|| Error::ModificationInvalid("Event name is required".to_string()))?;
let flags = self
.flags
.ok_or_else(|| Error::ModificationInvalid("Event flags are required".to_string()))?;
let event_type = self
.event_type
.ok_or_else(|| Error::ModificationInvalid("Event type is required".to_string()))?;
let valid_tables = CodedIndexType::TypeDefOrRef.tables();
if !valid_tables.contains(&event_type.tag) {
return Err(Error::ModificationInvalid(format!(
"Event type must be a TypeDefOrRef coded index (TypeDef/TypeRef/TypeSpec), got {:?}",
event_type.tag
)));
}
let name_index = assembly.string_get_or_add(&name)?.placeholder();
let rid = assembly.next_rid(TableId::Event)?;
let token = Token::from_parts(TableId::Event, rid);
let event_raw = EventRaw {
rid,
token,
offset: 0, flags,
name: name_index,
event_type,
};
assembly.table_row_add(TableId::Event, TableDataOwned::Event(event_raw))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
cilassembly::{ChangeRefKind, CilAssembly},
metadata::{cilassemblyview::CilAssemblyView, tables::EventAttributes},
};
use std::path::PathBuf;
#[test]
fn test_event_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 event_handler_type =
CodedIndex::new(TableId::TypeRef, 1, CodedIndexType::TypeDefOrRef);
let ref_ = EventBuilder::new()
.name("TestEvent")
.flags(0)
.event_type(event_handler_type)
.build(&mut assembly)
.unwrap();
assert_eq!(ref_.kind(), ChangeRefKind::TableRow(TableId::Event));
}
}
#[test]
fn test_event_builder_with_special_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 event_handler_type =
CodedIndex::new(TableId::TypeRef, 2, CodedIndexType::TypeDefOrRef);
let ref_ = EventBuilder::new()
.name("PropertyChanged")
.flags(EventAttributes::SPECIAL_NAME)
.event_type(event_handler_type)
.build(&mut assembly)
.unwrap();
assert_eq!(ref_.kind(), ChangeRefKind::TableRow(TableId::Event));
}
}
#[test]
fn test_event_builder_with_rtspecial_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 event_handler_type =
CodedIndex::new(TableId::TypeRef, 3, CodedIndexType::TypeDefOrRef);
let ref_ = EventBuilder::new()
.name("RuntimeSpecialEvent")
.flags(EventAttributes::RTSPECIAL_NAME)
.event_type(event_handler_type)
.build(&mut assembly)
.unwrap();
assert_eq!(ref_.kind(), ChangeRefKind::TableRow(TableId::Event));
}
}
#[test]
fn test_event_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 event_handler_type =
CodedIndex::new(TableId::TypeRef, 1, CodedIndexType::TypeDefOrRef);
let result = EventBuilder::new()
.flags(0)
.event_type(event_handler_type)
.build(&mut assembly);
assert!(result.is_err());
}
}
#[test]
fn test_event_builder_missing_flags() {
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 event_handler_type =
CodedIndex::new(TableId::TypeRef, 1, CodedIndexType::TypeDefOrRef);
let result = EventBuilder::new()
.name("TestEvent")
.event_type(event_handler_type)
.build(&mut assembly);
assert!(result.is_err());
}
}
#[test]
fn test_event_builder_missing_event_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 result = EventBuilder::new()
.name("TestEvent")
.flags(0)
.build(&mut assembly);
assert!(result.is_err());
}
}
#[test]
fn test_event_builder_invalid_coded_index_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 wrong_type = CodedIndex::new(TableId::MethodDef, 1, CodedIndexType::TypeDefOrRef);
let result = EventBuilder::new()
.name("TestEvent")
.flags(0)
.event_type(wrong_type)
.build(&mut assembly);
assert!(result.is_err());
}
}
#[test]
fn test_event_builder_multiple_events() {
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 event_handler_type1 =
CodedIndex::new(TableId::TypeRef, 1, CodedIndexType::TypeDefOrRef);
let event_handler_type2 =
CodedIndex::new(TableId::TypeRef, 2, CodedIndexType::TypeDefOrRef);
let event_handler_type3 =
CodedIndex::new(TableId::TypeRef, 3, CodedIndexType::TypeDefOrRef);
let ref1 = EventBuilder::new()
.name("Event1")
.flags(0)
.event_type(event_handler_type1)
.build(&mut assembly)
.unwrap();
let ref2 = EventBuilder::new()
.name("Event2")
.flags(EventAttributes::SPECIAL_NAME)
.event_type(event_handler_type2)
.build(&mut assembly)
.unwrap();
let ref3 = EventBuilder::new()
.name("Event3")
.flags(EventAttributes::RTSPECIAL_NAME)
.event_type(event_handler_type3)
.build(&mut assembly)
.unwrap();
assert!(!std::sync::Arc::ptr_eq(&ref1, &ref2));
assert!(!std::sync::Arc::ptr_eq(&ref1, &ref3));
assert!(!std::sync::Arc::ptr_eq(&ref2, &ref3));
assert_eq!(ref1.kind(), ChangeRefKind::TableRow(TableId::Event));
assert_eq!(ref2.kind(), ChangeRefKind::TableRow(TableId::Event));
assert_eq!(ref3.kind(), ChangeRefKind::TableRow(TableId::Event));
}
}
}