Skip to main content

baedeker_core/binary/
globalsec.rs

1// Copyright (C) 2026 Industrial Algebra
2// SPDX-License-Identifier: Apache-2.0
3
4//! Global section parsing.
5//!
6//! Decodes defined globals and their initializer expressions from the global section.
7//! See [Spec §5.5.11](https://webassembly.github.io/spec/core/binary/modules.html#global-section).
8
9use alloc::vec::Vec;
10
11use crate::binary::leb128::{self, Cursor};
12use crate::binary::section::RawSection;
13use crate::binary::typeparser::parse_val_type as parse_binary_val_type;
14use crate::error::{ByteOffset, DecodeContext, DecodeError, DecodeErrorKind};
15use crate::types::{Global, GlobalType, Mutability, ValType};
16
17pub fn parse_global_section<'a>(section: &RawSection<'a>) -> Result<Vec<Global<'a>>, DecodeError> {
18    let mut cursor = Cursor::new(section.data);
19    let count = decode_u32_in_section(&mut cursor, section.offset)?;
20
21    let mut globals = Vec::with_capacity(cursor.capacity_hint(count));
22    for _ in 0..count {
23        let global_type = parse_global_type(&mut cursor, section.offset)?;
24        let init_offset = section.offset + cursor.position();
25        let init_expr = parse_init_expr(&mut cursor, section.offset)?;
26        globals.push(Global {
27            global_type,
28            init_expr,
29            init_offset,
30        });
31    }
32
33    if !cursor.is_empty() {
34        return Err(DecodeError {
35            offset: ByteOffset(section.offset + cursor.position()),
36            context: DecodeContext::GlobalSection,
37            kind: DecodeErrorKind::SectionSizeMismatch {
38                expected: section.data.len() as u32,
39                consumed: cursor.position() as u32,
40            },
41        });
42    }
43
44    Ok(globals)
45}
46
47fn parse_global_type(
48    cursor: &mut Cursor<'_>,
49    base_offset: usize,
50) -> Result<GlobalType, DecodeError> {
51    let val_type = parse_val_type(cursor, base_offset)?;
52    let mutability = parse_mutability(cursor, base_offset)?;
53    Ok(GlobalType {
54        val_type,
55        mutability,
56    })
57}
58
59fn parse_val_type(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<ValType, DecodeError> {
60    parse_binary_val_type(cursor, base_offset, DecodeContext::GlobalSection)
61}
62
63fn parse_mutability(
64    cursor: &mut Cursor<'_>,
65    base_offset: usize,
66) -> Result<Mutability, DecodeError> {
67    let offset = cursor.position();
68    let byte = cursor.read_byte().map_err(|_| DecodeError {
69        offset: ByteOffset(base_offset + offset),
70        context: DecodeContext::GlobalSection,
71        kind: DecodeErrorKind::UnexpectedEof,
72    })?;
73
74    match byte {
75        0x00 => Ok(Mutability::Const),
76        0x01 => Ok(Mutability::Var),
77        _ => Err(DecodeError {
78            offset: ByteOffset(base_offset + offset),
79            context: DecodeContext::GlobalSection,
80            kind: DecodeErrorKind::InvalidMutability { byte },
81        }),
82    }
83}
84
85fn parse_init_expr<'a>(
86    cursor: &mut Cursor<'a>,
87    base_offset: usize,
88) -> Result<&'a [u8], DecodeError> {
89    let start = cursor.position();
90    loop {
91        let opcode_offset = cursor.position();
92        let byte = cursor.read_byte().map_err(|_| DecodeError {
93            offset: ByteOffset(base_offset + opcode_offset),
94            context: DecodeContext::GlobalSection,
95            kind: DecodeErrorKind::UnexpectedEof,
96        })?;
97
98        if byte == 0x0B {
99            let end = cursor.position();
100            return Ok(&cursor.original()[start..end]);
101        }
102    }
103}
104
105fn decode_u32_in_section(cursor: &mut Cursor<'_>, base_offset: usize) -> Result<u32, DecodeError> {
106    leb128::decode_u32(cursor).map_err(|mut e| {
107        e.context = DecodeContext::GlobalSection;
108        e.offset = ByteOffset(base_offset + e.offset.0);
109        e
110    })
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116    use crate::binary::section::SectionId;
117    use crate::types::NumType;
118
119    fn raw_global_section(data: &[u8]) -> RawSection<'_> {
120        RawSection {
121            id: SectionId::Global,
122            offset: 30,
123            data,
124        }
125    }
126
127    #[test]
128    fn parse_empty_global_section() {
129        let section = raw_global_section(&[0x00]);
130        let globals = parse_global_section(&section).unwrap();
131        assert!(globals.is_empty());
132    }
133
134    #[test]
135    fn parse_single_i32_global() {
136        let section = raw_global_section(&[0x01, 0x7F, 0x00, 0x41, 0x2A, 0x0B]);
137        let globals = parse_global_section(&section).unwrap();
138        assert_eq!(globals.len(), 1);
139        assert_eq!(globals[0].global_type.val_type, ValType::Num(NumType::I32));
140        assert_eq!(globals[0].global_type.mutability, Mutability::Const);
141        assert_eq!(globals[0].init_expr, &[0x41, 0x2A, 0x0B]);
142        assert_eq!(globals[0].init_offset, 33);
143    }
144
145    #[test]
146    fn reject_invalid_mutability() {
147        let section = raw_global_section(&[0x01, 0x7F, 0x02, 0x41, 0x00, 0x0B]);
148        let err = parse_global_section(&section).unwrap_err();
149        assert!(matches!(
150            err.kind,
151            DecodeErrorKind::InvalidMutability { byte: 0x02 }
152        ));
153    }
154
155    #[test]
156    fn reject_unterminated_init_expr() {
157        let section = raw_global_section(&[0x01, 0x7F, 0x00, 0x41, 0x00]);
158        let err = parse_global_section(&section).unwrap_err();
159        assert_eq!(err.kind, DecodeErrorKind::UnexpectedEof);
160    }
161}