gbf_core/decompiler/handlers/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#![deny(missing_docs)]

use std::{collections::HashMap, sync::OnceLock};

use bin_op::BinaryOperationHandler;
use identifier::IdentifierHandler;
use literal::LiteralHandler;
use nop::NopHandler;
use variable_operand::VariableOperandHandler;

use crate::{instruction::Instruction, opcode::Opcode};

use super::{
    function_decompiler::FunctionDecompilerError,
    function_decompiler_context::FunctionDecompilerContext, ProcessedInstruction,
};

/// Handles binary operation instructions.
pub mod bin_op;
/// Contains general handlers for instructions.
pub mod general;
/// Handles identifier instructions.
pub mod identifier;
/// Handles jump instructions.
pub mod jump;
/// Handles literal instructions.
pub mod literal;
/// Handles instructions that are not useful to our decompiler.
pub mod nop;
/// Handles instructions with one operand.
pub mod special_one_operand;
/// Handles member access instructions.
pub mod special_two_operand;
/// Handles cases with a variable number of operands.
pub mod variable_operand;

/// Represents an opcode handler for the decompiler.
pub trait OpcodeHandler: Send + Sync {
    /// Handles the given instruction.
    ///
    /// # Arguments
    /// - `context`: The decompiler context.
    /// - `instruction`: The instruction to handle.
    ///
    /// # Errors
    /// - Returns a `FunctionDecompilerError` if there is an issue handling the instruction.
    fn handle_instruction(
        &self,
        context: &mut FunctionDecompilerContext,
        instruction: &Instruction,
    ) -> Result<ProcessedInstruction, FunctionDecompilerError>;
}

static GLOBAL_OPCODE_HANDLERS: OnceLock<HashMap<Opcode, Box<dyn OpcodeHandler>>> = OnceLock::new();

/// Gets the global opcode handlers.
pub fn global_opcode_handlers() -> &'static HashMap<Opcode, Box<dyn OpcodeHandler>> {
    GLOBAL_OPCODE_HANDLERS.get_or_init(|| {
        let mut handlers: HashMap<Opcode, Box<dyn OpcodeHandler>> = HashMap::new();

        // General cases
        handlers.insert(Opcode::Pop, Box::new(general::GeneralHandler));

        // These handlers are used to create identifier nodes. All of them, with the
        // exception of `PushVariable`, use the lowercase opcode name as the identifier
        // name.
        handlers.insert(Opcode::Player, Box::new(IdentifierHandler));
        handlers.insert(Opcode::PlayerO, Box::new(IdentifierHandler));
        handlers.insert(Opcode::Temp, Box::new(IdentifierHandler));
        handlers.insert(Opcode::Level, Box::new(IdentifierHandler));
        handlers.insert(Opcode::This, Box::new(IdentifierHandler));
        handlers.insert(Opcode::ThisO, Box::new(IdentifierHandler));
        handlers.insert(Opcode::Params, Box::new(IdentifierHandler));
        handlers.insert(Opcode::PushVariable, Box::new(IdentifierHandler));

        // These handlers are used to create literal nodes.
        handlers.insert(Opcode::PushString, Box::new(LiteralHandler));
        handlers.insert(Opcode::PushNumber, Box::new(LiteralHandler));

        // These handlers are used to create binary operation nodes.
        handlers.insert(Opcode::Add, Box::new(BinaryOperationHandler));
        handlers.insert(Opcode::Subtract, Box::new(BinaryOperationHandler));
        handlers.insert(Opcode::Multiply, Box::new(BinaryOperationHandler));
        handlers.insert(Opcode::Divide, Box::new(BinaryOperationHandler));
        handlers.insert(Opcode::Modulo, Box::new(BinaryOperationHandler));
        handlers.insert(Opcode::BitwiseAnd, Box::new(BinaryOperationHandler));
        handlers.insert(Opcode::BitwiseOr, Box::new(BinaryOperationHandler));
        handlers.insert(Opcode::BitwiseXor, Box::new(BinaryOperationHandler));
        handlers.insert(Opcode::ShiftLeft, Box::new(BinaryOperationHandler));
        handlers.insert(Opcode::ShiftRight, Box::new(BinaryOperationHandler));
        handlers.insert(Opcode::Equal, Box::new(BinaryOperationHandler));
        handlers.insert(Opcode::NotEqual, Box::new(BinaryOperationHandler));
        handlers.insert(Opcode::LessThan, Box::new(BinaryOperationHandler));
        handlers.insert(Opcode::LessThanOrEqual, Box::new(BinaryOperationHandler));
        handlers.insert(Opcode::GreaterThan, Box::new(BinaryOperationHandler));
        handlers.insert(Opcode::GreaterThanOrEqual, Box::new(BinaryOperationHandler));
        handlers.insert(Opcode::ShortCircuitAnd, Box::new(BinaryOperationHandler));
        handlers.insert(Opcode::ShortCircuitOr, Box::new(BinaryOperationHandler));
        handlers.insert(Opcode::In, Box::new(BinaryOperationHandler));
        handlers.insert(Opcode::Join, Box::new(BinaryOperationHandler));

        // These opcodes do nothing ATM
        handlers.insert(Opcode::ConvertToFloat, Box::new(NopHandler));
        handlers.insert(Opcode::ConvertToObject, Box::new(NopHandler));
        handlers.insert(Opcode::FunctionStart, Box::new(NopHandler));
        handlers.insert(Opcode::IncreaseLoopCounter, Box::new(NopHandler));

        // Two operand handlers
        handlers.insert(
            Opcode::AccessMember,
            Box::new(special_two_operand::SpecialTwoOperandHandler),
        );
        handlers.insert(
            Opcode::Assign,
            Box::new(special_two_operand::SpecialTwoOperandHandler),
        );

        // One operand handlers
        handlers.insert(
            Opcode::Ret,
            Box::new(special_one_operand::SpecialOneOperandHandler),
        );

        // Variable operand handlers
        handlers.insert(Opcode::Call, Box::new(VariableOperandHandler));
        handlers.insert(Opcode::EndParams, Box::new(VariableOperandHandler));

        // Jump handlers
        handlers.insert(Opcode::Jne, Box::new(jump::JumpHandler));

        handlers
    })
}