use crate::{
metadata::{
signatures::{
types::{
SignatureField, SignatureLocalVariable, SignatureLocalVariables, SignatureMethod,
SignatureParameter, SignatureProperty, SignatureTypeSpec, TypeSignature,
},
CustomModifier,
},
token::Token,
},
Error, Result,
};
#[derive(Debug, Clone)]
pub struct MethodSignatureBuilder {
signature: SignatureMethod,
}
impl MethodSignatureBuilder {
#[must_use]
pub fn new() -> Self {
Self {
signature: SignatureMethod {
has_this: false,
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![],
by_ref: false,
base: TypeSignature::Void,
},
params: vec![],
varargs: vec![],
},
}
}
#[must_use]
pub fn calling_convention_default(mut self) -> Self {
self.clear_calling_conventions();
self.signature.default = true;
self
}
#[must_use]
pub fn calling_convention_vararg(mut self) -> Self {
self.clear_calling_conventions();
self.signature.vararg = true;
self
}
#[must_use]
pub fn calling_convention_cdecl(mut self) -> Self {
self.clear_calling_conventions();
self.signature.cdecl = true;
self
}
#[must_use]
pub fn calling_convention_stdcall(mut self) -> Self {
self.clear_calling_conventions();
self.signature.stdcall = true;
self
}
#[must_use]
pub fn calling_convention_thiscall(mut self) -> Self {
self.clear_calling_conventions();
self.signature.thiscall = true;
self
}
#[must_use]
pub fn calling_convention_fastcall(mut self) -> Self {
self.clear_calling_conventions();
self.signature.fastcall = true;
self
}
#[must_use]
pub fn has_this(mut self, has_this: bool) -> Self {
self.signature.has_this = has_this;
self
}
#[must_use]
pub fn explicit_this(mut self, explicit_this: bool) -> Self {
self.signature.explicit_this = explicit_this;
self
}
#[must_use]
pub fn generic_param_count(mut self, count: u32) -> Self {
self.signature.param_count_generic = count;
self
}
#[must_use]
pub fn returns(mut self, return_type: TypeSignature) -> Self {
self.signature.return_type.base = return_type;
self
}
#[must_use]
pub fn returns_by_ref(mut self) -> Self {
self.signature.return_type.by_ref = true;
self
}
#[must_use]
pub fn return_modifier(mut self, modifier_token: Token, is_required: bool) -> Self {
self.signature.return_type.modifiers.push(CustomModifier {
is_required,
modifier_type: modifier_token,
});
self
}
#[must_use]
pub fn param(mut self, param_type: TypeSignature) -> Self {
let param = SignatureParameter {
modifiers: vec![],
by_ref: false,
base: param_type,
};
self.signature.params.push(param);
self
}
#[must_use]
pub fn param_by_ref(mut self, param_type: TypeSignature) -> Self {
let param = SignatureParameter {
modifiers: vec![],
by_ref: true,
base: param_type,
};
self.signature.params.push(param);
self
}
#[must_use]
pub fn param_with_modifiers(
mut self,
param_type: TypeSignature,
modifiers: Vec<CustomModifier>,
) -> Self {
let param = SignatureParameter {
modifiers,
by_ref: false,
base: param_type,
};
self.signature.params.push(param);
self
}
#[must_use]
pub fn vararg_param(mut self, param_type: TypeSignature) -> Self {
let param = SignatureParameter {
modifiers: vec![],
by_ref: false,
base: param_type,
};
self.signature.varargs.push(param);
self
}
pub fn build(mut self) -> Result<SignatureMethod> {
let calling_conv_count = [
self.signature.default,
self.signature.vararg,
self.signature.cdecl,
self.signature.stdcall,
self.signature.thiscall,
self.signature.fastcall,
]
.iter()
.filter(|&&x| x)
.count();
if calling_conv_count == 0 {
return Err(Error::ModificationInvalid(
"Method signature must have exactly one calling convention".to_string(),
));
}
if calling_conv_count > 1 {
return Err(Error::ModificationInvalid(
"Method signature cannot have multiple calling conventions".to_string(),
));
}
if !self.signature.varargs.is_empty() && !self.signature.vararg {
return Err(Error::ModificationInvalid(
"Variable argument parameters require vararg calling convention".to_string(),
));
}
if self.signature.explicit_this && !self.signature.has_this {
return Err(Error::ModificationInvalid(
"explicit_this requires has_this to be true".to_string(),
));
}
self.signature.param_count = u32::try_from(self.signature.params.len()).map_err(|_| {
Error::ModificationInvalid(format!(
"Too many parameters: {}",
self.signature.params.len()
))
})?;
Ok(self.signature)
}
fn clear_calling_conventions(&mut self) {
self.signature.default = false;
self.signature.vararg = false;
self.signature.cdecl = false;
self.signature.stdcall = false;
self.signature.thiscall = false;
self.signature.fastcall = false;
}
}
impl Default for MethodSignatureBuilder {
fn default() -> Self {
Self::new()
}
}
impl MethodSignatureBuilder {
#[must_use]
pub fn simple_void_method() -> Self {
Self::new()
}
#[must_use]
pub fn simple_method(return_type: TypeSignature) -> Self {
Self::new().returns(return_type)
}
#[must_use]
pub fn instance_method(return_type: TypeSignature) -> Self {
Self::new().has_this(true).returns(return_type)
}
#[must_use]
pub fn static_method(return_type: TypeSignature) -> Self {
Self::new().returns(return_type)
}
#[must_use]
pub fn generic_method(return_type: TypeSignature, generic_count: u32) -> Self {
Self::new()
.generic_param_count(generic_count)
.returns(return_type)
}
#[must_use]
pub fn property_getter(return_type: TypeSignature) -> Self {
Self::instance_method(return_type)
}
#[must_use]
pub fn property_setter(value_type: TypeSignature) -> Self {
Self::instance_method(TypeSignature::Void).param(value_type)
}
#[must_use]
pub fn constructor() -> Self {
Self::instance_method(TypeSignature::Void)
}
#[must_use]
pub fn static_constructor() -> Self {
Self::simple_void_method()
}
#[must_use]
pub fn pinvoke_stdcall(return_type: TypeSignature) -> Self {
Self::new()
.calling_convention_stdcall()
.returns(return_type)
}
#[must_use]
pub fn pinvoke_cdecl(return_type: TypeSignature) -> Self {
Self::new().calling_convention_cdecl().returns(return_type)
}
}
#[derive(Debug, Clone)]
pub struct FieldSignatureBuilder {
field_type: Option<TypeSignature>,
modifiers: Vec<CustomModifier>,
}
impl FieldSignatureBuilder {
#[must_use]
pub fn new() -> Self {
Self {
field_type: None,
modifiers: vec![],
}
}
#[must_use]
pub fn field_type(mut self, field_type: TypeSignature) -> Self {
self.field_type = Some(field_type);
self
}
#[must_use]
pub fn custom_modifier(mut self, modifier_token: Token, is_required: bool) -> Self {
self.modifiers.push(CustomModifier {
is_required,
modifier_type: modifier_token,
});
self
}
pub fn build(self) -> Result<SignatureField> {
let field_type = self.field_type.ok_or_else(|| {
Error::ModificationInvalid("Field signature must specify a field type".to_string())
})?;
Ok(SignatureField {
modifiers: self.modifiers,
base: field_type,
})
}
}
impl Default for FieldSignatureBuilder {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct PropertySignatureBuilder {
signature: SignatureProperty,
}
impl PropertySignatureBuilder {
#[must_use]
pub fn new() -> Self {
Self {
signature: SignatureProperty {
has_this: false,
modifiers: vec![],
base: TypeSignature::Object, params: vec![],
},
}
}
#[must_use]
pub fn has_this(mut self, has_this: bool) -> Self {
self.signature.has_this = has_this;
self
}
#[must_use]
pub fn property_type(mut self, property_type: TypeSignature) -> Self {
self.signature.base = property_type;
self
}
#[must_use]
pub fn property_type_modifier(mut self, modifier_token: Token, is_required: bool) -> Self {
self.signature.modifiers.push(CustomModifier {
is_required,
modifier_type: modifier_token,
});
self
}
#[must_use]
pub fn param(mut self, param_type: TypeSignature) -> Self {
let param = SignatureParameter {
modifiers: vec![],
by_ref: false,
base: param_type,
};
self.signature.params.push(param);
self
}
#[must_use]
pub fn param_by_ref(mut self, param_type: TypeSignature) -> Self {
let param = SignatureParameter {
modifiers: vec![],
by_ref: true,
base: param_type,
};
self.signature.params.push(param);
self
}
pub fn build(self) -> Result<SignatureProperty> {
Ok(self.signature)
}
}
impl Default for PropertySignatureBuilder {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct LocalVariableSignatureBuilder {
signature: SignatureLocalVariables,
}
impl LocalVariableSignatureBuilder {
#[must_use]
pub fn new() -> Self {
Self {
signature: SignatureLocalVariables { locals: vec![] },
}
}
#[must_use]
pub fn add_local(mut self, local_type: TypeSignature) -> Self {
let local = SignatureLocalVariable {
modifiers: vec![],
is_byref: false,
is_pinned: false,
base: local_type,
};
self.signature.locals.push(local);
self
}
#[must_use]
pub fn add_pinned_local(mut self, local_type: TypeSignature) -> Self {
let local = SignatureLocalVariable {
modifiers: vec![],
is_byref: false,
is_pinned: true,
base: local_type,
};
self.signature.locals.push(local);
self
}
#[must_use]
pub fn add_byref_local(mut self, local_type: TypeSignature) -> Self {
let local = SignatureLocalVariable {
modifiers: vec![],
is_byref: true,
is_pinned: false,
base: local_type,
};
self.signature.locals.push(local);
self
}
#[must_use]
pub fn add_local_with_modifiers(
mut self,
local_type: TypeSignature,
modifiers: Vec<CustomModifier>,
) -> Self {
let local = SignatureLocalVariable {
modifiers,
is_byref: false,
is_pinned: false,
base: local_type,
};
self.signature.locals.push(local);
self
}
pub fn build(self) -> Result<SignatureLocalVariables> {
Ok(self.signature)
}
}
impl Default for LocalVariableSignatureBuilder {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct TypeSpecSignatureBuilder {
type_signature: Option<TypeSignature>,
}
impl TypeSpecSignatureBuilder {
#[must_use]
pub fn new() -> Self {
Self {
type_signature: None,
}
}
#[must_use]
pub fn type_signature(mut self, type_signature: TypeSignature) -> Self {
self.type_signature = Some(type_signature);
self
}
#[must_use]
pub fn generic_instantiation(
mut self,
base_type: TypeSignature,
type_args: Vec<TypeSignature>,
) -> Self {
self.type_signature = Some(TypeSignature::GenericInst(Box::new(base_type), type_args));
self
}
pub fn build(self) -> Result<SignatureTypeSpec> {
let type_signature = self.type_signature.ok_or_else(|| {
Error::ModificationInvalid(
"Type specification signature must specify a type".to_string(),
)
})?;
Ok(SignatureTypeSpec {
modifiers: Vec::new(),
base: type_signature,
})
}
}
impl Default for TypeSpecSignatureBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_method_signature_builder_basic() {
let signature = MethodSignatureBuilder::new()
.calling_convention_default()
.has_this(true)
.returns(TypeSignature::I4)
.param(TypeSignature::String)
.build()
.unwrap();
assert!(signature.has_this);
assert!(signature.default);
assert_eq!(signature.param_count, 1);
assert_eq!(signature.params.len(), 1);
assert_eq!(signature.return_type.base, TypeSignature::I4);
assert_eq!(signature.params[0].base, TypeSignature::String);
}
#[test]
fn test_method_signature_builder_generic() {
let signature = MethodSignatureBuilder::new()
.calling_convention_default()
.generic_param_count(1)
.returns(TypeSignature::GenericParamMethod(0))
.param(TypeSignature::GenericParamMethod(0))
.build()
.unwrap();
assert_eq!(signature.param_count_generic, 1);
assert_eq!(
signature.return_type.base,
TypeSignature::GenericParamMethod(0)
);
assert_eq!(
signature.params[0].base,
TypeSignature::GenericParamMethod(0)
);
}
#[test]
fn test_method_signature_builder_varargs() {
let signature = MethodSignatureBuilder::new()
.calling_convention_vararg()
.returns(TypeSignature::Void)
.param(TypeSignature::String)
.vararg_param(TypeSignature::Object)
.vararg_param(TypeSignature::I4)
.build()
.unwrap();
assert!(signature.vararg);
assert_eq!(signature.param_count, 1);
assert_eq!(signature.varargs.len(), 2);
assert_eq!(signature.varargs[0].base, TypeSignature::Object);
assert_eq!(signature.varargs[1].base, TypeSignature::I4);
}
#[test]
fn test_method_signature_builder_validation_no_calling_convention() {
let builder = MethodSignatureBuilder::new();
let mut builder = builder;
builder.signature.default = false;
let result = builder.build();
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("exactly one calling convention"));
}
#[test]
fn test_method_signature_builder_validation_multiple_calling_conventions() {
let signature = MethodSignatureBuilder::new()
.calling_convention_default()
.calling_convention_cdecl();
let result = signature.build();
assert!(result.is_ok());
let sig = result.unwrap();
assert!(!sig.default);
assert!(sig.cdecl);
}
#[test]
fn test_method_signature_builder_validation_varargs_without_vararg_convention() {
let signature = MethodSignatureBuilder::new()
.calling_convention_default()
.vararg_param(TypeSignature::Object);
let result = signature.build();
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("vararg calling convention"));
}
#[test]
fn test_field_signature_builder() {
let signature = FieldSignatureBuilder::new()
.field_type(TypeSignature::String)
.build()
.unwrap();
assert_eq!(signature.base, TypeSignature::String);
assert!(signature.modifiers.is_empty());
}
#[test]
fn test_field_signature_builder_with_modifiers() {
let modifier_token = Token::new(0x01000001);
let signature = FieldSignatureBuilder::new()
.field_type(TypeSignature::I4)
.custom_modifier(modifier_token, false) .build()
.unwrap();
assert_eq!(signature.base, TypeSignature::I4);
assert_eq!(signature.modifiers.len(), 1);
assert_eq!(signature.modifiers[0].modifier_type, modifier_token);
assert!(!signature.modifiers[0].is_required);
}
#[test]
fn test_field_signature_builder_validation_no_type() {
let result = FieldSignatureBuilder::new().build();
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("field type"));
}
#[test]
fn test_property_signature_builder() {
let signature = PropertySignatureBuilder::new()
.has_this(true)
.property_type(TypeSignature::String)
.param(TypeSignature::I4)
.build()
.unwrap();
assert!(signature.has_this);
assert_eq!(signature.base, TypeSignature::String);
assert_eq!(signature.params.len(), 1);
assert_eq!(signature.params[0].base, TypeSignature::I4);
}
#[test]
fn test_local_variable_signature_builder() {
let signature = LocalVariableSignatureBuilder::new()
.add_local(TypeSignature::I4)
.add_pinned_local(TypeSignature::String)
.add_byref_local(TypeSignature::Object)
.build()
.unwrap();
assert_eq!(signature.locals.len(), 3);
assert_eq!(signature.locals[0].base, TypeSignature::I4);
assert!(!signature.locals[0].is_byref);
assert!(!signature.locals[0].is_pinned);
assert_eq!(signature.locals[1].base, TypeSignature::String);
assert!(!signature.locals[1].is_byref);
assert!(signature.locals[1].is_pinned);
assert_eq!(signature.locals[2].base, TypeSignature::Object);
assert!(signature.locals[2].is_byref);
assert!(!signature.locals[2].is_pinned);
}
#[test]
fn test_type_spec_signature_builder() {
let list_token = Token::new(0x02000001);
let signature = TypeSpecSignatureBuilder::new()
.generic_instantiation(TypeSignature::Class(list_token), vec![TypeSignature::I4])
.build()
.unwrap();
if let TypeSignature::GenericInst(base_type, type_args) = &signature.base {
if let TypeSignature::Class(token) = base_type.as_ref() {
assert_eq!(*token, list_token);
} else {
panic!("Expected class type");
}
assert_eq!(type_args.len(), 1);
assert_eq!(type_args[0], TypeSignature::I4);
} else {
panic!("Expected generic instantiation");
}
}
#[test]
fn test_type_spec_signature_builder_validation_no_type() {
let result = TypeSpecSignatureBuilder::new().build();
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("specify a type"));
}
#[test]
fn test_simple_void_method() {
let signature = MethodSignatureBuilder::simple_void_method()
.build()
.unwrap();
assert!(!signature.has_this);
assert!(signature.default);
assert_eq!(signature.return_type.base, TypeSignature::Void);
assert!(signature.params.is_empty());
}
#[test]
fn test_simple_method() {
let signature = MethodSignatureBuilder::simple_method(TypeSignature::I4)
.build()
.unwrap();
assert!(!signature.has_this);
assert_eq!(signature.return_type.base, TypeSignature::I4);
}
#[test]
fn test_instance_method() {
let signature = MethodSignatureBuilder::instance_method(TypeSignature::String)
.build()
.unwrap();
assert!(signature.has_this);
assert_eq!(signature.return_type.base, TypeSignature::String);
}
#[test]
fn test_static_method() {
let signature = MethodSignatureBuilder::static_method(TypeSignature::Boolean)
.param(TypeSignature::String)
.build()
.unwrap();
assert!(!signature.has_this);
assert_eq!(signature.return_type.base, TypeSignature::Boolean);
assert_eq!(signature.params.len(), 1);
assert_eq!(signature.params[0].base, TypeSignature::String);
}
#[test]
fn test_generic_method_factory() {
let signature =
MethodSignatureBuilder::generic_method(TypeSignature::GenericParamMethod(0), 1)
.param(TypeSignature::GenericParamMethod(0))
.build()
.unwrap();
assert_eq!(signature.param_count_generic, 1);
assert_eq!(
signature.return_type.base,
TypeSignature::GenericParamMethod(0)
);
}
#[test]
fn test_property_getter_factory() {
let signature = MethodSignatureBuilder::property_getter(TypeSignature::String)
.build()
.unwrap();
assert!(signature.has_this);
assert_eq!(signature.return_type.base, TypeSignature::String);
assert!(signature.params.is_empty());
}
#[test]
fn test_property_setter_factory() {
let signature = MethodSignatureBuilder::property_setter(TypeSignature::String)
.build()
.unwrap();
assert!(signature.has_this);
assert_eq!(signature.return_type.base, TypeSignature::Void);
assert_eq!(signature.params.len(), 1);
assert_eq!(signature.params[0].base, TypeSignature::String);
}
#[test]
fn test_constructor_factory() {
let signature = MethodSignatureBuilder::constructor()
.param(TypeSignature::String)
.param(TypeSignature::I4)
.build()
.unwrap();
assert!(signature.has_this);
assert_eq!(signature.return_type.base, TypeSignature::Void);
assert_eq!(signature.params.len(), 2);
}
#[test]
fn test_static_constructor_factory() {
let signature = MethodSignatureBuilder::static_constructor()
.build()
.unwrap();
assert!(!signature.has_this);
assert_eq!(signature.return_type.base, TypeSignature::Void);
assert!(signature.params.is_empty());
}
#[test]
fn test_pinvoke_stdcall_factory() {
let signature = MethodSignatureBuilder::pinvoke_stdcall(TypeSignature::I4)
.param(TypeSignature::I4)
.build()
.unwrap();
assert!(signature.stdcall);
assert!(!signature.default);
assert_eq!(signature.return_type.base, TypeSignature::I4);
}
#[test]
fn test_pinvoke_cdecl_factory() {
let signature = MethodSignatureBuilder::pinvoke_cdecl(TypeSignature::Void)
.param(TypeSignature::String)
.build()
.unwrap();
assert!(signature.cdecl);
assert!(!signature.default);
assert_eq!(signature.return_type.base, TypeSignature::Void);
}
}