1 | use crate::{encode_section, ConstExpr, Encode, RefType, Section, SectionId}; |
2 | use 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)] |
42 | pub 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)] |
49 | pub 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)] |
58 | pub 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)] |
82 | pub 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 | |
89 | impl 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 | |
214 | impl Encode for ElementSection { |
215 | fn encode(&self, sink: &mut Vec<u8>) { |
216 | encode_section(sink, self.num_added, &self.bytes); |
217 | } |
218 | } |
219 | |
220 | impl Section for ElementSection { |
221 | fn id(&self) -> u8 { |
222 | SectionId::Element.into() |
223 | } |
224 | } |
225 | |