zingen/local.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
//! WASM local slot.
use crate::{
wasm::{ToLSBytes, Type},
Error, Result,
};
use smallvec::SmallVec;
use wasmparser::ValType;
/// The type of a local slot.
#[derive(Debug, PartialEq, Eq)]
pub enum LocalSlotType {
/// A function parameter.
Parameter,
/// A local variable.
Variable,
}
/// A local slot.
///
/// Represents the type, location and addressing mode of a local
/// in the stack's local and argument area.
#[derive(Debug)]
pub struct LocalSlot {
/// The type contained by this local slot.
inner: ValType,
/// The type of this local slot.
ty: LocalSlotType,
/// Stack pointer of the local slot.
pub sp: usize,
}
impl LocalSlot {
/// Create a new local slot.
pub fn new(inner: ValType, ty: LocalSlotType, sp: usize) -> Self {
Self { inner, ty, sp }
}
/// Get the type of this local slot.
pub fn ty(&self) -> &LocalSlotType {
&self.ty
}
/// Get the value type of this local slot.
pub fn val_ty(&self) -> &ValType {
&self.inner
}
}
impl Type for LocalSlot {
fn size(&self) -> usize {
self.inner.size()
}
}
/// Solidity's implementation uses 16 slots for locals.
/// ref: <https://docs.soliditylang.org/en/v0.8.20/internals/optimizer.html#stackcompressor>
#[derive(Default, Debug)]
pub struct Locals {
inner: SmallVec<[LocalSlot; 16]>,
}
impl Locals {
/// Get local from index.
pub fn get(&self, index: usize) -> Result<&LocalSlot> {
self.inner
.get(index)
.ok_or_else(|| Error::InvalidLocalIndex(index))
}
/// Get mutate local from index.
pub fn get_mut(&mut self, index: usize) -> Result<&mut LocalSlot> {
self.inner
.get_mut(index)
.ok_or_else(|| Error::InvalidLocalIndex(index))
}
/// Get the lower significant bytes of the byte offset of a local.
///
/// - **Parameter**: If the local is a parameter, the offset is relative to the offset
/// of the calldata.
/// - **Variable**: If the local is a variable, the offset is relative to the offset
/// of the memory.
pub fn offset_of(&self, index: usize) -> Result<SmallVec<[u8; 32]>> {
let local = self.get(index)?;
let offset = if local.ty() == &LocalSlotType::Parameter {
self.inner[..index].iter().fold(0, |acc, x| acc + x.align())
} else {
self.inner[..index]
.iter()
.filter(|x| x.ty() == &LocalSlotType::Variable)
.fold(0, |acc, x| acc + x.align())
}
.to_ls_bytes()
.to_vec()
.into();
Ok(offset)
}
/// Push a local slot.
pub fn push(&mut self, slot: impl Into<LocalSlot>) {
self.inner.push(slot.into())
}
/// Get the length of locals
pub fn len(&self) -> usize {
self.inner.len()
}
/// If the locals are empty.
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
}