1use alloc::vec::Vec;
10
11use crate::binary::leb128::{self, Cursor};
12use crate::binary::section::RawSection;
13use crate::binary::typeparser::{
14 parse_ref_type as parse_binary_ref_type, parse_ref_type_with_first_byte,
15};
16use crate::error::{ByteOffset, DecodeContext, DecodeError, DecodeErrorKind};
17use crate::types::{Limits, RefType, TableType};
18
19pub fn parse_table_section(section: &RawSection<'_>) -> Result<Vec<TableType>, DecodeError> {
20 let mut cursor = Cursor::new(section.data);
21 let count = decode_u32_in_section(&mut cursor, section.offset)?;
22
23 let mut tables = Vec::with_capacity(cursor.capacity_hint(count));
24 for _ in 0..count {
25 tables.push(parse_table_type(&mut cursor, section.offset)?);
26 }
27
28 if !cursor.is_empty() {
29 return Err(DecodeError {
30 offset: ByteOffset(section.offset + cursor.position()),
31 context: DecodeContext::TableSection,
32 kind: DecodeErrorKind::SectionSizeMismatch {
33 expected: section.data.len() as u32,
34 consumed: cursor.position() as u32,
35 },
36 });
37 }
38
39 Ok(tables)
40}
41
42fn parse_table_type(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<TableType, DecodeError> {
43 let first = cursor.read_byte().map_err(|_| DecodeError {
45 offset: ByteOffset(base_offset + cursor.position()),
46 context: DecodeContext::TableSection,
47 kind: DecodeErrorKind::UnexpectedEof,
48 })?;
49 if first == 0x40 {
50 let second = cursor.read_byte().map_err(|_| DecodeError {
53 offset: ByteOffset(base_offset + cursor.position()),
54 context: DecodeContext::TableSection,
55 kind: DecodeErrorKind::UnexpectedEof,
56 })?;
57 if second != 0x00 {
58 return Err(DecodeError {
59 offset: ByteOffset(base_offset + cursor.position() - 1),
60 context: DecodeContext::TableSection,
61 kind: DecodeErrorKind::UnknownRefType { byte: second },
62 });
63 }
64 let elem = parse_ref_type(cursor, base_offset)?;
65 let limits = parse_limits(cursor, base_offset)?;
66 let expr_start = cursor.position();
70 let mut block_depth = 0usize;
71 loop {
72 let instr = crate::binary::instr::decode_instr(cursor, base_offset)?;
73 match instr {
74 crate::binary::instr::Instr::Block(_)
75 | crate::binary::instr::Instr::Loop(_)
76 | crate::binary::instr::Instr::If(_) => block_depth += 1,
77 crate::binary::instr::Instr::End => {
78 if block_depth == 0 {
79 break;
80 }
81 block_depth -= 1;
82 }
83 _ => {}
84 }
85 }
86 let init = cursor.original()[expr_start..cursor.position()].to_vec();
87 return Ok(TableType {
88 elem,
89 limits,
90 init: Some(init),
91 });
92 }
93 let elem = parse_ref_type_with_first_byte(
94 cursor,
95 first,
96 base_offset + cursor.position() - 1,
97 DecodeContext::TableSection,
98 )?;
99 let limits = parse_limits(cursor, base_offset)?;
100 Ok(TableType {
101 elem,
102 limits,
103 init: None,
104 })
105}
106
107fn parse_ref_type(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<RefType, DecodeError> {
108 parse_binary_ref_type(cursor, base_offset, DecodeContext::TableSection)
109}
110
111fn parse_limits(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<Limits, DecodeError> {
112 let tag_offset = cursor.position();
113 let tag = cursor.read_byte().map_err(|_| DecodeError {
114 offset: ByteOffset(base_offset + tag_offset),
115 context: DecodeContext::TableSection,
116 kind: DecodeErrorKind::UnexpectedEof,
117 })?;
118
119 match tag {
120 0x00 => {
121 let min = decode_u32_in_section(cursor, base_offset)?;
122 Ok(Limits { min, max: None })
123 }
124 0x01 => {
125 let min = decode_u32_in_section(cursor, base_offset)?;
126 let max = decode_u32_in_section(cursor, base_offset)?;
127 Ok(Limits {
128 min,
129 max: Some(max),
130 })
131 }
132 _ => Err(DecodeError {
133 offset: ByteOffset(base_offset + tag_offset),
134 context: DecodeContext::TableSection,
135 kind: DecodeErrorKind::UnexpectedByte {
136 expected: 0x00,
137 found: tag,
138 },
139 }),
140 }
141}
142
143fn decode_u32_in_section(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<u32, DecodeError> {
144 leb128::decode_u32(cursor).map_err(|mut e| {
145 e.context = DecodeContext::TableSection;
146 e.offset = ByteOffset(base_offset + e.offset.0);
147 e
148 })
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154 use crate::binary::section::SectionId;
155
156 fn raw_table_section(data: &[u8]) -> RawSection<'_> {
157 RawSection {
158 id: SectionId::Table,
159 offset: 22,
160 data,
161 }
162 }
163
164 #[test]
165 fn parse_empty_table_section() {
166 let section = raw_table_section(&[0x00]);
167 let tables = parse_table_section(§ion).unwrap();
168 assert!(tables.is_empty());
169 }
170
171 #[test]
172 fn parse_single_funcref_table() {
173 let section = raw_table_section(&[0x01, 0x70, 0x00, 0x02]);
174 let tables = parse_table_section(§ion).unwrap();
175 assert_eq!(tables.len(), 1);
176 assert_eq!(tables[0].elem, RefType::FuncRef);
177 assert_eq!(tables[0].limits.min, 2);
178 assert_eq!(tables[0].limits.max, None);
179 }
180
181 #[test]
182 fn parse_single_externref_bounded_table() {
183 let section = raw_table_section(&[0x01, 0x6F, 0x01, 0x01, 0x03]);
184 let tables = parse_table_section(§ion).unwrap();
185 assert_eq!(tables.len(), 1);
186 assert_eq!(tables[0].elem, RefType::ExternRef);
187 assert_eq!(tables[0].limits.min, 1);
188 assert_eq!(tables[0].limits.max, Some(3));
189 }
190
191 #[test]
192 fn reject_unknown_ref_type() {
193 let section = raw_table_section(&[0x01, 0x6E, 0x00, 0x01]);
194 let err = parse_table_section(§ion).unwrap_err();
195 assert!(matches!(
196 err.kind,
197 DecodeErrorKind::UnknownRefType { byte: 0x6E }
198 ));
199 }
200
201 #[test]
202 fn parse_typed_function_reference_table() {
203 let section = raw_table_section(&[
204 0x01, 0x63, 0x00, 0x00, 0x01, ]);
208 let tables = parse_table_section(§ion).unwrap();
209
210 assert_eq!(
211 tables[0].elem,
212 RefType::concrete(true, crate::types::TypeIdx(0))
213 );
214 assert_eq!(tables[0].limits.min, 1);
215 assert_eq!(tables[0].limits.max, None);
216 }
217
218 #[test]
219 fn reject_invalid_limits_tag() {
220 let section = raw_table_section(&[0x01, 0x70, 0x02, 0x01]);
221 let err = parse_table_section(§ion).unwrap_err();
222 assert!(matches!(
223 err.kind,
224 DecodeErrorKind::UnexpectedByte {
225 expected: 0x00,
226 found: 0x02,
227 }
228 ));
229 }
230}