1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use crate::{
metadata::{
tables::{CodedIndex, CodedIndexType, MethodSpecRaw, RowReadable, TableId, TableInfoRef},
token::Token,
},
utils::read_le_at_dyn,
Result,
};
impl RowReadable for MethodSpecRaw {
const TABLE_ID: TableId = TableId::MethodSpec;
/// Reads a single `MethodSpec` table row from binary data.
///
/// Parses the binary representation according to ECMA-335 §II.22.29:
/// 1. **Method** (2-4 bytes): `MethodDefOrRef` coded index to the generic method
/// 2. **Instantiation** (2-4 bytes): Index into blob heap containing signature
///
/// ## Arguments
/// * `data` - Binary data containing the table
/// * `offset` - Current read position (updated by this method)
/// * `rid` - Row identifier for this entry (1-based)
/// * `sizes` - Table size information for proper index width calculation
///
/// ## Returns
/// Parsed [`MethodSpecRaw`] instance with populated fields
///
/// ## Errors
/// Returns an error if:
/// - Insufficient data remaining at offset
/// - Invalid coded index encoding
/// - Data corruption or malformed structure
fn row_read(data: &[u8], offset: &mut usize, rid: u32, sizes: &TableInfoRef) -> Result<Self> {
Ok(MethodSpecRaw {
rid,
token: Token::new(0x2B00_0000 + rid),
offset: *offset,
method: CodedIndex::read(data, offset, sizes, CodedIndexType::MethodDefOrRef)?,
instantiation: read_le_at_dyn(data, offset, sizes.is_large_blob())?,
})
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use super::*;
use crate::metadata::tables::{MetadataTable, TableId, TableInfo};
#[test]
fn crafted_short() {
let data = vec![
0x01, 0x00, // method
0x02, 0x02, // instantiation
];
let sizes = Arc::new(TableInfo::new_test(
&[
(TableId::MethodSpec, 1),
(TableId::MethodDef, 10),
(TableId::MemberRef, 10),
],
false,
false,
false,
));
let table = MetadataTable::<MethodSpecRaw>::new(&data, 1, sizes).unwrap();
let eval = |row: MethodSpecRaw| {
assert_eq!(row.rid, 1);
assert_eq!(row.token.value(), 0x2B000001);
assert_eq!(
row.method,
CodedIndex::new(TableId::MemberRef, 0, CodedIndexType::MethodDefOrRef)
);
assert_eq!(row.instantiation, 0x0202);
};
{
for row in table.iter() {
eval(row);
}
}
{
let row = table.get(1).unwrap();
eval(row);
}
}
#[test]
fn crafted_long() {
let data = vec![
0x01, 0x00, 0x00, 0x00, // method
0x02, 0x02, 0x02, 0x02, // instantiation
];
let sizes = Arc::new(TableInfo::new_test(
&[
(TableId::MethodSpec, u16::MAX as u32 + 3),
(TableId::MethodDef, u16::MAX as u32 + 3),
(TableId::MemberRef, u16::MAX as u32 + 3),
],
true,
true,
true,
));
let table = MetadataTable::<MethodSpecRaw>::new(&data, 1, sizes).unwrap();
let eval = |row: MethodSpecRaw| {
assert_eq!(row.rid, 1);
assert_eq!(row.token.value(), 0x2B000001);
assert_eq!(
row.method,
CodedIndex::new(TableId::MemberRef, 0, CodedIndexType::MethodDefOrRef)
);
assert_eq!(row.instantiation, 0x02020202);
};
{
for row in table.iter() {
eval(row);
}
}
{
let row = table.get(1).unwrap();
eval(row);
}
}
}