use crate::{
backtrace::Backtrace,
control::ControlStack,
jump::JumpTable,
local::{LocalSlot, LocalSlotType, Locals},
masm::MacroAssembler,
validator::ValidateThenVisit,
wasm::Env,
Buffer, Error, Result,
};
use wasmparser::{FuncType, FuncValidator, LocalsReader, OperatorsReader, ValidatorResources};
pub struct Function {
pub backtrace: Backtrace,
pub control: ControlStack,
pub env: Env,
pub locals: Locals,
pub masm: MacroAssembler,
pub table: JumpTable,
pub ty: FuncType,
pub is_main: bool,
}
impl Function {
pub fn new(env: Env, ty: FuncType, is_main: bool) -> Result<Self> {
let mut params_count = 0;
if !is_main {
params_count = ty.params().len() as u8;
}
let mut codegen = Self {
backtrace: Backtrace::default(),
control: ControlStack::default(),
env,
ty,
locals: Default::default(),
masm: Default::default(),
table: Default::default(),
is_main,
};
if !is_main {
codegen.masm.increment_sp(1 + params_count)?;
codegen.masm._jumpdest()?;
codegen.masm.shift_stack(params_count, true)?;
}
Ok(codegen)
}
pub fn emit_locals(
&mut self,
locals: &mut LocalsReader<'_>,
validator: &mut FuncValidator<ValidatorResources>,
) -> Result<()> {
let mut sp = if self.is_main { 0 } else { 1 };
for param in self.ty.params() {
self.locals
.push(LocalSlot::new(*param, LocalSlotType::Parameter, sp));
sp += 1;
}
while let Ok((count, val)) = locals.read() {
let validation_offset = locals.original_position();
for _ in 0..count {
self.masm.push(&[0])?;
self.locals
.push(LocalSlot::new(val, LocalSlotType::Variable, sp));
sp += 1;
}
validator.define_locals(validation_offset, count, val)?;
}
tracing::trace!("{:?}", self.locals);
Ok(())
}
pub fn emit_operators(
&mut self,
ops: &mut OperatorsReader<'_>,
validator: &mut FuncValidator<ValidatorResources>,
) -> Result<()> {
while !ops.eof() {
let offset = ops.original_position();
let mut validate_then_visit = ValidateThenVisit(validator.visitor(offset), self);
ops.visit_operator(&mut validate_then_visit)???;
}
Ok(())
}
pub fn finish(self, jump_table: &mut JumpTable, pc: u16) -> Result<Buffer> {
let sp = self.masm.sp();
if !self.is_main && self.masm.sp() != self.ty.results().len() as u8 {
return Err(Error::StackNotBalanced(sp));
}
jump_table.merge(self.table, pc)?;
Ok(self.masm.buffer().into())
}
}