use crate::ir::*;
use crate::tombstone_arena::TombstoneArena;
use crate::{FunctionId, LocalFunction, Module, TypeId, ValType};
use crate::{ModuleFunctions, ModuleTypes};
use std::mem;
use std::ops::{Deref, DerefMut, Drop};
#[derive(Default, Debug)]
pub struct FunctionBuilder {
pub(crate) arena: TombstoneArena<Expr>,
}
impl FunctionBuilder {
pub fn new() -> FunctionBuilder {
FunctionBuilder::default()
}
pub(crate) fn alloc<T>(&mut self, val: T) -> T::Id
where
T: Ast,
{
let id = self.arena.alloc(val.into());
T::new_id(id)
}
pub fn block<'a>(
&'a mut self,
params: Box<[ValType]>,
results: Box<[ValType]>,
) -> BlockBuilder<'a> {
self.block_builder(Block {
kind: BlockKind::Block,
params,
results,
exprs: Vec::new(),
})
}
pub fn loop_<'a>(&'a mut self, results: Box<[ValType]>) -> BlockBuilder<'a> {
self.block_builder(Block {
kind: BlockKind::Block,
params: Vec::new().into_boxed_slice(),
results,
exprs: Vec::new(),
})
}
pub fn if_else_block<'a>(
&'a mut self,
params: Box<[ValType]>,
results: Box<[ValType]>,
) -> BlockBuilder<'a> {
self.block_builder(Block {
kind: BlockKind::IfElse,
params,
results,
exprs: Vec::new(),
})
}
fn block_builder<'a>(&'a mut self, block: Block) -> BlockBuilder<'a> {
let id = self.alloc(block);
BlockBuilder {
id,
builder: self,
exprs: Vec::new(),
}
}
pub fn i32_const(&mut self, val: i32) -> ExprId {
self.const_(Value::I32(val))
}
pub fn i64_const(&mut self, val: i64) -> ExprId {
self.const_(Value::I64(val))
}
pub fn f32_const(&mut self, val: f32) -> ExprId {
self.const_(Value::F32(val))
}
pub fn f64_const(&mut self, val: f64) -> ExprId {
self.const_(Value::F64(val))
}
pub fn finish(
self,
ty_id: TypeId,
args: Vec<LocalId>,
exprs: Vec<ExprId>,
module: &mut Module,
) -> FunctionId {
self.finish_parts(ty_id, args, exprs, &mut module.types, &mut module.funcs)
}
pub fn finish_parts(
mut self,
ty_id: TypeId,
args: Vec<LocalId>,
exprs: Vec<ExprId>,
types: &mut ModuleTypes,
funcs: &mut ModuleFunctions,
) -> FunctionId {
let ty = types.get(ty_id);
let entry = self.alloc(Block {
kind: BlockKind::FunctionEntry,
params: ty.params().to_vec().into_boxed_slice(),
results: ty.results().to_vec().into_boxed_slice(),
exprs,
});
let func = LocalFunction::new(ty_id, args, self, entry);
funcs.add_local(func)
}
}
#[derive(Debug)]
pub struct BlockBuilder<'a> {
id: BlockId,
builder: &'a mut FunctionBuilder,
exprs: Vec<ExprId>,
}
impl BlockBuilder<'_> {
pub fn expr(&mut self, expr: ExprId) {
self.exprs.push(expr);
}
pub fn id(&self) -> BlockId {
self.id
}
}
impl Deref for BlockBuilder<'_> {
type Target = FunctionBuilder;
fn deref(&self) -> &FunctionBuilder {
&*self.builder
}
}
impl DerefMut for BlockBuilder<'_> {
fn deref_mut(&mut self) -> &mut FunctionBuilder {
&mut *self.builder
}
}
impl Drop for BlockBuilder<'_> {
fn drop(&mut self) {
let exprs = mem::replace(&mut self.exprs, Vec::new());
let block = match &mut self.builder.arena[self.id.into()] {
Expr::Block(b) => b,
_ => unreachable!(),
};
block.exprs = exprs;
}
}