use anyhow::{anyhow, bail, Result};
use walrus::{
ir::Value, ElementId, FunctionBuilder, FunctionId, FunctionKind, GlobalId, GlobalKind,
InitExpr, MemoryId, Module, ValType,
};
pub fn get_memory(module: &Module) -> Result<MemoryId> {
let mut memories = module.memories.iter().map(|m| m.id());
let memory = memories.next();
if memories.next().is_some() {
bail!(
"expected a single memory, found multiple; multiple memories \
currently not supported"
);
}
memory.ok_or_else(|| {
anyhow!(
"module does not have a memory; must have a memory \
to transform return pointers into Wasm multi-value"
)
})
}
pub fn get_shadow_stack_pointer(module: &Module) -> Option<GlobalId> {
let candidates = module
.globals
.iter()
.filter(|g| g.ty == ValType::I32)
.filter(|g| g.mutable)
.filter(|g| match g.kind {
GlobalKind::Local(InitExpr::Value(Value::I32(n))) => n != 0,
_ => false,
})
.collect::<Vec<_>>();
match candidates.len() {
0 => None,
1 => Some(candidates[0].id()),
_ => None,
}
}
pub fn get_tls_base(module: &Module) -> Option<GlobalId> {
let candidates = module
.exports
.iter()
.filter(|ex| ex.name == "__tls_base")
.filter_map(|ex| match ex.item {
walrus::ExportItem::Global(id) => Some(id),
_ => None,
})
.filter(|id| {
let global = module.globals.get(*id);
global.ty == ValType::I32
})
.collect::<Vec<_>>();
match candidates.len() {
1 => Some(candidates[0]),
_ => None,
}
}
pub struct FunctionTableEntry {
pub element: ElementId,
pub idx: usize,
pub func: Option<FunctionId>,
}
pub fn get_function_table_entry(module: &Module, idx: u32) -> Result<FunctionTableEntry> {
let table = module
.tables
.main_function_table()?
.ok_or_else(|| anyhow!("no function table found in module"))?;
let table = module.tables.get(table);
for &segment in table.elem_segments.iter() {
let segment = module.elements.get(segment);
let offset = match &segment.kind {
walrus::ElementKind::Active {
offset: InitExpr::Value(Value::I32(n)),
..
} => *n as u32,
_ => continue,
};
let idx = (idx - offset) as usize;
match segment.members.get(idx) {
Some(slot) => {
return Ok(FunctionTableEntry {
element: segment.id(),
idx,
func: *slot,
})
}
None => continue,
}
}
bail!("failed to find `{}` in function table", idx);
}
pub fn get_or_insert_start_builder(module: &mut Module) -> &mut FunctionBuilder {
let prev_start = {
match module.start {
Some(start) => match module.funcs.get_mut(start).kind {
FunctionKind::Import(_) => Err(Some(start)),
FunctionKind::Local(_) => Ok(start),
FunctionKind::Uninitialized(_) => unimplemented!(),
},
None => Err(None),
}
};
let id = match prev_start {
Ok(id) => id,
Err(prev_start) => {
let mut builder = FunctionBuilder::new(&mut module.types, &[], &[]);
if let Some(prev_start) = prev_start {
builder.func_body().call(prev_start);
}
let id = builder.finish(Vec::new(), &mut module.funcs);
module.start = Some(id);
id
}
};
module
.funcs
.get_mut(id)
.kind
.unwrap_local_mut()
.builder_mut()
}