gbf_core/decompiler/
function_decompiler_context.rs

1#![deny(missing_docs)]
2
3use crate::basic_block::BasicBlockId;
4use crate::instruction::Instruction;
5use crate::opcode::Opcode;
6use std::backtrace::Backtrace;
7use std::collections::HashMap;
8
9use super::ast::array_kind::ArrayKind;
10use super::ast::array_node::ArrayNode;
11use super::ast::expr::ExprKind;
12use super::ast::identifier::IdentifierNode;
13use super::ast::phi::PhiNode;
14use super::ast::phi_array::PhiArrayNode;
15use super::ast::ptr::P;
16use super::ast::ssa::SsaContext;
17use super::ast::{AstKind, new_phi};
18use super::execution_frame::ExecutionFrame;
19use super::function_decompiler::{FunctionDecompilerError, FunctionDecompilerErrorContext};
20use super::handlers::{OpcodeHandler, global_opcode_handlers};
21use super::{ProcessedInstruction, ProcessedInstructionBuilder};
22
23/// Manages the state of the decompiler, including per-block AST stacks and current processing context.
24pub struct FunctionDecompilerContext {
25    /// AST node stacks for each basic block.
26    pub block_ast_node_stack: HashMap<BasicBlockId, Vec<ExecutionFrame>>,
27    /// The number of phi nodes in each basic block.
28    pub block_ast_phi_count: HashMap<BasicBlockId, usize>,
29    /// The current basic block being processed.
30    pub current_block_id: BasicBlockId,
31    /// The handlers for each opcode.
32    pub opcode_handlers: HashMap<Opcode, Box<dyn OpcodeHandler>>,
33    /// The SSA Context
34    pub ssa_context: SsaContext,
35    /// The current instruction being processed.
36    pub current_instruction: Instruction,
37    /// Register mapping for the current function
38    pub register_mapping: HashMap<usize, ExprKind>,
39}
40
41impl FunctionDecompilerContext {
42    /// Creates a new, empty context. We initialize with the starting block ID and region ID.
43    pub fn new(start_block_id: BasicBlockId) -> Self {
44        Self {
45            block_ast_node_stack: HashMap::new(),
46            current_block_id: start_block_id,
47            opcode_handlers: HashMap::new(),
48            ssa_context: SsaContext::new(),
49            current_instruction: Instruction::default(),
50            register_mapping: HashMap::new(),
51            block_ast_phi_count: HashMap::new(),
52        }
53    }
54
55    /// Starts processing a new basic block.
56    ///
57    /// # Arguments
58    /// - `block_id`: The ID of the basic block to start processing.
59    /// - `region_id`: The ID of the region the basic block belongs to.
60    ///
61    /// # Errors
62    /// - Returns `FunctionDecompilerError` if there is an issue initializing the block stack.
63    pub fn start_block_processing(
64        &mut self,
65        block_id: BasicBlockId,
66    ) -> Result<(), FunctionDecompilerError> {
67        self.current_block_id = block_id;
68        self.block_ast_node_stack.insert(block_id, Vec::new());
69        Ok(())
70    }
71
72    /// Processes an instruction and updates the AST stack.
73    ///
74    /// # Arguments
75    /// - `instr`: The instruction to process.
76    ///
77    /// # Errors
78    /// - Returns `FunctionDecompilerError` for invalid or unexpected instructions.
79    pub fn process_instruction(
80        &mut self,
81        instr: &Instruction,
82    ) -> Result<ProcessedInstruction, FunctionDecompilerError> {
83        self.current_instruction = instr.clone();
84
85        let current_block_id = self.current_block_id;
86
87        // TODO: Better handle PushArray
88        if instr.opcode == Opcode::PushArray {
89            let stack = self
90                .block_ast_node_stack
91                .get_mut(&current_block_id)
92                .expect("Critical error: stack should always be set for each basic block");
93
94            stack.push(ExecutionFrame::BuildingArray(Vec::new()));
95            return Ok(ProcessedInstructionBuilder::new().build());
96        }
97        // TODO: Better handle swap
98        if instr.opcode == Opcode::Swap {
99            let error_context = self.get_error_context();
100
101            let last = {
102                let stack = self
103                    .block_ast_node_stack
104                    .get_mut(&current_block_id)
105                    .expect("Critical error: stack should always be set for each basic block");
106                stack.pop()
107            }
108            .unwrap_or_else(|| {
109                ExecutionFrame::StandaloneNode(AstKind::Expression(self.new_phi().into()))
110            });
111
112            let second_last = {
113                let stack = self
114                    .block_ast_node_stack
115                    .get_mut(&current_block_id)
116                    .expect("Critical error: stack should always be set for each basic block");
117                stack.pop()
118            }
119            .unwrap_or_else(|| {
120                ExecutionFrame::StandaloneNode(AstKind::Expression(self.new_phi().into()))
121            });
122
123            let stack = self
124                .block_ast_node_stack
125                .get_mut(&current_block_id)
126                .expect("Critical error: stack should always be set for each basic block");
127
128            // If `last` is a BuildingArray, merge it with `second_last`. Otherwise,
129            // push both back onto the stack.
130
131            match last {
132                ExecutionFrame::BuildingArray(mut last_array) => match second_last {
133                    ExecutionFrame::BuildingArray(mut second_last_array) => {
134                        last_array.append(&mut second_last_array);
135                        stack.push(ExecutionFrame::BuildingArray(last_array));
136                    }
137                    ExecutionFrame::StandaloneNode(n) => {
138                        // Convert n from AstKind to ExprKind
139                        let expr = match n {
140                            AstKind::Expression(e) => e,
141                            _ => {
142                                return Err(FunctionDecompilerError::UnexpectedNodeType {
143                                    expected: "Expression".to_string(),
144                                    context: error_context,
145                                    backtrace: Backtrace::capture(),
146                                });
147                            }
148                        };
149                        last_array.push(expr);
150                        stack.push(ExecutionFrame::BuildingArray(last_array));
151                    }
152                },
153                _ => {
154                    stack.push(last);
155                    stack.push(second_last);
156                }
157            }
158
159            return Ok(ProcessedInstructionBuilder::new().build());
160        }
161
162        let handlers = global_opcode_handlers();
163
164        let handler =
165            handlers
166                .get(&instr.opcode)
167                .ok_or(FunctionDecompilerError::UnimplementedOpcode {
168                    opcode: instr.opcode,
169                    context: self.get_error_context(),
170                    backtrace: Backtrace::capture(),
171                })?;
172
173        // Handle the instruction
174        // TODO: Since we have the instruction in the context, we may delete it from the
175        // TODO: arguments to avoid passing it around everywhere
176        let op = handler.handle_instruction(self, instr)?;
177
178        // Push the SSA ID onto the stack if it exists
179        if let Some(ssa_id) = &op.ssa_id {
180            self.push_one_node(ssa_id.clone().into())?;
181        }
182
183        Ok(op)
184    }
185
186    /// If an error happens, this helper function will return the context of the error.
187    pub fn get_error_context(&self) -> Box<FunctionDecompilerErrorContext> {
188        Box::new(FunctionDecompilerErrorContext {
189            current_block_id: self.current_block_id,
190            current_instruction: self.current_instruction.clone(),
191            current_ast_node_stack: self
192                .block_ast_node_stack
193                .get(&self.current_block_id)
194                .unwrap_or(&Vec::new())
195                .to_vec(),
196        })
197    }
198
199    /// Retrieves the AST stack for a basic block.
200    pub fn get_stack(&self, block_id: &BasicBlockId) -> Option<&Vec<ExecutionFrame>> {
201        self.block_ast_node_stack.get(block_id)
202    }
203
204    /// Create a new phi node, increment the phi count for the current block, and return the new phi node.
205    pub fn new_phi(&mut self) -> PhiNode {
206        let block_id = self.current_block_id;
207        let phi_count = *self.block_ast_phi_count.entry(block_id).or_insert(0);
208        let phi = new_phi(phi_count);
209        self.block_ast_phi_count.insert(block_id, phi_count + 1);
210        phi
211    }
212
213    /// Pops an array from the current basic block's stack and returns it as an ArrayKind.
214    /// - If no frame is available, returns a PhiArray with empty elements.
215    /// - If the last frame is a BuildingArray, returns it as a MergedArray.
216    /// - If the last frame is a StandaloneNode, pops additional nodes from the stack
217    ///   (both StandaloneNode and BuildingArray frames) and merges them into a PhiArray
218    ///   with a newly created phi node.
219    pub fn pop_building_array(&mut self) -> Result<ArrayKind, FunctionDecompilerError> {
220        let block_id = self.current_block_id;
221        let frame_opt = {
222            // Scope the mutable borrow so that it ends after pop.
223            let stack = self
224                .block_ast_node_stack
225                .get_mut(&block_id)
226                .expect("Critical error: stack should always be set for each basic block");
227            stack.pop()
228        };
229
230        match frame_opt {
231            // No frame in the current block: return an empty PhiArray.
232            None => {
233                let phi = self.new_phi();
234                Ok(ArrayKind::PhiArray(
235                    PhiArrayNode::new(phi.into(), Vec::new()).into(),
236                ))
237            }
238            Some(frame) => match frame {
239                ExecutionFrame::BuildingArray(array) => {
240                    // If we popped a building array, return it as a merged array.
241                    let rev: Vec<ExprKind> = array.into_iter().rev().collect();
242                    Ok(ArrayKind::MergedArray(ArrayNode::new(rev).into()))
243                }
244                ExecutionFrame::StandaloneNode(node) => {
245                    // Convert node to ExprKind or return an error
246                    let node = match node {
247                        AstKind::Expression(e) => e,
248                        _ => {
249                            return Err(FunctionDecompilerError::UnexpectedNodeType {
250                                expected: "Expression".to_string(),
251                                context: self.get_error_context(),
252                                backtrace: Backtrace::capture(),
253                            });
254                        }
255                    };
256                    // We encountered a standalone node, which indicates that
257                    // the array’s construction started in a predecessor block.
258                    // To merge the values, collect this node and any other nodes
259                    // currently on the stack.
260                    let mut nodes: Vec<ExprKind> = vec![node];
261
262                    // Continue popping nodes until the stack is exhausted or we hit a boundary.
263                    {
264                        let stack = self.block_ast_node_stack.get_mut(&block_id).expect(
265                            "Critical error: stack should always be set for each basic block",
266                        );
267                        while let Some(frame) = stack.pop() {
268                            match frame {
269                                ExecutionFrame::StandaloneNode(n) => {
270                                    // Convert n from AstKind to ExprKind, or return an error
271                                    let expr = match n {
272                                        AstKind::Expression(e) => e,
273                                        _ => {
274                                            return Err(
275                                                FunctionDecompilerError::UnexpectedNodeType {
276                                                    expected: "Expression".to_string(),
277                                                    context: self.get_error_context(),
278                                                    backtrace: Backtrace::capture(),
279                                                },
280                                            );
281                                        }
282                                    };
283                                    nodes.push(expr);
284                                }
285                                ExecutionFrame::BuildingArray(_) => {
286                                    return Err(FunctionDecompilerError::UnexpectedNodeType {
287                                        expected: "StandaloneNode".to_string(),
288                                        context: self.get_error_context(),
289                                        backtrace: Backtrace::capture(),
290                                    });
291                                }
292                            }
293                        }
294                    }
295
296                    // Create a new phi node to merge these values.
297                    let phi = self.new_phi();
298                    Ok(ArrayKind::PhiArray(
299                        PhiArrayNode::new(phi.into(), nodes).into(),
300                    ))
301                }
302            },
303        }
304    }
305
306    /// Pops an AST node from the current basic block's stack.
307    pub fn pop_one_node(&mut self) -> Result<AstKind, FunctionDecompilerError> {
308        let block_id = self.current_block_id;
309        let frame_opt = {
310            let stack = self
311                .block_ast_node_stack
312                .get_mut(&block_id)
313                .expect("Critical error: stack should always be set for each basic block");
314            stack.pop()
315        };
316
317        // If there's not a frame to pop, return a new phi node.
318        let mut last_frame = match frame_opt {
319            Some(frame) => frame,
320            None => return Ok(AstKind::Expression(self.new_phi().into())),
321        };
322
323        let result = match &mut last_frame {
324            ExecutionFrame::BuildingArray(array) => {
325                // Pop the node from the array
326                if let Some(node) = array.pop() {
327                    Ok(AstKind::Expression(node))
328                } else {
329                    Ok(AstKind::Expression(self.new_phi().into()))
330                }
331            }
332            ExecutionFrame::StandaloneNode(node) => Ok(node.clone()),
333        };
334
335        // Push the last frame back onto the stack, even if it's empty
336        if let ExecutionFrame::BuildingArray(_) = last_frame {
337            let stack = self
338                .block_ast_node_stack
339                .get_mut(&block_id)
340                .expect("Critical error: stack should always be set for each basic block");
341            stack.push(last_frame);
342        }
343
344        result
345    }
346
347    /// Pops an expression from the current basic block's stack.
348    pub fn pop_expression(&mut self) -> Result<ExprKind, FunctionDecompilerError> {
349        let node = self.pop_one_node()?;
350        match node {
351            AstKind::Expression(expr) => Ok(expr.clone()),
352            _ => Err(FunctionDecompilerError::UnexpectedNodeType {
353                expected: "Expression".to_string(),
354                context: self.get_error_context(),
355                backtrace: Backtrace::capture(),
356            }),
357        }
358    }
359
360    /// Pops an identifier from the current basic block's stack.
361    pub fn pop_identifier(&mut self) -> Result<P<IdentifierNode>, FunctionDecompilerError> {
362        let node = self.pop_expression()?;
363
364        match node {
365            ExprKind::Identifier(ident) => Ok(ident.clone()),
366            _ => Err(FunctionDecompilerError::UnexpectedNodeType {
367                expected: "Identifier".to_string(),
368                context: self.get_error_context(),
369                backtrace: Backtrace::capture(),
370            }),
371        }
372    }
373
374    /// Pushes an AST node to the current basic block's stack.
375    pub fn push_one_node(&mut self, node: AstKind) -> Result<(), FunctionDecompilerError> {
376        let block_id = self.current_block_id;
377
378        let stack = self
379            .block_ast_node_stack
380            .get_mut(&block_id)
381            .expect("Critical error: stack should always be set for each basic block");
382
383        // Check if we're in a frame that requires special handling
384        if let Some(last_frame) = stack.last_mut() {
385            match last_frame {
386                ExecutionFrame::BuildingArray(array) => {
387                    // Ensure the node is an expression before adding to the array
388                    if let AstKind::Expression(expr) = node {
389                        array.push(expr.clone());
390                        return Ok(());
391                    } else {
392                        return Err(FunctionDecompilerError::UnexpectedNodeType {
393                            expected: "Expression".to_string(),
394                            context: self.get_error_context(),
395                            backtrace: Backtrace::capture(),
396                        });
397                    }
398                }
399                ExecutionFrame::StandaloneNode(_) => {
400                    // Do nothing; let it fall through to the standalone push below
401                }
402            }
403        }
404
405        // If no special frame handling is required, push the node directly onto the stack
406        stack.push(ExecutionFrame::StandaloneNode(node));
407        Ok(())
408    }
409}