use crate::{
metadata::{
signatures::{CustomModifier, SignatureMethod, TypeSignature},
token::Token,
},
utils::{write_compressed_int, write_compressed_uint},
Error, Result,
};
const MAX_RECURSION_DEPTH: usize = 100;
pub struct TypeSignatureEncoder;
impl TypeSignatureEncoder {
pub fn encode(signature: &TypeSignature) -> Result<Vec<u8>> {
let mut buffer = Vec::new();
Self::encode_type_signature_internal(signature, &mut buffer, 0)?;
Ok(buffer)
}
pub fn encode_type_signature(signature: &TypeSignature, buffer: &mut Vec<u8>) -> Result<()> {
Self::encode_type_signature_internal(signature, buffer, 0)
}
fn encode_type_signature_internal(
signature: &TypeSignature,
buffer: &mut Vec<u8>,
depth: usize,
) -> Result<()> {
if depth >= MAX_RECURSION_DEPTH {
return Err(Error::RecursionLimit(MAX_RECURSION_DEPTH));
}
match signature {
TypeSignature::Void => buffer.push(0x01),
TypeSignature::Boolean => buffer.push(0x02),
TypeSignature::Char => buffer.push(0x03),
TypeSignature::I1 => buffer.push(0x04),
TypeSignature::U1 => buffer.push(0x05),
TypeSignature::I2 => buffer.push(0x06),
TypeSignature::U2 => buffer.push(0x07),
TypeSignature::I4 => buffer.push(0x08),
TypeSignature::U4 => buffer.push(0x09),
TypeSignature::I8 => buffer.push(0x0A),
TypeSignature::U8 => buffer.push(0x0B),
TypeSignature::R4 => buffer.push(0x0C),
TypeSignature::R8 => buffer.push(0x0D),
TypeSignature::String => buffer.push(0x0E),
TypeSignature::Object => buffer.push(0x1C),
TypeSignature::I => buffer.push(0x18),
TypeSignature::U => buffer.push(0x19),
TypeSignature::TypedByRef => buffer.push(0x16),
TypeSignature::ValueType(token) => {
buffer.push(0x11); Self::encode_typedefref_token(*token, buffer)?;
}
TypeSignature::Class(token) => {
buffer.push(0x12); Self::encode_typedefref_token(*token, buffer)?;
}
TypeSignature::GenericParamType(index) => {
buffer.push(0x13); write_compressed_uint(*index, buffer);
}
TypeSignature::GenericParamMethod(index) => {
buffer.push(0x1E); write_compressed_uint(*index, buffer);
}
TypeSignature::ByRef(inner) => {
buffer.push(0x10); Self::encode_type_signature_internal(inner, buffer, depth + 1)?;
}
TypeSignature::Ptr(pointer) => {
buffer.push(0x0F); Self::encode_custom_modifiers(&pointer.modifiers, buffer)?;
Self::encode_type_signature_internal(&pointer.base, buffer, depth + 1)?;
}
TypeSignature::Pinned(inner) => {
buffer.push(0x45); Self::encode_type_signature_internal(inner, buffer, depth + 1)?;
}
TypeSignature::SzArray(array) => {
buffer.push(0x1D); Self::encode_custom_modifiers(&array.modifiers, buffer)?;
Self::encode_type_signature_internal(&array.base, buffer, depth + 1)?;
}
TypeSignature::Array(array) => {
buffer.push(0x14); Self::encode_type_signature_internal(&array.base, buffer, depth + 1)?;
write_compressed_uint(array.rank, buffer);
let mut sizes = Vec::new();
let mut lower_bounds = Vec::new();
for dimension in &array.dimensions {
if let Some(size) = dimension.size {
sizes.push(size);
}
if let Some(lower_bound) = dimension.lower_bound {
lower_bounds.push(lower_bound);
}
}
write_compressed_uint(
u32::try_from(sizes.len()).map_err(|_| {
malformed_error!("Array sizes length out of range: {}", sizes.len())
})?,
buffer,
);
for size in sizes {
write_compressed_uint(size, buffer);
}
write_compressed_uint(
u32::try_from(lower_bounds.len()).map_err(|_| {
malformed_error!(
"Array lower bounds length out of range: {}",
lower_bounds.len()
)
})?,
buffer,
);
#[allow(clippy::cast_possible_wrap)]
for lower_bound in lower_bounds {
write_compressed_int(lower_bound as i32, buffer);
}
}
TypeSignature::GenericInst(base_type, type_args) => {
buffer.push(0x15); Self::encode_type_signature_internal(base_type, buffer, depth + 1)?;
write_compressed_uint(
u32::try_from(type_args.len()).map_err(|_| {
malformed_error!(
"Generic type arguments length out of range: {}",
type_args.len()
)
})?,
buffer,
);
for type_arg in type_args {
Self::encode_type_signature_internal(type_arg, buffer, depth + 1)?;
}
}
TypeSignature::FnPtr(method_sig) => {
buffer.push(0x1B); Self::encode_method_signature(method_sig.as_ref(), buffer)?;
}
TypeSignature::ModifiedRequired(modifiers)
| TypeSignature::ModifiedOptional(modifiers) => {
for modifier in modifiers {
let modifier_type = if modifier.is_required {
0x1F } else {
0x20 };
buffer.push(modifier_type);
Self::encode_typedefref_token(modifier.modifier_type, buffer)?;
}
}
TypeSignature::Type => buffer.push(0x50), TypeSignature::Boxed => buffer.push(0x51), TypeSignature::Field => {
return Err(Error::ModificationInvalid(
"Field signatures should not appear in type specifications".to_string(),
));
}
TypeSignature::Internal => {
return Err(Error::ModificationInvalid(
"Cannot encode internal type signature".to_string(),
));
}
TypeSignature::Modifier => buffer.push(0x22), TypeSignature::Sentinel => buffer.push(0x41), TypeSignature::Reserved => {
return Err(Error::ModificationInvalid(
"Cannot encode reserved type signature".to_string(),
));
}
TypeSignature::Unknown => {
return Err(Error::ModificationInvalid(
"Cannot encode unknown type signature".to_string(),
));
}
}
Ok(())
}
fn encode_method_signature(method_sig: &SignatureMethod, buffer: &mut Vec<u8>) -> Result<()> {
let mut calling_conv = 0u8;
if method_sig.has_this {
calling_conv |= 0x20;
}
if method_sig.explicit_this {
calling_conv |= 0x40;
}
if method_sig.default {
calling_conv |= 0x00;
}
if method_sig.vararg {
calling_conv |= 0x05;
}
if method_sig.cdecl {
calling_conv |= 0x01;
}
if method_sig.stdcall {
calling_conv |= 0x02;
}
if method_sig.thiscall {
calling_conv |= 0x03;
}
if method_sig.fastcall {
calling_conv |= 0x04;
}
buffer.push(calling_conv);
write_compressed_uint(
u32::try_from(method_sig.params.len()).map_err(|_| {
malformed_error!(
"Method parameters length out of range: {}",
method_sig.params.len()
)
})?,
buffer,
);
Self::encode_type_signature(&method_sig.return_type.base, buffer)?;
for param in &method_sig.params {
Self::encode_type_signature(¶m.base, buffer)?;
}
Ok(())
}
fn encode_custom_modifiers(modifiers: &[CustomModifier], buffer: &mut Vec<u8>) -> Result<()> {
for modifier in modifiers {
let modifier_type = if modifier.is_required {
0x1F } else {
0x20 };
buffer.push(modifier_type);
Self::encode_typedefref_token(modifier.modifier_type, buffer)?;
}
Ok(())
}
fn encode_typedefref_token(token: Token, buffer: &mut Vec<u8>) -> Result<()> {
let table_id = (token.value() >> 24) & 0xFF;
let rid = token.value() & 0x00FF_FFFF;
let coded_index = match table_id {
0x02 => rid << 2, 0x01 => (rid << 2) | 1, 0x1B => (rid << 2) | 2, _ => {
return Err(Error::ModificationInvalid(format!(
"Invalid token for TypeDefOrRef coded index: {:08x}",
token.value()
)));
}
};
write_compressed_uint(coded_index, buffer);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::metadata::signatures::{SignatureArray, SignaturePointer, SignatureSzArray};
use crate::metadata::typesystem::ArrayDimensions;
#[test]
fn test_encode_primitive_types() {
assert_eq!(
TypeSignatureEncoder::encode(&TypeSignature::Void).unwrap(),
vec![0x01]
);
assert_eq!(
TypeSignatureEncoder::encode(&TypeSignature::Boolean).unwrap(),
vec![0x02]
);
assert_eq!(
TypeSignatureEncoder::encode(&TypeSignature::Char).unwrap(),
vec![0x03]
);
assert_eq!(
TypeSignatureEncoder::encode(&TypeSignature::I1).unwrap(),
vec![0x04]
);
assert_eq!(
TypeSignatureEncoder::encode(&TypeSignature::U1).unwrap(),
vec![0x05]
);
assert_eq!(
TypeSignatureEncoder::encode(&TypeSignature::I2).unwrap(),
vec![0x06]
);
assert_eq!(
TypeSignatureEncoder::encode(&TypeSignature::U2).unwrap(),
vec![0x07]
);
assert_eq!(
TypeSignatureEncoder::encode(&TypeSignature::I4).unwrap(),
vec![0x08]
);
assert_eq!(
TypeSignatureEncoder::encode(&TypeSignature::U4).unwrap(),
vec![0x09]
);
assert_eq!(
TypeSignatureEncoder::encode(&TypeSignature::I8).unwrap(),
vec![0x0A]
);
assert_eq!(
TypeSignatureEncoder::encode(&TypeSignature::U8).unwrap(),
vec![0x0B]
);
assert_eq!(
TypeSignatureEncoder::encode(&TypeSignature::R4).unwrap(),
vec![0x0C]
);
assert_eq!(
TypeSignatureEncoder::encode(&TypeSignature::R8).unwrap(),
vec![0x0D]
);
assert_eq!(
TypeSignatureEncoder::encode(&TypeSignature::String).unwrap(),
vec![0x0E]
);
assert_eq!(
TypeSignatureEncoder::encode(&TypeSignature::Object).unwrap(),
vec![0x1C]
);
assert_eq!(
TypeSignatureEncoder::encode(&TypeSignature::I).unwrap(),
vec![0x18]
);
assert_eq!(
TypeSignatureEncoder::encode(&TypeSignature::U).unwrap(),
vec![0x19]
);
assert_eq!(
TypeSignatureEncoder::encode(&TypeSignature::TypedByRef).unwrap(),
vec![0x16]
);
}
#[test]
fn test_encode_reference_types() {
let valuetype_token = Token::new(0x02000001); let valuetype_sig = TypeSignature::ValueType(valuetype_token);
let encoded = TypeSignatureEncoder::encode(&valuetype_sig).unwrap();
assert_eq!(encoded, vec![0x11, 0x04]);
let class_token = Token::new(0x01000001); let class_sig = TypeSignature::Class(class_token);
let encoded = TypeSignatureEncoder::encode(&class_sig).unwrap();
assert_eq!(encoded, vec![0x12, 0x05]); }
#[test]
fn test_encode_generic_parameters() {
let type_param = TypeSignature::GenericParamType(0);
let encoded = TypeSignatureEncoder::encode(&type_param).unwrap();
assert_eq!(encoded, vec![0x13, 0x00]);
let method_param = TypeSignature::GenericParamMethod(1);
let encoded = TypeSignatureEncoder::encode(&method_param).unwrap();
assert_eq!(encoded, vec![0x1E, 0x01]); }
#[test]
fn test_encode_byref() {
let byref_sig = TypeSignature::ByRef(Box::new(TypeSignature::I4));
let encoded = TypeSignatureEncoder::encode(&byref_sig).unwrap();
assert_eq!(encoded, vec![0x10, 0x08]); }
#[test]
fn test_encode_pointer() {
let pointer_sig = TypeSignature::Ptr(SignaturePointer {
modifiers: vec![],
base: Box::new(TypeSignature::I4),
});
let encoded = TypeSignatureEncoder::encode(&pointer_sig).unwrap();
assert_eq!(encoded, vec![0x0F, 0x08]); }
#[test]
fn test_encode_szarray() {
let array_sig = TypeSignature::SzArray(SignatureSzArray {
modifiers: vec![],
base: Box::new(TypeSignature::String),
});
let encoded = TypeSignatureEncoder::encode(&array_sig).unwrap();
assert_eq!(encoded, vec![0x1D, 0x0E]); }
#[test]
fn test_encode_array() {
let array_sig = TypeSignature::Array(SignatureArray {
base: Box::new(TypeSignature::I4),
rank: 2,
dimensions: vec![
ArrayDimensions {
size: None,
lower_bound: None,
},
ArrayDimensions {
size: None,
lower_bound: None,
},
],
});
let encoded = TypeSignatureEncoder::encode(&array_sig).unwrap();
assert_eq!(encoded, vec![0x14, 0x08, 0x02, 0x00, 0x00]); }
#[test]
fn test_encode_generic_instantiation() {
let list_token = Token::new(0x02000001);
let generic_sig = TypeSignature::GenericInst(
Box::new(TypeSignature::Class(list_token)),
vec![TypeSignature::I4],
);
let encoded = TypeSignatureEncoder::encode(&generic_sig).unwrap();
assert_eq!(encoded, vec![0x15, 0x12, 0x04, 0x01, 0x08]); }
#[test]
fn test_encode_complex_nested_generic() {
let dict_token = Token::new(0x02000001);
let list_token = Token::new(0x02000002);
let nested_list = TypeSignature::GenericInst(
Box::new(TypeSignature::Class(list_token)),
vec![TypeSignature::I4],
);
let complex_sig = TypeSignature::GenericInst(
Box::new(TypeSignature::Class(dict_token)),
vec![TypeSignature::String, nested_list],
);
let encoded = TypeSignatureEncoder::encode(&complex_sig).unwrap();
assert_eq!(encoded[0], 0x15); assert_eq!(encoded[1], 0x12); assert_eq!(encoded[2], 0x04); assert_eq!(encoded[3], 0x02); assert_eq!(encoded[4], 0x0E); assert_eq!(encoded[5], 0x15); }
#[test]
fn test_encode_typedefref_tokens() {
let mut buffer = Vec::new();
let typedef_token = Token::new(0x02000001);
TypeSignatureEncoder::encode_typedefref_token(typedef_token, &mut buffer).unwrap();
assert_eq!(buffer, vec![0x04]); buffer.clear();
let typeref_token = Token::new(0x01000001);
TypeSignatureEncoder::encode_typedefref_token(typeref_token, &mut buffer).unwrap();
assert_eq!(buffer, vec![0x05]); buffer.clear();
let typespec_token = Token::new(0x1B000001);
TypeSignatureEncoder::encode_typedefref_token(typespec_token, &mut buffer).unwrap();
assert_eq!(buffer, vec![0x06]); }
#[test]
fn test_encode_invalid_token() {
let mut buffer = Vec::new();
let invalid_token = Token::new(0x03000001);
let result = TypeSignatureEncoder::encode_typedefref_token(invalid_token, &mut buffer);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Invalid token for TypeDefOrRef"));
}
#[test]
fn test_encode_unknown_signature() {
let unknown_sig = TypeSignature::Unknown;
let result = TypeSignatureEncoder::encode(&unknown_sig);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Cannot encode unknown type signature"));
}
#[test]
fn test_recursion_protection() {
let mut nested_sig = TypeSignature::I4;
for _ in 0..MAX_RECURSION_DEPTH + 10 {
nested_sig = TypeSignature::ByRef(Box::new(nested_sig));
}
let result = TypeSignatureEncoder::encode(&nested_sig);
assert!(result.is_err());
if let Err(err) = result {
if let Error::RecursionLimit(depth) = err {
assert_eq!(depth, MAX_RECURSION_DEPTH);
} else {
panic!("Expected RecursionLimit error, got: {err:?}");
}
}
}
#[test]
fn test_encode_pinned_type() {
let pinned_sig = TypeSignature::Pinned(Box::new(TypeSignature::I4));
let encoded = TypeSignatureEncoder::encode(&pinned_sig).unwrap();
assert_eq!(encoded, vec![0x45, 0x08]); }
#[test]
fn test_encode_special_types() {
assert_eq!(
TypeSignatureEncoder::encode(&TypeSignature::Type).unwrap(),
vec![0x50]
);
assert_eq!(
TypeSignatureEncoder::encode(&TypeSignature::Boxed).unwrap(),
vec![0x51]
);
assert_eq!(
TypeSignatureEncoder::encode(&TypeSignature::Modifier).unwrap(),
vec![0x22]
);
assert_eq!(
TypeSignatureEncoder::encode(&TypeSignature::Sentinel).unwrap(),
vec![0x41]
);
}
#[test]
fn test_encode_invalid_types() {
let internal_sig = TypeSignature::Internal;
let result = TypeSignatureEncoder::encode(&internal_sig);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Cannot encode internal type signature"));
let reserved_sig = TypeSignature::Reserved;
let result = TypeSignatureEncoder::encode(&reserved_sig);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Cannot encode reserved type signature"));
let field_sig = TypeSignature::Field;
let result = TypeSignatureEncoder::encode(&field_sig);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Field signatures should not appear"));
}
}