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}