use crate::{
cilassembly::{ChangeRefRc, CilAssembly},
metadata::{
tables::{CodedIndex, CodedIndexType, GenericParamConstraintRaw, TableDataOwned, TableId},
token::Token,
},
Error, Result,
};
pub struct GenericParamConstraintBuilder {
owner: Option<u32>,
constraint: Option<CodedIndex>,
}
impl Default for GenericParamConstraintBuilder {
fn default() -> Self {
Self::new()
}
}
impl GenericParamConstraintBuilder {
#[must_use]
pub fn new() -> Self {
Self {
owner: None,
constraint: None,
}
}
#[must_use]
pub fn owner(mut self, owner: u32) -> Self {
self.owner = Some(owner);
self
}
#[must_use]
pub fn constraint(mut self, constraint: CodedIndex) -> Self {
self.constraint = Some(constraint);
self
}
pub fn build(self, assembly: &mut CilAssembly) -> Result<ChangeRefRc> {
let owner = self.owner.ok_or_else(|| {
Error::ModificationInvalid("GenericParamConstraint owner is required".to_string())
})?;
let constraint = self.constraint.ok_or_else(|| {
Error::ModificationInvalid("GenericParamConstraint constraint is required".to_string())
})?;
if owner == 0 {
return Err(Error::ModificationInvalid(
"GenericParamConstraint owner RID cannot be 0".to_string(),
));
}
let valid_constraint_tables = CodedIndexType::TypeDefOrRef.tables();
if !valid_constraint_tables.contains(&constraint.tag) {
return Err(Error::ModificationInvalid(format!(
"Constraint must be a TypeDefOrRef coded index (TypeDef/TypeRef/TypeSpec), got {:?}",
constraint.tag
)));
}
let rid = assembly.next_rid(TableId::GenericParamConstraint)?;
let token_value = ((TableId::GenericParamConstraint as u32) << 24) | rid;
let token = Token::new(token_value);
let constraint_raw = GenericParamConstraintRaw {
rid,
token,
offset: 0, owner,
constraint,
};
assembly.table_row_add(
TableId::GenericParamConstraint,
TableDataOwned::GenericParamConstraint(constraint_raw),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
cilassembly::{ChangeRefKind, CilAssembly},
metadata::cilassemblyview::CilAssemblyView,
};
use std::path::PathBuf;
#[test]
fn test_generic_param_constraint_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 owner_rid = 1; let constraint_type =
CodedIndex::new(TableId::TypeRef, 1, CodedIndexType::TypeDefOrRef);
let constraint_ref = GenericParamConstraintBuilder::new()
.owner(owner_rid)
.constraint(constraint_type)
.build(&mut assembly)
.unwrap();
assert_eq!(
constraint_ref.kind(),
ChangeRefKind::TableRow(TableId::GenericParamConstraint)
);
}
}
#[test]
fn test_generic_param_constraint_builder_base_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 generic_param_rid = 1; let base_class = CodedIndex::new(TableId::TypeDef, 1, CodedIndexType::TypeDefOrRef);
let constraint_ref = GenericParamConstraintBuilder::new()
.owner(generic_param_rid)
.constraint(base_class)
.build(&mut assembly)
.unwrap();
assert_eq!(
constraint_ref.kind(),
ChangeRefKind::TableRow(TableId::GenericParamConstraint)
);
}
}
#[test]
fn test_generic_param_constraint_builder_interface() {
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_param_rid = 2; let interface_ref = CodedIndex::new(TableId::TypeRef, 2, CodedIndexType::TypeDefOrRef);
let constraint_ref = GenericParamConstraintBuilder::new()
.owner(generic_param_rid)
.constraint(interface_ref)
.build(&mut assembly)
.unwrap();
assert_eq!(
constraint_ref.kind(),
ChangeRefKind::TableRow(TableId::GenericParamConstraint)
);
}
}
#[test]
fn test_generic_param_constraint_builder_generic_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 generic_param_rid = 3; let generic_interface =
CodedIndex::new(TableId::TypeSpec, 1, CodedIndexType::TypeDefOrRef);
let constraint_ref = GenericParamConstraintBuilder::new()
.owner(generic_param_rid)
.constraint(generic_interface)
.build(&mut assembly)
.unwrap();
assert_eq!(
constraint_ref.kind(),
ChangeRefKind::TableRow(TableId::GenericParamConstraint)
);
}
}
#[test]
fn test_generic_param_constraint_builder_missing_owner() {
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 constraint_type =
CodedIndex::new(TableId::TypeRef, 1, CodedIndexType::TypeDefOrRef);
let result = GenericParamConstraintBuilder::new()
.constraint(constraint_type)
.build(&mut assembly);
assert!(result.is_err());
}
}
#[test]
fn test_generic_param_constraint_builder_missing_constraint() {
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 owner_rid = 1;
let result = GenericParamConstraintBuilder::new()
.owner(owner_rid)
.build(&mut assembly);
assert!(result.is_err());
}
}
#[test]
fn test_generic_param_constraint_builder_zero_owner_rid() {
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_owner = 0; let constraint_type =
CodedIndex::new(TableId::TypeRef, 1, CodedIndexType::TypeDefOrRef);
let result = GenericParamConstraintBuilder::new()
.owner(invalid_owner)
.constraint(constraint_type)
.build(&mut assembly);
assert!(result.is_err());
}
}
#[test]
fn test_generic_param_constraint_builder_invalid_constraint_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 owner_rid = 1; let invalid_constraint =
CodedIndex::new(TableId::Field, 1, CodedIndexType::TypeDefOrRef);
let result = GenericParamConstraintBuilder::new()
.owner(owner_rid)
.constraint(invalid_constraint)
.build(&mut assembly);
assert!(result.is_err());
}
}
#[test]
fn test_generic_param_constraint_builder_multiple_constraints() {
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_param_rid = 1;
let base_class = CodedIndex::new(TableId::TypeDef, 1, CodedIndexType::TypeDefOrRef); let interface1 = CodedIndex::new(TableId::TypeRef, 1, CodedIndexType::TypeDefOrRef); let interface2 = CodedIndex::new(TableId::TypeRef, 2, CodedIndexType::TypeDefOrRef); let generic_interface =
CodedIndex::new(TableId::TypeSpec, 1, CodedIndexType::TypeDefOrRef);
let constraint1_ref = GenericParamConstraintBuilder::new()
.owner(generic_param_rid)
.constraint(base_class)
.build(&mut assembly)
.unwrap();
let constraint2_ref = GenericParamConstraintBuilder::new()
.owner(generic_param_rid) .constraint(interface1)
.build(&mut assembly)
.unwrap();
let constraint3_ref = GenericParamConstraintBuilder::new()
.owner(generic_param_rid) .constraint(interface2)
.build(&mut assembly)
.unwrap();
let constraint4_ref = GenericParamConstraintBuilder::new()
.owner(generic_param_rid) .constraint(generic_interface)
.build(&mut assembly)
.unwrap();
assert!(!std::sync::Arc::ptr_eq(&constraint1_ref, &constraint2_ref));
assert!(!std::sync::Arc::ptr_eq(&constraint1_ref, &constraint3_ref));
assert!(!std::sync::Arc::ptr_eq(&constraint1_ref, &constraint4_ref));
assert!(!std::sync::Arc::ptr_eq(&constraint2_ref, &constraint3_ref));
assert!(!std::sync::Arc::ptr_eq(&constraint2_ref, &constraint4_ref));
assert!(!std::sync::Arc::ptr_eq(&constraint3_ref, &constraint4_ref));
assert_eq!(
constraint1_ref.kind(),
ChangeRefKind::TableRow(TableId::GenericParamConstraint)
);
assert_eq!(
constraint2_ref.kind(),
ChangeRefKind::TableRow(TableId::GenericParamConstraint)
);
assert_eq!(
constraint3_ref.kind(),
ChangeRefKind::TableRow(TableId::GenericParamConstraint)
);
assert_eq!(
constraint4_ref.kind(),
ChangeRefKind::TableRow(TableId::GenericParamConstraint)
);
}
}
#[test]
fn test_generic_param_constraint_builder_different_parameters() {
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_param_rid = 1; let method_param_rid = 2;
let type_constraint =
CodedIndex::new(TableId::TypeRef, 1, CodedIndexType::TypeDefOrRef); let method_constraint =
CodedIndex::new(TableId::TypeRef, 2, CodedIndexType::TypeDefOrRef);
let type_const_ref = GenericParamConstraintBuilder::new()
.owner(type_param_rid)
.constraint(type_constraint)
.build(&mut assembly)
.unwrap();
let method_const_ref = GenericParamConstraintBuilder::new()
.owner(method_param_rid)
.constraint(method_constraint)
.build(&mut assembly)
.unwrap();
assert!(!std::sync::Arc::ptr_eq(&type_const_ref, &method_const_ref));
assert_eq!(
type_const_ref.kind(),
ChangeRefKind::TableRow(TableId::GenericParamConstraint)
);
assert_eq!(
method_const_ref.kind(),
ChangeRefKind::TableRow(TableId::GenericParamConstraint)
);
}
}
#[test]
fn test_generic_param_constraint_builder_all_constraint_types() {
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_param_rid = 1;
let typedef_constraint_ref = GenericParamConstraintBuilder::new()
.owner(generic_param_rid)
.constraint(CodedIndex::new(
TableId::TypeDef,
1,
CodedIndexType::TypeDefOrRef,
))
.build(&mut assembly)
.unwrap();
let typeref_constraint_ref = GenericParamConstraintBuilder::new()
.owner(generic_param_rid)
.constraint(CodedIndex::new(
TableId::TypeRef,
1,
CodedIndexType::TypeDefOrRef,
))
.build(&mut assembly)
.unwrap();
let typespec_constraint_ref = GenericParamConstraintBuilder::new()
.owner(generic_param_rid)
.constraint(CodedIndex::new(
TableId::TypeSpec,
1,
CodedIndexType::TypeDefOrRef,
))
.build(&mut assembly)
.unwrap();
assert!(!std::sync::Arc::ptr_eq(
&typedef_constraint_ref,
&typeref_constraint_ref
));
assert!(!std::sync::Arc::ptr_eq(
&typedef_constraint_ref,
&typespec_constraint_ref
));
assert!(!std::sync::Arc::ptr_eq(
&typeref_constraint_ref,
&typespec_constraint_ref
));
assert_eq!(
typedef_constraint_ref.kind(),
ChangeRefKind::TableRow(TableId::GenericParamConstraint)
);
assert_eq!(
typeref_constraint_ref.kind(),
ChangeRefKind::TableRow(TableId::GenericParamConstraint)
);
assert_eq!(
typespec_constraint_ref.kind(),
ChangeRefKind::TableRow(TableId::GenericParamConstraint)
);
}
}
#[test]
fn test_generic_param_constraint_builder_realistic_scenario() {
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_param_rid = 1;
let base_class_constraint_ref = GenericParamConstraintBuilder::new()
.owner(type_param_rid)
.constraint(CodedIndex::new(
TableId::TypeDef,
1,
CodedIndexType::TypeDefOrRef,
)) .build(&mut assembly)
.unwrap();
let comparable_constraint_ref = GenericParamConstraintBuilder::new()
.owner(type_param_rid)
.constraint(CodedIndex::new(
TableId::TypeSpec,
1,
CodedIndexType::TypeDefOrRef,
)) .build(&mut assembly)
.unwrap();
let disposable_constraint_ref = GenericParamConstraintBuilder::new()
.owner(type_param_rid)
.constraint(CodedIndex::new(
TableId::TypeRef,
1,
CodedIndexType::TypeDefOrRef,
)) .build(&mut assembly)
.unwrap();
assert_eq!(
base_class_constraint_ref.kind(),
ChangeRefKind::TableRow(TableId::GenericParamConstraint)
);
assert_eq!(
comparable_constraint_ref.kind(),
ChangeRefKind::TableRow(TableId::GenericParamConstraint)
);
assert_eq!(
disposable_constraint_ref.kind(),
ChangeRefKind::TableRow(TableId::GenericParamConstraint)
);
assert!(!std::sync::Arc::ptr_eq(
&base_class_constraint_ref,
&comparable_constraint_ref
));
assert!(!std::sync::Arc::ptr_eq(
&base_class_constraint_ref,
&disposable_constraint_ref
));
assert!(!std::sync::Arc::ptr_eq(
&comparable_constraint_ref,
&disposable_constraint_ref
));
}
}
}