hlbc_decompiler/
ast.rs

1use std::collections::HashMap;
2
3use hlbc::fmt::EnhancedFmt;
4use hlbc::types::{RefEnumConstruct, RefField, RefFloat, RefFun, RefInt, RefString, RefType, Reg};
5use hlbc::{Bytecode, Str};
6
7#[derive(Debug)]
8pub struct SourceFile {
9    pub class: Class,
10}
11
12#[derive(Debug)]
13pub struct Class {
14    pub name: Str,
15    pub parent: Option<Str>,
16    pub fields: Vec<ClassField>,
17    pub methods: Vec<Method>,
18}
19
20#[derive(Debug)]
21pub struct ClassField {
22    pub name: Str,
23    pub ty: RefType,
24    pub static_: bool,
25}
26
27#[derive(Debug)]
28pub struct Method {
29    pub fun: RefFun,
30    pub static_: bool,
31    pub dynamic: bool,
32    pub statements: Vec<Statement>,
33}
34
35#[derive(Debug, Clone, Copy)]
36pub enum Constant {
37    InlineInt(usize),
38    Int(RefInt),
39    Float(RefFloat),
40    String(RefString),
41    Bool(bool),
42    Null,
43    /// 'this' instance
44    This,
45}
46
47#[derive(Debug, Clone)]
48pub enum Operation {
49    /// `+`
50    Add(Box<Expr>, Box<Expr>),
51    /// `-`
52    Sub(Box<Expr>, Box<Expr>),
53    /// `*`
54    Mul(Box<Expr>, Box<Expr>),
55    /// `/`
56    Div(Box<Expr>, Box<Expr>),
57    /// `%`
58    Mod(Box<Expr>, Box<Expr>),
59    /// `<<`
60    Shl(Box<Expr>, Box<Expr>),
61    /// `>>`
62    Shr(Box<Expr>, Box<Expr>),
63    /// && &
64    And(Box<Expr>, Box<Expr>),
65    /// || |
66    Or(Box<Expr>, Box<Expr>),
67    /// ^
68    Xor(Box<Expr>, Box<Expr>),
69    /// \-
70    Neg(Box<Expr>),
71    /// !
72    Not(Box<Expr>),
73    /// ++
74    Incr(Box<Expr>),
75    /// --
76    Decr(Box<Expr>),
77    /// ==
78    Eq(Box<Expr>, Box<Expr>),
79    /// !=
80    NotEq(Box<Expr>, Box<Expr>),
81    /// \>
82    Gt(Box<Expr>, Box<Expr>),
83    /// \>=
84    Gte(Box<Expr>, Box<Expr>),
85    /// \<
86    Lt(Box<Expr>, Box<Expr>),
87    /// \<=
88    Lte(Box<Expr>, Box<Expr>),
89}
90
91/// Constructor call
92#[derive(Debug, Clone)]
93pub struct ConstructorCall {
94    pub ty: RefType,
95    pub args: Vec<Expr>,
96}
97
98impl ConstructorCall {
99    pub fn new(ty: RefType, args: Vec<Expr>) -> Self {
100        Self { ty, args }
101    }
102}
103
104/// Function or method call
105#[derive(Debug, Clone)]
106pub struct Call {
107    pub fun: Expr,
108    pub args: Vec<Expr>,
109}
110
111impl Call {
112    pub fn new(fun: Expr, args: Vec<Expr>) -> Self {
113        Self { fun, args }
114    }
115
116    pub fn new_fun(fun: RefFun, args: Vec<Expr>) -> Self {
117        Self {
118            fun: Expr::FunRef(fun),
119            args,
120        }
121    }
122}
123
124/// An expression with a value
125#[derive(Debug, Clone)]
126pub enum Expr {
127    /// An anonymous structure : { field: value }
128    Anonymous(RefType, HashMap<RefField, Expr>),
129    /// Array access : array\[index]
130    Array(Box<Expr>, Box<Expr>),
131    /// Function call
132    Call(Box<Call>),
133    /// Constant value
134    Constant(Constant),
135    /// Constructor call
136    Constructor(ConstructorCall),
137    /// Arrow function (...) -> {...}
138    Closure(RefFun, Vec<Statement>),
139    EnumConstr(RefType, RefEnumConstruct, Vec<Expr>),
140    /// Field access : obj.field
141    Field(Box<Expr>, Str),
142    /// Function reference
143    FunRef(RefFun),
144    /// If/Else expression, both branches expressions types must unify (https://haxe.org/manual/expression-if.html)
145    IfElse {
146        cond: Box<Expr>,
147        /// Not empty
148        if_: Vec<Statement>,
149        /// Not empty
150        else_: Vec<Statement>,
151    },
152    /// Operator
153    Op(Operation),
154    // For when there should be something, but we don't known what
155    Unknown(String),
156    /// Variable identifier
157    Variable(Reg, Option<Str>),
158}
159
160pub const fn cst_int(cst: RefInt) -> Expr {
161    Expr::Constant(Constant::Int(cst))
162}
163
164pub const fn cst_float(cst: RefFloat) -> Expr {
165    Expr::Constant(Constant::Float(cst))
166}
167
168pub const fn cst_bool(cst: bool) -> Expr {
169    Expr::Constant(Constant::Bool(cst))
170}
171
172pub const fn cst_string(cst: RefString) -> Expr {
173    Expr::Constant(Constant::String(cst))
174}
175
176pub const fn cst_null() -> Expr {
177    Expr::Constant(Constant::Null)
178}
179
180pub const fn cst_this() -> Expr {
181    Expr::Constant(Constant::This)
182}
183
184/// Create a shorthand function to create an expression from an operator
185macro_rules! make_op_shorthand {
186    ($name:ident, $op:ident, $( $e:ident ),+) => {
187        pub(crate) fn $name($( $e: Expr ),+) -> Expr {
188            Expr::Op(Operation::$op($( Box::new($e) ),+))
189        }
190    }
191}
192
193make_op_shorthand!(add, Add, e1, e2);
194make_op_shorthand!(sub, Sub, e1, e2);
195make_op_shorthand!(mul, Mul, e1, e2);
196make_op_shorthand!(div, Div, e1, e2);
197make_op_shorthand!(modulo, Mod, e1, e2);
198make_op_shorthand!(shl, Shl, e1, e2);
199make_op_shorthand!(shr, Shr, e1, e2);
200make_op_shorthand!(and, And, e1, e2);
201make_op_shorthand!(or, Or, e1, e2);
202make_op_shorthand!(xor, Xor, e1, e2);
203make_op_shorthand!(neg, Neg, e1);
204make_op_shorthand!(incr, Incr, e1);
205make_op_shorthand!(decr, Decr, e1);
206make_op_shorthand!(eq, Eq, e1, e2);
207make_op_shorthand!(noteq, NotEq, e1, e2);
208make_op_shorthand!(gt, Gt, e1, e2);
209make_op_shorthand!(gte, Gte, e1, e2);
210make_op_shorthand!(lt, Lt, e1, e2);
211make_op_shorthand!(lte, Lte, e1, e2);
212
213/// Invert an expression, will also optimize the expression.
214pub fn not(e: Expr) -> Expr {
215    use Expr::Op;
216    use Operation::*;
217    match e {
218        Op(Not(a)) => *a,
219        Op(Eq(a, b)) => Op(NotEq(a, b)),
220        Op(NotEq(a, b)) => Op(Eq(a, b)),
221        Op(Gt(a, b)) => Op(Lte(a, b)),
222        Op(Gte(a, b)) => Op(Lt(a, b)),
223        Op(Lt(a, b)) => Op(Gte(a, b)),
224        Op(Lte(a, b)) => Op(Gt(a, b)),
225        _ => Op(Not(Box::new(e))),
226    }
227}
228
229/// Flip the operands of an expression
230pub fn flip(e: Expr) -> Expr {
231    use Expr::Op;
232    use Operation::*;
233    match e {
234        Op(Add(a, b)) => Op(Add(b, a)),
235        Op(Eq(a, b)) => Op(Eq(b, a)),
236        Op(NotEq(a, b)) => Op(NotEq(b, a)),
237        Op(Gt(a, b)) => Op(Lt(b, a)),
238        Op(Gte(a, b)) => Op(Lte(b, a)),
239        Op(Lt(a, b)) => Op(Gt(b, a)),
240        Op(Lte(a, b)) => Op(Gte(b, a)),
241        _ => e,
242    }
243}
244
245pub fn array(array: Expr, index: Expr) -> Expr {
246    Expr::Array(Box::new(array), Box::new(index))
247}
248
249pub fn call(fun: Expr, args: Vec<Expr>) -> Expr {
250    Expr::Call(Box::new(Call::new(fun, args)))
251}
252
253pub fn call_fun(fun: RefFun, args: Vec<Expr>) -> Expr {
254    Expr::Call(Box::new(Call::new_fun(fun, args)))
255}
256
257pub fn field(expr: Expr, obj: RefType, field: RefField, code: &Bytecode) -> Expr {
258    // FIXME meh
259    Expr::Field(
260        Box::new(expr),
261        Str::from(field.display::<EnhancedFmt>(code, &code[obj]).to_string()),
262    )
263}
264
265#[derive(Debug, Clone)]
266pub enum Statement {
267    /// Variable assignment
268    Assign {
269        /// Should 'var' appear
270        declaration: bool,
271        variable: Expr,
272        assign: Expr,
273    },
274    /// Expression statement
275    ExprStatement(Expr),
276    /// Return an expression or nothing (void)
277    Return(Option<Expr>),
278    /// If/Else statement
279    IfElse {
280        cond: Expr,
281        if_: Vec<Statement>,
282        /// Else clause if the vec isn't empty
283        else_: Vec<Statement>,
284    },
285    Switch {
286        arg: Expr,
287        default: Vec<Statement>,
288        cases: Vec<(Expr, Vec<Statement>)>,
289    },
290    /// While statement
291    While {
292        cond: Expr,
293        stmts: Vec<Statement>,
294    },
295    Break,
296    Continue,
297    Throw(Expr),
298    Try {
299        stmts: Vec<Statement>,
300    },
301    Catch {
302        stmts: Vec<Statement>,
303    },
304    Comment(String),
305}
306
307/// Create an expression statement
308pub fn stmt(e: Expr) -> Statement {
309    Statement::ExprStatement(e)
310}
311
312pub fn comment(comment: impl Into<String>) -> Statement {
313    Statement::Comment(comment.into())
314}