jdc_core/ctx.rs
1//! What the core needs to ask the front-end.
2//!
3//! Everything in this crate works on machine-neutral IR; the few questions
4//! that depend on the machine's *class metadata* — how nested classes are
5//! related, what a method's signature says, how a nested method body looks —
6//! go through [`Ctx`]. A front-end implements it once (JVM: over its class
7//! pool; DEX: over its class definitions) and every shared pass/emitter works.
8//!
9//! The trait is deliberately *semantic*, not a raw pool: the core never sees
10//! a constant pool, a `Signature` attribute, or an `InnerClasses` table.
11
12use std::collections::{HashMap, HashSet};
13
14use crate::ir::expr::Expr;
15use crate::ir::stmt::Stmt;
16use crate::ir::expr::TypeRef;
17use crate::types::{ClassAccessFlags, MethodDescriptor};
18
19/// How a nested class was classified.
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum NestedKind {
22 /// Declared as a member of its outer class.
23 Member,
24 /// An anonymous class: inlined at its `new` site, never printed separately.
25 Anonymous,
26 /// A local class: declared at its use site inside a method.
27 Local,
28 /// A lambda implementation class: skipped entirely.
29 Lambda,
30}
31
32/// One nested class of a family.
33#[derive(Debug, Clone)]
34pub struct NestedClass {
35 /// Internal name (e.g. `p/Outer$Inner`).
36 pub name: String,
37 /// Source-visible simple name (`Inner`, or a synthesized name for locals).
38 pub simple: String,
39 pub kind: NestedKind,
40 pub access: ClassAccessFlags,
41 /// Source header rendered from the class's generic signature when the
42 /// container records one; `None` for front-ends without signature info
43 /// (DEX), in which case tracking falls back to the erased rendering.
44 pub sig_header: Option<String>,
45}
46
47/// The nested-class family of one top-level class.
48#[derive(Debug, Default, Clone)]
49pub struct Family {
50 pub root: String,
51 /// All nested classes (any depth) keyed by internal name.
52 pub nested: HashMap<String, NestedClass>,
53 /// Anonymous classes inlined at `new` sites.
54 pub anonymous: HashSet<String>,
55 /// Local classes declared at their use site.
56 pub locals: HashSet<String>,
57 /// Lambda implementation classes (skipped).
58 pub lambdas: HashSet<String>,
59}
60
61/// A decompiled nested method body (lambda body, anonymous-class method, ...).
62pub struct MethodBody {
63 /// Final statement tree.
64 pub body: Stmt,
65 pub vt: crate::var::VarTable,
66 pub desc: MethodDescriptor,
67}
68
69/// Front-end services used by the shared passes and the emitter.
70pub trait Ctx {
71 /// Internal name of the class currently being emitted
72 /// (e.g. `java/util/ArrayList`).
73 fn class_name(&self) -> &str;
74
75 /// Source level gate for version-dependent rendering: the JVM front-end
76 /// returns the class-file major version; a DEX front-end returns the
77 /// equivalent language level it decided to target. `52` means "Java 8
78 /// features available" (lambdas, diamond, ...).
79 fn source_level(&self) -> u16 {
80 52
81 }
82
83 /// The class that `internal` is nested in, when the container records the
84 /// nesting (JVM `InnerClasses`/`NestHost`; DEX inner-class annotations).
85 /// `None` for a top-level class — including literal-`$` names that only
86 /// *look* nested.
87 fn find_outer(&self, internal: &str) -> Option<String>;
88
89 /// Nested-class family of `root`, as far as the container records it.
90 fn family(&self, root: &str) -> Family;
91
92 /// True when a nested class is static (`Outer$Inner` with ACC_STATIC), so
93 /// it is not constructed with an enclosing instance.
94 fn nested_is_static(&self, internal: &str) -> bool;
95
96 /// True when the class carries a synthetic `this$0` field (an inner class
97 /// with an enclosing instance parameter).
98 fn class_has_this0(&self, internal: &str) -> bool;
99
100 /// True when `sub` is assignable to `sup` (both internal names).
101 fn is_subtype_of(&self, sub: &crate::types::JavaType, sup: &str) -> bool;
102
103 /// Superclasses/interfaces of `internal` with the given type arguments
104 /// applied: `[(java/lang/Object, []), ...]`, most-derived first.
105 fn class_supers_args(
106 &self,
107 internal: &str,
108 args: &[crate::types::GenericType],
109 ) -> Vec<(String, Vec<crate::types::GenericType>)>;
110
111 /// True when the call carries inferred type arguments that the source
112 /// must spell out.
113 fn is_generic_call(&self, e: &Expr) -> bool;
114
115 /// Instantiated formal parameter types of a generic call plus the callee's
116 /// own method type variables (for witness casts). `None` when the call has
117 /// no inferred type arguments to spell out.
118 fn generic_call_formals(
119 &self,
120 e: &Expr,
121 ) -> Option<(Vec<crate::types::GenericType>, Vec<String>)>;
122
123 /// The instantiated return type of a call whose methodref owner redeclares
124 /// the method with a self-covariant return (Stream over BaseStream).
125 fn polymorphic_ret_cast(&self, cls: &str, name: &str, desc: &MethodDescriptor) -> Option<crate::types::JavaType>;
126
127 /// Constructor parameter types of `internal` matching `arity`, for
128 /// rendering `new` arguments against the right overload.
129 fn ctor_formals_by_arity(
130 &self,
131 internal: &str,
132 arity: usize,
133 ) -> Option<Vec<crate::types::GenericType>>;
134
135 /// Local-class name recorded for a `$`-style synthetic marker, when the
136 /// container records one (`Outer$1Local` -> `Local`).
137 fn local_class_internal(&self, simple: &str) -> Option<String> {
138 let _ = simple;
139 None
140 }
141
142 /// Generic type parameters declared by `internal` (from its class
143 /// signature); empty when the container records none.
144 fn class_type_params(&self, internal: &str) -> Vec<crate::types::TypeParam> {
145 let _ = internal;
146 Vec::new()
147 }
148
149 /// Type variables of the *method* the call refers to (used to decide
150 /// whether a formal still carries the callee's own typevars).
151 fn generic_call_mt_typevars(&self, e: &Expr) -> Vec<String> {
152 let _ = e;
153 Vec::new()
154 }
155
156 /// Superclass of `internal` (internal name); `None` at `Object`/unknown.
157 fn super_name(&self, internal: &str) -> Option<String> {
158 let _ = internal;
159 None
160 }
161
162 /// Access flags of the field `internal.name`; `None` when absent.
163 fn field_flags(&self, internal: &str, name: &str) -> Option<crate::types::FieldAccessFlags> {
164 let _ = (internal, name);
165 None
166 }
167
168 /// Access flags of the method `internal.name(desc)`; `None` when absent.
169 fn method_flags(
170 &self,
171 internal: &str,
172 name: &str,
173 desc: &str,
174 ) -> Option<crate::types::MethodAccessFlags> {
175 let _ = (internal, name, desc);
176 None
177 }
178
179 /// True when `internal` declares any field named `name`.
180 fn declares_field(&self, internal: &str, name: &str) -> bool {
181 self.field_flags(internal, name).is_some()
182 }
183
184 /// True when `internal` declares a method named `name` (any descriptor).
185 fn declares_method_named(&self, internal: &str, name: &str) -> bool {
186 let _ = (internal, name);
187 false
188 }
189
190 /// True when `internal` is an interface.
191 fn is_interface(&self, internal: &str) -> bool;
192
193 /// True when `internal` is sealed (its permitted subclasses are recorded).
194 fn is_sealed(&self, internal: &str) -> bool;
195
196 /// True when the container holds this class at all (naming probes use
197 /// it to test `Outer$1Local`-style candidates).
198 fn has_class(&self, internal: &str) -> bool;
199
200 /// Interfaces and superclass of `internal` (internal names, declaration
201 /// order). `None` when the class is unknown to the front-end.
202 fn class_bases(&self, internal: &str) -> Option<(Vec<String>, Option<String>)>;
203
204 /// True when `internal` is an inner class whose constructor forwards its
205 /// enclosing instance straight to `super` (javac 21+ drops `this$0` in
206 /// that case, but call sites still need the qualified-new form).
207 fn outer_param_via_super(&self, internal: &str) -> bool;
208
209 /// Decompile the nested method a lambda site refers to — a lambda body,
210 /// an anonymous-class method, or a local-class method — and run the
211 /// front-end's own idiom recovery on it.
212 ///
213 /// This is where machine-specific desugaring lives: the shared emitter
214 /// asks for a finished statement tree and prints it. The `LambdaExpr`
215 /// carries the call-site facts (impl owner/name/descriptor, the SAM's
216 /// parameter names, the capture expressions, and any front-end
217 /// annotations it attached, e.g. capture snapshots); `outer_vt` is the
218 /// enclosing method's variable table — the lexical scope the body will be
219 /// printed in.
220 fn nested_method(
221 &self,
222 l: &crate::ir::expr::LambdaExpr,
223 outer_vt: &crate::var::VarTable,
224 ) -> Option<MethodBody>;
225
226 /// Parameter types of the `<init>` of `internal` matching `skip + n`
227 /// descriptor args, with `skip` leading synthetic parameters removed.
228 /// Used to print `new` arguments with boolean/char constant adjustment.
229 fn ctor_param_types(
230 &self,
231 internal: &str,
232 skip: usize,
233 n: usize,
234 args: &[Expr],
235 ) -> Option<Vec<crate::types::JavaType>> {
236 let _ = (internal, skip, n, args);
237 None
238 }
239
240 /// Instantiated SAM return of a functional interface's method, resolved
241 /// against a cast's type arguments (`(Function<I,R>) lambda` needs
242 /// `(R) value` witnesses on the impl body's returns).
243 fn sam_ret_cast(
244 &self,
245 g: &crate::types::GenericType,
246 sam_name: &str,
247 ) -> Option<TypeRef> {
248 let _ = (g, sam_name);
249 None
250 }
251
252 /// True when `internal` declares class-level type parameters (so
253 /// `new internal<>(...)` should carry a diamond).
254 fn class_declares_generics(&self, internal: &str) -> bool {
255 !self.class_type_params(internal).is_empty()
256 }
257
258}
259
260/// A `Ctx` with no metadata: every query answers "unknown".
261///
262/// Useful for tests and for front-ends that have not implemented a query yet —
263/// the core degrades gracefully (no nesting assumptions, no generic casts).
264pub struct NullCtx {
265 pub class_name: String,
266 pub level: u16,
267}
268
269impl Default for NullCtx {
270 fn default() -> Self {
271 NullCtx { class_name: String::new(), level: 52 }
272 }
273}
274
275impl Ctx for NullCtx {
276 fn class_name(&self) -> &str {
277 &self.class_name
278 }
279 fn source_level(&self) -> u16 {
280 self.level
281 }
282 fn find_outer(&self, _internal: &str) -> Option<String> {
283 None
284 }
285 fn family(&self, root: &str) -> Family {
286 Family { root: root.to_string(), ..Default::default() }
287 }
288 fn nested_is_static(&self, _internal: &str) -> bool {
289 true
290 }
291 fn class_has_this0(&self, _internal: &str) -> bool {
292 false
293 }
294 fn is_subtype_of(&self, _sub: &crate::types::JavaType, _sup: &str) -> bool {
295 false
296 }
297 fn class_supers_args(
298 &self,
299 _internal: &str,
300 _args: &[crate::types::GenericType],
301 ) -> Vec<(String, Vec<crate::types::GenericType>)> {
302 Vec::new()
303 }
304 fn is_generic_call(&self, _e: &Expr) -> bool {
305 false
306 }
307 fn generic_call_formals(
308 &self,
309 _e: &Expr,
310 ) -> Option<(Vec<crate::types::GenericType>, Vec<String>)> {
311 None
312 }
313 fn polymorphic_ret_cast(&self, _cls: &str, _name: &str, _desc: &MethodDescriptor) -> Option<crate::types::JavaType> {
314 None
315 }
316 fn ctor_formals_by_arity(
317 &self,
318 _internal: &str,
319 _arity: usize,
320 ) -> Option<Vec<crate::types::GenericType>> {
321 None
322 }
323 fn is_interface(&self, _internal: &str) -> bool {
324 false
325 }
326 fn is_sealed(&self, _internal: &str) -> bool {
327 false
328 }
329 fn has_class(&self, _internal: &str) -> bool {
330 false
331 }
332 fn class_bases(&self, _internal: &str) -> Option<(Vec<String>, Option<String>)> {
333 None
334 }
335 fn outer_param_via_super(&self, _internal: &str) -> bool {
336 false
337 }
338 fn nested_method(
339 &self,
340 _l: &crate::ir::expr::LambdaExpr,
341 _outer_vt: &crate::var::VarTable,
342 ) -> Option<MethodBody> {
343 None
344 }
345}
346
347/// Assemble a [`MethodBody`] (front-ends return these from `Ctx::nested_method`).
348pub fn ctx_method_body(
349 body: crate::ir::stmt::Stmt,
350 vt: crate::var::VarTable,
351 desc: MethodDescriptor,
352) -> MethodBody {
353 MethodBody { body, vt, desc }
354}