use crate::{parser::Parser, Artifact, Config, Error, Result};
use zabi::Abi;
use zingen::{
wasm::{self, Env},
Buffer, Dispatcher, Function, JumpTable, BUFFER_LIMIT,
};
#[derive(Default)]
pub struct Compiler {
pub(crate) abi: Vec<Abi>,
pub(crate) buffer: Buffer,
pub config: Config,
table: JumpTable,
}
impl Compiler {
pub fn new(config: Config) -> Self {
Self {
config,
..Default::default()
}
}
pub fn compile(mut self, wasm: &[u8]) -> Result<Artifact> {
let mut parser = Parser::try_from(wasm)?;
let env = parser.env.clone();
self.compile_dispatcher(&mut parser)?;
for func in parser.funcs.into_funcs() {
self.compile_func(env.with_index(func.index()), func)?;
}
self.table.code_offset(self.buffer.len() as u16);
self.table.relocate(&mut self.buffer)?;
self.artifact()
}
fn artifact(self) -> Result<Artifact> {
let Compiler {
abi,
buffer,
config,
..
} = self;
tracing::debug!("code length: {}", buffer.len());
Ok(Artifact {
abi,
config,
runtime_bytecode: buffer.to_vec(),
})
}
fn compile_dispatcher(&mut self, parser: &mut Parser) -> Result<()> {
let selectors = parser.drain_selectors();
let env = parser.env.clone();
if !self.config.dispatcher {
self.abi.append(&mut env.load_abis(&selectors)?);
return Ok(());
}
let mut dispatcher = Dispatcher::new(env, &parser.funcs)?;
let buffer = dispatcher.finish(selectors, &mut self.table)?;
self.buffer.extend_from_slice(&buffer);
if self.buffer.len() > BUFFER_LIMIT {
return Err(Error::BufferOverflow(self.buffer.len()));
}
self.abi.append(&mut dispatcher.abi);
Ok(())
}
fn compile_func(&mut self, env: Env, mut func: wasm::Function<'_>) -> Result<()> {
let func_index = func.index();
let sig = func.sig()?;
let abi = self.abi(&env, func_index);
tracing::debug!("compile function {func_index} {:?}, abi: {abi:#?}", sig);
let is_main = !self.config.dispatcher && env.is_main(func_index);
let mut codegen = Function::new(env, sig, abi, is_main)?;
let mut locals_reader = func.body.get_locals_reader()?;
let mut ops_reader = func.body.get_operators_reader()?;
codegen.emit_locals(&mut locals_reader, &mut func.validator)?;
codegen.emit_operators(&mut ops_reader, &mut func.validator)?;
self.emit_buffer(func_index, codegen)?;
Ok(())
}
fn emit_buffer(&mut self, func_index: u32, codegen: Function) -> Result<()> {
let buffer = codegen.finish(&mut self.table, self.buffer.len() as u16)?;
self.table
.call_offset(func_index, self.buffer.len() as u16)?;
self.buffer.extend_from_slice(&buffer);
if self.buffer.len() > BUFFER_LIMIT {
return Err(Error::BufferOverflow(buffer.len()));
}
Ok(())
}
fn abi(&self, env: &Env, index: u32) -> Option<Abi> {
let name = env.exports.get(&index)?;
self.abi.iter().find(|a| name == &a.name).cloned()
}
}