1 | use crate::{encode_section, ConstExpr, Encode, RefType, Section, SectionId}; |
2 | use alloc::borrow::Cow; |
3 | use 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)] |
43 | pub 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)] |
50 | pub 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)] |
59 | pub 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)] |
83 | pub 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 | |
90 | impl 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 | |
215 | impl Encode for ElementSection { |
216 | fn encode(&self, sink: &mut Vec<u8>) { |
217 | encode_section(sink, self.num_added, &self.bytes); |
218 | } |
219 | } |
220 | |
221 | impl Section for ElementSection { |
222 | fn id(&self) -> u8 { |
223 | SectionId::Element.into() |
224 | } |
225 | } |
226 | |