mod abi;
mod data;
mod func;
mod host;
pub use self::{
abi::{ToLSBytes, Type},
data::Data,
func::{Function, Functions},
host::HostFunc,
};
use crate::{Error, Result};
use host::CompilerLabel;
use smallvec::SmallVec;
use std::collections::BTreeMap;
use wasmparser::Operator;
use zabi::Abi;
macro_rules! impl_deref {
($doc:literal, $name:ident, $target:ty) => {
#[derive(Clone, Debug, Default)]
#[doc = concat!(" ", $doc)]
pub struct $name($target);
impl core::ops::Deref for $name {
type Target = $target;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl core::ops::DerefMut for $name {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
};
($(($doc:literal, $name:ident, $target:ty)),*) => {
$( impl_deref!($doc, $name, $target); )*
};
}
impl_deref! {
("WASM import section", Imports, BTreeMap<u32, HostFunc>),
("WASM export section", Exports, BTreeMap<u32, String>),
("WASM slot registry", Slots, BTreeMap<u32, u32>),
("WASM function registry", Funcs, BTreeMap<u32, (u32, u32)>)
}
#[derive(Clone, Debug, Default)]
pub struct Env {
pub imports: Imports,
pub exports: Exports,
pub slots: Slots,
pub funcs: Funcs,
pub data: Data,
pub index: Option<u32>,
}
impl Env {
pub fn load_abis(&self, funs: &Functions<'_>) -> Result<Vec<Abi>> {
let mut abis: Vec<_> = Default::default();
for (_, fun) in funs.iter() {
abis.push(self.load_abi(fun)?);
}
Ok(abis)
}
pub fn load_abi(&self, fun: &Function<'_>) -> Result<Abi> {
let mut reader = fun.body.get_operators_reader()?;
let Operator::I32Const { value: offset } = reader.read()? else {
return Err(Error::InvalidSelector);
};
let Operator::I32Const { value: length } = reader.read()? else {
return Err(Error::InvalidSelector);
};
let Operator::Call {
function_index: index,
} = reader.read()?
else {
return Err(Error::InvalidSelector);
};
if !self.imports.is_emit_abi(index) {
return Err(Error::FuncNotImported("emit_abi".into()));
}
let abi = self.data.load(offset, length as usize)?;
Abi::from_hex(String::from_utf8_lossy(&abi)).map_err(Into::into)
}
pub fn query_func(&self, name: &str) -> Result<u32> {
for (index, export) in self.exports.iter() {
if export == name {
return Ok(*index);
}
}
Err(Error::FuncNotImported(name.into()))
}
pub fn is_external(&self, index: u32) -> bool {
let Some(name) = self.exports.get(&index) else {
return false;
};
let selector = name.to_owned() + "_selector";
self.exports.iter().any(|(_, n)| **n == selector)
}
pub fn is_main(&self, index: u32) -> bool {
self.imports.len() as u32 == index
}
pub fn with_index(&self, index: u32) -> Self {
let mut this = self.clone();
this.index = Some(index);
this
}
pub fn reserved(&self) -> u32 {
let Some(index) = self.index else {
return 0;
};
*self.slots.get(&index).unwrap_or(&0)
}
pub fn alloc(&self, index: u32) -> SmallVec<[u8; 4]> {
let slots = index + self.reserved();
tracing::debug!(
"allocating memory for local {index} of function {:?}, slot: {slots}",
self.index
);
(slots * 0x20).to_ls_bytes()
}
}
impl Imports {
pub fn is_emit_abi(&self, index: u32) -> bool {
self.get(&index) == Some(&HostFunc::EmitABI)
}
pub fn reserved(&self) -> u32 {
let mut reserved = 0;
for host_fn in self.0.values() {
match *host_fn {
HostFunc::Label(CompilerLabel::ReserveMemory32) => reserved = 1,
HostFunc::Label(CompilerLabel::ReserveMemory64) => {
return 2;
}
_ => {}
}
}
reserved
}
}
impl Exports {
pub fn selectors(&self) -> Vec<u32> {
self.iter()
.filter_map(|(index, export)| {
if export.ends_with("_selector") {
Some(*index)
} else {
None
}
})
.collect::<Vec<_>>()
}
}