Skip to main content

ddc_dec/
ctx.rs

1//! The DEX front-end's implementation of `jdc_core::Ctx`.
2//!
3//! Every method here answers a *semantic* question about class metadata
4//! using the pooled DEX class table; generics are absent (erased), so all
5//! generic-aware queries answer "unknown" and the core degrades gracefully.
6
7use jdc_core::ir::expr::{Expr, TypeRef};
8use jdc_core::types::{
9    ClassAccessFlags, FieldAccessFlags, GenericType, JavaType, MethodAccessFlags, MethodDescriptor,
10};
11use jdc_core::{Ctx, Family, NestedClass, NestedKind};
12
13use crate::access::*;
14use crate::{desc_type, DexPool, PoolClass};
15
16/// Front-end context handed to the core's structurer and printer.
17pub struct DexCtx<'a> {
18    pub pool: &'a DexPool,
19    pub class: &'a PoolClass,
20}
21
22impl<'a> DexCtx<'a> {
23    pub fn new(pool: &'a DexPool, class: &'a PoolClass) -> Self {
24        DexCtx { pool, class }
25    }
26
27    fn find_class(&self, internal: &str) -> Option<&PoolClass> {
28        self.pool.get(internal)
29    }
30
31    /// Nesting evidence (see `find_outer_name`).
32    fn outer_of(&self, internal: &str) -> Option<String> {
33        crate::find_outer_name(self.pool, internal)
34    }
35
36    fn class_access_flags(&self, internal: &str) -> ClassAccessFlags {
37        let mut f = ClassAccessFlags::empty();
38        let Some(pc) = self.find_class(internal) else {
39            return f;
40        };
41        let a = pc.access;
42        if a & ACC_PUBLIC != 0 {
43            f |= ClassAccessFlags::PUBLIC;
44        }
45        if a & ACC_FINAL != 0 {
46            f |= ClassAccessFlags::FINAL;
47        }
48        if a & ACC_INTERFACE != 0 {
49            f |= ClassAccessFlags::INTERFACE;
50        }
51        if a & ACC_ABSTRACT != 0 {
52            f |= ClassAccessFlags::ABSTRACT;
53        }
54        if a & ACC_SYNTHETIC != 0 {
55            f |= ClassAccessFlags::SYNTHETIC;
56        }
57        if a & ACC_ANNOTATION != 0 {
58            f |= ClassAccessFlags::ANNOTATION;
59        }
60        if a & ACC_ENUM != 0 {
61            f |= ClassAccessFlags::ENUM;
62        }
63        f
64    }
65}
66
67impl<'a> Ctx for DexCtx<'a> {
68    fn class_name(&self) -> &str {
69        &self.class.name
70    }
71
72    fn source_level(&self) -> u16 {
73        52
74    }
75
76    fn find_outer(&self, internal: &str) -> Option<String> {
77        if internal == self.class.name {
78            return self.outer_of(internal);
79        }
80        self.outer_of(internal)
81    }
82
83    fn family(&self, root: &str) -> Family {
84        let mut fam = Family {
85            root: root.to_string(),
86            ..Default::default()
87        };
88        // Cached child index: BFS over the `$` chain from `root`.
89        let mut queue: std::collections::VecDeque<String> =
90            self.pool.children_of(root).iter().cloned().collect();
91        let mut seen: std::collections::HashSet<String> = queue.iter().cloned().collect();
92        while let Some(name) = queue.pop_front() {
93            for c in self.pool.children_of(&name) {
94                if seen.insert(c.clone()) {
95                    queue.push_back(c.clone());
96                }
97            }
98            let Some(_) = self.find_class(&name) else {
99                continue;
100            };
101            let root_prefix = format!("{}$", root);
102            let rest: String = if name.starts_with(&root_prefix) {
103                name[root_prefix.len()..].to_string()
104            } else {
105                name.rsplit('$').next().unwrap_or(&name).to_string()
106            };
107            let simple = rest.rsplit('$').next().unwrap_or(&rest).to_string();
108            let kind = classify_nested(&rest);
109            let access = self.class_access_flags(&name);
110            fam.nested.insert(
111                name.clone(),
112                NestedClass {
113                    name: name.clone(),
114                    simple,
115                    kind,
116                    access,
117                    sig_header: None,
118                },
119            );
120            match kind {
121                NestedKind::Anonymous => {
122                    fam.anonymous.insert(name.clone());
123                }
124                NestedKind::Local => {
125                    fam.locals.insert(name.clone());
126                }
127                NestedKind::Lambda => {
128                    fam.lambdas.insert(name.clone());
129                }
130                NestedKind::Member => {}
131            }
132        }
133        fam
134    }
135
136    fn nested_is_static(&self, internal: &str) -> bool {
137        match self.find_class(internal) {
138            Some(pc) => pc.is_static_nested(),
139            None => true,
140        }
141    }
142
143    fn class_has_this0(&self, internal: &str) -> bool {
144        // Inner classes without ACC_STATIC carry an enclosing instance.
145        match self.find_class(internal) {
146            Some(pc) => self.find_outer(internal).is_some() && !pc.is_static_nested(),
147            None => false,
148        }
149    }
150
151    fn is_subtype_of(&self, sub: &JavaType, sup: &str) -> bool {
152        if let JavaType::Object(n) = sub {
153            self.pool.is_subtype(n, sup)
154        } else {
155            false
156        }
157    }
158
159    fn is_interface(&self, internal: &str) -> bool {
160        self.find_class(internal)
161            .map(|pc| pc.is_interface())
162            .unwrap_or(false)
163    }
164
165    fn is_sealed(&self, _internal: &str) -> bool {
166        false
167    }
168
169    fn super_name(&self, internal: &str) -> Option<String> {
170        self.find_class(internal)
171            .and_then(|pc| pc.super_name.clone())
172    }
173
174    fn has_class(&self, internal: &str) -> bool {
175        self.pool.get(internal).is_some() || internal == self.class.name
176    }
177
178    fn class_supers_args(
179        &self,
180        internal: &str,
181        _args: &[GenericType],
182    ) -> Vec<(String, Vec<GenericType>)> {
183        let mut out = Vec::new();
184        let mut cur = self.find_class(internal).map(|pc| pc.name.clone());
185        let mut hops = 0;
186        while let Some(name) = cur {
187            hops += 1;
188            if hops > 64 {
189                break;
190            }
191            if name == "java/lang/Object" {
192                break;
193            }
194            out.push((name.clone(), Vec::new()));
195            cur = self.find_class(&name).and_then(|pc| pc.super_name.clone());
196        }
197        out
198    }
199
200    fn class_bases(&self, internal: &str) -> Option<(Vec<String>, Option<String>)> {
201        let pc = self.find_class(internal)?;
202        Some((pc.interfaces.clone(), pc.super_name.clone()))
203    }
204
205    fn field_flags(&self, internal: &str, name: &str) -> Option<FieldAccessFlags> {
206        let pc = self.find_class(internal)?;
207        let raw = pc
208            .static_fields
209            .iter()
210            .chain(pc.instance_fields.iter())
211            .find(|f| f.name == name)?
212            .access;
213        Some(raw_field_flags(raw))
214    }
215
216    fn method_flags(&self, internal: &str, name: &str, desc: &str) -> Option<MethodAccessFlags> {
217        let pc = self.find_class(internal)?;
218        let m = pc.find_method(name, desc)?;
219        Some(raw_method_flags(m.access))
220    }
221
222    fn declares_field(&self, internal: &str, name: &str) -> bool {
223        self.find_class(internal)
224            .map(|pc| pc.field_flags_of(name).is_some())
225            .unwrap_or(false)
226    }
227
228    fn declares_method_named(&self, internal: &str, name: &str) -> bool {
229        self.find_class(internal)
230            .map(|pc| pc.all_methods().any(|m| &*m.name == name))
231            .unwrap_or(false)
232    }
233
234    fn is_generic_call(&self, _e: &Expr) -> bool {
235        false
236    }
237
238    fn generic_call_formals(&self, _e: &Expr) -> Option<(Vec<GenericType>, Vec<String>)> {
239        None
240    }
241
242    fn polymorphic_ret_cast(
243        &self,
244        _cls: &str,
245        _name: &str,
246        _desc: &MethodDescriptor,
247    ) -> Option<JavaType> {
248        None
249    }
250
251    fn ctor_formals_by_arity(&self, internal: &str, arity: usize) -> Option<Vec<GenericType>> {
252        let pc = self.find_class(internal)?;
253        let ctors = pc.ctors_by_arity(arity);
254        let m = ctors.first()?;
255        let md = m.parsed_desc()?;
256        Some(md.args.iter().map(java_type_to_generic).collect())
257    }
258
259    fn ctor_param_types(
260        &self,
261        internal: &str,
262        skip: usize,
263        n: usize,
264        args: &[Expr],
265    ) -> Option<Vec<JavaType>> {
266        let _ = args;
267        let pc = self.find_class(internal)?;
268        let ctors = pc.ctors_by_arity(skip + n);
269        let m = ctors.first()?;
270        let md = m.parsed_desc()?;
271        Some(md.args.iter().skip(skip).cloned().collect())
272    }
273
274    fn outer_param_via_super(&self, _internal: &str) -> bool {
275        false
276    }
277
278    fn nested_method(
279        &self,
280        _l: &jdc_core::ir::expr::LambdaExpr,
281        _outer_vt: &jdc_core::var::VarTable,
282    ) -> Option<jdc_core::MethodBody> {
283        None
284    }
285}
286
287/// `$`-suffix classification (javac conventions d8 preserves).
288fn classify_nested(rest: &str) -> NestedKind {
289    // rest is the text after `Outer$`.
290    let tail = rest.rsplit('$').next().unwrap_or(rest);
291    if rest.starts_with("-$$Lambda$") || tail.starts_with("-$$Lambda$") {
292        return NestedKind::Lambda;
293    }
294    if !tail.is_empty() && tail.chars().all(|c| c.is_ascii_digit()) {
295        return NestedKind::Anonymous;
296    }
297    if tail.starts_with(|c: char| c.is_ascii_digit()) {
298        return NestedKind::Local;
299    }
300    NestedKind::Member
301}
302
303fn raw_field_flags(raw: u32) -> FieldAccessFlags {
304    let mut f = FieldAccessFlags::empty();
305    if raw & ACC_PUBLIC != 0 {
306        f |= FieldAccessFlags::PUBLIC;
307    }
308    if raw & ACC_PRIVATE != 0 {
309        f |= FieldAccessFlags::PRIVATE;
310    }
311    if raw & ACC_PROTECTED != 0 {
312        f |= FieldAccessFlags::PROTECTED;
313    }
314    if raw & ACC_STATIC != 0 {
315        f |= FieldAccessFlags::STATIC;
316    }
317    if raw & ACC_FINAL != 0 {
318        f |= FieldAccessFlags::FINAL;
319    }
320    if raw & ACC_SYNTHETIC != 0 {
321        f |= FieldAccessFlags::SYNTHETIC;
322    }
323    if raw & ACC_ENUM != 0 {
324        f |= FieldAccessFlags::ENUM;
325    }
326    f
327}
328
329fn raw_method_flags(raw: u32) -> MethodAccessFlags {
330    let mut f = MethodAccessFlags::empty();
331    if raw & ACC_PUBLIC != 0 {
332        f |= MethodAccessFlags::PUBLIC;
333    }
334    if raw & ACC_PRIVATE != 0 {
335        f |= MethodAccessFlags::PRIVATE;
336    }
337    if raw & ACC_PROTECTED != 0 {
338        f |= MethodAccessFlags::PROTECTED;
339    }
340    if raw & ACC_STATIC != 0 {
341        f |= MethodAccessFlags::STATIC;
342    }
343    if raw & ACC_FINAL != 0 {
344        f |= MethodAccessFlags::FINAL;
345    }
346    if raw & ACC_SYNCHRONIZED != 0 || raw & ACC_DECLARED_SYNCHRONIZED != 0 {
347        f |= MethodAccessFlags::SYNCHRONIZED;
348    }
349    if raw & ACC_BRIDGE != 0 {
350        f |= MethodAccessFlags::BRIDGE;
351    }
352    if raw & ACC_VARARGS != 0 {
353        f |= MethodAccessFlags::VARARGS;
354    }
355    if raw & ACC_NATIVE != 0 {
356        f |= MethodAccessFlags::NATIVE;
357    }
358    if raw & ACC_ABSTRACT != 0 {
359        f |= MethodAccessFlags::ABSTRACT;
360    }
361    if raw & ACC_STRICT != 0 {
362        f |= MethodAccessFlags::STRICT;
363    }
364    if raw & ACC_SYNTHETIC != 0 {
365        f |= MethodAccessFlags::SYNTHETIC;
366    }
367    f
368}
369
370/// Erased JavaType → generic type view (no signature data in DEX).
371pub fn java_type_to_generic(t: &JavaType) -> GenericType {
372    match t {
373        JavaType::Object(n) => GenericType::Class(class_sig_of(n)),
374        JavaType::Array(inner) => GenericType::Array(Box::new(java_type_to_generic(inner))),
375        other => GenericType::Primitive(other.primitive_char().unwrap_or('V')),
376    }
377}
378
379fn class_sig_of(internal: &str) -> jdc_core::types::ClassSig {
380    let (package, name) = match internal.rfind('/') {
381        Some(i) => (internal[..i].to_string(), internal[i + 1..].to_string()),
382        None => (String::new(), internal.to_string()),
383    };
384    jdc_core::types::ClassSig {
385        package,
386        parts: vec![jdc_core::types::ClassSigPart { name, args: vec![] }],
387    }
388}
389
390/// Render a type reference for signatures where the printer is not used.
391pub fn type_ref_of(desc: &str) -> TypeRef {
392    TypeRef::J(desc_type(desc))
393}