zingen/
local.rs

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