use crate::decls::{ModuleDecls, TableDecl};
use crate::error::Error;
use crate::module::UniqueFuncIndex;
use crate::pointer::NATIVE_POINTER_SIZE;
use byteorder::{LittleEndian, WriteBytesExt};
use cranelift_codegen::entity::EntityRef;
use cranelift_module::{Backend as ClifBackend, DataContext, Module as ClifModule};
use cranelift_wasm::{TableElementType, TableIndex};
use std::io::Cursor;
pub const TABLE_SYM: &str = "lucet_tables";
pub const TABLE_REF_SIZE: usize = NATIVE_POINTER_SIZE * 2;
#[derive(Debug, Clone)]
enum Elem {
Func(UniqueFuncIndex),
Empty,
}
fn table_elements(decl: &TableDecl<'_>) -> Result<Vec<Elem>, Error> {
match decl.table.ty {
TableElementType::Func => Ok(()),
_ => {
let message = format!("table with non-function elements: {:?}", decl);
Err(Error::Unsupported(message))
}
}?;
let mut elems = Vec::new();
for initializer in decl.elems.iter() {
if initializer.base.is_some() {
let message = format!("table elements with global index base: {:?}", initializer);
Err(Error::Unsupported(message))?
}
let final_len = initializer.offset + initializer.elements.len();
if final_len > elems.len() {
elems.resize(final_len, Elem::Empty);
}
for (ix, func_ix) in initializer.elements.iter().enumerate() {
elems[initializer.offset + ix] = Elem::Func(*func_ix);
}
}
Ok(elems)
}
pub fn write_table_data<B: ClifBackend>(
clif_module: &mut ClifModule<B>,
decls: &ModuleDecls<'_>,
) -> Result<usize, Error> {
let mut tables_vec = Cursor::new(Vec::new());
let mut table_ctx = DataContext::new();
let mut table_len = 0;
if let Ok(table_decl) = decls.get_table(TableIndex::new(0)) {
let elements = table_elements(&table_decl)?;
let mut table_data =
Cursor::new(Vec::with_capacity(elements.len() * 2 * NATIVE_POINTER_SIZE));
fn putelem(table: &mut Cursor<Vec<u8>>, elem: u64) {
table.write_u64::<LittleEndian>(elem).unwrap()
}
let mut table_data_ctx = DataContext::new();
for elem in elements.iter() {
match elem {
Elem::Func(func_index) => {
if let Some(func) = decls.get_func(*func_index) {
putelem(&mut table_data, func.signature_index.as_u32() as u64);
let funcref = table_data_ctx.import_function(func.name.into());
let position = table_data.position();
assert!(position < <u32>::max_value() as u64);
table_data_ctx.write_function_addr(position as u32, funcref);
putelem(&mut table_data, 0);
} else {
let message = format!("{:?}", func_index);
return Err(Error::FunctionIndexError(message));
}
}
Elem::Empty => {
putelem(&mut table_data, <u64>::max_value());
putelem(&mut table_data, 0);
}
}
}
table_data_ctx.define(table_data.into_inner().into_boxed_slice());
let table_id = table_decl
.contents_name
.as_dataid()
.expect("tables are data");
clif_module.define_data(table_id, &table_data_ctx)?;
let dataref = clif_module.declare_data_in_data(table_id, &mut table_ctx);
let position = tables_vec.position();
assert!(position < <u32>::max_value() as u64);
table_ctx.write_data_addr(position as u32, dataref, 0 as i64);
tables_vec.write_u64::<LittleEndian>(0).unwrap();
table_len = elements.len();
tables_vec
.write_u64::<LittleEndian>(table_len as u64)
.unwrap();
}
let inner = tables_vec.into_inner();
table_ctx.define(inner.into_boxed_slice());
clif_module.define_data(
decls
.get_tables_list_name()
.as_dataid()
.expect("lucet_tables is declared as data"),
&table_ctx,
)?;
Ok(table_len)
}