use crate::{
metadata::{
signatures::{
CustomModifier, SignatureField, SignatureLocalVariables, SignatureMethod,
SignatureParameter, SignatureProperty, SignatureTypeSpec, CALLING_CONVENTION,
SIGNATURE_HEADER,
},
token::Token,
typesystem::{TypeSignatureEncoder, ELEMENT_TYPE},
},
utils::write_compressed_uint,
Error, Result,
};
fn encode_custom_modifier(modifier: &CustomModifier, buffer: &mut Vec<u8>) -> Result<()> {
let modifier_type = if modifier.is_required {
ELEMENT_TYPE::CMOD_REQD
} else {
ELEMENT_TYPE::CMOD_OPT
};
buffer.push(modifier_type);
let coded_index = encode_type_def_or_ref_coded_index(modifier.modifier_type)?;
write_compressed_uint(coded_index, buffer);
Ok(())
}
fn encode_type_def_or_ref_coded_index(token: Token) -> Result<u32> {
let table_id = token.table();
let rid = token.row();
match table_id {
0x02 => Ok(rid << 2), 0x01 => Ok((rid << 2) | 1), 0x1B => Ok((rid << 2) | 2), _ => Err(Error::ModificationInvalid(format!(
"Invalid token table 0x{:02X} for TypeDefOrRef coded index. \
Expected TypeDef (0x02), TypeRef (0x01), or TypeSpec (0x1B). Token: 0x{:08X}",
table_id,
token.value()
))),
}
}
fn encode_parameter(parameter: &SignatureParameter, buffer: &mut Vec<u8>) -> Result<()> {
for modifier in ¶meter.modifiers {
encode_custom_modifier(modifier, buffer)?;
}
if parameter.by_ref {
buffer.push(ELEMENT_TYPE::BYREF);
}
TypeSignatureEncoder::encode_type_signature(¶meter.base, buffer)?;
Ok(())
}
pub fn encode_method_signature(signature: &SignatureMethod) -> Result<Vec<u8>> {
let mut buffer = Vec::new();
let mut calling_convention = if signature.vararg {
CALLING_CONVENTION::VARARG
} else if signature.fastcall {
CALLING_CONVENTION::FASTCALL
} else if signature.thiscall {
CALLING_CONVENTION::THISCALL
} else if signature.stdcall {
CALLING_CONVENTION::STDCALL
} else if signature.cdecl {
CALLING_CONVENTION::C
} else {
CALLING_CONVENTION::DEFAULT
};
if signature.has_this {
calling_convention |= CALLING_CONVENTION::HASTHIS;
}
if signature.explicit_this {
calling_convention |= CALLING_CONVENTION::EXPLICITTHIS;
}
if signature.param_count_generic > 0 {
calling_convention |= CALLING_CONVENTION::GENERIC;
}
buffer.push(calling_convention);
if signature.param_count_generic > 0 {
write_compressed_uint(signature.param_count_generic, &mut buffer);
}
let param_count = u32::try_from(signature.params.len()).map_err(|_| {
Error::ModificationInvalid(format!(
"Too many parameters in method signature: {}",
signature.params.len()
))
})?;
write_compressed_uint(param_count, &mut buffer);
encode_parameter(&signature.return_type, &mut buffer)?;
for param in &signature.params {
encode_parameter(param, &mut buffer)?;
}
Ok(buffer)
}
pub fn encode_field_signature(signature: &SignatureField) -> Result<Vec<u8>> {
let mut buffer = Vec::new();
buffer.push(SIGNATURE_HEADER::FIELD);
for modifier in &signature.modifiers {
encode_custom_modifier(modifier, &mut buffer)?;
}
TypeSignatureEncoder::encode_type_signature(&signature.base, &mut buffer)?;
Ok(buffer)
}
pub fn encode_property_signature(signature: &SignatureProperty) -> Result<Vec<u8>> {
let mut buffer = Vec::new();
let mut prolog = SIGNATURE_HEADER::PROPERTY;
if signature.has_this {
prolog |= CALLING_CONVENTION::HASTHIS;
}
buffer.push(prolog);
let param_count = u32::try_from(signature.params.len()).map_err(|_| {
Error::ModificationInvalid(format!(
"Too many parameters in property signature: {}",
signature.params.len()
))
})?;
write_compressed_uint(param_count, &mut buffer);
for modifier in &signature.modifiers {
encode_custom_modifier(modifier, &mut buffer)?;
}
TypeSignatureEncoder::encode_type_signature(&signature.base, &mut buffer)?;
for param in &signature.params {
encode_parameter(param, &mut buffer)?;
}
Ok(buffer)
}
pub fn encode_local_var_signature(signature: &SignatureLocalVariables) -> Result<Vec<u8>> {
let mut buffer = Vec::new();
buffer.push(SIGNATURE_HEADER::LOCAL_SIG);
write_compressed_uint(
u32::try_from(signature.locals.len()).map_err(|_| {
malformed_error!(
"LocalVar signature has too many locals: {}",
signature.locals.len()
)
})?,
&mut buffer,
);
for local in &signature.locals {
if local.is_pinned {
buffer.push(ELEMENT_TYPE::PINNED);
}
if local.is_byref {
buffer.push(ELEMENT_TYPE::BYREF);
}
TypeSignatureEncoder::encode_type_signature(&local.base, &mut buffer)?;
}
Ok(buffer)
}
pub fn encode_typespec_signature(signature: &SignatureTypeSpec) -> Result<Vec<u8>> {
TypeSignatureEncoder::encode(&signature.base)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::metadata::{
signatures::{
CustomModifier, FieldSignatureBuilder, LocalVariableSignatureBuilder,
MethodSignatureBuilder, PropertySignatureBuilder, TypeSignature,
TypeSpecSignatureBuilder, SIGNATURE_HEADER,
},
token::Token,
typesystem::ELEMENT_TYPE,
};
#[test]
fn test_encode_method_signature() {
let signature = MethodSignatureBuilder::new()
.calling_convention_default()
.returns(TypeSignature::Void)
.param(TypeSignature::I4)
.build()
.unwrap();
let result = encode_method_signature(&signature);
assert!(result.is_ok(), "Method signature encoding should succeed");
let encoded = result.unwrap();
assert!(!encoded.is_empty(), "Encoded signature should not be empty");
assert!(
encoded.len() >= 3,
"Encoded signature should have minimum structure"
);
}
#[test]
fn test_encode_field_signature() {
let signature = FieldSignatureBuilder::new()
.field_type(TypeSignature::String)
.build()
.unwrap();
let result = encode_field_signature(&signature);
assert!(result.is_ok(), "Field signature encoding should succeed");
let encoded = result.unwrap();
assert!(!encoded.is_empty(), "Encoded signature should not be empty");
assert_eq!(
encoded[0],
SIGNATURE_HEADER::FIELD,
"Field signature should start with SIGNATURE_HEADER::FIELD"
);
}
#[test]
fn test_encode_property_signature() {
let signature = PropertySignatureBuilder::new()
.property_type(TypeSignature::I4)
.build()
.unwrap();
let result = encode_property_signature(&signature);
assert!(result.is_ok(), "Property signature encoding should succeed");
let encoded = result.unwrap();
assert!(!encoded.is_empty(), "Encoded signature should not be empty");
assert_eq!(
encoded[0],
SIGNATURE_HEADER::PROPERTY,
"Property signature should start with SIGNATURE_HEADER::PROPERTY"
);
}
#[test]
fn test_encode_local_var_signature() {
let signature = LocalVariableSignatureBuilder::new()
.add_local(TypeSignature::I4)
.add_pinned_local(TypeSignature::String)
.build()
.unwrap();
let result = encode_local_var_signature(&signature);
assert!(
result.is_ok(),
"Local variable signature encoding should succeed"
);
let encoded = result.unwrap();
assert!(!encoded.is_empty(), "Encoded signature should not be empty");
assert_eq!(
encoded[0],
SIGNATURE_HEADER::LOCAL_SIG,
"Local variable signature should start with SIGNATURE_HEADER::LOCAL_SIG"
);
}
#[test]
fn test_encode_typespec_signature() {
let signature = TypeSpecSignatureBuilder::new()
.type_signature(TypeSignature::String)
.build()
.unwrap();
let result = encode_typespec_signature(&signature);
assert!(
result.is_ok(),
"Type specification signature encoding should succeed"
);
let encoded = result.unwrap();
assert!(!encoded.is_empty(), "Encoded signature should not be empty");
}
#[test]
fn test_encode_custom_modifier() {
let mut buffer = Vec::new();
let optional_modifier = CustomModifier {
is_required: false,
modifier_type: Token::new(0x01000001), };
encode_custom_modifier(&optional_modifier, &mut buffer).unwrap();
assert_eq!(
buffer[0],
ELEMENT_TYPE::CMOD_OPT,
"Optional modifier should start with ELEMENT_TYPE_CMOD_OPT"
);
assert!(buffer.len() > 1, "Modifier should include coded index");
buffer.clear();
let required_modifier = CustomModifier {
is_required: true,
modifier_type: Token::new(0x01000001),
};
encode_custom_modifier(&required_modifier, &mut buffer).unwrap();
assert_eq!(
buffer[0],
ELEMENT_TYPE::CMOD_REQD,
"Required modifier should start with ELEMENT_TYPE_CMOD_REQD"
);
assert!(buffer.len() > 1, "Modifier should include coded index");
}
#[test]
fn test_encode_type_def_or_ref_coded_index_error() {
let invalid_token = Token::new(0x06000001);
let result = encode_type_def_or_ref_coded_index(invalid_token);
assert!(
result.is_err(),
"Should return error for invalid token table"
);
let typedef_token = Token::new(0x02000001);
assert!(encode_type_def_or_ref_coded_index(typedef_token).is_ok());
let typeref_token = Token::new(0x01000001);
assert!(encode_type_def_or_ref_coded_index(typeref_token).is_ok());
let typespec_token = Token::new(0x1B000001);
assert!(encode_type_def_or_ref_coded_index(typespec_token).is_ok());
}
#[test]
fn test_encode_type_def_or_ref_coded_index() {
let typedef_token = Token::new(0x02000001); let coded_index = encode_type_def_or_ref_coded_index(typedef_token).unwrap();
assert_eq!(coded_index, 1 << 2, "TypeDef should encode as (rid << 2)");
let typeref_token = Token::new(0x01000005); let coded_index = encode_type_def_or_ref_coded_index(typeref_token).unwrap();
assert_eq!(
coded_index,
(5 << 2) | 1,
"TypeRef should encode as (rid << 2) | 1"
);
let typespec_token = Token::new(0x1B000003); let coded_index = encode_type_def_or_ref_coded_index(typespec_token).unwrap();
assert_eq!(
coded_index,
(3 << 2) | 2,
"TypeSpec should encode as (rid << 2) | 2"
);
}
}