gbf_core/decompiler/handlers/
special_one_operand.rs

1#![deny(missing_docs)]
2
3use std::backtrace::Backtrace;
4
5use crate::{
6    decompiler::{
7        ProcessedInstruction, ProcessedInstructionBuilder,
8        ast::{
9            bin_op::BinOpType, expr::ExprKind, new_assignment, new_bin_op, new_id_with_version,
10            new_num, new_return, new_uninitialized_array,
11        },
12        function_decompiler::FunctionDecompilerError,
13        function_decompiler_context::FunctionDecompilerContext,
14    },
15    instruction::Instruction,
16    opcode::Opcode,
17};
18
19use super::OpcodeHandler;
20
21/// Handles other instructions.
22pub struct SpecialOneOperandHandler;
23
24impl OpcodeHandler for SpecialOneOperandHandler {
25    fn handle_instruction(
26        &self,
27        context: &mut FunctionDecompilerContext,
28        instruction: &Instruction,
29    ) -> Result<ProcessedInstruction, FunctionDecompilerError> {
30        match instruction.opcode {
31            Opcode::Ret => {
32                let ret_val = context.pop_expression()?;
33
34                let ret = new_return(ret_val);
35                Ok(ProcessedInstructionBuilder::new()
36                    .push_to_region(ret.into())
37                    .build())
38            }
39            Opcode::Copy => {
40                let operand = context.pop_expression()?;
41                context.push_one_node(operand.clone().into())?;
42                context.push_one_node(operand.clone().into())?;
43                Ok(ProcessedInstructionBuilder::new().build())
44            }
45            Opcode::GetRegister => {
46                let register_id = instruction
47                    .operand
48                    .as_ref()
49                    .ok_or(FunctionDecompilerError::InstructionMustHaveOperand {
50                        opcode: instruction.opcode,
51                        context: context.get_error_context(),
52                        backtrace: Backtrace::capture(),
53                    })?
54                    .get_number_value()
55                    .map_err(|e| FunctionDecompilerError::OperandError {
56                        source: e,
57                        context: context.get_error_context(),
58                        backtrace: Backtrace::capture(),
59                    })?;
60
61                let ssa_id = context
62                    .register_mapping
63                    .get(&(register_id as usize))
64                    .ok_or(FunctionDecompilerError::RegisterNotFound {
65                        register_id: register_id as usize,
66                        context: context.get_error_context(),
67                        backtrace: Backtrace::capture(),
68                    })?;
69                context.push_one_node(ssa_id.clone().into())?;
70                Ok(ProcessedInstructionBuilder::new().build())
71            }
72            Opcode::SetRegister => {
73                let register_id = instruction
74                    .operand
75                    .as_ref()
76                    .ok_or(FunctionDecompilerError::InstructionMustHaveOperand {
77                        opcode: instruction.opcode,
78                        context: context.get_error_context(),
79                        backtrace: Backtrace::capture(),
80                    })?
81                    .get_number_value()
82                    .map_err(|e| FunctionDecompilerError::OperandError {
83                        source: e,
84                        context: context.get_error_context(),
85                        backtrace: Backtrace::capture(),
86                    })?;
87
88                let register_store = context.pop_expression()?;
89
90                // If register_store is an identifier, we can use it directly
91                let (register_map_add, processed_instruction): (ExprKind, ProcessedInstruction) =
92                    match register_store.clone() {
93                        ExprKind::Identifier(assignable) => (
94                            assignable.clone().into(),
95                            ProcessedInstructionBuilder::new().build(),
96                        ),
97                        ExprKind::MemberAccess(member_access) => (
98                            member_access.clone().into(),
99                            ProcessedInstructionBuilder::new().build(),
100                        ),
101                        _ => {
102                            let var = context.ssa_context.new_ssa_version_for("set_register");
103                            let ssa_id = new_id_with_version("set_register", var);
104                            let stmt = new_assignment(ssa_id.clone(), register_store.clone());
105                            (
106                                ssa_id.clone().into(),
107                                ProcessedInstructionBuilder::new()
108                                    .push_to_region(stmt.into())
109                                    .ssa_id(ssa_id.into())
110                                    .build(),
111                            )
112                        }
113                    };
114
115                // push to the stack
116                context.push_one_node(register_store.clone().into())?;
117
118                context
119                    .register_mapping
120                    .insert(register_id as usize, register_map_add);
121
122                Ok(processed_instruction)
123            }
124            Opcode::Inc => {
125                // Pop the last assignable from the stack, create AST node for assignment + 1, push it back to the stack
126                let expr = context.pop_expression()?;
127                let bin_op = new_bin_op(expr.clone(), new_num(1), BinOpType::Add).map_err(|e| {
128                    FunctionDecompilerError::AstNodeError {
129                        source: e,
130                        context: context.get_error_context(),
131                        backtrace: Backtrace::capture(),
132                    }
133                })?;
134
135                // an assignment bumps the version of the lhs
136                let mut lhs = expr;
137                if let ExprKind::Identifier(mut id) = lhs {
138                    let ver = context.ssa_context.new_ssa_version_for(id.id());
139                    id.ssa_version = Some(ver);
140                    lhs = id.clone().into();
141                }
142                let stmt = new_assignment(lhs.clone(), bin_op);
143
144                context.push_one_node(lhs.clone().into())?;
145
146                Ok(ProcessedInstructionBuilder::new()
147                    .push_to_region(stmt.into())
148                    .build())
149            }
150            Opcode::Dec => {
151                // Pop the last assignable from the stack, create AST node for assignment + 1, push it back to the stack
152                let expr = context.pop_expression()?;
153                let bin_op = new_bin_op(expr.clone(), new_num(1), BinOpType::Sub).map_err(|e| {
154                    FunctionDecompilerError::AstNodeError {
155                        source: e,
156                        context: context.get_error_context(),
157                        backtrace: Backtrace::capture(),
158                    }
159                })?;
160
161                // an assignment bumps the version of the lhs
162                let mut lhs = expr;
163                if let ExprKind::Identifier(mut id) = lhs {
164                    let ver = context.ssa_context.new_ssa_version_for(id.id());
165                    id.ssa_version = Some(ver);
166                    lhs = id.clone().into();
167                }
168                let stmt = new_assignment(lhs.clone(), bin_op);
169
170                context.push_one_node(lhs.clone().into())?;
171
172                Ok(ProcessedInstructionBuilder::new()
173                    .push_to_region(stmt.into())
174                    .build())
175            }
176            Opcode::New => {
177                // Pop the last expr from the stack, create AST node for new expr, push it back to the stack
178                let new_type = context.pop_expression()?;
179
180                // Create assignment for new node
181                let var = context.ssa_context.new_ssa_version_for("gbf_new_obj");
182                let ssa_id = new_id_with_version("gbf_new_obj", var);
183                let stmt = new_assignment(ssa_id.clone(), new_type.clone());
184
185                context.push_one_node(ssa_id.clone().into())?;
186
187                Ok(ProcessedInstructionBuilder::new()
188                    .push_to_region(stmt.into())
189                    .build())
190            }
191            Opcode::NewUninitializedArray => {
192                // Pop the last expr from the stack, create AST node for new array expr, push it back to the stack
193                let num_elems = context.pop_expression()?;
194
195                // Create the node
196                let new_array = new_uninitialized_array(num_elems);
197                context.push_one_node(new_array.into())?;
198                Ok(ProcessedInstructionBuilder::new().build())
199            }
200            _ => Err(FunctionDecompilerError::UnimplementedOpcode {
201                opcode: instruction.opcode,
202                context: context.get_error_context(),
203                backtrace: Backtrace::capture(),
204            }),
205        }
206    }
207}