use std::{rc::Rc, vec};
use dfwasm_template::{split_templates, Args, Block, Item, Template};
use wasmparser::{ConstExpr, MemoryType, Operator, Parser, RecGroup};
use crate::{
df_helper::{format_df_number_u64, var, DF_VAR_MEM_SIZE},
DFWasmError, DFWasmResult,
};
use super::{
df_helper::{format_df_number_i64, num, DF_FUNC_CALL_FUNC},
sections::compile_section,
};
#[derive(Debug, Clone, PartialEq)]
pub(crate) enum ControlStackEntry {
FunctionStart(usize),
Block(usize),
Loop(usize),
If(usize),
Else(usize),
}
#[derive(Default, Debug, Clone)]
pub struct DFWasmCompilerOptions {
pub module_name: Option<String>,
pub debugger: bool,
pub skip_nop_debugger: bool,
pub max_template_size: Option<usize>,
pub batch_data_size: Option<usize>,
pub only_include_module_init: bool,
}
pub struct DFWasmCompiler<'a> {
pub wasm: &'a [u8],
pub options: DFWasmCompilerOptions,
pub(crate) templates: Vec<Template>,
pub(crate) memory_type: Option<MemoryType>,
pub(crate) start_method: Option<usize>,
pub(crate) control_stack: Vec<ControlStackEntry>,
pub(crate) table_to_init_expr: Vec<Item>,
pub(crate) function_counter: usize,
pub(crate) table_counter: usize,
pub(crate) function_to_type_signature: Vec<Rc<RecGroup>>,
pub(crate) function_signatures: Vec<Rc<RecGroup>>,
}
impl<'a> DFWasmCompiler<'a> {
pub fn compile_wasm(
wasm: &'a [u8],
options: DFWasmCompilerOptions,
) -> DFWasmResult<Vec<Template>> {
let this = Self {
wasm,
options,
templates: Vec::new(),
memory_type: None,
start_method: None,
control_stack: Vec::new(),
table_to_init_expr: Vec::new(),
function_counter: 0,
table_counter: 0,
function_to_type_signature: Vec::new(),
function_signatures: Vec::new(),
};
this.compile()
}
fn compile(mut self) -> DFWasmResult<Vec<Template>> {
let parser = Parser::new(0);
let module_template_name = match &self.options.module_name {
Some(name) => format!("wasm.{}.init", name),
None => "wasm.module.init".to_string(),
};
let mut module_template = Template::start_function(module_template_name.clone());
for section in parser.parse_all(self.wasm) {
let section = section?;
compile_section(&mut self, &mut module_template, section)?;
}
if let Some(start_method) = self.start_method {
module_template.blocks.push(Block::CallFunction {
args: Args::with(vec![num(start_method), num(0), num(0)]),
func: DF_FUNC_CALL_FUNC.to_string(),
});
}
if let Some(memory_type) = self.memory_type {
module_template.set_var(
"=",
Args::with(vec![
var(DF_VAR_MEM_SIZE),
num(format_df_number_u64(memory_type.initial)),
]),
);
}
if self.options.only_include_module_init {
self.templates.clear();
}
self.templates.push(module_template);
if let Some(max_template_size) = self.options.max_template_size {
Ok(split_templates(self.templates, max_template_size))
} else {
Ok(self.templates)
}
}
pub(crate) fn get_current_template_idx(&self) -> Option<usize> {
self.control_stack.last().map(|entry| match entry {
ControlStackEntry::FunctionStart(template_idx)
| ControlStackEntry::Block(template_idx)
| ControlStackEntry::Loop(template_idx)
| ControlStackEntry::If(template_idx)
| ControlStackEntry::Else(template_idx) => *template_idx,
})
}
pub(crate) fn get_current_template(&mut self) -> &mut Template {
let template_idx = self.get_current_template_idx().expect("No template");
self.templates
.get_mut(template_idx)
.expect("Template not found")
}
pub(crate) fn eval_const_expr(&self, expr: &ConstExpr) -> DFWasmResult<Item> {
let mut reader = expr.get_operators_reader();
let op = reader.read()?;
let result = match op {
Operator::I32Const { value } => num(format_df_number_i64(value as i64)),
Operator::I64Const { value } => num(format_df_number_i64(value)),
Operator::F32Const { value: _ } => todo!("float constant expressions"),
Operator::F64Const { value: _ } => todo!("float constant expressions"),
Operator::RefNull { hty: _ } => num(-1),
Operator::RefFunc { function_index } => num(function_index),
Operator::GlobalGet { global_index: _ } => todo!("global get within a const expr"),
_ => return Err(DFWasmError::UnsupportedConstExpr),
};
if reader.eof() {
Ok(result)
} else {
match reader.read()? {
Operator::End => Ok(result),
_ => Err(DFWasmError::UnsupportedConstExpr),
}
}
}
pub(crate) fn eval_const_expr_as_offset(&self, expr: &ConstExpr) -> DFWasmResult<usize> {
let mut reader = expr.get_operators_reader();
let op = reader.read()?;
let result = match op {
Operator::I32Const { value } => usize::from_le_bytes((value as i64).to_le_bytes()),
Operator::I64Const { value } => usize::from_le_bytes(value.to_le_bytes()),
Operator::GlobalGet { global_index: _ } => todo!("global get within a const expr"),
_ => return Err(DFWasmError::UnsupportedConstExpr),
};
if reader.eof() {
Ok(result)
} else {
match reader.read()? {
Operator::End => Ok(result),
_ => Err(DFWasmError::UnsupportedConstExpr),
}
}
}
pub(crate) fn arg_count_of_type(signature: &RecGroup) -> Option<usize> {
Some(signature.types().next()?.unwrap_func().params().len())
}
pub(crate) fn result_count_of_type(signature: &RecGroup) -> Option<usize> {
Some(signature.types().next()?.unwrap_func().results().len())
}
}