jdc-core 0.2.17

Machine-neutral decompiler core: IR, CFG structuring, statement conversion, Java emission. Front-end agnostic (JVM class files, DEX, ...).
Documentation
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
//! What the core needs to ask the front-end.
//!
//! Everything in this crate works on machine-neutral IR; the few questions
//! that depend on the machine's *class metadata* — how nested classes are
//! related, what a method's signature says, how a nested method body looks —
//! go through [`Ctx`]. A front-end implements it once (JVM: over its class
//! pool; DEX: over its class definitions) and every shared pass/emitter works.
//!
//! The trait is deliberately *semantic*, not a raw pool: the core never sees
//! a constant pool, a `Signature` attribute, or an `InnerClasses` table.

use std::collections::{HashMap, HashSet};

use crate::ir::expr::Expr;
use crate::ir::expr::TypeRef;
use crate::ir::stmt::Stmt;
use crate::types::{ClassAccessFlags, MethodDescriptor};

/// How a nested class was classified.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NestedKind {
    /// Declared as a member of its outer class.
    Member,
    /// An anonymous class: inlined at its `new` site, never printed separately.
    Anonymous,
    /// A local class: declared at its use site inside a method.
    Local,
    /// A lambda implementation class: skipped entirely.
    Lambda,
}

/// One nested class of a family.
#[derive(Debug, Clone)]
pub struct NestedClass {
    /// Internal name (e.g. `p/Outer$Inner`).
    pub name: String,
    /// Source-visible simple name (`Inner`, or a synthesized name for locals).
    pub simple: String,
    pub kind: NestedKind,
    pub access: ClassAccessFlags,
    /// Source header rendered from the class's generic signature when the
    /// container records one; `None` for front-ends without signature info
    /// (DEX), in which case tracking falls back to the erased rendering.
    pub sig_header: Option<String>,
}

/// The nested-class family of one top-level class.
#[derive(Debug, Default, Clone)]
pub struct Family {
    pub root: String,
    /// All nested classes (any depth) keyed by internal name.
    pub nested: HashMap<String, NestedClass>,
    /// Anonymous classes inlined at `new` sites.
    pub anonymous: HashSet<String>,
    /// Local classes declared at their use site.
    pub locals: HashSet<String>,
    /// Lambda implementation classes (skipped).
    pub lambdas: HashSet<String>,
}

/// A decompiled nested method body (lambda body, anonymous-class method, ...).
pub struct MethodBody {
    /// Final statement tree.
    pub body: Stmt,
    pub vt: crate::var::VarTable,
    pub desc: MethodDescriptor,
}

/// Front-end services used by the shared passes and the emitter.
pub trait Ctx {
    /// Internal name of the class currently being emitted
    /// (e.g. `java/util/ArrayList`).
    fn class_name(&self) -> &str;

    /// Source level gate for version-dependent rendering: the JVM front-end
    /// returns the class-file major version; a DEX front-end returns the
    /// equivalent language level it decided to target. `52` means "Java 8
    /// features available" (lambdas, diamond, ...).
    fn source_level(&self) -> u16 {
        52
    }

    /// The front-end's render-time import map: when a referenced
    /// internal name is OBSCURED in the current compilation unit (its
    /// qualified form's first segment binds to an in-scope class name
    /// — JLS 6.4.2), the front-end emits a single-type import for it
    /// and the render must use the SIMPLE name. Returns the simple
    /// name when so; `None` renders normally. Default None (no import
    /// machinery).
    /// 静态成员引用的遮蔽逃逸:owner 点分渲染的首段被作用域内的
    /// 名字(字段/继承成员)遮蔽、且类型导入的简单名同样会被捕获时,
    /// 成员引用改走**单静态导入**(渲染为裸成员名,前端记录
    /// `import static owner.member;`)。JLS 6.4.2:表达式名解析优先
    /// 绑定变量——`d.j.v` 的 d 绑定到继承字段 d 后成员链断裂
    /// (找不到符号 变量 j)。默认 false(无该机制的前端)。
    fn obscured_member_escape(&self, _owner: &str, _member: &str) -> bool {
        false
    }

    fn obscured_simple(&self, _internal: &str) -> Option<String> {
        None
    }

    /// Identity of the backing container (pool), for caches keyed on
    /// pool-dependent pure queries (`Printer::shorten` memoization).
    /// Default 0 is safe only for single-pool processes; multi-pool
    /// front-ends (ddc/jcdc) override with the pool pointer so a second
    /// pool never reads the first pool's cached shortenings.
    fn pool_id(&self) -> u64 {
        0
    }

    /// The class that `internal` is nested in, when the container records the
    /// nesting (JVM `InnerClasses`/`NestHost`; DEX inner-class annotations).
    /// `None` for a top-level class — including literal-`$` names that only
    /// *look* nested.
    fn find_outer(&self, internal: &str) -> Option<String>;

    /// Nested-class family of `root`, as far as the container records it.
    fn family(&self, root: &str) -> Family;

    /// True when a nested class is static (`Outer$Inner` with ACC_STATIC), so
    /// it is not constructed with an enclosing instance.
    fn nested_is_static(&self, internal: &str) -> bool;

    /// True when the class carries a synthetic `this$0` field (an inner class
    /// with an enclosing instance parameter).
    fn class_has_this0(&self, internal: &str) -> bool;

    /// True when `sub` is assignable to `sup` (both internal names).
    fn is_subtype_of(&self, sub: &crate::types::JavaType, sup: &str) -> bool;

    /// Superclasses/interfaces of `internal` with the given type arguments
    /// applied: `[(java/lang/Object, []), ...]`, most-derived first.
    fn class_supers_args(
        &self,
        internal: &str,
        args: &[crate::types::GenericType],
    ) -> Vec<(String, Vec<crate::types::GenericType>)>;

    /// True when the call carries inferred type arguments that the source
    /// must spell out.
    fn is_generic_call(&self, e: &Expr) -> bool;

    /// Instantiated formal parameter types of a generic call plus the callee's
    /// own method type variables (for witness casts). `None` when the call has
    /// no inferred type arguments to spell out.
    fn generic_call_formals(
        &self,
        e: &Expr,
    ) -> Option<(Vec<crate::types::GenericType>, Vec<String>)>;

    /// The instantiated return type of a call whose methodref owner redeclares
    /// the method with a self-covariant return (Stream over BaseStream).
    fn polymorphic_ret_cast(
        &self,
        cls: &str,
        name: &str,
        desc: &MethodDescriptor,
    ) -> Option<crate::types::JavaType>;

    /// Constructor parameter types of `internal` matching `arity`, for
    /// rendering `new` arguments against the right overload.
    fn ctor_formals_by_arity(
        &self,
        internal: &str,
        arity: usize,
    ) -> Option<Vec<crate::types::GenericType>>;

    /// Local-class name recorded for a `$`-style synthetic marker, when the
    /// container records one (`Outer$1Local` -> `Local`).
    fn local_class_internal(&self, simple: &str) -> Option<String> {
        let _ = simple;
        None
    }

    /// Generic type parameters declared by `internal` (from its class
    /// signature); empty when the container records none.
    fn class_type_params(&self, internal: &str) -> Vec<crate::types::TypeParam> {
        let _ = internal;
        Vec::new()
    }

    /// Type variables of the *method* the call refers to (used to decide
    /// whether a formal still carries the callee's own typevars).
    fn generic_call_mt_typevars(&self, e: &Expr) -> Vec<String> {
        let _ = e;
        Vec::new()
    }

    /// Superclass of `internal` (internal name); `None` at `Object`/unknown.
    fn super_name(&self, internal: &str) -> Option<String> {
        let _ = internal;
        None
    }

    /// Access flags of the field `internal.name`; `None` when absent.
    fn field_flags(&self, internal: &str, name: &str) -> Option<crate::types::FieldAccessFlags> {
        let _ = (internal, name);
        None
    }

    /// Access flags of the method `internal.name(desc)`; `None` when absent.
    fn method_flags(
        &self,
        internal: &str,
        name: &str,
        desc: &str,
    ) -> Option<crate::types::MethodAccessFlags> {
        let _ = (internal, name, desc);
        None
    }

    /// True when `internal` declares any field named `name`.
    fn declares_field(&self, internal: &str, name: &str) -> bool {
        self.field_flags(internal, name).is_some()
    }

    /// True when `internal` declares any field whose RENDERED display
    /// equals `display` (the registry's rename applied; the raw name
    /// when no entry exists). Field references carry the DISPLAY name
    /// at emit time, so shadow checks must compare in display space —
    /// a raw-named field can render exactly the name another class's
    /// field displays.
    fn declares_field_display(&self, internal: &str, display: &str) -> bool {
        let _ = (internal, display);
        false
    }

    /// True when `internal` declares a method named `name` (any descriptor).
    fn declares_method_named(&self, internal: &str, name: &str) -> bool {
        let _ = (internal, name);
        false
    }

    /// True when `internal` is a framework-shadow stub class (the
    /// front-end relocates its FILE identity; references render the raw
    /// framework FQN — the platform class is what the bytecode means).
    fn is_fw_shadow(&self, internal: &str) -> bool {
        let _ = internal;
        false
    }

    /// True when `internal` is an interface.
    fn is_interface(&self, internal: &str) -> bool;

    /// True when `internal` is sealed (its permitted subclasses are recorded).
    fn is_sealed(&self, internal: &str) -> bool;

    /// True when the container holds this class at all (naming probes use
    /// it to test `Outer$1Local`-style candidates).
    fn has_class(&self, internal: &str) -> bool;

    /// Interfaces and superclass of `internal` (internal names, declaration
    /// order). `None` when the class is unknown to the front-end.
    fn class_bases(&self, internal: &str) -> Option<(Vec<String>, Option<String>)>;

    /// True when `internal` is an inner class whose constructor forwards its
    /// enclosing instance straight to `super` (javac 21+ drops `this$0` in
    /// that case, but call sites still need the qualified-new form).
    fn outer_param_via_super(&self, internal: &str) -> bool;

    /// Decompile the nested method a lambda site refers to — a lambda body,
    /// an anonymous-class method, or a local-class method — and run the
    /// front-end's own idiom recovery on it.
    ///
    /// This is where machine-specific desugaring lives: the shared emitter
    /// asks for a finished statement tree and prints it. The `LambdaExpr`
    /// carries the call-site facts (impl owner/name/descriptor, the SAM's
    /// parameter names, the capture expressions, and any front-end
    /// annotations it attached, e.g. capture snapshots); `outer_vt` is the
    /// enclosing method's variable table — the lexical scope the body will be
    /// printed in.
    fn nested_method(
        &self,
        l: &crate::ir::expr::LambdaExpr,
        outer_vt: &crate::var::VarTable,
    ) -> Option<MethodBody>;

    /// Parameter types of the `<init>` of `internal` matching `skip + n`
    /// descriptor args, with `skip` leading synthetic parameters removed.
    /// Used to print `new` arguments with boolean/char constant adjustment.
    fn ctor_param_types(
        &self,
        internal: &str,
        skip: usize,
        n: usize,
        args: &[Expr],
    ) -> Option<Vec<crate::types::JavaType>> {
        let _ = (internal, skip, n, args);
        None
    }

    /// Instantiated SAM return of a functional interface's method, resolved
    /// against a cast's type arguments (`(Function<I,R>) lambda` needs
    /// `(R) value` witnesses on the impl body's returns).
    fn sam_ret_cast(&self, g: &crate::types::GenericType, sam_name: &str) -> Option<TypeRef> {
        let _ = (g, sam_name);
        None
    }

    /// True when `internal` declares class-level type parameters (so
    /// `new internal<>(...)` should carry a diamond).
    fn class_declares_generics(&self, internal: &str) -> bool {
        !self.class_type_params(internal).is_empty()
    }
}

/// A `Ctx` with no metadata: every query answers "unknown".
///
/// Useful for tests and for front-ends that have not implemented a query yet —
/// the core degrades gracefully (no nesting assumptions, no generic casts).
pub struct NullCtx {
    pub class_name: String,
    pub level: u16,
}

impl Default for NullCtx {
    fn default() -> Self {
        NullCtx {
            class_name: String::new(),
            level: 52,
        }
    }
}

impl Ctx for NullCtx {
    fn class_name(&self) -> &str {
        &self.class_name
    }
    fn source_level(&self) -> u16 {
        self.level
    }
    fn find_outer(&self, _internal: &str) -> Option<String> {
        None
    }
    fn family(&self, root: &str) -> Family {
        Family {
            root: root.to_string(),
            ..Default::default()
        }
    }
    fn nested_is_static(&self, _internal: &str) -> bool {
        true
    }
    fn class_has_this0(&self, _internal: &str) -> bool {
        false
    }
    fn is_subtype_of(&self, _sub: &crate::types::JavaType, _sup: &str) -> bool {
        false
    }
    fn class_supers_args(
        &self,
        _internal: &str,
        _args: &[crate::types::GenericType],
    ) -> Vec<(String, Vec<crate::types::GenericType>)> {
        Vec::new()
    }
    fn is_generic_call(&self, _e: &Expr) -> bool {
        false
    }
    fn generic_call_formals(
        &self,
        _e: &Expr,
    ) -> Option<(Vec<crate::types::GenericType>, Vec<String>)> {
        None
    }
    fn polymorphic_ret_cast(
        &self,
        _cls: &str,
        _name: &str,
        _desc: &MethodDescriptor,
    ) -> Option<crate::types::JavaType> {
        None
    }
    fn ctor_formals_by_arity(
        &self,
        _internal: &str,
        _arity: usize,
    ) -> Option<Vec<crate::types::GenericType>> {
        None
    }
    fn is_interface(&self, _internal: &str) -> bool {
        false
    }
    fn is_sealed(&self, _internal: &str) -> bool {
        false
    }
    fn has_class(&self, _internal: &str) -> bool {
        false
    }
    fn class_bases(&self, _internal: &str) -> Option<(Vec<String>, Option<String>)> {
        None
    }
    fn outer_param_via_super(&self, _internal: &str) -> bool {
        false
    }
    fn nested_method(
        &self,
        _l: &crate::ir::expr::LambdaExpr,
        _outer_vt: &crate::var::VarTable,
    ) -> Option<MethodBody> {
        None
    }
}

/// Assemble a [`MethodBody`] (front-ends return these from `Ctx::nested_method`).
pub fn ctx_method_body(
    body: crate::ir::stmt::Stmt,
    vt: crate::var::VarTable,
    desc: MethodDescriptor,
) -> MethodBody {
    MethodBody { body, vt, desc }
}