tjs2dec\decompile/
expr.rs

1use std::fmt;
2
3use super::ssa::{Var, VarId};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum UnOp {
7    Neg,
8    Not,
9    BitNot,
10    Typeof,
11    Delete,
12}
13
14impl UnOp {
15    pub fn op_str(self) -> &'static str {
16        match self {
17            UnOp::Neg => "-",
18            UnOp::Not => "!",
19            UnOp::BitNot => "~",
20            UnOp::Typeof => "typeof",
21            UnOp::Delete => "delete",
22        }
23    }
24
25    pub fn needs_space(self) -> bool {
26        matches!(self, UnOp::Typeof | UnOp::Delete)
27    }
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub enum BinOp {
32    Add,
33    Sub,
34    Mul,
35    Div,
36    Mod,
37
38    Shl,
39    Shr,
40    UShr,
41
42    Lt,
43    Le,
44    Gt,
45    Ge,
46
47    In,
48
49    Eq,
50    Ne,
51    StrictEq,
52    StrictNe,
53
54    BitAnd,
55    BitOr,
56    BitXor,
57
58    LogAnd,
59    LogOr,
60
61    Assign,
62
63    AddAssign,
64    SubAssign,
65    MulAssign,
66    DivAssign,
67    ModAssign,
68    ShlAssign,
69    ShrAssign,
70    UShrAssign,
71    AndAssign,
72    OrAssign,
73    XorAssign,
74}
75
76impl BinOp {
77    pub fn op_str(self) -> &'static str {
78        match self {
79            BinOp::Add => "+",
80            BinOp::Sub => "-",
81            BinOp::Mul => "*",
82            BinOp::Div => "/",
83            BinOp::Mod => "%",
84
85            BinOp::Shl => "<<",
86            BinOp::Shr => ">>",
87            BinOp::UShr => ">>>",
88
89            BinOp::Lt => "<",
90            BinOp::Le => "<=",
91            BinOp::Gt => ">",
92            BinOp::Ge => ">=",
93
94            BinOp::In => "in",
95
96            BinOp::Eq => "==",
97            BinOp::Ne => "!=",
98            BinOp::StrictEq => "===",
99            BinOp::StrictNe => "!==",
100
101            BinOp::BitAnd => "&",
102            BinOp::BitOr => "|",
103            BinOp::BitXor => "^",
104
105            BinOp::LogAnd => "&&",
106            BinOp::LogOr => "||",
107
108            BinOp::Assign => "=",
109
110            BinOp::AddAssign => "+=",
111            BinOp::SubAssign => "-=",
112            BinOp::MulAssign => "*=",
113            BinOp::DivAssign => "/=",
114            BinOp::ModAssign => "%=",
115            BinOp::ShlAssign => "<<=",
116            BinOp::ShrAssign => ">>=",
117            BinOp::UShrAssign => ">>>=",
118            BinOp::AndAssign => "&=",
119            BinOp::OrAssign => "|=",
120            BinOp::XorAssign => "^=",
121        }
122    }
123
124    // Higher = tighter
125    pub fn precedence(self) -> u8 {
126        match self {
127            BinOp::Assign
128            | BinOp::AddAssign
129            | BinOp::SubAssign
130            | BinOp::MulAssign
131            | BinOp::DivAssign
132            | BinOp::ModAssign
133            | BinOp::ShlAssign
134            | BinOp::ShrAssign
135            | BinOp::UShrAssign
136            | BinOp::AndAssign
137            | BinOp::OrAssign
138            | BinOp::XorAssign => 0,
139
140            BinOp::LogOr => 1,
141            BinOp::LogAnd => 2,
142
143            BinOp::BitOr => 3,
144            BinOp::BitXor => 4,
145            BinOp::BitAnd => 5,
146
147            BinOp::Eq | BinOp::Ne | BinOp::StrictEq | BinOp::StrictNe => 6,
148
149            BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge | BinOp::In => 7,
150
151            BinOp::Shl | BinOp::Shr | BinOp::UShr => 8,
152
153            BinOp::Add | BinOp::Sub => 9,
154
155            BinOp::Mul | BinOp::Div | BinOp::Mod => 10,
156        }
157    }
158
159    pub fn tightens_right(self) -> bool {
160        matches!(
161            self,
162            BinOp::Assign
163                | BinOp::AddAssign
164                | BinOp::SubAssign
165                | BinOp::MulAssign
166                | BinOp::DivAssign
167                | BinOp::ModAssign
168                | BinOp::ShlAssign
169                | BinOp::ShrAssign
170                | BinOp::UShrAssign
171                | BinOp::AndAssign
172                | BinOp::OrAssign
173                | BinOp::XorAssign
174        )
175    }
176}
177
178#[derive(Debug, Clone)]
179pub enum Expr {
180    // Raw / non-SSA forms (sometimes useful)
181    Reg(i32),
182    Flag,
183    ConstData(i32),
184
185    // SSA form used by expr_build.rs
186    SsaVar(VarId),
187
188    Void,
189    Null,
190    Bool(bool),
191    Int(i64),
192    Real(f64),
193    Str(String),
194    Octet(Vec<u8>),
195
196    Unary(UnOp, Box<Expr>),
197    Deref(Box<Expr>),
198
199    Binary(BinOp, Box<Expr>, Box<Expr>),
200
201    Call(Box<Expr>, Vec<Expr>),
202    New(Box<Expr>, Vec<Expr>),
203
204    Index(Box<Expr>, Box<Expr>),
205    Member(Box<Expr>, String),
206
207    MethodCall {
208        base: Box<Expr>,
209        member: String,
210        args: Vec<Expr>,
211    },
212
213    Opaque(String, Vec<Expr>),
214}
215
216impl Expr {
217    pub fn to_tjs(&self) -> String {
218        self.to_tjs_with(&fmt_vid_default)
219    }
220
221    pub fn to_tjs_with(&self, fmt_var: &dyn Fn(VarId) -> String) -> String {
222        let mut out = String::new();
223        self.fmt_with_prec(&mut out, 0, fmt_var)
224            .expect("fmt never fails for String");
225        out
226    }
227
228    fn precedence(&self) -> u8 {
229        match self {
230            Expr::Reg(_)
231            | Expr::Flag
232            | Expr::ConstData(_)
233            | Expr::SsaVar(_)
234            | Expr::Void
235            | Expr::Null
236            | Expr::Bool(_)
237            | Expr::Int(_)
238            | Expr::Real(_)
239            | Expr::Str(_)
240            | Expr::Octet(_) => 20,
241
242            Expr::Member(..) | Expr::Index(..) | Expr::Call(..) | Expr::MethodCall { .. } => 19,
243
244            Expr::New(..) => 18,
245
246            Expr::Unary(..) | Expr::Deref(..) => 17,
247
248            Expr::Binary(op, ..) => op.precedence(),
249            Expr::Opaque(_, _) => 1,
250        }
251    }
252
253    fn fmt_with_prec(
254        &self,
255        f: &mut dyn fmt::Write,
256        outer_prec: u8,
257        fmt_var: &dyn Fn(VarId) -> String,
258    ) -> fmt::Result {
259        let my_prec = self.precedence();
260        let needs_paren = my_prec < outer_prec;
261
262        if needs_paren {
263            write!(f, "(")?;
264        }
265
266        match self {
267            Expr::Reg(r) => {
268                // write!(f, "r{}", r)?
269                if *r <= -3 {
270                    write!(f, "a{}", -3 - r)?
271                } else if *r == -2 {
272                    write!(f, "global")?
273                } else if *r == -1 {
274                    write!(f, "this")?
275                } else {
276                    write!(f, "r{}", r)?
277                }
278            }
279            Expr::Flag => write!(f, "flag")?,
280            Expr::ConstData(i) => write!(f, "__d[{}]", i)?,
281
282            Expr::SsaVar(v) => write!(f, "{}", fmt_var(*v))?,
283
284            Expr::Void => write!(f, "void")?,
285            Expr::Null => write!(f, "null")?,
286            Expr::Bool(b) => write!(f, "{}", if *b { "true" } else { "false" })?,
287            Expr::Int(i) => write!(f, "{}", i)?,
288            Expr::Real(x) => write!(f, "{}", x)?,
289            Expr::Str(s) => write!(f, "\"{}\"", escape_tjs_string(s))?,
290            Expr::Octet(bytes) => {
291                if bytes.is_empty() {
292                    write!(f, "octet(0)")?;
293                } else {
294                    let mut preview = String::new();
295                    let n = bytes.len().min(16);
296                    for (i, b) in bytes.iter().take(n).enumerate() {
297                        if i != 0 {
298                            preview.push(' ');
299                        }
300                        preview.push_str(&format!("{:02x}", b));
301                    }
302                    if bytes.len() > n {
303                        write!(f, "octet(len={}, head={})", bytes.len(), preview)?;
304                    } else {
305                        write!(f, "octet(len={}, bytes={})", bytes.len(), preview)?;
306                    }
307                }
308            }
309
310            Expr::Unary(op, e) => {
311                if op.needs_space() {
312                    write!(f, "{} ", op.op_str())?;
313                } else {
314                    write!(f, "{}", op.op_str())?;
315                }
316                e.fmt_with_prec(f, my_prec, fmt_var)?;
317            }
318
319            Expr::Deref(e) => {
320                write!(f, "*")?;
321                e.fmt_with_prec(f, my_prec, fmt_var)?;
322            }
323
324            Expr::Binary(op, l, r) => {
325                let lp = op.precedence();
326                let rp = if op.tightens_right() { lp } else { lp + 1 };
327                l.fmt_with_prec(f, lp, fmt_var)?;
328                write!(f, " {} ", op.op_str())?;
329                r.fmt_with_prec(f, rp, fmt_var)?;
330            }
331
332            Expr::Call(callee, args) => {
333                callee.fmt_with_prec(f, 19, fmt_var)?;
334                write!(f, "(")?;
335                for (i, a) in args.iter().enumerate() {
336                    if i != 0 {
337                        write!(f, ", ")?;
338                    }
339                    a.fmt_with_prec(f, 0, fmt_var)?;
340                }
341                write!(f, ")")?;
342            }
343
344            Expr::New(callee, args) => {
345                write!(f, "new ")?;
346                callee.fmt_with_prec(f, 19, fmt_var)?;
347                write!(f, "(")?;
348                for (i, a) in args.iter().enumerate() {
349                    if i != 0 {
350                        write!(f, ", ")?;
351                    }
352                    a.fmt_with_prec(f, 0, fmt_var)?;
353                }
354                write!(f, ")")?;
355            }
356
357            Expr::Index(base, key) => {
358                base.fmt_with_prec(f, 19, fmt_var)?;
359                write!(f, "[")?;
360                key.fmt_with_prec(f, 0, fmt_var)?;
361                write!(f, "]")?;
362            }
363
364            Expr::Member(base, member) => {
365                base.fmt_with_prec(f, 19, fmt_var)?;
366                write!(f, ".{}", member)?;
367            }
368
369            Expr::MethodCall { base, member, args } => {
370                base.fmt_with_prec(f, 19, fmt_var)?;
371                write!(f, ".{}(", member)?;
372                for (i, a) in args.iter().enumerate() {
373                    if i != 0 {
374                        write!(f, ", ")?;
375                    }
376                    a.fmt_with_prec(f, 0, fmt_var)?;
377                }
378                write!(f, ")")?;
379            }
380
381            Expr::Opaque(name, args) => {
382                if args.is_empty() {
383                    write!(f, "{}", name)?;
384                } else {
385                    write!(f, "{}(", name)?;
386                    for (i, a) in args.iter().enumerate() {
387                        if i != 0 {
388                            write!(f, ", ")?;
389                        }
390                        a.fmt_with_prec(f, 0, fmt_var)?;
391                    }
392                    write!(f, ")")?;
393                }
394            }
395        }
396
397        if needs_paren {
398            write!(f, ")")?;
399        }
400        Ok(())
401    }
402}
403
404impl fmt::Display for Expr {
405    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
406        write!(f, "{}", self.to_tjs())
407    }
408}
409
410fn fmt_vid_default(vid: VarId) -> String {
411    match vid.var {
412        Var::Reg(r) => {
413            if r <= -3 {
414                format!("a{}", -3 - r)
415            } else if r == -2 {
416                "global".to_string()
417            } else if r == -1 {
418                "this".to_string()
419            } else {
420                format!("r{}#{}", r, vid.ver)
421            }
422        }
423        Var::Flag => format!("flag#{}", vid.ver),
424        Var::Exception => format!("exc#{}", vid.ver),
425    }
426}
427
428pub fn escape_tjs_string(s: &str) -> String {
429    let mut out = String::new();
430    for c in s.chars() {
431        match c {
432            '\\' => out.push_str("\\\\"),
433            '"' => out.push_str("\\\""),
434            '\n' => out.push_str("\\n"),
435            '\r' => out.push_str("\\r"),
436            '\t' => out.push_str("\\t"),
437            '\0' => out.push_str("\\0"),
438            _ => out.push(c),
439        }
440    }
441    out
442}