use biome_rowan::{Language, SyntaxElement, SyntaxNode};
use crate::{
BasicBlock, ControlFlowGraph, ExceptionHandler, ExceptionHandlerKind, Instruction,
InstructionKind,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct BlockId {
pub(crate) index: u32,
}
impl BlockId {
pub fn index(self) -> u32 {
self.index
}
}
pub const ROOT_BLOCK_ID: BlockId = BlockId { index: 0 };
pub struct FunctionBuilder<L: Language> {
result: ControlFlowGraph<L>,
exception_target: Vec<ExceptionHandler>,
block_cursor: BlockId,
}
impl<L: Language> FunctionBuilder<L> {
pub fn new(node: SyntaxNode<L>) -> Self {
Self {
result: ControlFlowGraph::new(node),
exception_target: Vec::new(),
block_cursor: BlockId { index: 0 },
}
}
pub fn finish(mut self) -> ControlFlowGraph<L> {
self.append_return();
self.result
}
pub fn append_block(&mut self) -> BlockId {
let index = self
.result
.blocks
.len()
.try_into()
.expect("BlockId overflow");
let mut has_catch_handler = false;
self.result.blocks.push(BasicBlock::new(
self.exception_target
.iter()
.rev()
.copied()
.take_while(|handler| {
let has_previous_catch = has_catch_handler;
has_catch_handler |= matches!(handler.kind, ExceptionHandlerKind::Catch);
!has_previous_catch
}),
self.exception_target
.iter()
.rev()
.filter_map(|handler| match handler.kind {
ExceptionHandlerKind::Finally => Some(*handler),
ExceptionHandlerKind::Catch => None,
}),
));
BlockId { index }
}
pub fn cursor(&self) -> BlockId {
self.block_cursor
}
pub fn set_cursor(&mut self, block: BlockId) {
debug_assert!(block.index < self.result.blocks.len() as u32);
self.block_cursor = block;
}
pub fn push_exception_target(&mut self, kind: ExceptionHandlerKind, target: BlockId) {
self.exception_target
.push(ExceptionHandler { kind, target });
}
pub fn pop_exception_target(&mut self) {
self.exception_target.pop();
}
fn append_instruction(&mut self, kind: InstructionKind) -> InstructionBuilder<L> {
let index = self.block_cursor.index as usize;
let block = &mut self.result.blocks[index];
let index = block.instructions.len();
block.instructions.push(Instruction { kind, node: None });
InstructionBuilder(&mut block.instructions[index])
}
pub fn append_statement(&mut self) -> InstructionBuilder<L> {
self.append_instruction(InstructionKind::Statement)
}
pub fn append_return(&mut self) -> InstructionBuilder<L> {
self.append_instruction(InstructionKind::Return)
}
pub fn append_jump(&mut self, conditional: bool, block: BlockId) -> InstructionBuilder<L> {
self.append_instruction(InstructionKind::Jump {
conditional,
block,
finally_fallthrough: false,
})
}
pub fn append_finally_fallthrough(&mut self, block: BlockId) -> InstructionBuilder<L> {
self.append_instruction(InstructionKind::Jump {
conditional: false,
block,
finally_fallthrough: true,
})
}
}
pub struct InstructionBuilder<'a, L: Language>(&'a mut Instruction<L>);
impl<'a, L: Language> InstructionBuilder<'a, L> {
pub fn with_node(self, node: impl Into<SyntaxElement<L>>) -> Self {
self.0.node = Some(node.into());
self
}
}