zingen/wasm/
mod.rs

1//! WASM related primitives.
2
3mod abi;
4mod data;
5mod func;
6mod host;
7
8pub use self::{
9    abi::{ToLSBytes, Type},
10    data::Data,
11    func::{Function, Functions},
12    host::HostFunc,
13};
14use crate::{Error, Result};
15use host::CompilerLabel;
16use smallvec::SmallVec;
17use std::collections::BTreeMap;
18use wasmparser::Operator;
19use zabi::Abi;
20
21macro_rules! impl_deref {
22    ($doc:literal, $name:ident, $target:ty) => {
23        #[derive(Clone, Debug, Default)]
24        #[doc = concat!(" ", $doc)]
25        pub struct $name($target);
26
27        impl core::ops::Deref for $name {
28            type Target = $target;
29
30            fn deref(&self) -> &Self::Target {
31                &self.0
32            }
33        }
34
35        impl core::ops::DerefMut for $name {
36            fn deref_mut(&mut self) -> &mut Self::Target {
37                &mut self.0
38            }
39        }
40    };
41    ($(($doc:literal, $name:ident, $target:ty)),*) => {
42        $( impl_deref!($doc, $name, $target); )*
43    };
44}
45
46impl_deref! {
47    ("WASM import section", Imports, BTreeMap<u32, HostFunc>),
48    ("WASM export section", Exports, BTreeMap<u32, String>),
49    ("WASM slot registry", Slots, BTreeMap<u32, u32>),
50    ("WASM function registry", Funcs, BTreeMap<u32, (u32, u32)>)
51}
52
53/// A struct that holds the environment wasm module.
54#[derive(Clone, Debug, Default)]
55pub struct Env {
56    /// WASM imports
57    pub imports: Imports,
58    /// WASM exports
59    pub exports: Exports,
60    /// Function memory slots
61    pub slots: Slots,
62    /// Function params count
63    pub funcs: Funcs,
64    /// WASM data slots
65    pub data: Data,
66    /// Current function index
67    pub index: Option<u32>,
68}
69
70impl Env {
71    /// Load abis from functions
72    pub fn load_abis(&self, funs: &Functions<'_>) -> Result<Vec<Abi>> {
73        let mut abis: Vec<_> = Default::default();
74        for (_, fun) in funs.iter() {
75            abis.push(self.load_abi(fun)?);
76        }
77
78        Ok(abis)
79    }
80
81    /// Load abi from function
82    pub fn load_abi(&self, fun: &Function<'_>) -> Result<Abi> {
83        let mut reader = fun.body.get_operators_reader()?;
84
85        let Operator::I32Const { value: offset } = reader.read()? else {
86            return Err(Error::InvalidSelector);
87        };
88        let Operator::I32Const { value: length } = reader.read()? else {
89            return Err(Error::InvalidSelector);
90        };
91
92        // Validate zinkc helper `emit_abi`
93        let Operator::Call {
94            function_index: index,
95        } = reader.read()?
96        else {
97            return Err(Error::InvalidSelector);
98        };
99
100        if !self.imports.is_emit_abi(index) {
101            return Err(Error::FuncNotImported("emit_abi".into()));
102        }
103
104        let abi = self.data.load(offset, length as usize)?;
105        Abi::from_hex(String::from_utf8_lossy(&abi)).map_err(Into::into)
106    }
107
108    /// Query exported function from selector.
109    pub fn query_func(&self, name: &str) -> Result<u32> {
110        for (index, export) in self.exports.iter() {
111            if export == name {
112                return Ok(*index);
113            }
114        }
115
116        Err(Error::FuncNotImported(name.into()))
117    }
118
119    /// Check if the input function is external function
120    pub fn is_external(&self, index: u32) -> bool {
121        let Some(name) = self.exports.get(&index) else {
122            return false;
123        };
124
125        let selector = name.to_owned() + "_selector";
126        self.exports.iter().any(|(_, n)| **n == selector)
127    }
128
129    /// If the present function index is the main function
130    ///
131    /// NOTE: in wasm the indexes of the imports will be ordered
132    /// before the functions
133    pub fn is_main(&self, index: u32) -> bool {
134        self.imports.len() as u32 == index
135    }
136
137    /// Clone a new environment with function index provided
138    pub fn with_index(&self, index: u32) -> Self {
139        let mut this = self.clone();
140        this.index = Some(index);
141        this
142    }
143
144    /// Get reserved slots
145    pub fn reserved(&self) -> u32 {
146        let Some(index) = self.index else {
147            return 0;
148        };
149
150        *self.slots.get(&index).unwrap_or(&0)
151    }
152
153    /// Allocate memory slots from local index
154    pub fn alloc(&self, index: u32) -> SmallVec<[u8; 4]> {
155        let slots = index + self.reserved();
156        tracing::debug!(
157            "allocating memory for local {index} of function {:?}, slot: {slots}",
158            self.index
159        );
160        (slots * 0x20).to_ls_bytes()
161    }
162}
163
164impl Imports {
165    /// If the function is `emit_abi`.
166    pub fn is_emit_abi(&self, index: u32) -> bool {
167        self.get(&index) == Some(&HostFunc::EmitABI)
168    }
169
170    /// Get reserved slots in memory for storage calculations
171    pub fn reserved(&self) -> u32 {
172        let mut reserved = 0;
173        for host_fn in self.0.values() {
174            match *host_fn {
175                HostFunc::Label(CompilerLabel::ReserveMemory32) => reserved = 1,
176                HostFunc::Label(CompilerLabel::ReserveMemory64) => {
177                    return 2;
178                }
179                _ => {}
180            }
181        }
182
183        reserved
184    }
185}
186
187impl Exports {
188    /// Get all function selectors
189    pub fn selectors(&self) -> Vec<u32> {
190        self.iter()
191            .filter_map(|(index, export)| {
192                if export.ends_with("_selector") {
193                    Some(*index)
194                } else {
195                    None
196                }
197            })
198            .collect::<Vec<_>>()
199    }
200}