pub use config::{Config, SizeConfig};
pub use error::Error;
use instruction_visit::Output;
use prefix_sum_vec::PrefixSumVec;
use wasmparser::{BlockType, ValType};
mod config;
mod error;
mod instruction_visit;
#[cfg(test)]
mod test;
pub struct ModuleState {
functions: Vec<u32>,
types: Vec<wasmparser::FuncType>,
globals: Vec<wasmparser::ValType>,
tables: Vec<wasmparser::RefType>,
}
impl ModuleState {
pub fn new() -> Self {
Self {
functions: vec![],
types: vec![],
globals: vec![],
tables: vec![],
}
}
}
pub struct FunctionState {
locals: PrefixSumVec<wasmparser::ValType, u32>,
operands: Vec<u8>,
size: u64,
pub(crate) max_size: u64,
frames: Vec<Frame>,
current_frame: Frame,
}
impl FunctionState {
pub fn new() -> Self {
Self {
locals: PrefixSumVec::new(),
operands: vec![],
size: 0,
max_size: 0,
frames: vec![],
current_frame: Frame {
height: 0,
block_type: BlockType::Empty,
stack_polymorphic: false,
},
}
}
pub(crate) fn clear(&mut self) {
self.locals.clear();
self.operands.clear();
self.size = 0;
self.max_size = 0;
self.frames.clear();
self.current_frame = Frame {
height: 0,
block_type: BlockType::Empty,
stack_polymorphic: false,
};
}
pub(crate) fn add_locals(&mut self, count: u32, ty: wasmparser::ValType) -> Result<(), Error> {
self.locals
.try_push_more(count, ty)
.map_err(Error::TooManyLocals)
}
}
#[derive(Debug)]
pub(crate) struct Frame {
pub(crate) height: usize,
pub(crate) block_type: BlockType,
pub(crate) stack_polymorphic: bool,
}
pub struct Visitor<'s, Cfg: ?Sized> {
pub(crate) offset: usize,
pub(crate) config: &'s Cfg,
pub(crate) module_state: &'s ModuleState,
pub(crate) function_state: &'s mut FunctionState,
}
impl<'b, 's, Cfg: SizeConfig + ?Sized> Visitor<'s, Cfg> {
fn function_type_index(&self, function_index: u32) -> Result<u32, Error> {
let function_index_usize = usize::try_from(function_index)
.map_err(|e| Error::FunctionIndexRange(function_index, e))?;
self.module_state
.functions
.get(function_index_usize)
.copied()
.ok_or(Error::FunctionIndex(function_index))
}
fn type_params_results(&self, type_idx: u32) -> Result<(&'s [ValType], &'s [ValType]), Error> {
let type_idx_usize =
usize::try_from(type_idx).map_err(|e| Error::TypeIndexRange(type_idx, e))?;
let fnty = self
.module_state
.types
.get(type_idx_usize)
.ok_or(Error::TypeIndex(type_idx))?;
Ok((fnty.params(), fnty.results()))
}
fn with_block_types<F>(&mut self, block_type: BlockType, cb: F) -> Result<(), Error>
where
F: FnOnce(&mut Self, &[ValType], &[ValType]) -> Result<(), Error>,
{
match block_type {
BlockType::Empty => cb(self, &[], &[]),
BlockType::Type(result) => cb(self, &[], &[result]),
BlockType::FuncType(type_index) => {
let (params, results) = self.type_params_results(type_index)?;
cb(self, params, results)
}
}
}
fn new_frame(&mut self, block_type: BlockType, shift_operands: usize) -> Result<(), Error> {
let stack_polymorphic = self.function_state.current_frame.stack_polymorphic;
let height = if stack_polymorphic {
self.function_state.operands.len()
} else {
self.function_state
.operands
.len()
.checked_sub(shift_operands)
.ok_or(Error::EmptyStack(self.offset))?
};
self.function_state.frames.push(std::mem::replace(
&mut self.function_state.current_frame,
Frame {
height,
block_type,
stack_polymorphic,
},
));
Ok(())
}
fn end_frame(&mut self) -> Result<Option<Frame>, Error> {
if let Some(frame) = self.function_state.frames.pop() {
let frame = std::mem::replace(&mut self.function_state.current_frame, frame);
let to_pop = self
.function_state
.operands
.len()
.checked_sub(frame.height)
.ok_or(Error::TruncatedOperandStack(self.offset))?;
self.pop_many(to_pop)?;
Ok(Some(frame))
} else {
Ok(None)
}
}
fn make_polymorphic(&mut self) {
self.function_state.current_frame.stack_polymorphic = true;
}
fn push(&mut self, t: ValType) {
if !self.function_state.current_frame.stack_polymorphic {
let value_size = self.config.size_of_value(t);
self.function_state.operands.push(value_size);
self.function_state.size += u64::from(value_size);
self.function_state.max_size =
std::cmp::max(self.function_state.size, self.function_state.max_size);
}
}
fn pop(&mut self) -> Result<(), Error> {
if !self.function_state.current_frame.stack_polymorphic {
let operand_size = u64::from(
self.function_state
.operands
.pop()
.ok_or(Error::EmptyStack(self.offset))?,
);
self.function_state.size = self
.function_state
.size
.checked_sub(operand_size)
.expect("stack size is going negative");
}
Ok(())
}
fn pop_many(&mut self, count: usize) -> Result<(), Error> {
if count == 0 || self.function_state.current_frame.stack_polymorphic {
Ok(())
} else {
let operand_count = self.function_state.operands.len();
let split_point = operand_count
.checked_sub(count)
.ok_or(Error::EmptyStack(self.offset))?;
let size: u64 = self
.function_state
.operands
.drain(split_point..)
.map(u64::from)
.sum();
self.function_state.size = self
.function_state
.size
.checked_sub(size)
.expect("stack size is going negative");
Ok(())
}
}
fn visit_const(&mut self, t: ValType) -> Output {
self.push(t);
Ok(())
}
fn visit_unop(&mut self) -> Output {
Ok(())
}
fn visit_binop(&mut self) -> Output {
self.pop()?;
Ok(())
}
fn visit_testop(&mut self) -> Output {
self.pop()?;
self.push(ValType::I32);
Ok(())
}
fn visit_relop(&mut self) -> Output {
self.pop_many(2)?;
self.push(ValType::I32);
Ok(())
}
fn visit_cvtop(&mut self, result_ty: ValType) -> Output {
self.pop()?;
self.push(result_ty);
Ok(())
}
fn visit_vternop(&mut self) -> Output {
self.pop_many(2)?;
Ok(())
}
fn visit_vrelop(&mut self) -> Output {
self.visit_binop()
}
fn visit_vishiftop(&mut self) -> Output {
self.pop()?;
Ok(())
}
fn visit_vinarrowop(&mut self) -> Output {
self.pop()?;
Ok(())
}
fn visit_vbitmask(&mut self) -> Output {
self.pop()?;
self.push(ValType::I32);
Ok(())
}
fn visit_splat(&mut self) -> Output {
self.pop()?;
self.push(ValType::V128);
Ok(())
}
fn visit_replace_lane(&mut self) -> Output {
self.pop()?;
Ok(())
}
fn visit_extract_lane(&mut self, unpacked_shape: ValType) -> Output {
self.pop()?;
self.push(unpacked_shape);
Ok(())
}
fn visit_load(&mut self, t: ValType) -> Output {
self.pop()?;
self.push(t);
Ok(())
}
fn visit_load_lane(&mut self) -> Output {
self.pop_many(2)?;
self.push(ValType::V128);
Ok(())
}
fn visit_store(&mut self) -> Output {
self.pop_many(2)?;
Ok(())
}
fn visit_store_lane(&mut self) -> Output {
self.pop_many(2)?;
Ok(())
}
fn visit_atomic_rmw(&mut self, t: ValType) -> Output {
self.pop_many(2)?;
self.push(t);
Ok(())
}
fn visit_atomic_cmpxchg(&mut self, t: ValType) -> Output {
self.pop_many(3)?;
self.push(t);
Ok(())
}
fn visit_function_call(&mut self, type_index: u32) -> Output {
let (params, results) = self.type_params_results(type_index)?;
self.pop_many(params.len())?;
for result_ty in results {
self.push(*result_ty);
}
Ok(())
}
fn visit_return_call_type_index(&mut self, _type_index: u32) -> Output {
<Self as wasmparser::VisitOperator>::visit_return(self)
}
}