Skip to main content

baedeker_core/binary/
elemsec.rs

1// Copyright (C) 2026 Industrial Algebra
2// SPDX-License-Identifier: Apache-2.0
3
4//! Element section parsing.
5//!
6//! Decodes defined element segments from the element section.
7//! See [Spec §5.5.12](https://webassembly.github.io/spec/core/binary/modules.html#element-section).
8
9use 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(&section).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(&section).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(&section).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(&section).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(&section).unwrap_err();
329        assert!(matches!(
330            err.kind,
331            DecodeErrorKind::UnexpectedByte {
332                expected: 0x00,
333                found: 0x01,
334            }
335        ));
336    }
337}