diff --git a/gbf_core/src/decompiler/function_decompiler.rs b/gbf_core/src/decompiler/function_decompiler.rs index 3f899d4..0964ad0 100644 --- a/gbf_core/src/decompiler/function_decompiler.rs +++ b/gbf_core/src/decompiler/function_decompiler.rs @@ -16,7 +16,7 @@ use super::ast::expr::ExprKind; use super::ast::function::FunctionNode; use super::ast::visitors::emit_context::EmitContext; use super::ast::visitors::emitter::Gs2Emitter; -use super::ast::{AstKind, AstVisitable, new_array}; +use super::ast::{AstKind, AstVisitable, new_array, new_assignment, new_id_with_version}; use super::execution_frame::ExecutionFrame; use super::function_decompiler_context::FunctionDecompilerContext; use super::structure_analysis::region::{RegionId, RegionType}; @@ -284,7 +284,14 @@ impl FunctionDecompiler<'_> { &mut self, emit_context: EmitContext, ) -> Result { - self.process_regions()?; + { + self.process_regions()?; + } + + // Create SSA variables from leftover stack nodes + { + self.create_ssa_vars_from_stack()?; + } let entry_block_id = self.function.get_entry_basic_block().id; let entry_region_id = self.block_to_region.get(&entry_block_id).unwrap(); @@ -335,6 +342,61 @@ impl FunctionDecompiler<'_> { .cloned() } + /// Create unresolved SSA variables from leftover stack nodes + fn create_ssa_vars_from_stack(&mut self) -> Result<(), FunctionDecompilerError> { + for (block_id, region_id) in &self.block_to_region { + // Grab the execution_frame from context + let execution_frame = { + let ctx = self.context.as_ref().unwrap(); + ctx.block_ast_node_stack + .get(block_id) + .expect("[Bug] The block should have an AST node stack.") + // TODO: Cloning here is not ideal, but it's necessary to avoid borrowing issues. + .clone() + }; + + let region = self + .struct_analysis + .get_region_mut(*region_id) + .expect("[Bug] The region should exist."); + + // Get the leftover nodes from the execution frame + for frame in execution_frame.iter() { + let node = match frame { + // TODO: This should be handled differently. Technically, StandaloneNodes could be part of a + // BuildingArray in a predecessor block. E.g. arr = {one, two == 0 ? three : four, five}; + // In the above case, one will appear as a BuildingArray since it's the first node in the array. + // However, two will appear as a StandaloneNode since it's part of a ternary expression. + // Lastly, five will appear as a standalone node, but this is handled in a previous step that assumes + // EndArray is the last node in the array. + ExecutionFrame::BuildingArray(arr) => new_array::(arr.clone()).into(), + ExecutionFrame::StandaloneNode(node) => { + if let AstKind::Expression(expr) = node { + expr.clone() + } else { + let ctx = self.context.as_ref().unwrap(); + return Err(FunctionDecompilerError::UnexpectedNodeType { + expected: "Expression".to_string(), + context: ctx.get_error_context(), + backtrace: Backtrace::capture(), + }); + } + } + }; + + // Create a new SSA variable for the leftover node + let ssa_context = &mut self.context.as_mut().unwrap().ssa_context; + let var = ssa_context.new_ssa_version_for("leftover"); + let ssa_id = new_id_with_version("leftover", var); + let stmt = new_assignment(ssa_id.clone(), node); + + region.push_node(stmt.into()); + region.push_unresolved_node(ssa_id.into()); + } + } + Ok(()) + } + fn generate_regions(&mut self) -> Result<(), FunctionDecompilerError> { for block in self.function.iter() { // If the block is the end of the module, it is a tail region diff --git a/gbf_core/src/decompiler/structure_analysis/mod.rs b/gbf_core/src/decompiler/structure_analysis/mod.rs index f209c5a..4a32ce8 100644 --- a/gbf_core/src/decompiler/structure_analysis/mod.rs +++ b/gbf_core/src/decompiler/structure_analysis/mod.rs @@ -741,6 +741,24 @@ impl NodeResolver for StructureAnalysis { } } +impl<'a> IntoIterator for &'a StructureAnalysis { + type Item = &'a Region; + type IntoIter = std::slice::Iter<'a, Region>; + + fn into_iter(self) -> Self::IntoIter { + self.regions.iter() + } +} + +impl<'a> IntoIterator for &'a mut StructureAnalysis { + type Item = &'a mut Region; + type IntoIter = std::slice::IterMut<'a, Region>; + + fn into_iter(self) -> Self::IntoIter { + self.regions.iter_mut() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/gbf_core/tests/test_ast_construction.rs b/gbf_core/tests/test_ast_construction.rs index de44b71..4c2dad4 100644 --- a/gbf_core/tests/test_ast_construction.rs +++ b/gbf_core/tests/test_ast_construction.rs @@ -1,32 +1,18 @@ -use common::{load_bytecode, load_expected_output}; -use gbf_core::decompiler::{ - ast::visitors::emit_context::EmitContext, function_decompiler::FunctionDecompilerBuilder, -}; +use common::{get_all_bytecode_files, load_bytecode}; +use gbf_core::decompiler::ast::visitors::emit_context::EmitContext; pub mod common; #[test] -fn load_simple_gs2() { - let reader = load_bytecode("simple.gs2bc").unwrap(); - let _expected = load_expected_output("simple.gs2") - .unwrap() - .trim() - .to_string(); +fn test_all_decompile() { + for fname in get_all_bytecode_files().unwrap().iter() { + let fname = fname.to_string(); + let reader = load_bytecode(&fname).unwrap(); + let module = gbf_core::module::ModuleBuilder::new() + .name(fname.clone()) + .reader(Box::new(reader)) + .build() + .unwrap(); - let module = gbf_core::module::ModuleBuilder::new() - .name("simple.gs2".to_string()) - .reader(Box::new(reader)) - .build() - .unwrap(); - - // Get the entry function - let entry_function = module.get_entry_function(); - - // Decompile the entry function - let mut decompiler = FunctionDecompilerBuilder::new(entry_function).build(); - let decompiled = decompiler.decompile(EmitContext::default()); - - // For now, assert that the decompiler did not fail - // TODO: We need to update the test to compare the decompiled output with the expected output - // once the decompiler is more stable. - assert!(decompiled.is_ok()); + assert!(module.decompile(EmitContext::default()).is_ok()); + } }