wasm_encoder/core/
elements.rs

1use crate::{encode_section, ConstExpr, Encode, RefType, Section, SectionId};
2use alloc::borrow::Cow;
3use alloc::vec::Vec;
4
5/// An encoder for the element section.
6///
7/// Element sections are only supported for modules.
8///
9/// # Example
10///
11/// ```
12/// use std::borrow::Cow;
13/// use wasm_encoder::{
14///     Elements, ElementSection, Module, TableSection, TableType,
15///     RefType, ConstExpr
16/// };
17///
18/// let mut tables = TableSection::new();
19/// tables.table(TableType {
20///     element_type: RefType::FUNCREF,
21///     minimum: 128,
22///     maximum: None,
23///     table64: false,
24///     shared: false,
25/// });
26///
27/// let mut elements = ElementSection::new();
28/// let table_index = 0;
29/// let offset = ConstExpr::i32_const(42);
30/// let functions = Elements::Functions(Cow::Borrowed(&[
31///     // Function indices...
32/// ]));
33/// elements.active(Some(table_index), &offset, functions);
34///
35/// let mut module = Module::new();
36/// module
37///     .section(&tables)
38///     .section(&elements);
39///
40/// let wasm_bytes = module.finish();
41/// ```
42#[derive(Clone, Default, Debug)]
43pub struct ElementSection {
44    bytes: Vec<u8>,
45    num_added: u32,
46}
47
48/// A sequence of elements in a segment in the element section.
49#[derive(Clone, Debug)]
50pub enum Elements<'a> {
51    /// A sequences of references to functions by their indices.
52    Functions(Cow<'a, [u32]>),
53    /// A sequence of reference expressions.
54    Expressions(RefType, Cow<'a, [ConstExpr]>),
55}
56
57/// An element segment's mode.
58#[derive(Clone, Debug)]
59pub enum ElementMode<'a> {
60    /// A passive element segment.
61    ///
62    /// Passive segments are part of the bulk memory proposal.
63    Passive,
64    /// A declared element segment.
65    ///
66    /// Declared segments are part of the bulk memory proposal.
67    Declared,
68    /// An active element segment.
69    Active {
70        /// The table index.
71        ///
72        /// `Active` element specifying a `None` table forces the MVP encoding and refers to the
73        /// 0th table holding `funcref`s. Non-`None` tables use the encoding introduced with the
74        /// bulk memory proposal and can refer to tables with any valid reference type.
75        table: Option<u32>,
76        /// The offset within the table to place this segment.
77        offset: &'a ConstExpr,
78    },
79}
80
81/// An element segment in the element section.
82#[derive(Clone, Debug)]
83pub struct ElementSegment<'a> {
84    /// The element segment's mode.
85    pub mode: ElementMode<'a>,
86    /// This segment's elements.
87    pub elements: Elements<'a>,
88}
89
90impl ElementSection {
91    /// Create a new element section encoder.
92    pub fn new() -> Self {
93        Self::default()
94    }
95
96    /// The number of element segments in the section.
97    pub fn len(&self) -> u32 {
98        self.num_added
99    }
100
101    /// Determines if the section is empty.
102    pub fn is_empty(&self) -> bool {
103        self.num_added == 0
104    }
105
106    /// Define an element segment.
107    pub fn segment<'a>(&mut self, segment: ElementSegment<'a>) -> &mut Self {
108        let expr_bit = match segment.elements {
109            Elements::Expressions(..) => 0b100u32,
110            Elements::Functions(_) => 0b000u32,
111        };
112        let mut encode_type = false;
113        match &segment.mode {
114            ElementMode::Passive => {
115                (0x01 | expr_bit).encode(&mut self.bytes);
116                encode_type = true;
117            }
118            ElementMode::Active { table, offset } => {
119                match (table, &segment.elements) {
120                    // If the `table` is not specified then the 0x00 encoding
121                    // can be used with either function indices or expressions
122                    // that have a `funcref` type.
123                    (None, Elements::Functions(_) | Elements::Expressions(RefType::FUNCREF, _)) => {
124                        (/* 0x00 | */expr_bit).encode(&mut self.bytes);
125                    }
126
127                    // ... otherwise fall through for all other expressions here
128                    // with table 0 or an explicitly specified table to the 0x02
129                    // encoding.
130                    (None, Elements::Expressions(..)) | (Some(_), _) => {
131                        (0x02 | expr_bit).encode(&mut self.bytes);
132                        table.unwrap_or(0).encode(&mut self.bytes);
133                        encode_type = true;
134                    }
135                }
136                offset.encode(&mut self.bytes);
137            }
138            ElementMode::Declared => {
139                (0x03 | expr_bit).encode(&mut self.bytes);
140                encode_type = true;
141            }
142        }
143
144        match segment.elements {
145            Elements::Functions(fs) => {
146                if encode_type {
147                    // elemkind == funcref
148                    self.bytes.push(0x00);
149                }
150                fs.encode(&mut self.bytes);
151            }
152            Elements::Expressions(ty, e) => {
153                if encode_type {
154                    ty.encode(&mut self.bytes);
155                }
156                e.len().encode(&mut self.bytes);
157                for expr in e.iter() {
158                    expr.encode(&mut self.bytes);
159                }
160            }
161        }
162
163        self.num_added += 1;
164        self
165    }
166
167    /// Define an active element segment.
168    ///
169    /// `Active` element specifying a `None` table forces the MVP encoding and refers to the 0th
170    /// table holding `funcref`s. Non-`None` tables use the encoding introduced with the bulk
171    /// memory proposal and can refer to tables with any valid reference type.
172    pub fn active(
173        &mut self,
174        table_index: Option<u32>,
175        offset: &ConstExpr,
176        elements: Elements<'_>,
177    ) -> &mut Self {
178        self.segment(ElementSegment {
179            mode: ElementMode::Active {
180                table: table_index,
181                offset,
182            },
183            elements,
184        })
185    }
186
187    /// Encode a passive element segment.
188    ///
189    /// Passive segments are part of the bulk memory proposal.
190    pub fn passive<'a>(&mut self, elements: Elements<'a>) -> &mut Self {
191        self.segment(ElementSegment {
192            mode: ElementMode::Passive,
193            elements,
194        })
195    }
196
197    /// Encode a declared element segment.
198    ///
199    /// Declared segments are part of the bulk memory proposal.
200    pub fn declared<'a>(&mut self, elements: Elements<'a>) -> &mut Self {
201        self.segment(ElementSegment {
202            mode: ElementMode::Declared,
203            elements,
204        })
205    }
206
207    /// Copy a raw, already-encoded element segment into this elements section.
208    pub fn raw(&mut self, raw_bytes: &[u8]) -> &mut Self {
209        self.bytes.extend_from_slice(raw_bytes);
210        self.num_added += 1;
211        self
212    }
213}
214
215impl Encode for ElementSection {
216    fn encode(&self, sink: &mut Vec<u8>) {
217        encode_section(sink, self.num_added, &self.bytes);
218    }
219}
220
221impl Section for ElementSection {
222    fn id(&self) -> u8 {
223        SectionId::Element.into()
224    }
225}