use crate::abi::{align_to, local::LocalSlot, ty_size, ABIArg, ABISig, ABI};
use anyhow::Result;
use smallvec::SmallVec;
use std::ops::Range;
use wasmparser::{BinaryReader, FuncValidator, ValType, ValidatorResources};
pub(crate) type Locals = SmallVec<[LocalSlot; 16]>;
pub(crate) struct DefinedLocalsRange(Range<u32>);
impl DefinedLocalsRange {
pub fn as_range(&self) -> &Range<u32> {
&self.0
}
}
pub(crate) struct Frame {
pub locals_size: u32,
pub defined_locals_range: DefinedLocalsRange,
pub locals: Locals,
}
impl Frame {
pub fn new<A: ABI>(
sig: &ABISig,
body: &mut BinaryReader<'_>,
validator: &mut FuncValidator<ValidatorResources>,
abi: &A,
) -> Result<Self> {
let (mut locals, defined_locals_start) = Self::compute_arg_slots(sig, abi)?;
let (defined_slots, defined_locals_end) =
Self::compute_defined_slots(body, validator, defined_locals_start)?;
locals.extend(defined_slots);
let locals_size = align_to(defined_locals_end, abi.stack_align().into());
Ok(Self {
locals,
locals_size,
defined_locals_range: DefinedLocalsRange(defined_locals_start..defined_locals_end),
})
}
pub fn get_local(&self, index: u32) -> Option<&LocalSlot> {
self.locals.get(index as usize)
}
fn compute_arg_slots<A: ABI>(sig: &ABISig, abi: &A) -> Result<(Locals, u32)> {
let arg_base_offset = abi.arg_base_offset().into();
let mut next_stack = 0u32;
let slots: Locals = sig
.params
.iter()
.map(|arg| Self::abi_arg_slot(&arg, &mut next_stack, arg_base_offset))
.collect();
Ok((slots, next_stack))
}
fn abi_arg_slot(arg: &ABIArg, next_stack: &mut u32, arg_base_offset: u32) -> LocalSlot {
match arg {
ABIArg::Reg { ty, reg: _ } => {
let ty_size = ty_size(&ty);
*next_stack = align_to(*next_stack, ty_size) + ty_size;
LocalSlot::new(*ty, *next_stack)
}
ABIArg::Stack { ty, offset } => LocalSlot::stack_arg(*ty, offset + arg_base_offset),
}
}
fn compute_defined_slots(
reader: &mut BinaryReader<'_>,
validator: &mut FuncValidator<ValidatorResources>,
next_stack: u32,
) -> Result<(Locals, u32)> {
let mut next_stack = next_stack;
let local_count = reader.read_var_u32()?;
let mut slots: Locals = Default::default();
for _ in 0..local_count {
let position = reader.original_position();
let count = reader.read_var_u32()?;
let ty = reader.read_val_type()?;
validator.define_locals(position, count, ty)?;
let ty: ValType = ty.try_into()?;
for _ in 0..count {
let ty_size = ty_size(&ty);
next_stack = align_to(next_stack, ty_size) + ty_size;
slots.push(LocalSlot::new(ty, next_stack));
}
}
Ok((slots, next_stack))
}
}