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 {
84        e: Expr,
85    },
86    /// Raw monitorenter (before synchronized reconstruction).
87    MonitorEnter(Expr),
88    /// Raw monitorexit.
89    MonitorExit(Expr),
90    /// Fallback: label (for unstructured control flow / jsr-ret leftovers).
91    Label(u32),
92    /// Fallback: goto a label.
93    Goto(u32),
94    /// Local class declaration rendered inline in a method body.
95    /// `header` is e.g. `Local implements Runnable`.
96    ClassDecl {
97        name: String,
98        header: String,
99        body: String,
100    },
101    /// `label: statement`
102    Labeled {
103        label: String,
104        body: Box<Stmt>,
105    },
106    /// `break;` / `break label;`
107    Break(Option<String>),
108    /// `continue;` / `continue label;`
109    Continue(Option<String>),
110    /// A raw line we could not decompile (error resilience).
111    Comment(String),
112}
113
114#[derive(Debug, Clone, PartialEq)]
115pub struct CaseGroup {
116    /// Case labels (empty = fallthrough continuation of previous group).
117    pub labels: Vec<i64>,
118    /// String case values (when on_string).
119    pub string_labels: Vec<String>,
120    /// Enum constant names (restored $SwitchMap switch).
121    pub enum_labels: Vec<String>,
122    /// Verbatim case labels (`null`, type patterns) from typeSwitch
123    /// restoration.
124    pub raw_labels: Vec<String>,
125    /// Pattern guard (`case T v when GUARD:`) folded from the desugared
126    /// restart shape (guarded typeSwitch: `if (guard) body else
127    /// {state=N; continue;}`). Printed after the raw label.
128    pub guard: Option<crate::ir::expr::Expr>,
129    pub body: Vec<Stmt>,
130}
131
132#[derive(Debug, Clone, PartialEq)]
133pub struct Catch {
134    /// Exception class internal names (multi-catch when >1);
135    /// empty = catch-all (`finally` style / Throwable).
136    pub exc: Vec<String>,
137    /// Variable holding the exception parameter.
138    pub var: u32,
139    /// Per-occurrence printed name (avoids nested-catch collisions).
140    pub var_name: Option<String>,
141    pub body: Box<Stmt>,
142}
143
144impl Stmt {
145    pub fn is_empty_block(&self) -> bool {
146        matches!(self, Stmt::Block(b) if b.is_empty())
147    }
148
149    /// Wrap in a block if not already one.
150    pub fn as_block(self) -> Stmt {
151        match self {
152            Stmt::Block(_) => self,
153            other => Stmt::Block(vec![other]),
154        }
155    }
156}
157
158/// Flatten nested single-statement blocks for cleaner output.
159pub fn flatten(s: &mut Stmt) {
160    match s {
161        Stmt::Block(v) => {
162            for x in v.iter_mut() {
163                flatten(x);
164            }
165        }
166        Stmt::If {
167            then_stmt,
168            else_stmt,
169            ..
170        } => {
171            flatten(then_stmt);
172            if let Some(e) = else_stmt {
173                flatten(e);
174            }
175        }
176        Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => flatten(body),
177        Stmt::For { body, .. } | Stmt::ForEach { body, .. } => flatten(body),
178        Stmt::Try {
179            body,
180            catches,
181            finally,
182        } => {
183            flatten(body);
184            for c in catches {
185                flatten(&mut c.body);
186            }
187            if let Some(f) = finally {
188                flatten(f);
189            }
190        }
191        Stmt::TryWithResources {
192            body,
193            catches,
194            finally,
195            ..
196        } => {
197            flatten(body);
198            for c in catches {
199                flatten(&mut c.body);
200            }
201            if let Some(f) = finally {
202                flatten(f);
203            }
204        }
205        Stmt::Synchronized { body, .. } => flatten(body),
206        Stmt::Switch { cases, default, .. } => {
207            for c in cases {
208                for s in c.body.iter_mut() {
209                    flatten(s);
210                }
211            }
212            if let Some(d) = default {
213                flatten(d);
214            }
215        }
216        _ => {}
217    }
218}