gbf_core/decompiler/
region.rs#![deny(missing_docs)]
use crate::cfg_dot::RenderableNode;
use crate::decompiler::ast::visitors::emit_context::{EmitContextBuilder, EmitVerbosity};
use crate::decompiler::ast::visitors::emitter::Gs2Emitter;
use crate::decompiler::ast::visitors::AstVisitor;
use crate::decompiler::ast::AstKind;
use crate::utils::GBF_YELLOW;
use serde::{Deserialize, Serialize};
use std::fmt::Write;
use std::slice::Iter;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Copy)]
pub enum RegionType {
Linear,
ControlFlow,
Tail,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Copy)]
pub struct RegionId {
pub index: usize,
pub region_type: RegionType,
}
impl RegionId {
pub fn new(index: usize, region_type: RegionType) -> Self {
Self { index, region_type }
}
}
#[derive(Debug, Clone)]
pub struct Region {
id: RegionId,
nodes: Vec<AstKind>,
}
impl Region {
pub fn new(id: RegionId) -> Self {
Self {
id,
nodes: Vec::new(),
}
}
pub fn region_type(&self) -> &RegionType {
&self.id.region_type
}
pub fn push_node(&mut self, node: AstKind) {
self.nodes.push(node);
}
pub fn iter_statements(&self) -> Iter<AstKind> {
self.nodes.iter()
}
}
impl<'a> IntoIterator for &'a Region {
type Item = &'a AstKind;
type IntoIter = Iter<'a, AstKind>;
fn into_iter(self) -> Self::IntoIter {
self.nodes.iter()
}
}
impl<'a> IntoIterator for &'a mut Region {
type Item = &'a mut AstKind;
type IntoIter = std::slice::IterMut<'a, AstKind>;
fn into_iter(self) -> Self::IntoIter {
self.nodes.iter_mut()
}
}
impl RenderableNode for Region {
fn render_node(&self, padding: usize) -> String {
let mut label = String::new();
let indent = " ".repeat(padding);
writeln!(
&mut label,
r#"{indent}<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="0">"#,
indent = indent
)
.unwrap();
for node in &self.nodes {
let context = EmitContextBuilder::default()
.verbosity(EmitVerbosity::Debug)
.include_ssa_versions(true)
.build();
let mut emitter = Gs2Emitter::new(context);
emitter.visit_node(node);
let result = emitter.output();
writeln!(
&mut label,
r##"{indent} <TR>
{indent} <TD ALIGN="LEFT"><FONT COLOR="{GBF_YELLOW}">{}</FONT></TD>
{indent} </TR>"##,
result,
indent = indent
)
.unwrap();
}
writeln!(&mut label, "{indent}</TABLE>", indent = indent).unwrap();
label
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::decompiler::ast::assignable::AssignableKind;
use crate::decompiler::ast::bin_op::{BinOpType, BinaryOperationNode};
use crate::decompiler::ast::expr::ExprKind;
use crate::decompiler::ast::identifier::IdentifierNode;
use crate::decompiler::ast::literal::LiteralNode;
use crate::decompiler::ast::statement::StatementNode;
fn create_identifier(id: &str) -> Box<AssignableKind> {
Box::new(AssignableKind::Identifier(IdentifierNode::new(
id.to_string(),
)))
}
fn create_integer_literal(value: i32) -> Box<ExprKind> {
Box::new(ExprKind::Literal(LiteralNode::Number(value)))
}
fn create_addition(lhs: Box<ExprKind>, rhs: Box<ExprKind>) -> Box<ExprKind> {
Box::new(ExprKind::BinOp(
BinaryOperationNode::new(lhs, rhs, BinOpType::Add).unwrap(),
))
}
fn create_subtraction(lhs: Box<ExprKind>, rhs: Box<ExprKind>) -> Box<ExprKind> {
Box::new(ExprKind::BinOp(
BinaryOperationNode::new(lhs, rhs, BinOpType::Sub).unwrap(),
))
}
fn create_statement(lhs: Box<AssignableKind>, rhs: Box<ExprKind>) -> StatementNode {
StatementNode::new(lhs, rhs).unwrap()
}
#[test]
fn test_region_creation_and_instruction_addition() {
let region_id = RegionId::new(0, RegionType::Linear);
let mut region = Region::new(region_id);
assert_eq!(region.region_type(), &RegionType::Linear);
assert_eq!(region.iter_statements().count(), 0);
let ast_node1 = create_statement(
create_identifier("x"),
create_addition(create_integer_literal(1), create_integer_literal(2)),
);
let ast_node2 = create_statement(
create_identifier("y"),
create_subtraction(create_integer_literal(3), create_integer_literal(4)),
);
region.push_node(AstKind::Statement(ast_node1.clone()));
region.push_node(AstKind::Statement(ast_node2.clone()));
let mut iter = region.iter_statements();
assert_eq!(iter.next(), Some(&AstKind::Statement(ast_node1)));
assert_eq!(iter.next(), Some(&AstKind::Statement(ast_node2)));
}
#[test]
fn test_region_into_iter() {
let region_id = RegionId::new(0, RegionType::Linear);
let region = Region::new(region_id);
let mut iter = region.into_iter();
assert_eq!(iter.next(), None);
}
}