zinkc/
compiler.rs

1//! Zink compiler
2
3use crate::{parser::Parser, Artifact, Config, Error, Result};
4use zabi::Abi;
5use zingen::{
6    wasm::{self, Env},
7    Buffer, Dispatcher, Function, JumpTable, BUFFER_LIMIT,
8};
9
10/// Zink Compiler
11#[derive(Default)]
12pub struct Compiler {
13    /// ABIs of the compiled contract.
14    pub(crate) abi: Vec<Abi>,
15    /// EVM bytecode buffer.
16    pub(crate) buffer: Buffer,
17    /// Compiler configuration.
18    pub config: Config,
19    /// Global jump table.
20    table: JumpTable,
21}
22
23impl Compiler {
24    /// Create a new compiler from config.
25    pub fn new(config: Config) -> Self {
26        Self {
27            config,
28            ..Default::default()
29        }
30    }
31
32    /// Compile wasm module to evm bytecode.
33    ///
34    /// Returns runtime bytecode.
35    pub fn compile(mut self, wasm: &[u8]) -> Result<Artifact> {
36        let mut parser = Parser::try_from(wasm)?;
37        let env = parser.env.clone();
38
39        self.compile_dispatcher(&mut parser)?;
40        for func in parser.funcs.into_funcs() {
41            self.compile_func(env.with_index(func.index()), func)?;
42        }
43
44        self.table.code_offset(self.buffer.len() as u16);
45        self.table.relocate(&mut self.buffer)?;
46        self.artifact()
47    }
48
49    /// Generate artifact
50    ///
51    /// yields runtime bytecode and construct bytecode
52    fn artifact(self) -> Result<Artifact> {
53        let Compiler {
54            abi,
55            buffer,
56            config,
57            ..
58        } = self;
59
60        tracing::debug!("code length: {}", buffer.len());
61        Ok(Artifact {
62            abi,
63            config,
64            runtime_bytecode: buffer.to_vec(),
65        })
66    }
67
68    /// Compile EVM dispatcher.
69    ///
70    /// Drain selectors anyway, compile dispatcher if it is enabled.
71    fn compile_dispatcher(&mut self, parser: &mut Parser) -> Result<()> {
72        let selectors = parser.drain_selectors();
73        let env = parser.env.clone();
74
75        if !self.config.dispatcher {
76            self.abi.append(&mut env.load_abis(&selectors)?);
77            return Ok(());
78        }
79
80        let mut dispatcher = Dispatcher::new(env, &parser.funcs)?;
81        let buffer = dispatcher.finish(selectors, &mut self.table)?;
82        self.buffer.extend_from_slice(&buffer);
83        if self.buffer.len() > BUFFER_LIMIT {
84            return Err(Error::BufferOverflow(self.buffer.len()));
85        }
86
87        self.abi.append(&mut dispatcher.abi);
88        Ok(())
89    }
90
91    /// Compile WASM function.
92    fn compile_func(&mut self, env: Env, mut func: wasm::Function<'_>) -> Result<()> {
93        let func_index = func.index();
94        let sig = func.sig()?;
95        let abi = self.abi(&env, func_index);
96
97        tracing::debug!("compile function {func_index} {:?}, abi: {abi:#?}", sig);
98        let is_main = !self.config.dispatcher && env.is_main(func_index);
99
100        let mut codegen = Function::new(env, sig, abi, is_main)?;
101        let mut locals_reader = func.body.get_locals_reader()?;
102        let mut ops_reader = func.body.get_operators_reader()?;
103
104        codegen.emit_locals(&mut locals_reader, &mut func.validator)?;
105        codegen.emit_operators(&mut ops_reader, &mut func.validator)?;
106
107        self.emit_buffer(func_index, codegen)?;
108        Ok(())
109    }
110
111    /// Emit buffer to the inner buffer.
112    fn emit_buffer(&mut self, func_index: u32, codegen: Function) -> Result<()> {
113        let buffer = codegen.finish(&mut self.table, self.buffer.len() as u16)?;
114        self.table
115            .call_offset(func_index, self.buffer.len() as u16)?;
116        self.buffer.extend_from_slice(&buffer);
117
118        if self.buffer.len() > BUFFER_LIMIT {
119            return Err(Error::BufferOverflow(buffer.len()));
120        }
121
122        Ok(())
123    }
124
125    /// Get abi from env and function index
126    fn abi(&self, env: &Env, index: u32) -> Option<Abi> {
127        let name = env.exports.get(&index)?;
128        self.abi.iter().find(|a| name == &a.name).cloned()
129    }
130}