use crate::{
cilassembly::{ChangeRefRc, CilAssembly},
metadata::{
method::{MethodAccessFlags, MethodModifiers},
signatures::{encode_method_signature, SignatureMethod, SignatureParameter, TypeSignature},
tables::{
CodedIndex, CodedIndexType, InterfaceImplBuilder, MethodDefBuilder,
MethodSemanticsAttributes, MethodSemanticsBuilder, TableId, TypeAttributes,
TypeDefBuilder,
},
},
Error, Result,
};
use super::property::PropertyBuilder;
struct InterfaceMethodDefinition {
name: String,
return_type: TypeSignature,
parameters: Vec<(String, TypeSignature)>,
attributes: u32,
}
struct InterfacePropertyDefinition {
name: String,
property_type: TypeSignature,
has_getter: bool,
has_setter: bool,
}
pub struct InterfaceBuilder {
name: String,
namespace: Option<String>,
visibility: u32,
attributes: u32,
methods: Vec<InterfaceMethodDefinition>,
properties: Vec<InterfacePropertyDefinition>,
extends: Vec<CodedIndex>,
}
impl InterfaceBuilder {
#[must_use]
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
namespace: None,
visibility: TypeAttributes::PUBLIC,
attributes: TypeAttributes::INTERFACE | TypeAttributes::ABSTRACT,
methods: Vec::new(),
properties: Vec::new(),
extends: Vec::new(),
}
}
#[must_use]
pub fn namespace(mut self, namespace: &str) -> Self {
self.namespace = Some(namespace.to_string());
self
}
#[must_use]
pub fn public(mut self) -> Self {
self.visibility = TypeAttributes::PUBLIC;
self
}
#[must_use]
pub fn internal(mut self) -> Self {
self.visibility = TypeAttributes::NOT_PUBLIC;
self
}
#[must_use]
pub fn extends(mut self, interface: CodedIndex) -> Self {
self.extends.push(interface);
self
}
#[must_use]
pub fn extends_row(mut self, interface_row: u32) -> Self {
let coded_index = CodedIndex::new(
TableId::TypeDef,
interface_row,
CodedIndexType::TypeDefOrRef,
);
self.extends.push(coded_index);
self
}
#[must_use]
pub fn method_signature(
mut self,
name: &str,
return_type: TypeSignature,
parameters: Vec<(String, TypeSignature)>,
) -> Self {
self.methods.push(InterfaceMethodDefinition {
name: name.to_string(),
return_type,
parameters,
attributes: MethodModifiers::ABSTRACT.bits()
| MethodAccessFlags::PUBLIC.bits()
| MethodModifiers::HIDE_BY_SIG.bits(),
});
self
}
#[must_use]
pub fn simple_method(self, name: &str, return_type: TypeSignature) -> Self {
self.method_signature(name, return_type, vec![])
}
#[must_use]
pub fn property(
mut self,
name: &str,
property_type: TypeSignature,
has_getter: bool,
has_setter: bool,
) -> Self {
self.properties.push(InterfacePropertyDefinition {
name: name.to_string(),
property_type,
has_getter,
has_setter,
});
self
}
#[must_use]
pub fn readonly_property(self, name: &str, property_type: TypeSignature) -> Self {
self.property(name, property_type, true, false)
}
#[must_use]
pub fn readwrite_property(self, name: &str, property_type: TypeSignature) -> Self {
self.property(name, property_type, true, true)
}
pub fn build(self, assembly: &mut CilAssembly) -> Result<ChangeRefRc> {
if self.name.is_empty() {
return Err(Error::ModificationInvalid(
"Interface name cannot be empty".to_string(),
));
}
let mut typedef_builder = TypeDefBuilder::new()
.name(&self.name)
.flags(self.visibility | self.attributes);
if let Some(namespace) = &self.namespace {
typedef_builder = typedef_builder.namespace(namespace);
}
let interface_ref = typedef_builder.build(assembly)?;
for method_def in self.methods {
let signature_params: Vec<SignatureParameter> = method_def
.parameters
.iter()
.map(|(_, param_type)| SignatureParameter {
modifiers: Vec::new(),
by_ref: false,
base: param_type.clone(),
})
.collect();
let method_signature = SignatureMethod {
has_this: true,
explicit_this: false,
default: true,
vararg: false,
cdecl: false,
stdcall: false,
thiscall: false,
fastcall: false,
param_count_generic: 0,
param_count: u32::try_from(method_def.parameters.len())
.map_err(|_| malformed_error!("Method parameter count exceeds u32 range"))?,
return_type: SignatureParameter {
modifiers: Vec::new(),
by_ref: false,
base: method_def.return_type.clone(),
},
params: signature_params,
varargs: Vec::new(),
};
let signature_bytes = encode_method_signature(&method_signature)?;
MethodDefBuilder::new()
.name(&method_def.name)
.flags(method_def.attributes)
.impl_flags(0x0000) .signature(&signature_bytes)
.build(assembly)?;
}
for prop_def in self.properties {
let mut getter_placeholder: Option<u32> = None;
let mut setter_placeholder: Option<u32> = None;
if prop_def.has_getter {
let getter_name = format!("get_{}", prop_def.name);
let getter_signature = SignatureMethod {
has_this: true,
explicit_this: false,
default: true,
vararg: false,
cdecl: false,
stdcall: false,
thiscall: false,
fastcall: false,
param_count_generic: 0,
param_count: 0,
return_type: SignatureParameter {
modifiers: Vec::new(),
by_ref: false,
base: prop_def.property_type.clone(),
},
params: Vec::new(),
varargs: Vec::new(),
};
let getter_signature_bytes = encode_method_signature(&getter_signature)?;
let getter_ref = MethodDefBuilder::new()
.name(&getter_name)
.flags(
MethodModifiers::ABSTRACT.bits()
| MethodAccessFlags::PUBLIC.bits()
| MethodModifiers::HIDE_BY_SIG.bits()
| MethodModifiers::SPECIAL_NAME.bits(),
)
.impl_flags(0x0000) .signature(&getter_signature_bytes)
.build(assembly)?;
getter_placeholder = Some(getter_ref.placeholder());
}
if prop_def.has_setter {
let setter_name = format!("set_{}", prop_def.name);
let setter_signature = SignatureMethod {
has_this: true,
explicit_this: false,
default: true,
vararg: false,
cdecl: false,
stdcall: false,
thiscall: false,
fastcall: false,
param_count_generic: 0,
param_count: 1,
return_type: SignatureParameter {
modifiers: Vec::new(),
by_ref: false,
base: TypeSignature::Void,
},
params: vec![SignatureParameter {
modifiers: Vec::new(),
by_ref: false,
base: prop_def.property_type.clone(),
}],
varargs: Vec::new(),
};
let setter_signature_bytes = encode_method_signature(&setter_signature)?;
let setter_ref = MethodDefBuilder::new()
.name(&setter_name)
.flags(
MethodModifiers::ABSTRACT.bits()
| MethodAccessFlags::PUBLIC.bits()
| MethodModifiers::HIDE_BY_SIG.bits()
| MethodModifiers::SPECIAL_NAME.bits(),
)
.impl_flags(0x0000) .signature(&setter_signature_bytes)
.build(assembly)?;
setter_placeholder = Some(setter_ref.placeholder());
}
let property_ref =
PropertyBuilder::new(&prop_def.name, prop_def.property_type).build(assembly)?;
let property_placeholder = property_ref.placeholder();
if let Some(getter_row) = getter_placeholder {
MethodSemanticsBuilder::new()
.semantics(MethodSemanticsAttributes::GETTER)
.method(getter_row)
.association_from_property(property_placeholder)
.build(assembly)?;
}
if let Some(setter_row) = setter_placeholder {
MethodSemanticsBuilder::new()
.semantics(MethodSemanticsAttributes::SETTER)
.method(setter_row)
.association_from_property(property_placeholder)
.build(assembly)?;
}
}
for interface_index in self.extends {
InterfaceImplBuilder::new()
.class(interface_ref.placeholder())
.interface(interface_index)
.build(assembly)?;
}
Ok(interface_ref)
}
}
impl Default for InterfaceBuilder {
fn default() -> Self {
Self::new("DefaultInterface")
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
cilassembly::{ChangeRefKind, CilAssembly},
metadata::{cilassemblyview::CilAssemblyView, signatures::TypeSignature, tables::TableId},
};
use std::path::PathBuf;
fn get_test_assembly() -> Result<CilAssembly> {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
let view = CilAssemblyView::from_path(&path)?;
Ok(CilAssembly::new(view))
}
#[test]
fn test_simple_interface() -> Result<()> {
let mut assembly = get_test_assembly()?;
let interface_ref = InterfaceBuilder::new("ICalculator")
.public()
.namespace("MyApp.Interfaces")
.method_signature(
"Add",
TypeSignature::I4,
vec![
("a".to_string(), TypeSignature::I4),
("b".to_string(), TypeSignature::I4),
],
)
.build(&mut assembly)?;
assert_eq!(
interface_ref.kind(),
ChangeRefKind::TableRow(TableId::TypeDef)
);
Ok(())
}
#[test]
fn test_interface_with_properties() -> Result<()> {
let mut assembly = get_test_assembly()?;
let interface_ref = InterfaceBuilder::new("IRepository")
.public()
.readonly_property("Count", TypeSignature::I4)
.readwrite_property("IsEnabled", TypeSignature::Boolean)
.build(&mut assembly)?;
assert_eq!(
interface_ref.kind(),
ChangeRefKind::TableRow(TableId::TypeDef)
);
Ok(())
}
#[test]
fn test_interface_inheritance() -> Result<()> {
let mut assembly = get_test_assembly()?;
let base_ref = InterfaceBuilder::new("IBase")
.public()
.simple_method("BaseMethod", TypeSignature::Void)
.build(&mut assembly)?;
let base_placeholder = base_ref.placeholder();
let derived_ref = InterfaceBuilder::new("IDerived")
.public()
.extends_row(base_placeholder)
.simple_method("DerivedMethod", TypeSignature::Void)
.build(&mut assembly)?;
assert_eq!(base_ref.kind(), ChangeRefKind::TableRow(TableId::TypeDef));
assert_eq!(
derived_ref.kind(),
ChangeRefKind::TableRow(TableId::TypeDef)
);
Ok(())
}
#[test]
fn test_internal_interface() -> Result<()> {
let mut assembly = get_test_assembly()?;
let interface_ref = InterfaceBuilder::new("IInternalInterface")
.internal()
.simple_method("InternalMethod", TypeSignature::Void)
.build(&mut assembly)?;
assert_eq!(
interface_ref.kind(),
ChangeRefKind::TableRow(TableId::TypeDef)
);
Ok(())
}
#[test]
fn test_empty_interface() -> Result<()> {
let mut assembly = get_test_assembly()?;
let interface_ref = InterfaceBuilder::new("IMarker")
.public()
.build(&mut assembly)?;
assert_eq!(
interface_ref.kind(),
ChangeRefKind::TableRow(TableId::TypeDef)
);
Ok(())
}
#[test]
fn test_empty_name_fails() {
let mut assembly = get_test_assembly().unwrap();
let result = InterfaceBuilder::new("").public().build(&mut assembly);
assert!(result.is_err());
}
}