1use 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
16pub struct DexCtx<'a> {
18 pub pool: &'a DexPool,
19 pub class: &'a PoolClass,
20 inherited_fields: jdc_core::FxHashSet<String>,
27}
28
29impl<'a> DexCtx<'a> {
30 pub fn new(pool: &'a DexPool, class: &'a PoolClass) -> Self {
31 let inherited_fields = {
32 let mut set: jdc_core::FxHashSet<String> = jdc_core::FxHashSet::default();
33 let mut level = class.name.clone();
34 loop {
35 let mut queue = vec![level.clone()];
36 let mut seen: jdc_core::FxHashSet<String> =
37 jdc_core::FxHashSet::default();
38 let mut hops = 0u32;
39 while let Some(c) = queue.pop() {
40 if hops >= 64 || !seen.insert(c.clone()) {
41 continue;
42 }
43 hops += 1;
44 let Some(pc) = pool.get(&c) else { continue };
45 if c != class.name {
46 for f in pc.static_fields.iter().chain(pc.instance_fields.iter()) {
47 set.insert(crate::classdec::java_ident(&f.name).into_owned());
48 }
49 }
50 if let Some(sup) = &pc.super_name {
51 queue.push(sup.clone());
52 }
53 queue.extend(pc.interfaces.iter().cloned());
54 }
55 let Some(i) = level.rfind('$') else { break };
56 level.truncate(i);
57 }
58 set
59 };
60 DexCtx { pool, class, inherited_fields }
61 }
62
63 fn find_class(&self, internal: &str) -> Option<&PoolClass> {
64 self.pool.get(internal)
65 }
66
67 fn outer_is_formal0(&self, internal: &str, pc: &PoolClass) -> bool {
85 let Some(outer) = self.find_outer(internal) else {
86 return false;
87 };
88 pc.all_methods().any(|m| {
89 &*m.name == "<init>"
90 && m.parsed_desc().is_some_and(|d| {
91 d.args.first().is_some_and(|t| {
92 matches!(t, JavaType::Object(n) if n.as_ref() == outer.as_str())
93 })
94 })
95 })
96 }
97
98 fn holds_this0(&self, internal: &str, pc: &PoolClass) -> bool {
99 let Some(outer) = self.find_outer(internal) else {
100 return false;
101 };
102 pc.instance_fields.iter().any(|f| {
103 f.name.as_ref() == "this$0"
104 && f
105 .desc
106 .strip_prefix('L')
107 .and_then(|d| d.strip_suffix(';'))
108 .is_some_and(|ty| ty == outer)
109 })
110 }
111
112 fn outer_of(&self, internal: &str) -> Option<String> {
114 crate::find_outer_name(self.pool, internal)
115 }
116
117 fn class_access_flags(&self, internal: &str) -> ClassAccessFlags {
118 let mut f = ClassAccessFlags::empty();
119 let Some(pc) = self.find_class(internal) else {
120 return f;
121 };
122 let a = pc.access;
123 if a & ACC_PUBLIC != 0 {
124 f |= ClassAccessFlags::PUBLIC;
125 }
126 if a & ACC_FINAL != 0 {
127 f |= ClassAccessFlags::FINAL;
128 }
129 if a & ACC_INTERFACE != 0 {
130 f |= ClassAccessFlags::INTERFACE;
131 }
132 if a & ACC_ABSTRACT != 0 {
133 f |= ClassAccessFlags::ABSTRACT;
134 }
135 if a & ACC_SYNTHETIC != 0 {
136 f |= ClassAccessFlags::SYNTHETIC;
137 }
138 if a & ACC_ANNOTATION != 0 {
139 f |= ClassAccessFlags::ANNOTATION;
140 }
141 if a & ACC_ENUM != 0 {
142 f |= ClassAccessFlags::ENUM;
143 }
144 f
145 }
146}
147
148impl<'a> Ctx for DexCtx<'a> {
149 fn pool_id(&self) -> u64 {
150 self.pool as *const DexPool as u64
154 }
155
156 fn obscured_simple(&self, internal: &str) -> Option<String> {
157 crate::classdec::obscured_render_pub(internal)
158 }
159
160 fn obscured_member_escape(&self, owner: &str, member: &str) -> bool {
161 crate::classdec::obscured_member_escape_pub(owner, member)
162 }
163
164 fn class_name(&self) -> &str {
165 &self.class.name
166 }
167
168 fn source_level(&self) -> u16 {
169 52
170 }
171
172 fn find_outer(&self, internal: &str) -> Option<String> {
173 if let Some(rp) = crate::root_pkg_display() {
187 if internal.len() > rp.len() + 1
188 && internal.starts_with(rp.as_str())
189 && internal.as_bytes()[rp.len()] == b'/'
190 {
191 let stripped = &internal[rp.len() + 1..];
192 return crate::find_outer_name(self.pool, stripped)
193 .map(|o| format!("{rp}/{o}"));
194 }
195 }
196 self.outer_of(internal)
197 }
198
199 fn family(&self, root: &str) -> Family {
200 let mut fam = Family {
201 root: root.to_string(),
202 ..Default::default()
203 };
204 let mut queue: std::collections::VecDeque<String> =
206 self.pool.children_of(root).iter().cloned().collect();
207 let mut seen: jdc_core::FxHashSet<String> = queue.iter().cloned().collect();
208 while let Some(name) = queue.pop_front() {
209 for c in self.pool.children_of(&name) {
210 if seen.insert(c.clone()) {
211 queue.push_back(c.clone());
212 }
213 }
214 let Some(_) = self.find_class(&name) else {
215 continue;
216 };
217 let root_prefix = format!("{}$", root);
218 let rest: String = if name.starts_with(&root_prefix) {
219 name[root_prefix.len()..].to_string()
220 } else {
221 name.rsplit('$').next().unwrap_or(&name).to_string()
222 };
223 let simple = rest.rsplit('$').next().unwrap_or(&rest).to_string();
224 let kind = classify_nested(&rest);
225 let access = self.class_access_flags(&name);
226 fam.nested.insert(
227 name.clone(),
228 NestedClass {
229 name: name.clone(),
230 simple,
231 kind,
232 access,
233 sig_header: None,
234 },
235 );
236 match kind {
237 NestedKind::Anonymous => {
238 fam.anonymous.insert(name.clone());
239 }
240 NestedKind::Local => {
241 fam.locals.insert(name.clone());
242 }
243 NestedKind::Lambda => {
244 fam.lambdas.insert(name.clone());
245 }
246 NestedKind::Member => {}
247 }
248 }
249 fam
250 }
251
252 fn nested_is_static(&self, internal: &str) -> bool {
253 match self.find_class(internal) {
266 Some(pc) => nested_is_static(self.pool, pc),
267 None => true,
268 }
269 }
270
271 fn class_has_this0(&self, internal: &str) -> bool {
272 match self.find_class(internal) {
274 Some(pc) => {
275 !pc.is_static_nested()
276 && self.holds_this0(internal, pc)
277 && self.outer_is_formal0(internal, pc)
278 }
279 None => false,
280 }
281 }
282
283 fn is_subtype_of(&self, sub: &JavaType, sup: &str) -> bool {
284 if let JavaType::Object(n) = sub {
285 self.pool.is_subtype(n, sup)
286 } else {
287 false
288 }
289 }
290
291 fn is_interface(&self, internal: &str) -> bool {
292 self.find_class(internal)
293 .map(|pc| pc.is_interface())
294 .unwrap_or(false)
295 }
296
297 fn is_sealed(&self, _internal: &str) -> bool {
298 false
299 }
300
301 fn super_name(&self, internal: &str) -> Option<String> {
302 self.find_class(internal)
303 .and_then(|pc| pc.super_name.clone())
304 }
305
306 fn has_class(&self, internal: &str) -> bool {
307 self.pool.get(internal).is_some()
308 || internal == self.class.name
309 || crate::root_pkg_display().is_some_and(|rp| {
319 internal.len() > rp.len() + 1
320 && internal.starts_with(rp.as_str())
321 && internal.as_bytes()[rp.len()] == b'/'
322 && self.pool.get(&internal[rp.len() + 1..]).is_some()
323 })
324 }
325
326 fn class_supers_args(
327 &self,
328 internal: &str,
329 _args: &[GenericType],
330 ) -> Vec<(String, Vec<GenericType>)> {
331 let mut out = Vec::new();
332 let mut cur = self.find_class(internal).map(|pc| pc.name.clone());
333 let mut hops = 0;
334 while let Some(name) = cur {
335 hops += 1;
336 if hops > 64 {
337 break;
338 }
339 if name == "java/lang/Object" {
340 break;
341 }
342 out.push((name.clone(), Vec::new()));
343 cur = self.find_class(&name).and_then(|pc| pc.super_name.clone());
344 }
345 out
346 }
347
348 fn class_bases(&self, internal: &str) -> Option<(Vec<String>, Option<String>)> {
349 let pc = self.find_class(internal)?;
350 Some((pc.interfaces.clone(), pc.super_name.clone()))
351 }
352
353 fn field_flags(&self, internal: &str, name: &str) -> Option<FieldAccessFlags> {
354 let pc = self.find_class(internal)?;
355 let raw = pc
356 .static_fields
357 .iter()
358 .chain(pc.instance_fields.iter())
359 .find(|f| f.name.as_ref() == name)?
360 .access;
361 Some(raw_field_flags(raw))
362 }
363
364 fn method_flags(&self, internal: &str, name: &str, desc: &str) -> Option<MethodAccessFlags> {
365 let pc = self.find_class(internal)?;
366 let m = pc.find_method(name, desc)?;
367 Some(raw_method_flags(m.access))
368 }
369
370 fn declares_field(&self, internal: &str, name: &str) -> bool {
371 if internal == self.class.name {
372 return self.inherited_fields.contains(name)
373 || self.class.field_flags_of(name).is_some();
374 }
375 self.find_class(internal)
376 .map(|pc| pc.field_flags_of(name).is_some())
377 .unwrap_or(false)
378 }
379
380 fn is_fw_shadow(&self, internal: &str) -> bool {
381 crate::is_fw_shadow(internal)
382 }
383
384 fn declares_field_display(&self, internal: &str, display: &str) -> bool {
385 let pc = if internal == self.class.name {
386 self.class
387 } else {
388 match self.find_class(internal) {
389 Some(pc) => pc,
390 None => return false,
391 }
392 };
393 pc.static_fields
394 .iter()
395 .chain(pc.instance_fields.iter())
396 .any(|f| {
397 let disp = jdc_core::rename::field_display(internal, &f.name, &f.desc)
398 .map(|d| d.to_string())
399 .unwrap_or_else(|| crate::classdec::java_ident(&f.name).into_owned());
400 disp == display
401 })
402 }
403
404 fn declares_method_named(&self, internal: &str, name: &str) -> bool {
405 self.find_class(internal)
406 .map(|pc| pc.all_methods().any(|m| &*m.name == name))
407 .unwrap_or(false)
408 }
409
410 fn is_generic_call(&self, _e: &Expr) -> bool {
411 false
412 }
413
414 fn generic_call_formals(&self, _e: &Expr) -> Option<(Vec<GenericType>, Vec<String>)> {
415 None
416 }
417
418 fn polymorphic_ret_cast(
419 &self,
420 _cls: &str,
421 _name: &str,
422 _desc: &MethodDescriptor,
423 ) -> Option<JavaType> {
424 None
425 }
426
427 fn ctor_formals_by_arity(&self, internal: &str, arity: usize) -> Option<Vec<GenericType>> {
428 let pc = self.find_class(internal)?;
429 let ctors = pc.ctors_by_arity(arity);
430 let m = ctors.first()?;
431 let md = m.parsed_desc()?;
432 Some(md.args.iter().map(java_type_to_generic).collect())
433 }
434
435 fn ctor_param_types(
436 &self,
437 internal: &str,
438 skip: usize,
439 n: usize,
440 args: &[Expr],
441 ) -> Option<Vec<JavaType>> {
442 let _ = args;
443 let pc = self.find_class(internal)?;
444 let ctors = pc.ctors_by_arity(skip + n);
445 let m = ctors.first()?;
446 let md = m.parsed_desc()?;
447 Some(md.args.iter().skip(skip).cloned().collect())
448 }
449
450 fn outer_param_via_super(&self, _internal: &str) -> bool {
451 false
452 }
453
454 fn nested_method(
455 &self,
456 _l: &jdc_core::ir::expr::LambdaExpr,
457 _outer_vt: &jdc_core::var::VarTable,
458 ) -> Option<jdc_core::MethodBody> {
459 None
460 }
461}
462
463fn classify_nested(rest: &str) -> NestedKind {
465 let tail = rest.rsplit('$').next().unwrap_or(rest);
467 if rest.starts_with("-$$Lambda$") || tail.starts_with("-$$Lambda$") {
468 return NestedKind::Lambda;
469 }
470 if !tail.is_empty() && tail.chars().all(|c| c.is_ascii_digit()) {
471 return NestedKind::Anonymous;
472 }
473 if tail.starts_with(|c: char| c.is_ascii_digit()) {
474 return NestedKind::Local;
475 }
476 NestedKind::Member
477}
478
479fn raw_field_flags(raw: u32) -> FieldAccessFlags {
480 let mut f = FieldAccessFlags::empty();
481 if raw & ACC_PUBLIC != 0 {
482 f |= FieldAccessFlags::PUBLIC;
483 }
484 if raw & ACC_PRIVATE != 0 {
485 f |= FieldAccessFlags::PRIVATE;
486 }
487 if raw & ACC_PROTECTED != 0 {
488 f |= FieldAccessFlags::PROTECTED;
489 }
490 if raw & ACC_STATIC != 0 {
491 f |= FieldAccessFlags::STATIC;
492 }
493 if raw & ACC_FINAL != 0 {
494 f |= FieldAccessFlags::FINAL;
495 }
496 if raw & ACC_SYNTHETIC != 0 {
497 f |= FieldAccessFlags::SYNTHETIC;
498 }
499 if raw & ACC_ENUM != 0 {
500 f |= FieldAccessFlags::ENUM;
501 }
502 f
503}
504
505fn raw_method_flags(raw: u32) -> MethodAccessFlags {
506 let mut f = MethodAccessFlags::empty();
507 if raw & ACC_PUBLIC != 0 {
508 f |= MethodAccessFlags::PUBLIC;
509 }
510 if raw & ACC_PRIVATE != 0 {
511 f |= MethodAccessFlags::PRIVATE;
512 }
513 if raw & ACC_PROTECTED != 0 {
514 f |= MethodAccessFlags::PROTECTED;
515 }
516 if raw & ACC_STATIC != 0 {
517 f |= MethodAccessFlags::STATIC;
518 }
519 if raw & ACC_FINAL != 0 {
520 f |= MethodAccessFlags::FINAL;
521 }
522 if raw & ACC_SYNCHRONIZED != 0 || raw & ACC_DECLARED_SYNCHRONIZED != 0 {
523 f |= MethodAccessFlags::SYNCHRONIZED;
524 }
525 if raw & ACC_BRIDGE != 0 {
526 f |= MethodAccessFlags::BRIDGE;
527 }
528 if raw & ACC_VARARGS != 0 {
529 f |= MethodAccessFlags::VARARGS;
530 }
531 if raw & ACC_NATIVE != 0 {
532 f |= MethodAccessFlags::NATIVE;
533 }
534 if raw & ACC_ABSTRACT != 0 {
535 f |= MethodAccessFlags::ABSTRACT;
536 }
537 if raw & ACC_STRICT != 0 {
538 f |= MethodAccessFlags::STRICT;
539 }
540 if raw & ACC_SYNTHETIC != 0 {
541 f |= MethodAccessFlags::SYNTHETIC;
542 }
543 f
544}
545
546pub fn java_type_to_generic(t: &JavaType) -> GenericType {
548 match t {
549 JavaType::Object(n) => GenericType::Class(class_sig_of(n)),
550 JavaType::Array(inner) => GenericType::Array(Box::new(java_type_to_generic(inner))),
551 other => GenericType::Primitive(other.primitive_char().unwrap_or('V')),
552 }
553}
554
555fn class_sig_of(internal: &str) -> jdc_core::types::ClassSig {
556 let (package, name) = match internal.rfind('/') {
557 Some(i) => (internal[..i].to_string(), internal[i + 1..].to_string()),
558 None => (String::new(), internal.to_string()),
559 };
560 jdc_core::types::ClassSig {
561 package,
562 parts: vec![jdc_core::types::ClassSigPart { name, args: vec![] }],
563 }
564}
565
566pub fn type_ref_of(desc: &str) -> TypeRef {
568 TypeRef::J(desc_type(desc))
569}
570
571pub fn nested_is_static(pool: &DexPool, pc: &PoolClass) -> bool {
577 if pc.is_static_nested() {
578 return true;
579 }
580 let Some(outer) = crate::find_outer_name(pool, &pc.name) else {
581 return true;
582 };
583 let holds = pc.instance_fields.iter().any(|f| {
584 f.name.as_ref() == "this$0"
585 && f
586 .desc
587 .strip_prefix('L')
588 .and_then(|d| d.strip_suffix(';'))
589 .is_some_and(|ty| ty == outer)
590 });
591 if !holds {
592 return true;
593 }
594 !pc.all_methods().any(|m| {
595 &*m.name == "<init>"
596 && m.parsed_desc().is_some_and(|d| {
597 d.args.first().is_some_and(|t| {
598 matches!(t, JavaType::Object(n) if n.as_ref() == outer.as_str())
599 })
600 })
601 })
602}