use crate::{
file::parser::Parser,
metadata::{
signatures::{
CustomModifier, SignatureArray, SignatureField, SignatureLocalVariable,
SignatureLocalVariables, SignatureMethod, SignatureMethodSpec, SignatureParameter,
SignaturePointer, SignatureProperty, SignatureSzArray, SignatureTypeSpec,
TypeSignature,
},
typesystem::{ArrayDimensions, ELEMENT_TYPE},
},
Error::DepthLimitExceeded,
Result,
};
#[allow(non_snake_case)]
pub mod SIGNATURE_HEADER {
pub const FIELD: u8 = 0x06;
pub const LOCAL_SIG: u8 = 0x07;
pub const PROPERTY: u8 = 0x08;
pub const GENRICINST: u8 = 0x0A;
}
#[allow(non_snake_case)]
pub mod CALLING_CONVENTION {
pub const DEFAULT: u8 = 0x00;
pub const C: u8 = 0x01;
pub const STDCALL: u8 = 0x02;
pub const THISCALL: u8 = 0x03;
pub const FASTCALL: u8 = 0x04;
pub const VARARG: u8 = 0x05;
pub const KIND_MASK: u8 = 0x0F;
pub const GENERIC: u8 = 0x10;
pub const HASTHIS: u8 = 0x20;
pub const EXPLICITTHIS: u8 = 0x40;
}
const MAX_NESTING_DEPTH: usize = 1000;
const MAX_ARRAY_DIMENSIONS: u32 = 32;
const MAX_SIGNATURE_PARAMS: u32 = 1024;
const MAX_GENERIC_ARGS: u32 = 256;
const MAX_LOCAL_VARIABLES: u32 = 65536;
pub struct SignatureParser<'a> {
parser: Parser<'a>,
}
impl<'a> SignatureParser<'a> {
#[must_use]
pub fn new(data: &'a [u8]) -> Self {
SignatureParser {
parser: Parser::new(data),
}
}
fn parse_type(&mut self) -> Result<TypeSignature> {
enum WorkItem {
ParseType,
Seek(usize),
BuildPtr(Vec<CustomModifier>),
BuildByRef,
BuildArray {
rank: u32,
dimensions: Vec<ArrayDimensions>,
},
BuildGenericInst { arg_count: u32 },
BuildSzArray(Vec<CustomModifier>),
BuildPinned,
}
let mut work_stack: Vec<WorkItem> = Vec::new();
let mut result_stack: Vec<TypeSignature> = Vec::new();
work_stack.push(WorkItem::ParseType);
while let Some(work) = work_stack.pop() {
if work_stack.len() + result_stack.len() > MAX_NESTING_DEPTH {
return Err(DepthLimitExceeded(MAX_NESTING_DEPTH));
}
match work {
WorkItem::Seek(pos) => {
self.parser.seek(pos)?;
}
WorkItem::ParseType => {
let current_byte = self.parser.read_le::<u8>()?;
match current_byte {
ELEMENT_TYPE::VOID => result_stack.push(TypeSignature::Void),
ELEMENT_TYPE::BOOLEAN => result_stack.push(TypeSignature::Boolean),
ELEMENT_TYPE::CHAR => result_stack.push(TypeSignature::Char),
ELEMENT_TYPE::I1 => result_stack.push(TypeSignature::I1),
ELEMENT_TYPE::U1 => result_stack.push(TypeSignature::U1),
ELEMENT_TYPE::I2 => result_stack.push(TypeSignature::I2),
ELEMENT_TYPE::U2 => result_stack.push(TypeSignature::U2),
ELEMENT_TYPE::I4 => result_stack.push(TypeSignature::I4),
ELEMENT_TYPE::U4 => result_stack.push(TypeSignature::U4),
ELEMENT_TYPE::I8 => result_stack.push(TypeSignature::I8),
ELEMENT_TYPE::U8 => result_stack.push(TypeSignature::U8),
ELEMENT_TYPE::R4 => result_stack.push(TypeSignature::R4),
ELEMENT_TYPE::R8 => result_stack.push(TypeSignature::R8),
ELEMENT_TYPE::STRING => result_stack.push(TypeSignature::String),
ELEMENT_TYPE::VALUETYPE => {
let token = self.parser.read_compressed_token()?;
result_stack.push(TypeSignature::ValueType(token));
}
ELEMENT_TYPE::CLASS => {
let token = self.parser.read_compressed_token()?;
result_stack.push(TypeSignature::Class(token));
}
ELEMENT_TYPE::VAR => {
let index = self.parser.read_compressed_uint()?;
result_stack.push(TypeSignature::GenericParamType(index));
}
ELEMENT_TYPE::TYPEDBYREF => result_stack.push(TypeSignature::TypedByRef),
ELEMENT_TYPE::I => result_stack.push(TypeSignature::I),
ELEMENT_TYPE::U => result_stack.push(TypeSignature::U),
ELEMENT_TYPE::FNPTR => {
let method_sig = self.parse_method_signature()?;
result_stack.push(TypeSignature::FnPtr(Box::new(method_sig)));
}
ELEMENT_TYPE::OBJECT => result_stack.push(TypeSignature::Object),
ELEMENT_TYPE::MVAR => {
let index = self.parser.read_compressed_uint()?;
result_stack.push(TypeSignature::GenericParamMethod(index));
}
ELEMENT_TYPE::INTERNAL => result_stack.push(TypeSignature::Internal),
ELEMENT_TYPE::MODIFIER => result_stack.push(TypeSignature::Modifier),
ELEMENT_TYPE::SENTINEL => result_stack.push(TypeSignature::Sentinel),
ELEMENT_TYPE::END => {
result_stack.push(TypeSignature::Void);
}
ELEMENT_TYPE::PTR => {
let modifiers = self.parse_custom_mods()?;
work_stack.push(WorkItem::BuildPtr(modifiers));
work_stack.push(WorkItem::ParseType);
}
ELEMENT_TYPE::BYREF => {
work_stack.push(WorkItem::BuildByRef);
work_stack.push(WorkItem::ParseType);
}
ELEMENT_TYPE::ARRAY => {
let elem_pos = self.parser.pos();
self.parse_type_simple()?;
let rank = self.parser.read_compressed_uint()?;
let num_sizes = self.parser.read_compressed_uint()?;
if num_sizes > MAX_ARRAY_DIMENSIONS {
return Err(malformed_error!(
"Array signature has too many dimensions: {} (max: {})",
num_sizes,
MAX_ARRAY_DIMENSIONS
));
}
let mut dimensions: Vec<ArrayDimensions> =
Vec::with_capacity(num_sizes as usize);
for _ in 0..num_sizes {
dimensions.push(ArrayDimensions {
size: Some(self.parser.read_compressed_uint()?),
lower_bound: None,
});
}
let num_lo_bounds = self.parser.read_compressed_uint()?;
if num_lo_bounds > rank {
return Err(malformed_error!(
"Array signature has more lower bounds ({}) than dimensions (rank {})",
num_lo_bounds,
rank
));
}
while dimensions.len() < num_lo_bounds as usize {
dimensions.push(ArrayDimensions {
size: None,
lower_bound: None,
});
}
for i in 0..num_lo_bounds {
if let Some(dimension) = dimensions.get_mut(i as usize) {
dimension.lower_bound =
Some(self.parser.read_compressed_uint()?);
}
}
self.parser.seek(elem_pos)?;
work_stack.push(WorkItem::BuildArray { rank, dimensions });
work_stack.push(WorkItem::ParseType);
}
ELEMENT_TYPE::GENERICINST => {
let peek_byte = self.parser.peek_byte()?;
if peek_byte != 0x12 && peek_byte != 0x11 {
return Err(malformed_error!(
"GENERICINST - Next byte is not TYPE_CLASS or TYPE_VALUE - {}",
peek_byte
));
}
let base_pos = self.parser.pos();
self.parse_type_simple()?;
let arg_count = self.parser.read_compressed_uint()?;
if arg_count > MAX_GENERIC_ARGS {
return Err(malformed_error!(
"Generic instantiation has too many type arguments: {} (max: {})",
arg_count,
MAX_GENERIC_ARGS
));
}
let args_pos = self.parser.pos();
self.parser.seek(base_pos)?;
work_stack.push(WorkItem::BuildGenericInst { arg_count });
for _ in 0..arg_count {
work_stack.push(WorkItem::ParseType);
}
work_stack.push(WorkItem::Seek(args_pos));
work_stack.push(WorkItem::ParseType);
}
ELEMENT_TYPE::SZARRAY => {
let modifiers = self.parse_custom_mods()?;
work_stack.push(WorkItem::BuildSzArray(modifiers));
work_stack.push(WorkItem::ParseType);
}
ELEMENT_TYPE::CMOD_REQD => {
self.parser.seek(self.parser.pos() - 1)?;
let modifiers = self.parse_custom_mods()?;
result_stack.push(TypeSignature::ModifiedRequired(modifiers));
}
ELEMENT_TYPE::CMOD_OPT => {
self.parser.seek(self.parser.pos() - 1)?;
let modifiers = self.parse_custom_mods()?;
result_stack.push(TypeSignature::ModifiedOptional(modifiers));
}
ELEMENT_TYPE::PINNED => {
work_stack.push(WorkItem::BuildPinned);
work_stack.push(WorkItem::ParseType);
}
_ => {
return Err(malformed_error!(
"Unsupported ELEMENT_TYPE - {}",
current_byte
));
}
}
}
WorkItem::BuildPtr(modifiers) => {
let base_type = result_stack
.pop()
.ok_or_else(|| malformed_error!("Missing base type for PTR"))?;
result_stack.push(TypeSignature::Ptr(SignaturePointer {
modifiers,
base: Box::new(base_type),
}));
}
WorkItem::BuildByRef => {
let base_type = result_stack
.pop()
.ok_or_else(|| malformed_error!("Missing base type for BYREF"))?;
result_stack.push(TypeSignature::ByRef(Box::new(base_type)));
}
WorkItem::BuildArray { rank, dimensions } => {
let elem_type = result_stack
.pop()
.ok_or_else(|| malformed_error!("Missing element type for ARRAY"))?;
result_stack.push(TypeSignature::Array(SignatureArray {
base: Box::new(elem_type),
rank,
dimensions,
}));
}
WorkItem::BuildGenericInst { arg_count } => {
if result_stack.len() < (arg_count as usize + 1) {
return Err(malformed_error!(
"Insufficient types on stack for GENERICINST"
));
}
let mut type_args = Vec::with_capacity(arg_count as usize);
for _ in 0..arg_count {
type_args.push(
result_stack.pop().ok_or_else(|| {
malformed_error!("Missing type arg for GENERICINST")
})?,
);
}
type_args.reverse();
let base_type = result_stack
.pop()
.ok_or_else(|| malformed_error!("Missing base type for GENERICINST"))?;
result_stack.push(TypeSignature::GenericInst(Box::new(base_type), type_args));
}
WorkItem::BuildSzArray(modifiers) => {
let base_type = result_stack
.pop()
.ok_or_else(|| malformed_error!("Missing base type for SZARRAY"))?;
result_stack.push(TypeSignature::SzArray(SignatureSzArray {
modifiers,
base: Box::new(base_type),
}));
}
WorkItem::BuildPinned => {
let base_type = result_stack
.pop()
.ok_or_else(|| malformed_error!("Missing base type for PINNED"))?;
result_stack.push(TypeSignature::Pinned(Box::new(base_type)));
}
}
}
if result_stack.len() != 1 {
return Err(malformed_error!(
"Internal error: expected 1 type on stack, got {}",
result_stack.len()
));
}
result_stack
.pop()
.ok_or_else(|| malformed_error!("internal: result stack empty after validation"))
}
fn parse_type_simple(&mut self) -> Result<()> {
let current_byte = self.parser.read_le::<u8>()?;
match current_byte {
ELEMENT_TYPE::VOID
| ELEMENT_TYPE::BOOLEAN
| ELEMENT_TYPE::CHAR
| ELEMENT_TYPE::I1
| ELEMENT_TYPE::U1
| ELEMENT_TYPE::I2
| ELEMENT_TYPE::U2
| ELEMENT_TYPE::I4
| ELEMENT_TYPE::U4
| ELEMENT_TYPE::I8
| ELEMENT_TYPE::U8
| ELEMENT_TYPE::R4
| ELEMENT_TYPE::R8
| ELEMENT_TYPE::STRING
| ELEMENT_TYPE::TYPEDBYREF
| ELEMENT_TYPE::I
| ELEMENT_TYPE::U
| ELEMENT_TYPE::OBJECT
| ELEMENT_TYPE::INTERNAL
| ELEMENT_TYPE::MODIFIER
| ELEMENT_TYPE::SENTINEL
| ELEMENT_TYPE::END => Ok(()),
ELEMENT_TYPE::VALUETYPE | ELEMENT_TYPE::CLASS => {
self.parser.read_compressed_token()?;
Ok(())
}
ELEMENT_TYPE::VAR | ELEMENT_TYPE::MVAR => {
self.parser.read_compressed_uint()?;
Ok(())
}
ELEMENT_TYPE::PTR | ELEMENT_TYPE::SZARRAY => {
let _ = self.parse_custom_mods()?;
self.parse_type_simple()
}
ELEMENT_TYPE::BYREF | ELEMENT_TYPE::PINNED => self.parse_type_simple(),
ELEMENT_TYPE::ARRAY => {
self.parse_type_simple()?;
let _rank = self.parser.read_compressed_uint()?;
let num_sizes = self.parser.read_compressed_uint()?;
for _ in 0..num_sizes {
self.parser.read_compressed_uint()?;
}
let num_lo_bounds = self.parser.read_compressed_uint()?;
for _ in 0..num_lo_bounds {
self.parser.read_compressed_uint()?;
}
Ok(())
}
ELEMENT_TYPE::GENERICINST => {
self.parse_type_simple()?;
let arg_count = self.parser.read_compressed_uint()?;
for _ in 0..arg_count {
self.parse_type_simple()?;
}
Ok(())
}
ELEMENT_TYPE::FNPTR => {
let _ = self.parse_method_signature()?;
Ok(())
}
ELEMENT_TYPE::CMOD_REQD | ELEMENT_TYPE::CMOD_OPT => {
self.parser.seek(self.parser.pos() - 1)?;
let _ = self.parse_custom_mods()?;
Ok(())
}
_ => Err(malformed_error!(
"Unsupported ELEMENT_TYPE in simple parse - {}",
current_byte
)),
}
}
fn parse_custom_mods(&mut self) -> Result<Vec<CustomModifier>> {
let mut mods = Vec::new();
while self.parser.has_more_data() {
let is_required = match self.parser.peek_byte()? {
0x20 => false,
0x1F => true,
_ => break,
};
self.parser.advance()?;
let modifier_token = self.parser.read_compressed_token()?;
mods.push(CustomModifier {
is_required,
modifier_type: modifier_token,
});
}
Ok(mods)
}
fn parse_param(&mut self) -> Result<SignatureParameter> {
let custom_mods = self.parse_custom_mods()?;
let mut by_ref = false;
if self.parser.peek_byte()? == 0x10 {
self.parser.advance()?;
by_ref = true;
}
Ok(SignatureParameter {
modifiers: custom_mods,
by_ref,
base: self.parse_type()?,
})
}
pub fn parse_method_signature(&mut self) -> Result<SignatureMethod> {
let convention_byte = self.parser.read_le::<u8>()?;
let calling_convention_kind = convention_byte & CALLING_CONVENTION::KIND_MASK;
let mut method = SignatureMethod {
has_this: convention_byte & CALLING_CONVENTION::HASTHIS != 0,
explicit_this: convention_byte & CALLING_CONVENTION::EXPLICITTHIS != 0,
default: calling_convention_kind == CALLING_CONVENTION::DEFAULT,
vararg: calling_convention_kind == CALLING_CONVENTION::VARARG,
cdecl: calling_convention_kind == CALLING_CONVENTION::C,
stdcall: calling_convention_kind == CALLING_CONVENTION::STDCALL,
thiscall: calling_convention_kind == CALLING_CONVENTION::THISCALL,
fastcall: calling_convention_kind == CALLING_CONVENTION::FASTCALL,
param_count_generic: if convention_byte & CALLING_CONVENTION::GENERIC != 0 {
let gen_count = self.parser.read_compressed_uint()?;
if gen_count > MAX_GENERIC_ARGS {
return Err(malformed_error!(
"Method signature has too many generic parameters: {} (max: {})",
gen_count,
MAX_GENERIC_ARGS
));
}
gen_count
} else {
0
},
param_count: {
let p_count = self.parser.read_compressed_uint()?;
if p_count > MAX_SIGNATURE_PARAMS {
return Err(malformed_error!(
"Method signature has too many parameters: {} (max: {})",
p_count,
MAX_SIGNATURE_PARAMS
));
}
p_count
},
return_type: self.parse_param()?,
params: Vec::new(),
varargs: Vec::new(),
};
let (params, varargs) = self.parse_method_parameters(method.param_count)?;
method.params = params;
method.varargs = varargs;
Ok(method)
}
fn parse_method_parameters(
&mut self,
param_count: u32,
) -> Result<(Vec<SignatureParameter>, Vec<SignatureParameter>)> {
let mut params = Vec::with_capacity(param_count as usize);
let mut varargs = Vec::new();
let mut hit_sentinel = false;
for _ in 0..param_count {
if self.parser.peek_byte()? == ELEMENT_TYPE::SENTINEL {
self.parser.advance()?;
hit_sentinel = true;
break;
}
params.push(self.parse_param()?);
}
if hit_sentinel {
while self.parser.has_more_data() && self.parser.peek_byte()? != ELEMENT_TYPE::END {
if varargs.len() >= MAX_SIGNATURE_PARAMS as usize {
return Err(malformed_error!(
"Method signature has too many varargs: {} (max: {})",
varargs.len(),
MAX_SIGNATURE_PARAMS
));
}
varargs.push(self.parse_param()?);
}
}
Ok((params, varargs))
}
pub fn parse_field_signature(&mut self) -> Result<SignatureField> {
let head_byte = self.parser.read_le::<u8>()?;
if head_byte != SIGNATURE_HEADER::FIELD {
return Err(malformed_error!(
"SignatureField - invalid start - {} (expected {})",
head_byte,
SIGNATURE_HEADER::FIELD
));
}
let custom_mods = self.parse_custom_mods()?;
let type_sig = self.parse_type()?;
Ok(SignatureField {
modifiers: custom_mods,
base: type_sig,
})
}
pub fn parse_property_signature(&mut self) -> Result<SignatureProperty> {
let head_byte = self.parser.read_le::<u8>()?;
if (head_byte & SIGNATURE_HEADER::PROPERTY) == 0 {
return Err(malformed_error!(
"SignatureProperty - invalid start - {} (expected PROPERTY bit {})",
head_byte,
SIGNATURE_HEADER::PROPERTY
));
}
let has_this = (head_byte & CALLING_CONVENTION::HASTHIS) != 0;
let param_count = self.parser.read_compressed_uint()?;
if param_count > MAX_SIGNATURE_PARAMS {
return Err(malformed_error!(
"Property signature has too many parameters: {} (max: {})",
param_count,
MAX_SIGNATURE_PARAMS
));
}
let custom_mods = self.parse_custom_mods()?;
let type_sig = self.parse_type()?;
let mut params = Vec::with_capacity(param_count as usize);
for _ in 0..param_count {
params.push(self.parse_param()?);
}
Ok(SignatureProperty {
has_this,
modifiers: custom_mods,
base: type_sig,
params,
})
}
pub fn parse_local_var_signature(&mut self) -> Result<SignatureLocalVariables> {
let head_byte = self.parser.read_le::<u8>()?;
if head_byte != SIGNATURE_HEADER::LOCAL_SIG {
return Err(malformed_error!(
"SignatureLocalVar - invalid start - {} (expected {})",
head_byte,
SIGNATURE_HEADER::LOCAL_SIG
));
}
let count = self.parser.read_compressed_uint()?;
if count > MAX_LOCAL_VARIABLES {
return Err(malformed_error!(
"Local variable signature has too many locals: {} (max: {})",
count,
MAX_LOCAL_VARIABLES
));
}
let mut locals = Vec::with_capacity(count as usize);
for _ in 0..count {
locals.push(self.parse_single_local_variable()?);
}
Ok(SignatureLocalVariables { locals })
}
fn parse_single_local_variable(&mut self) -> Result<SignatureLocalVariable> {
if self.parser.peek_byte()? == ELEMENT_TYPE::TYPEDBYREF {
self.parser.advance()?;
return Ok(SignatureLocalVariable {
modifiers: Vec::new(),
is_byref: false,
is_pinned: false,
base: TypeSignature::TypedByRef,
});
}
let (custom_mods, pinned) = self.parse_local_var_constraints()?;
let by_ref = if self.parser.peek_byte()? == ELEMENT_TYPE::BYREF {
self.parser.advance()?;
true
} else {
false
};
let type_sig = self.parse_type()?;
Ok(SignatureLocalVariable {
modifiers: custom_mods,
is_byref: by_ref,
is_pinned: pinned,
base: type_sig,
})
}
fn parse_local_var_constraints(&mut self) -> Result<(Vec<CustomModifier>, bool)> {
let mut custom_mods = Vec::new();
let mut pinned = false;
while self.parser.has_more_data() {
match self.parser.peek_byte()? {
ELEMENT_TYPE::CMOD_REQD | ELEMENT_TYPE::CMOD_OPT => {
let is_required = self.parser.peek_byte()? == ELEMENT_TYPE::CMOD_REQD;
self.parser.advance()?;
let modifier_token = self.parser.read_compressed_token()?;
custom_mods.push(CustomModifier {
is_required,
modifier_type: modifier_token,
});
}
ELEMENT_TYPE::PINNED => {
self.parser.advance()?;
pinned = true;
}
_ => break,
}
}
Ok((custom_mods, pinned))
}
pub fn parse_type_spec_signature(&mut self) -> Result<SignatureTypeSpec> {
let modifiers = self.parse_custom_mods()?;
let base_type_sig = self.parse_type()?;
match base_type_sig {
TypeSignature::ModifiedRequired(mod_modifiers)
| TypeSignature::ModifiedOptional(mod_modifiers) => {
let mut all_modifiers = modifiers;
all_modifiers.extend(mod_modifiers);
let base_type = self.parse_type()?;
Ok(SignatureTypeSpec {
modifiers: all_modifiers,
base: base_type,
})
}
_ => {
Ok(SignatureTypeSpec {
modifiers,
base: base_type_sig,
})
}
}
}
pub fn parse_method_spec_signature(&mut self) -> Result<SignatureMethodSpec> {
let head_byte = self.parser.read_le::<u8>()?;
if head_byte != 0x0A {
return Err(malformed_error!(
"SignatureMethodSpec - invalid start - {}",
head_byte
));
}
let arg_count = self.parser.read_compressed_uint()?;
if arg_count > MAX_GENERIC_ARGS {
return Err(malformed_error!(
"Method specification has too many type arguments: {} (max: {})",
arg_count,
MAX_GENERIC_ARGS
));
}
let mut generic_args = Vec::with_capacity(arg_count as usize);
for _ in 0..arg_count {
generic_args.push(self.parse_type()?);
}
Ok(SignatureMethodSpec { generic_args })
}
}
#[cfg(test)]
mod tests {
use crate::prelude::Token;
use crate::Error;
use super::*;
#[test]
fn test_parse_primitive_types() {
let test_cases = [
(vec![0x01], TypeSignature::Void),
(vec![0x02], TypeSignature::Boolean),
(vec![0x03], TypeSignature::Char),
(vec![0x04], TypeSignature::I1),
(vec![0x05], TypeSignature::U1),
(vec![0x06], TypeSignature::I2),
(vec![0x07], TypeSignature::U2),
(vec![0x08], TypeSignature::I4),
(vec![0x09], TypeSignature::U4),
(vec![0x0A], TypeSignature::I8),
(vec![0x0B], TypeSignature::U8),
(vec![0x0C], TypeSignature::R4),
(vec![0x0D], TypeSignature::R8),
(vec![0x0E], TypeSignature::String),
(vec![0x1C], TypeSignature::Object),
(vec![0x18], TypeSignature::I),
(vec![0x19], TypeSignature::U),
];
for (bytes, expected_type) in test_cases {
let mut parser = SignatureParser::new(&bytes);
let result = parser.parse_type().unwrap();
assert_eq!(result, expected_type);
}
}
#[test]
fn test_parse_class_and_valuetype() {
let mut parser = SignatureParser::new(&[0x12, 0x42]);
assert_eq!(
parser.parse_type().unwrap(),
TypeSignature::Class(Token::new(0x1B000010))
);
let mut parser = SignatureParser::new(&[0x11, 0x35]);
assert_eq!(
parser.parse_type().unwrap(),
TypeSignature::ValueType(Token::new(0x100000D))
);
let mut parser = SignatureParser::new(&[0x13, 0x03]);
assert_eq!(
parser.parse_type().unwrap(),
TypeSignature::GenericParamType(0x03)
);
}
#[test]
fn test_parse_arrays() {
let mut parser = SignatureParser::new(&[0x1D, 0x08]);
let result = parser.parse_type().unwrap();
assert!(matches!(result, TypeSignature::SzArray(_)));
if let TypeSignature::SzArray(inner) = result {
assert_eq!(*inner.base, TypeSignature::I4);
}
let mut parser = SignatureParser::new(&[
0x14, 0x08, 0x02, 0x00, 0x00, ]);
let result = parser.parse_type().unwrap();
assert!(matches!(result, TypeSignature::Array(_)));
if let TypeSignature::Array(array) = result {
assert_eq!(*array.base, TypeSignature::I4);
assert_eq!(array.rank, 2);
assert_eq!(array.dimensions.len(), 0)
}
let mut parser = SignatureParser::new(&[
0x14, 0x08, 0x02, 0x02, 0x02, 0x03, 0x00, ]);
let result = parser.parse_type().unwrap();
assert!(matches!(result, TypeSignature::Array(_)));
if let TypeSignature::Array(array) = result {
assert_eq!(*array.base, TypeSignature::I4);
assert_eq!(array.rank, 2);
assert_eq!(array.dimensions.len(), 2);
assert_eq!(array.dimensions[0].lower_bound, None);
assert_eq!(array.dimensions[0].size, Some(2));
assert_eq!(array.dimensions[1].lower_bound, None);
assert_eq!(array.dimensions[1].size, Some(3));
}
}
#[test]
fn test_parse_pointers_and_byrefs() {
let mut parser = SignatureParser::new(&[0x0F, 0x08]);
let result = parser.parse_type().unwrap();
assert!(matches!(result, TypeSignature::Ptr(_)));
if let TypeSignature::Ptr(inner) = result {
assert_eq!(*inner.base, TypeSignature::I4);
}
let mut parser = SignatureParser::new(&[0x10, 0x08]);
let result = parser.parse_type().unwrap();
assert!(matches!(result, TypeSignature::ByRef(_)));
if let TypeSignature::ByRef(inner) = result {
assert_eq!(*inner, TypeSignature::I4);
}
}
#[test]
fn test_parse_generic_instance() {
let mut parser = SignatureParser::new(&[
0x15, 0x12, 0x49, 0x01, 0x08, ]);
let result = parser.parse_type().unwrap();
assert!(matches!(result, TypeSignature::GenericInst(_, _)));
if let TypeSignature::GenericInst(class, args) = result {
assert!(matches!(*class, TypeSignature::Class(_)));
assert_eq!(args.len(), 1);
assert_eq!(args[0], TypeSignature::I4);
}
let mut parser = SignatureParser::new(&[
0x15, 0x12, 0x2A, 0x02, 0x0E, 0x08, ]);
let result = parser.parse_type().unwrap();
assert!(matches!(result, TypeSignature::GenericInst(_, _)));
if let TypeSignature::GenericInst(class, args) = result {
assert!(matches!(*class, TypeSignature::Class(_)));
assert_eq!(args.len(), 2);
assert_eq!(args[0], TypeSignature::String);
assert_eq!(args[1], TypeSignature::I4);
}
}
#[test]
fn test_parse_custom_mods() {
let mut parser = SignatureParser::new(&[
0x20, 0x42, 0x1F, 0x49, 0x08, ]);
let mods = parser.parse_custom_mods().unwrap();
assert_eq!(
mods,
vec![
CustomModifier {
is_required: false,
modifier_type: Token::new(0x1B000010)
},
CustomModifier {
is_required: true,
modifier_type: Token::new(0x01000012)
}
]
);
let type_sig = parser.parse_type().unwrap();
assert_eq!(type_sig, TypeSignature::I4);
let mut parser = SignatureParser::new(&[0x08]); let mods = parser.parse_custom_mods().unwrap();
assert!(mods.is_empty());
}
#[test]
fn test_complex_signature() {
let mut parser = SignatureParser::new(&[
0x30, 0x01, 0x02, 0x15, 0x12, 0x2A, 0x02, 0x15, 0x12, 0x49, 0x01, 0x08, 0x1D, 0x0E, 0x10, 0x13, 0x00, 0x1D, 0x15, 0x12, 0x42, 0x01, 0x08, ]);
let result = parser.parse_method_signature().unwrap();
assert!(result.has_this);
assert_eq!(result.param_count_generic, 1);
assert_eq!(result.params.len(), 2);
assert!(matches!(
result.return_type.base,
TypeSignature::GenericInst(_, _)
));
assert!(result.params[0].by_ref);
assert_eq!(result.params[0].base, TypeSignature::GenericParamType(0));
assert!(!result.params[1].by_ref);
assert!(matches!(result.params[1].base, TypeSignature::SzArray(_)));
}
#[test]
fn test_error_handling() {
let mut parser = SignatureParser::new(&[0xFF, 0x01]);
assert!(matches!(
parser.parse_method_signature(),
Err(Error::OutOfBounds { .. })
));
let mut parser = SignatureParser::new(&[0x07, 0x08]); assert!(parser.parse_field_signature().is_err());
}
}