Skip to main content

jdc_core/ir/
stmt.rs

1//! Statement tree for decompiled code.
2
3use crate::ir::expr::Expr;
4
5#[derive(Debug, Clone, PartialEq)]
6pub enum Stmt {
7    /// A plain `{ ... }` block (or statement sequence during construction).
8    Block(Vec<Stmt>),
9    /// Expression statement (the expression must have side effects).
10    ExprStmt(Expr),
11    /// Local variable declaration: `[final] T name [= init];`
12    LocalDef {
13        var: u32,
14        init: Option<Expr>,
15        is_final: bool,
16        /// Force declaration with explicit type even when inferrable.
17        force_type: bool,
18    },
19    Return(Option<Expr>),
20    Throw(Expr),
21    If {
22        cond: Expr,
23        then_stmt: Box<Stmt>,
24        else_stmt: Option<Box<Stmt>>,
25    },
26    /// `while (cond) body`; cond may be `true` for infinite loops.
27    While {
28        cond: Expr,
29        body: Box<Stmt>,
30    },
31    DoWhile {
32        body: Box<Stmt>,
33        cond: Expr,
34    },
35    /// Classic `for (init; cond; update) body`.
36    For {
37        init: Vec<Stmt>,
38        cond: Option<Expr>,
39        update: Vec<Expr>,
40        body: Box<Stmt>,
41    },
42    /// Enhanced for: `for (T v : iterable) body`.
43    ForEach {
44        var: u32,
45        iterable: Expr,
46        /// true if iterating an array (index-based lowering).
47        is_array: bool,
48        body: Box<Stmt>,
49    },
50    Switch {
51        selector: Expr,
52        cases: Vec<CaseGroup>,
53        default: Option<Box<Stmt>>,
54        /// true if this was a string switch (selector hashed).
55        on_string: bool,
56    },
57    Try {
58        body: Box<Stmt>,
59        catches: Vec<Catch>,
60        finally: Option<Box<Stmt>>,
61    },
62    /// `try (resources) body` — restored try-with-resources.
63    TryWithResources {
64        /// Resource declarations (`LocalDef` statements).
65        resources: Vec<Stmt>,
66        body: Box<Stmt>,
67        catches: Vec<Catch>,
68        finally: Option<Box<Stmt>>,
69    },
70    /// `assert cond [: msg];` restored from the $assertionsDisabled idiom.
71    Assert {
72        cond: Expr,
73        msg: Option<Expr>,
74    },
75    /// `synchronized (lock) body`
76    Synchronized {
77        lock: Expr,
78        body: Box<Stmt>,
79    },
80    /// A folded value-diamond: this "statement" represents a value left on
81    /// the operand stack (consumed by the merge block). Removed after the
82    /// merge statements are woven.
83    TernaryValue { e: Expr },
84    /// Raw monitorenter (before synchronized reconstruction).
85    MonitorEnter(Expr),
86    /// Raw monitorexit.
87    MonitorExit(Expr),
88    /// Fallback: label (for unstructured control flow / jsr-ret leftovers).
89    Label(u32),
90    /// Fallback: goto a label.
91    Goto(u32),
92    /// Local class declaration rendered inline in a method body.
93    /// `header` is e.g. `Local implements Runnable`.
94    ClassDecl { name: String, header: String, body: String },
95    /// `label: statement`
96    Labeled { label: String, body: Box<Stmt> },
97    /// `break;` / `break label;`
98    Break(Option<String>),
99    /// `continue;` / `continue label;`
100    Continue(Option<String>),
101    /// A raw line we could not decompile (error resilience).
102    Comment(String),
103}
104
105#[derive(Debug, Clone, PartialEq)]
106pub struct CaseGroup {
107    /// Case labels (empty = fallthrough continuation of previous group).
108    pub labels: Vec<i64>,
109    /// String case values (when on_string).
110    pub string_labels: Vec<String>,
111    /// Enum constant names (restored $SwitchMap switch).
112    pub enum_labels: Vec<String>,
113    /// Verbatim case labels (`null`, type patterns) from typeSwitch
114    /// restoration.
115    pub raw_labels: Vec<String>,
116    /// Pattern guard (`case T v when GUARD:`) folded from the desugared
117    /// restart shape (guarded typeSwitch: `if (guard) body else
118    /// {state=N; continue;}`). Printed after the raw label.
119    pub guard: Option<crate::ir::expr::Expr>,
120    pub body: Vec<Stmt>,
121}
122
123#[derive(Debug, Clone, PartialEq)]
124pub struct Catch {
125    /// Exception class internal names (multi-catch when >1);
126    /// empty = catch-all (`finally` style / Throwable).
127    pub exc: Vec<String>,
128    /// Variable holding the exception parameter.
129    pub var: u32,
130    /// Per-occurrence printed name (avoids nested-catch collisions).
131    pub var_name: Option<String>,
132    pub body: Box<Stmt>,
133}
134
135impl Stmt {
136    pub fn is_empty_block(&self) -> bool {
137        matches!(self, Stmt::Block(b) if b.is_empty())
138    }
139
140    /// Wrap in a block if not already one.
141    pub fn as_block(self) -> Stmt {
142        match self {
143            Stmt::Block(_) => self,
144            other => Stmt::Block(vec![other]),
145        }
146    }
147}
148
149/// Flatten nested single-statement blocks for cleaner output.
150pub fn flatten(s: &mut Stmt) {
151    match s {
152        Stmt::Block(v) => {
153            for x in v.iter_mut() {
154                flatten(x);
155            }
156        }
157        Stmt::If { then_stmt, else_stmt, .. } => {
158            flatten(then_stmt);
159            if let Some(e) = else_stmt {
160                flatten(e);
161            }
162        }
163        Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => flatten(body),
164        Stmt::For { body, .. } | Stmt::ForEach { body, .. } => flatten(body),
165        Stmt::Try { body, catches, finally } => {
166            flatten(body);
167            for c in catches {
168                flatten(&mut c.body);
169            }
170            if let Some(f) = finally {
171                flatten(f);
172            }
173        }
174        Stmt::TryWithResources { body, catches, finally, .. } => {
175            flatten(body);
176            for c in catches {
177                flatten(&mut c.body);
178            }
179            if let Some(f) = finally {
180                flatten(f);
181            }
182        }
183        Stmt::Synchronized { body, .. } => flatten(body),
184        Stmt::Switch { cases, default, .. } => {
185            for c in cases {
186                for s in c.body.iter_mut() {
187                    flatten(s);
188                }
189            }
190            if let Some(d) = default {
191                flatten(d);
192            }
193        }
194        _ => {}
195    }
196}