1use crate::emit::{Emit, EmitContext};
4use crate::parse::IndicesToIds;
5use crate::tombstone_arena::{Id, Tombstone, TombstoneArena};
6use crate::{ir::Value, ConstExpr, FunctionId, Module, RefType, Result, TableId, ValType};
7use anyhow::{bail, Context};
8
9pub type ElementId = Id<Element>;
11
12#[derive(Debug)]
14pub struct Element {
15 id: Id<Element>,
16 pub kind: ElementKind,
18 pub items: ElementItems,
20 pub name: Option<String>,
23}
24
25#[derive(Debug, Clone)]
27pub enum ElementKind {
28 Passive,
30 Declared,
32 Active {
34 table: TableId,
36 offset: ConstExpr,
38 },
39}
40
41#[derive(Debug, Clone)]
43pub enum ElementItems {
44 Functions(Vec<FunctionId>),
46 Expressions(RefType, Vec<ConstExpr>),
48}
49
50impl Element {
51 pub fn id(&self) -> Id<Element> {
53 self.id
54 }
55}
56
57impl Tombstone for Element {
58 fn on_delete(&mut self) {
59 }
61}
62
63#[derive(Debug, Default)]
66pub struct ModuleElements {
67 arena: TombstoneArena<Element>,
68}
69
70impl ModuleElements {
71 pub fn get(&self, id: ElementId) -> &Element {
73 &self.arena[id]
74 }
75
76 pub fn get_mut(&mut self, id: ElementId) -> &mut Element {
78 &mut self.arena[id]
79 }
80
81 pub fn delete(&mut self, id: ElementId) {
86 self.arena.delete(id);
87 }
88
89 pub fn iter(&self) -> impl Iterator<Item = &Element> {
91 self.arena.iter().map(|(_, f)| f)
92 }
93
94 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Element> {
96 self.arena.iter_mut().map(|(_, f)| f)
97 }
98
99 pub fn add(&mut self, kind: ElementKind, items: ElementItems) -> ElementId {
101 let id = self.arena.next_id();
102 let id2 = self.arena.alloc(Element {
103 id,
104 kind,
105 items,
106 name: None,
107 });
108 debug_assert_eq!(id, id2);
109 id
110 }
111}
112
113impl Module {
114 pub(crate) fn parse_elements(
116 &mut self,
117 section: wasmparser::ElementSectionReader,
118 ids: &mut IndicesToIds,
119 ) -> Result<()> {
120 log::debug!("parse element section");
121 for (i, segment) in section.into_iter().enumerate() {
122 let element = segment?;
123
124 let items = match element.items {
125 wasmparser::ElementItems::Functions(funcs) => {
126 let mut function_ids = Vec::with_capacity(funcs.count() as usize);
127 for func in funcs {
128 match ids.get_func(func?) {
129 Ok(func) => function_ids.push(func),
130 Err(_) => bail!("invalid function index in element segment {}", i),
131 }
132 }
133 ElementItems::Functions(function_ids)
134 }
135 wasmparser::ElementItems::Expressions(ref_type, items) => {
136 let ty = match ref_type {
137 wasmparser::RefType::FUNCREF => RefType::Funcref,
138 wasmparser::RefType::EXTERNREF => RefType::Externref,
139 wasmparser::RefType::EXNREF => RefType::Exnref,
140 _ => bail!("unsupported ref type in element segment {}", i),
141 };
142 let mut const_exprs = Vec::with_capacity(items.count() as usize);
143 for item in items {
144 let const_expr = item?;
145 let expr = ConstExpr::eval(&const_expr, ids).with_context(|| {
146 format!(
147 "Failed to evaluate a const expr in element segment {}:\n{:?}",
148 i, const_expr
149 )
150 })?;
151 const_exprs.push(expr);
152 }
153 ElementItems::Expressions(ty, const_exprs)
154 }
155 };
156
157 let id = self.elements.arena.next_id();
158
159 let kind = match element.kind {
160 wasmparser::ElementKind::Passive => ElementKind::Passive,
161 wasmparser::ElementKind::Declared => ElementKind::Declared,
162 wasmparser::ElementKind::Active {
163 table_index,
164 offset_expr,
165 } => {
166 let table_id = ids.get_table(table_index.unwrap_or_default())?;
168 let table = self.tables.get_mut(table_id);
169 table.elem_segments.insert(id);
170
171 let offset = ConstExpr::eval(&offset_expr, ids).with_context(|| {
172 format!("failed to evaluate the offset of element {}", i)
173 })?;
174 if table.table64 {
175 match offset {
176 ConstExpr::Value(Value::I64(_)) => {}
177 ConstExpr::Global(global)
178 if self.globals.get(global).ty == ValType::I64 => {}
179 _ => bail!(
180 "element {} is active for 64-bit table but has non-i64 offset",
181 i
182 ),
183 }
184 } else {
185 match offset {
186 ConstExpr::Value(Value::I32(_)) => {}
187 ConstExpr::Global(global)
188 if self.globals.get(global).ty == ValType::I32 => {}
189 _ => bail!(
190 "element {} is active for 32-bit table but has non-i32 offset",
191 i
192 ),
193 }
194 }
195
196 ElementKind::Active {
197 table: table_id,
198 offset,
199 }
200 }
201 };
202 self.elements.arena.alloc(Element {
203 id,
204 kind,
205 items,
206 name: None,
207 });
208 ids.push_element(id);
209 }
210 Ok(())
211 }
212}
213
214impl Emit for ModuleElements {
215 fn emit(&self, cx: &mut EmitContext) {
216 if self.arena.len() == 0 {
217 return;
218 }
219
220 let mut wasm_element_section = wasm_encoder::ElementSection::new();
221
222 for (id, element) in self.arena.iter() {
223 cx.indices.push_element(id);
224
225 match &element.items {
226 ElementItems::Functions(function_ids) => {
227 let idx = function_ids
228 .iter()
229 .map(|&func| cx.indices.get_func_index(func))
230 .collect::<Vec<_>>();
231 let els = wasm_encoder::Elements::Functions(std::borrow::Cow::Borrowed(&idx));
232 emit_elem(cx, &mut wasm_element_section, &element.kind, els);
233 }
234 ElementItems::Expressions(ty, const_exprs) => {
235 let ref_type = match ty {
236 RefType::Funcref => wasm_encoder::RefType::FUNCREF,
237 RefType::Externref => wasm_encoder::RefType::EXTERNREF,
238 RefType::Exnref => wasm_encoder::RefType::EXNREF,
239 };
240 let const_exprs = const_exprs
241 .iter()
242 .map(|expr| expr.to_wasmencoder_type(cx))
243 .collect::<Vec<_>>();
244 let els = wasm_encoder::Elements::Expressions(
245 ref_type,
246 std::borrow::Cow::Borrowed(&const_exprs),
247 );
248 emit_elem(cx, &mut wasm_element_section, &element.kind, els);
249 }
250 }
251
252 fn emit_elem(
253 cx: &mut EmitContext,
254 wasm_element_section: &mut wasm_encoder::ElementSection,
255 kind: &ElementKind,
256 els: wasm_encoder::Elements,
257 ) {
258 match kind {
259 ElementKind::Active { table, offset } => {
260 let table_index =
263 Some(cx.indices.get_table_index(*table)).filter(|&index| index != 0);
264 wasm_element_section.active(
265 table_index,
266 &offset.to_wasmencoder_type(cx),
267 els,
268 );
269 }
270 ElementKind::Passive => {
271 wasm_element_section.passive(els);
272 }
273 ElementKind::Declared => {
274 wasm_element_section.declared(els);
275 }
276 }
277 }
278 }
279
280 cx.wasm_module.section(&wasm_element_section);
281 }
282}