1use alloc::vec::Vec;
10
11use crate::binary::leb128::{self, Cursor};
12use crate::binary::section::RawSection;
13use crate::binary::typeparser::parse_ref_type as parse_binary_ref_type;
14use crate::error::{ByteOffset, DecodeContext, DecodeError, DecodeErrorKind};
15use crate::types::{
16 ElementExpr, ElementInit, ElementMode, ElementSegment, FuncIdx, RefType, TableIdx,
17};
18
19pub fn parse_element_section<'a>(
20 section: &RawSection<'a>,
21) -> Result<Vec<ElementSegment<'a>>, DecodeError> {
22 let mut cursor = Cursor::new(section.data);
23 let count = decode_u32_in_section(&mut cursor, section.offset)?;
24
25 let mut segments = Vec::with_capacity(cursor.capacity_hint(count));
26 for _ in 0..count {
27 segments.push(parse_element_segment(&mut cursor, section.offset)?);
28 }
29
30 if !cursor.is_empty() {
31 return Err(DecodeError {
32 offset: ByteOffset(section.offset + cursor.position()),
33 context: DecodeContext::ElementSection,
34 kind: DecodeErrorKind::SectionSizeMismatch {
35 expected: section.data.len() as u32,
36 consumed: cursor.position() as u32,
37 },
38 });
39 }
40
41 Ok(segments)
42}
43
44fn parse_element_segment<'a>(
45 cursor: &mut Cursor<'a>,
46 base_offset: usize,
47) -> Result<ElementSegment<'a>, DecodeError> {
48 let flag = decode_u32_in_section(cursor, base_offset)?;
49 match flag {
50 0 => {
51 let offset_offset = base_offset + cursor.position();
52 let offset_expr = parse_init_expr(cursor, base_offset)?;
53 let init = parse_funcidx_vec(cursor, base_offset)?;
54 Ok(ElementSegment {
55 mode: ElementMode::Active {
56 table: TableIdx(0),
57 offset_expr,
58 offset_offset,
59 },
60 elem_type: RefType::FuncRef,
61 init: ElementInit::FuncIndices(init),
62 })
63 }
64 1 => {
65 let elem_type = parse_elemkind(cursor, base_offset)?;
66 let init = parse_funcidx_vec(cursor, base_offset)?;
67 Ok(ElementSegment {
68 mode: ElementMode::Passive,
69 elem_type,
70 init: ElementInit::FuncIndices(init),
71 })
72 }
73 2 => {
74 let table = TableIdx(decode_u32_in_section(cursor, base_offset)?);
75 let offset_offset = base_offset + cursor.position();
76 let offset_expr = parse_init_expr(cursor, base_offset)?;
77 let elem_type = parse_elemkind(cursor, base_offset)?;
78 let init = parse_funcidx_vec(cursor, base_offset)?;
79 Ok(ElementSegment {
80 mode: ElementMode::Active {
81 table,
82 offset_expr,
83 offset_offset,
84 },
85 elem_type,
86 init: ElementInit::FuncIndices(init),
87 })
88 }
89 3 => {
90 let elem_type = parse_elemkind(cursor, base_offset)?;
91 let init = parse_funcidx_vec(cursor, base_offset)?;
92 Ok(ElementSegment {
93 mode: ElementMode::Declarative,
94 elem_type,
95 init: ElementInit::FuncIndices(init),
96 })
97 }
98 4 => {
99 let offset_offset = base_offset + cursor.position();
100 let offset_expr = parse_init_expr(cursor, base_offset)?;
101 let init = parse_expr_vec(cursor, base_offset)?;
102 Ok(ElementSegment {
103 mode: ElementMode::Active {
104 table: TableIdx(0),
105 offset_expr,
106 offset_offset,
107 },
108 elem_type: RefType::FuncRef,
109 init: ElementInit::Expressions(init),
110 })
111 }
112 5 => {
113 let elem_type = parse_ref_type(cursor, base_offset)?;
114 let init = parse_expr_vec(cursor, base_offset)?;
115 Ok(ElementSegment {
116 mode: ElementMode::Passive,
117 elem_type,
118 init: ElementInit::Expressions(init),
119 })
120 }
121 6 => {
122 let table = TableIdx(decode_u32_in_section(cursor, base_offset)?);
123 let offset_offset = base_offset + cursor.position();
124 let offset_expr = parse_init_expr(cursor, base_offset)?;
125 let elem_type = parse_ref_type(cursor, base_offset)?;
126 let init = parse_expr_vec(cursor, base_offset)?;
127 Ok(ElementSegment {
128 mode: ElementMode::Active {
129 table,
130 offset_expr,
131 offset_offset,
132 },
133 elem_type,
134 init: ElementInit::Expressions(init),
135 })
136 }
137 7 => {
138 let elem_type = parse_ref_type(cursor, base_offset)?;
139 let init = parse_expr_vec(cursor, base_offset)?;
140 Ok(ElementSegment {
141 mode: ElementMode::Declarative,
142 elem_type,
143 init: ElementInit::Expressions(init),
144 })
145 }
146 _ => Err(DecodeError {
147 offset: ByteOffset(base_offset),
148 context: DecodeContext::ElementSection,
149 kind: DecodeErrorKind::UnexpectedByte {
150 expected: 0x00,
151 found: flag as u8,
152 },
153 }),
154 }
155}
156
157fn parse_funcidx_vec(
158 cursor: &mut Cursor<'_>,
159 base_offset: usize,
160) -> Result<Vec<FuncIdx>, DecodeError> {
161 let count = decode_u32_in_section(cursor, base_offset)?;
162 let mut funcs = Vec::with_capacity(cursor.capacity_hint(count));
163 for _ in 0..count {
164 funcs.push(FuncIdx(decode_u32_in_section(cursor, base_offset)?));
165 }
166 Ok(funcs)
167}
168
169fn parse_expr_vec<'a>(
170 cursor: &mut Cursor<'a>,
171 base_offset: usize,
172) -> Result<Vec<ElementExpr<'a>>, DecodeError> {
173 let count = decode_u32_in_section(cursor, base_offset)?;
174 let mut exprs = Vec::with_capacity(cursor.capacity_hint(count));
175 for _ in 0..count {
176 let offset = base_offset + cursor.position();
177 let expr = parse_init_expr(cursor, base_offset)?;
178 exprs.push(ElementExpr { expr, offset });
179 }
180 Ok(exprs)
181}
182
183fn parse_init_expr<'a>(
184 cursor: &mut Cursor<'a>,
185 base_offset: usize,
186) -> Result<&'a [u8], DecodeError> {
187 let start = cursor.position();
188 loop {
189 let pos = cursor.position();
190 let byte = cursor.read_byte().map_err(|_| DecodeError {
191 offset: ByteOffset(base_offset + pos),
192 context: DecodeContext::ElementSection,
193 kind: DecodeErrorKind::UnexpectedEof,
194 })?;
195 if byte == 0x0B {
196 return Ok(&cursor.original()[start..cursor.position()]);
197 }
198 }
199}
200
201fn parse_elemkind(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<RefType, DecodeError> {
202 let offset = cursor.position();
203 let byte = cursor.read_byte().map_err(|_| DecodeError {
204 offset: ByteOffset(base_offset + offset),
205 context: DecodeContext::ElementSection,
206 kind: DecodeErrorKind::UnexpectedEof,
207 })?;
208
209 match byte {
210 0x00 => Ok(RefType::FuncRef),
211 _ => Err(DecodeError {
212 offset: ByteOffset(base_offset + offset),
213 context: DecodeContext::ElementSection,
214 kind: DecodeErrorKind::UnexpectedByte {
215 expected: 0x00,
216 found: byte,
217 },
218 }),
219 }
220}
221
222fn parse_ref_type(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<RefType, DecodeError> {
223 parse_binary_ref_type(cursor, base_offset, DecodeContext::ElementSection)
224}
225
226fn decode_u32_in_section(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<u32, DecodeError> {
227 leb128::decode_u32(cursor).map_err(|mut e| {
228 e.context = DecodeContext::ElementSection;
229 e.offset = ByteOffset(base_offset + e.offset.0);
230 e
231 })
232}
233
234#[cfg(test)]
235mod tests {
236 use alloc::vec;
237
238 use super::*;
239 use crate::binary::section::SectionId;
240
241 fn raw_element_section(data: &[u8]) -> RawSection<'_> {
242 RawSection {
243 id: SectionId::Element,
244 offset: 34,
245 data,
246 }
247 }
248
249 #[test]
250 fn parse_active_funcidx_elements() {
251 let section = raw_element_section(&[0x01, 0x00, 0x41, 0x00, 0x0B, 0x02, 0x00, 0x01]);
252 let elems = parse_element_section(§ion).unwrap();
253 assert_eq!(elems.len(), 1);
254 match &elems[0].mode {
255 ElementMode::Active {
256 table, offset_expr, ..
257 } => {
258 assert_eq!(*table, TableIdx(0));
259 assert_eq!(*offset_expr, &[0x41, 0x00, 0x0B]);
260 }
261 _ => panic!("expected active element segment"),
262 }
263 assert_eq!(elems[0].elem_type, RefType::FuncRef);
264 assert_eq!(
265 elems[0].init,
266 ElementInit::FuncIndices(vec![FuncIdx(0), FuncIdx(1)])
267 );
268 }
269
270 #[test]
271 fn parse_passive_expr_elements() {
272 let section = raw_element_section(&[0x01, 0x05, 0x70, 0x01, 0xD2, 0x70, 0x0B]);
273 let elems = parse_element_section(§ion).unwrap();
274 assert_eq!(elems.len(), 1);
275 assert!(matches!(elems[0].mode, ElementMode::Passive));
276 assert_eq!(elems[0].elem_type, RefType::FuncRef);
277 match &elems[0].init {
278 ElementInit::Expressions(exprs) => {
279 assert_eq!(exprs.len(), 1);
280 assert_eq!(exprs[0].expr, &[0xD2, 0x70, 0x0B]);
281 }
282 _ => panic!("expected expression initializers"),
283 }
284 }
285
286 #[test]
287 fn parse_typed_active_expr_elements() {
288 let section = raw_element_section(&[
289 0x01, 0x06, 0x00, 0x41, 0x00, 0x0B, 0x63, 0x01, 0x01, 0xD2, 0x00, 0x0B,
290 ]);
291 let elems = parse_element_section(§ion).unwrap();
292 assert_eq!(elems.len(), 1);
293 match &elems[0].mode {
294 ElementMode::Active {
295 table, offset_expr, ..
296 } => {
297 assert_eq!(*table, TableIdx(0));
298 assert_eq!(*offset_expr, &[0x41, 0x00, 0x0B]);
299 }
300 _ => panic!("expected active element segment"),
301 }
302 assert_eq!(
303 elems[0].elem_type,
304 RefType::concrete(true, crate::types::TypeIdx(1))
305 );
306 match &elems[0].init {
307 ElementInit::Expressions(exprs) => {
308 assert_eq!(exprs.len(), 1);
309 assert_eq!(exprs[0].expr, &[0xD2, 0x00, 0x0B]);
310 }
311 _ => panic!("expected expression initializers"),
312 }
313 }
314
315 #[test]
316 fn reject_unknown_reference_type() {
317 let section = raw_element_section(&[0x01, 0x05, 0x6E, 0x00]);
318 let err = parse_element_section(§ion).unwrap_err();
319 assert!(matches!(
320 err.kind,
321 DecodeErrorKind::UnknownRefType { byte: 0x6E }
322 ));
323 }
324
325 #[test]
326 fn reject_invalid_elemkind() {
327 let section = raw_element_section(&[0x01, 0x01, 0x01, 0x00]);
328 let err = parse_element_section(§ion).unwrap_err();
329 assert!(matches!(
330 err.kind,
331 DecodeErrorKind::UnexpectedByte {
332 expected: 0x00,
333 found: 0x01,
334 }
335 ));
336 }
337}