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