use crate::emit::{Emit, EmitContext};
use crate::parse::IndicesToIds;
use crate::tombstone_arena::{Id, Tombstone, TombstoneArena};
use crate::{ir::Value, FunctionId, InitExpr, Module, Result, TableId, ValType};
use anyhow::{bail, Context};
pub type ElementId = Id<Element>;
#[derive(Debug)]
pub struct Element {
id: Id<Element>,
pub kind: ElementKind,
pub ty: ValType,
pub members: Vec<Option<FunctionId>>,
pub name: Option<String>,
}
#[allow(missing_docs)]
#[derive(Debug, Copy, Clone)]
pub enum ElementKind {
Passive,
Declared,
Active { table: TableId, offset: InitExpr },
}
impl Element {
pub fn id(&self) -> Id<Element> {
self.id
}
}
impl Tombstone for Element {
fn on_delete(&mut self) {
self.members = Vec::new();
}
}
#[derive(Debug, Default)]
pub struct ModuleElements {
arena: TombstoneArena<Element>,
}
impl ModuleElements {
pub fn get(&self, id: ElementId) -> &Element {
&self.arena[id]
}
pub fn get_mut(&mut self, id: ElementId) -> &mut Element {
&mut self.arena[id]
}
pub fn delete(&mut self, id: ElementId) {
self.arena.delete(id);
}
pub fn iter(&self) -> impl Iterator<Item = &Element> {
self.arena.iter().map(|(_, f)| f)
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Element> {
self.arena.iter_mut().map(|(_, f)| f)
}
pub fn add(
&mut self,
kind: ElementKind,
ty: ValType,
members: Vec<Option<FunctionId>>,
) -> ElementId {
let id = self.arena.next_id();
let id2 = self.arena.alloc(Element {
id,
kind,
ty,
members,
name: None,
});
debug_assert_eq!(id, id2);
id
}
}
impl Module {
pub(crate) fn parse_elements(
&mut self,
section: wasmparser::ElementSectionReader,
ids: &mut IndicesToIds,
) -> Result<()> {
log::debug!("parse element section");
for (i, segment) in section.into_iter().enumerate() {
let segment = segment?;
let ty = ValType::parse(&segment.ty)?;
match ty {
ValType::Funcref => {}
_ => bail!("only funcref type allowed in element segments"),
}
let members = segment
.items
.get_items_reader()?
.into_iter()
.map(|e| -> Result<_> {
Ok(match e? {
wasmparser::ElementItem::Func(f) => Some(ids.get_func(f)?),
wasmparser::ElementItem::Null(_) => None,
})
})
.collect::<Result<_>>()?;
let id = self.elements.arena.next_id();
let kind = match segment.kind {
wasmparser::ElementKind::Passive => ElementKind::Passive,
wasmparser::ElementKind::Declared => ElementKind::Declared,
wasmparser::ElementKind::Active {
table_index,
init_expr,
} => {
let table = ids.get_table(table_index)?;
self.tables.get_mut(table).elem_segments.insert(id);
let offset = InitExpr::eval(&init_expr, ids)
.with_context(|| format!("in segment {}", i))?;
match offset {
InitExpr::Value(Value::I32(_)) => {}
InitExpr::Global(global) if self.globals.get(global).ty == ValType::I32 => {
}
_ => bail!("non-i32 constant in segment {}", i),
}
ElementKind::Active { table, offset }
}
};
self.elements.arena.alloc(Element {
id,
ty,
kind,
members,
name: None,
});
ids.push_element(id);
}
Ok(())
}
}
impl Emit for ModuleElements {
fn emit(&self, cx: &mut EmitContext) {
if self.arena.len() == 0 {
return;
}
let mut wasm_element_section = wasm_encoder::ElementSection::new();
for (id, element) in self.arena.iter() {
cx.indices.push_element(id);
if element.members.iter().any(|i| i.is_none()) {
let els_vec: Vec<wasm_encoder::ConstExpr> = element
.members
.iter()
.map(|func| match func {
Some(func) => {
wasm_encoder::ConstExpr::ref_func(cx.indices.get_func_index(*func))
}
None => wasm_encoder::ConstExpr::ref_null(wasm_encoder::HeapType::Func),
})
.collect();
let els = wasm_encoder::Elements::Expressions(els_vec.as_slice());
emit_elem(cx, &mut wasm_element_section, &element.kind, els);
} else {
let els_vec: Vec<u32> = element
.members
.iter()
.map(|func| cx.indices.get_func_index(func.unwrap()))
.collect();
let els = wasm_encoder::Elements::Functions(els_vec.as_slice());
emit_elem(cx, &mut wasm_element_section, &element.kind, els);
}
fn emit_elem(
cx: &mut EmitContext,
wasm_element_section: &mut wasm_encoder::ElementSection,
kind: &ElementKind,
els: wasm_encoder::Elements,
) {
match kind {
ElementKind::Active { table, offset } => {
let table_index =
Some(cx.indices.get_table_index(*table)).filter(|&index| index != 0);
wasm_element_section.active(
table_index,
&offset.to_wasmencoder_type(&cx),
wasm_encoder::RefType::FUNCREF,
els,
);
}
ElementKind::Passive => {
wasm_element_section.passive(wasm_encoder::RefType::FUNCREF, els);
}
ElementKind::Declared => {
wasm_element_section.declared(wasm_encoder::RefType::FUNCREF, els);
}
}
}
}
cx.wasm_module.section(&wasm_element_section);
}
}