1#![allow(clippy::collapsible_match)]
14
15use jdc_core::FxHashSet as HashSet;
16
17use jdc_core::ir::build::has_side_effects;
21use jdc_core::emit::is_java_keyword;
22use jdc_core::ir::expr::{AssignOp, BinOp, ConcatPart, ConstVal, Expr, TypeRef, UnOp};
23use jdc_core::ir::stmt::{CaseGroup, Catch, Stmt};
24use jdc_core::types::{JavaType, MethodDescriptor};
25use jdc_core::var::VarTable;
26
27use crate::lift::MethodEnv;
28use crate::{access, desc_type, DexPool};
29use ddc_dex::insn::{Insn, InsnKind, InvokeKind};
30use jdc_core::types::parse_method_descriptor;
31
32pub fn rewrite_exprs<F: FnMut(&mut Expr)>(s: &mut Stmt, f: &mut F) {
38 walk_stmt_exprs(s, f);
39}
40
41pub(crate) fn walk_stmt_exprs<F: FnMut(&mut Expr)>(s: &mut Stmt, f: &mut F) {
42 match s {
43 Stmt::Block(v) => {
44 for x in v.iter_mut() {
45 walk_stmt_exprs(x, f);
46 }
47 }
48 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
49 f(e);
50 }
51 Stmt::Return(Some(e)) => f(e),
52 Stmt::LocalDef { init, .. } => {
53 if let Some(e) = init {
54 f(e);
55 }
56 }
57 Stmt::If {
58 cond,
59 then_stmt,
60 else_stmt,
61 } => {
62 f(cond);
63 walk_stmt_exprs(then_stmt, f);
64 if let Some(e) = else_stmt {
65 walk_stmt_exprs(e, f);
66 }
67 }
68 Stmt::While { cond, body } => {
69 f(cond);
70 walk_stmt_exprs(body, f);
71 }
72 Stmt::DoWhile { body, cond } => {
73 walk_stmt_exprs(body, f);
74 f(cond);
75 }
76 Stmt::For {
77 init,
78 cond,
79 update,
80 body,
81 } => {
82 for x in init.iter_mut() {
83 walk_stmt_exprs(x, f);
84 }
85 if let Some(c) = cond {
86 f(c);
87 }
88 for u in update.iter_mut() {
89 f(u);
90 }
91 walk_stmt_exprs(body, f);
92 }
93 Stmt::ForEach { iterable, body, .. } => {
94 f(iterable);
95 walk_stmt_exprs(body, f);
96 }
97 Stmt::Switch {
98 selector,
99 cases,
100 default,
101 ..
102 } => {
103 f(selector);
104 for c in cases {
105 for x in c.body.iter_mut() {
106 walk_stmt_exprs(x, f);
107 }
108 if let Some(g) = &mut c.guard {
109 f(g);
110 }
111 }
112 if let Some(d) = default {
113 walk_stmt_exprs(d, f);
114 }
115 }
116 Stmt::Try {
117 body,
118 catches,
119 finally,
120 } => {
121 walk_stmt_exprs(body, f);
122 for c in catches {
123 walk_stmt_exprs(&mut c.body, f);
124 }
125 if let Some(fl) = finally {
126 walk_stmt_exprs(fl, f);
127 }
128 }
129 Stmt::TryWithResources {
130 resources,
131 body,
132 catches,
133 finally,
134 } => {
135 for x in resources.iter_mut() {
136 walk_stmt_exprs(x, f);
137 }
138 walk_stmt_exprs(body, f);
139 for c in catches {
140 walk_stmt_exprs(&mut c.body, f);
141 }
142 if let Some(fl) = finally {
143 walk_stmt_exprs(fl, f);
144 }
145 }
146 Stmt::Assert { cond, msg } => {
147 f(cond);
148 if let Some(m) = msg {
149 f(m);
150 }
151 }
152 Stmt::Synchronized { lock, body } => {
153 f(lock);
154 walk_stmt_exprs(body, f);
155 }
156 Stmt::TernaryValue { e } => f(e),
157 Stmt::Labeled { body, .. } => walk_stmt_exprs(body, f),
158 Stmt::Goto(_) | Stmt::Label(_) | Stmt::Break(_) | Stmt::Continue(_) => {}
159 _ => {}
160 }
161}
162
163pub(crate) fn visit_stmt_exprs_ro<F: FnMut(&Expr)>(s: &Stmt, f: &mut F) {
168 match s {
169 Stmt::Block(v) => {
170 for x in v {
171 visit_stmt_exprs_ro(x, f);
172 }
173 }
174 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
175 f(e);
176 }
177 Stmt::Return(Some(e)) => f(e),
178 Stmt::LocalDef { init: Some(e), .. } => f(e),
179 Stmt::If {
180 cond,
181 then_stmt,
182 else_stmt,
183 } => {
184 f(cond);
185 visit_stmt_exprs_ro(then_stmt, f);
186 if let Some(e) = else_stmt {
187 visit_stmt_exprs_ro(e, f);
188 }
189 }
190 Stmt::While { cond, body } => {
191 f(cond);
192 visit_stmt_exprs_ro(body, f);
193 }
194 Stmt::DoWhile { body, cond } => {
195 visit_stmt_exprs_ro(body, f);
196 f(cond);
197 }
198 Stmt::For {
199 init,
200 cond,
201 update,
202 body,
203 } => {
204 for x in init {
205 visit_stmt_exprs_ro(x, f);
206 }
207 if let Some(c) = cond {
208 f(c);
209 }
210 for u in update {
211 f(u);
212 }
213 visit_stmt_exprs_ro(body, f);
214 }
215 Stmt::ForEach { iterable, body, .. } => {
216 f(iterable);
217 visit_stmt_exprs_ro(body, f);
218 }
219 Stmt::Switch {
220 selector,
221 cases,
222 default,
223 ..
224 } => {
225 f(selector);
226 for c in cases {
227 for x in &c.body {
228 visit_stmt_exprs_ro(x, f);
229 }
230 if let Some(g) = &c.guard {
231 f(g);
232 }
233 }
234 if let Some(d) = default {
235 visit_stmt_exprs_ro(d, f);
236 }
237 }
238 Stmt::Try {
239 body,
240 catches,
241 finally,
242 } => {
243 visit_stmt_exprs_ro(body, f);
244 for c in catches {
245 visit_stmt_exprs_ro(&c.body, f);
246 }
247 if let Some(fl) = finally {
248 visit_stmt_exprs_ro(fl, f);
249 }
250 }
251 Stmt::TryWithResources {
252 resources,
253 body,
254 catches,
255 finally,
256 ..
257 } => {
258 for x in resources {
259 visit_stmt_exprs_ro(x, f);
260 }
261 visit_stmt_exprs_ro(body, f);
262 for c in catches {
263 visit_stmt_exprs_ro(&c.body, f);
264 }
265 if let Some(fl) = finally {
266 visit_stmt_exprs_ro(fl, f);
267 }
268 }
269 Stmt::Assert { cond, msg } => {
270 f(cond);
271 if let Some(m) = msg {
272 f(m);
273 }
274 }
275 Stmt::Synchronized { lock, body } => {
276 f(lock);
277 visit_stmt_exprs_ro(body, f);
278 }
279 Stmt::TernaryValue { e } => f(e),
280 Stmt::Labeled { body, .. } => visit_stmt_exprs_ro(body, f),
281 _ => {}
282 }
283}
284
285fn count_expr_ro(e: &Expr, m: &mut jdc_core::FxHashMap<u32, usize>) {
291 if let Expr::Local { var, .. } = e {
292 *m.entry(*var).or_insert(0) += 1;
293 return;
294 }
295 if let Expr::Method {
296 name,
297 is_special: true,
298 owner: Some(o),
299 args,
300 ..
301 } = e
302 {
303 if &**name == "<init>" && !matches!(**o, Expr::This) {
304 for a in args {
305 count_expr_ro(a, m);
306 }
307 return;
308 }
309 }
310 for_each_child(e, &mut |c| count_expr_ro(c, m));
311}
312
313fn visit_exprs<F: FnMut(&Expr)>(e: &Expr, f: &mut F) {
315 f(e);
316 for_each_child(e, &mut |c| visit_exprs(c, f));
317}
318
319fn for_each_child<F: FnMut(&Expr)>(e: &Expr, f: &mut F) {
320 match e {
321 Expr::Un { e, .. } | Expr::Cast { e, .. } | Expr::InstanceOf { e, .. } => f(e),
322 Expr::Bin { l, r, .. } => {
323 f(l);
324 f(r);
325 }
326 Expr::Cond { c, t, f: fe } => {
327 f(c);
328 f(t);
329 f(fe);
330 }
331 Expr::Assign { target, value, .. } => {
332 f(target);
333 f(value);
334 }
335 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => f(e),
336 Expr::Field { owner, .. } => {
337 if let Some(o) = owner {
338 f(o);
339 }
340 }
341 Expr::Method { owner, args, .. } => {
342 if let Some(o) = owner {
343 f(o);
344 }
345 for a in args {
346 f(a);
347 }
348 }
349 Expr::ArrayIndex { array, index } => {
350 f(array);
351 f(index);
352 }
353 Expr::New { args, .. } => {
354 for a in args {
355 f(a);
356 }
357 }
358 Expr::NewArray { dims, init, .. } => {
359 for d in dims {
360 f(d);
361 }
362 if let Some(v) = init {
363 for x in v {
364 f(x);
365 }
366 }
367 }
368 Expr::NewMultiArray { dims, .. } => {
369 for d in dims {
370 f(d);
371 }
372 }
373 Expr::StringConcat(parts) => {
374 for p in parts {
375 if let ConcatPart::Str(x) = p {
376 f(x);
377 }
378 }
379 }
380 Expr::Invokedynamic { args, .. } => {
381 for a in args {
382 f(a);
383 }
384 }
385 Expr::AnonNew { args, .. } => {
386 for a in args {
387 f(a);
388 }
389 }
390 _ => {}
391 }
392}
393
394fn for_each_child_mut<F: FnMut(&mut Expr)>(e: &mut Expr, f: &mut F) {
396 match e {
397 Expr::Un { e, .. } | Expr::Cast { e, .. } | Expr::InstanceOf { e, .. } => f(e),
398 Expr::Bin { l, r, .. } => {
399 f(l);
400 f(r);
401 }
402 Expr::Cond { c, t, f: fe } => {
403 f(c);
404 f(t);
405 f(fe);
406 }
407 Expr::Assign { target, value, .. } => {
408 f(target);
409 f(value);
410 }
411 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => f(e),
412 Expr::Field { owner, .. } => {
413 if let Some(o) = owner {
414 f(o);
415 }
416 }
417 Expr::Method { owner, args, .. } => {
418 if let Some(o) = owner {
419 f(o);
420 }
421 for a in args.iter_mut() {
422 f(a);
423 }
424 }
425 Expr::ArrayIndex { array, index } => {
426 f(array);
427 f(index);
428 }
429 Expr::New { args, .. } => {
430 for a in args.iter_mut() {
431 f(a);
432 }
433 }
434 Expr::NewArray { dims, init, .. } => {
435 for d in dims.iter_mut() {
436 f(d);
437 }
438 if let Some(v) = init {
439 for x in v.iter_mut() {
440 f(x);
441 }
442 }
443 }
444 Expr::NewMultiArray { dims, .. } => {
445 for d in dims.iter_mut() {
446 f(d);
447 }
448 }
449 Expr::StringConcat(parts) => {
450 for p in parts {
451 if let ConcatPart::Str(x) = p {
452 f(x);
453 }
454 }
455 }
456 Expr::Invokedynamic { args, .. } => {
457 for a in args.iter_mut() {
458 f(a);
459 }
460 }
461 Expr::AnonNew { args, .. } => {
462 for a in args.iter_mut() {
463 f(a);
464 }
465 }
466 _ => {}
467 }
468}
469
470pub(crate) fn deep_rewrite<F: FnMut(&mut Expr)>(e: &mut Expr, f: &mut F) {
472 f(e);
473 for_each_child_mut(e, &mut |c| deep_rewrite(c, f));
474}
475
476fn visit_exprs_reads<F: FnMut(&Expr)>(e: &Expr, f: &mut F) {
488 f(e);
489 match e {
490 Expr::Assign { target, value, .. } => {
491 if !matches!(**target, Expr::Local { .. }) {
492 visit_exprs_reads(target, f);
493 }
494 visit_exprs_reads(value, f);
495 }
496 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
497 if !matches!(**inner, Expr::Local { .. }) {
498 visit_exprs_reads(inner, f);
499 }
500 }
501 _ => for_each_child(e, &mut |c| visit_exprs_reads(c, f)),
502 }
503}
504
505fn deep_rewrite_reads<F: FnMut(&mut Expr)>(e: &mut Expr, f: &mut F) {
506 f(e);
507 match e {
508 Expr::Assign { target, value, .. } => {
509 if !matches!(**target, Expr::Local { .. }) {
510 deep_rewrite_reads(target, f);
511 }
512 deep_rewrite_reads(value, f);
513 }
514 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
515 if !matches!(**inner, Expr::Local { .. }) {
516 deep_rewrite_reads(inner, f);
517 }
518 }
519 _ => for_each_child_mut(e, &mut |c| deep_rewrite_reads(c, f)),
520 }
521}
522
523fn collect_vars(e: &Expr, out: &mut HashSet<u32>) {
524 visit_exprs(e, &mut |x| {
525 if let Expr::Local { var, .. } = x {
526 out.insert(*var);
527 }
528 });
529}
530
531fn stmt_collect_vars(s: &Stmt, out: &mut HashSet<u32>, assignments: bool) {
536 match s {
537 Stmt::Block(v) => {
538 for x in v {
539 stmt_collect_vars(x, out, assignments);
540 }
541 }
542 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
543 if let Expr::Local { var, .. } = &**target {
544 if assignments {
545 out.insert(*var);
546 }
547 } else {
548 collect_vars(target, out);
549 }
550 collect_vars(value, out);
551 }
552 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
553 collect_vars(e, out)
554 }
555 Stmt::Return(Some(e)) => collect_vars(e, out),
556 Stmt::LocalDef { var, init, .. } => {
557 if assignments {
558 out.insert(*var);
559 }
560 if let Some(e) = init {
561 collect_vars(e, out);
562 }
563 }
564 Stmt::If {
565 cond,
566 then_stmt,
567 else_stmt,
568 } => {
569 collect_vars(cond, out);
570 stmt_collect_vars(then_stmt, out, assignments);
571 if let Some(e) = else_stmt {
572 stmt_collect_vars(e, out, assignments);
573 }
574 }
575 Stmt::While { cond, body } => {
576 collect_vars(cond, out);
577 stmt_collect_vars(body, out, assignments);
578 }
579 Stmt::DoWhile { body, cond } => {
580 stmt_collect_vars(body, out, assignments);
581 collect_vars(cond, out);
582 }
583 Stmt::For {
584 init,
585 cond,
586 update,
587 body,
588 } => {
589 for x in init {
590 stmt_collect_vars(x, out, assignments);
591 }
592 if let Some(c) = cond {
593 collect_vars(c, out);
594 }
595 for u in update {
596 collect_vars(u, out);
597 }
598 stmt_collect_vars(body, out, assignments);
599 }
600 Stmt::ForEach {
601 var,
602 iterable,
603 body,
604 ..
605 } => {
606 if assignments {
607 out.insert(*var);
608 }
609 collect_vars(iterable, out);
610 stmt_collect_vars(body, out, assignments);
611 }
612 Stmt::Switch {
613 selector,
614 cases,
615 default,
616 ..
617 } => {
618 collect_vars(selector, out);
619 for c in cases {
620 for x in &c.body {
621 stmt_collect_vars(x, out, assignments);
622 }
623 }
624 if let Some(d) = default {
625 stmt_collect_vars(d, out, assignments);
626 }
627 }
628 Stmt::Try {
629 body,
630 catches,
631 finally,
632 } => {
633 stmt_collect_vars(body, out, assignments);
634 for c in catches {
635 stmt_collect_vars(&c.body, out, assignments);
636 }
637 if let Some(f) = finally {
638 stmt_collect_vars(f, out, assignments);
639 }
640 }
641 Stmt::TryWithResources {
642 resources,
643 body,
644 catches,
645 finally,
646 } => {
647 for x in resources {
648 stmt_collect_vars(x, out, assignments);
649 }
650 stmt_collect_vars(body, out, assignments);
651 for c in catches {
652 stmt_collect_vars(&c.body, out, assignments);
653 }
654 if let Some(f) = finally {
655 stmt_collect_vars(f, out, assignments);
656 }
657 }
658 Stmt::Synchronized { lock, body } => {
659 collect_vars(lock, out);
660 stmt_collect_vars(body, out, assignments);
661 }
662 Stmt::Assert { cond, msg } => {
663 collect_vars(cond, out);
664 if let Some(m) = msg {
665 collect_vars(m, out);
666 }
667 }
668 Stmt::Labeled { body, .. } => stmt_collect_vars(body, out, assignments),
669 _ => {}
670 }
671}
672
673fn walk_all<F: FnMut(&Stmt)>(s: &Stmt, f: &mut F) {
674 f(s);
675 match s {
676 Stmt::Block(v) => {
677 for x in v {
678 walk_all(x, f);
679 }
680 }
681 Stmt::If {
682 then_stmt,
683 else_stmt,
684 ..
685 } => {
686 walk_all(then_stmt, f);
687 if let Some(e) = else_stmt {
688 walk_all(e, f);
689 }
690 }
691 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => walk_all(body, f),
692 Stmt::For { init, body, .. } => {
693 for x in init {
694 walk_all(x, f);
695 }
696 walk_all(body, f);
697 }
698 Stmt::ForEach { body, .. } => walk_all(body, f),
699 Stmt::Switch { cases, default, .. } => {
700 for c in cases {
701 for x in &c.body {
702 walk_all(x, f);
703 }
704 }
705 if let Some(d) = default {
706 walk_all(d, f);
707 }
708 }
709 Stmt::Try {
710 body,
711 catches,
712 finally,
713 } => {
714 walk_all(body, f);
715 for c in catches {
716 walk_all(&c.body, f);
717 }
718 if let Some(fl) = finally {
719 walk_all(fl, f);
720 }
721 }
722 Stmt::TryWithResources {
723 resources,
724 body,
725 catches,
726 finally,
727 } => {
728 for x in resources {
729 walk_all(x, f);
730 }
731 walk_all(body, f);
732 for c in catches {
733 walk_all(&c.body, f);
734 }
735 if let Some(fl) = finally {
736 walk_all(fl, f);
737 }
738 }
739 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => walk_all(body, f),
740 _ => {}
741 }
742}
743
744pub fn bind_catches(s: &mut Stmt, vt: &mut VarTable) {
751 let mut defined: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
756 for v in &vt.vars {
757 if v.is_param {
758 defined.insert(v.id);
759 }
760 }
761 collect_defined_locals(s, &mut defined);
762 let reads_all = count_locals_stmts(std::slice::from_ref(s));
763 let mut assigned: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
767 collect_assigned_locals(s, &mut assigned);
768 let mut fallback_bound: Vec<u32> = Vec::new();
769 bind_catches_walk(s, vt, &defined, &reads_all, &assigned, &mut fallback_bound);
770 if !fallback_bound.is_empty() {
773 remove_bare_decls(s, &fallback_bound);
774 }
775}
776
777fn collect_defined_locals(s: &Stmt, out: &mut jdc_core::FxHashSet<u32>) {
778 crate::passes::walk_all(s, &mut |st| {
779 match st {
780 Stmt::LocalDef { var, .. } => {
781 out.insert(*var);
782 }
783 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
784 if let Expr::Local { var, .. } = &**target {
785 out.insert(*var);
786 }
787 }
788 _ => {}
789 }
790 });
791}
792
793fn collect_bare_decls(s: &Stmt, out: &mut Vec<u32>) {
796 walk_all(s, &mut |st| {
797 if let Stmt::LocalDef { var, init: None, .. } = st {
798 if !out.contains(var) {
799 out.push(*var);
800 }
801 }
802 });
803}
804
805
806fn collect_assigned_locals(s: &Stmt, out: &mut jdc_core::FxHashSet<u32>) {
811 crate::passes::walk_all(s, &mut |st| {
812 match st {
813 Stmt::LocalDef { var, init: Some(_), .. } => {
814 out.insert(*var);
815 }
816 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
817 if let Expr::Local { var, .. } = &**target {
818 out.insert(*var);
819 }
820 }
821 _ => {}
822 }
823 });
824}
825
826fn first_thrown_unassigned_local(
829 body: &Stmt,
830 vt: &VarTable,
831 assigned: &jdc_core::FxHashSet<u32>,
832) -> Option<u32> {
833 let mut found: Option<u32> = None;
834 let c = body.clone();
835 walk_all(&c, &mut |st| {
836 if let Stmt::Throw(th) = st {
837 if let Expr::Local { var, .. } = th {
838 let v = *var;
839 if !assigned.contains(&v) {
840 if let JavaType::Object(o) = vt.var(v).ty.erased() {
841 if o.as_ref() == "java/lang/Throwable" {
842 found = Some(v);
843 }
844 }
845 }
846 }
847 }
848 });
849 found
850}
851
852fn remove_first_leaf(s: &mut Stmt) {
856 if let Stmt::Block(v) = s {
857 if let Some(f) = v.first_mut() {
858 remove_first_leaf(f);
859 }
860 while matches!(v.first(), Some(Stmt::Block(b)) if b.is_empty()) {
861 v.remove(0);
862 }
863 } else {
864 *s = Stmt::Block(vec![]);
865 }
866}
867
868fn remove_bare_decls(s: &mut Stmt, vars: &[u32]) {
871 let vars: jdc_core::FxHashSet<u32> = vars.iter().copied().collect();
872 walk_mut_deep(s, &mut |st| {
873 if let Stmt::Block(v) = st {
874 v.retain(|x| {
875 !matches!(x, Stmt::LocalDef { var, init: None, .. } if vars.contains(var))
876 });
877 }
878 });
879}
880
881fn first_undefined_local_read(
882 body: &Stmt,
883 defined: &jdc_core::FxHashSet<u32>,
884) -> Option<u32> {
885 let mut found: Option<u32> = None;
886 visit_stmt_exprs_ro(body, &mut |e| {
887 if found.is_none() {
888 visit_exprs(e, &mut |x| {
889 if found.is_none() {
890 if let Expr::Local { var, .. } = x {
891 if !defined.contains(var) {
892 found = Some(*var);
893 }
894 }
895 }
896 });
897 }
898 });
899 found
900}
901
902#[allow(clippy::too_many_arguments)]
903fn bind_catches_walk(
904 s: &mut Stmt,
905 vt: &mut VarTable,
906 defined: &jdc_core::FxHashSet<u32>,
907 reads_all: &jdc_core::FxHashMap<u32, usize>,
908 assigned: &jdc_core::FxHashSet<u32>,
909 fallback_bound: &mut Vec<u32>,
910) {
911 match s {
912 Stmt::Try {
913 body,
914 catches,
915 finally,
916 } => {
917 bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound);
918 for c in catches.iter_mut() {
919 if c.var == u32::MAX {
920 let stored = match first_leaf_stmt(c.body.as_ref()) {
927 Stmt::LocalDef { var, init: None, .. } => Some(*var),
939 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
943 match (&**target, &**value) {
944 (Expr::Local { var, .. }, Expr::Local { var: src, .. })
945 if !defined.contains(src) =>
946 {
947 Some(*var)
948 }
949 _ => None,
950 }
951 }
952 _ => None,
953 };
954 if let Some(v) = stored {
955 let exc = c
956 .exc
957 .first()
958 .cloned()
959 .unwrap_or_else(|| "java/lang/Throwable".into());
960 let slot = vt.var(v).slot;
961 let name = "e".to_string();
962 let new_var =
963 vt.add_catch_var(slot, name, TypeRef::J(JavaType::Object(exc)));
964 remove_first_leaf(c.body.as_mut());
965 rewrite_local_refs(c.body.as_mut(), v, new_var);
966 c.var = new_var;
967 } else if let Some(v) = first_undefined_local_read(c.body.as_ref(), defined)
968 .filter(|v| {
969 let in_catch = count_locals_stmts(std::slice::from_ref(c.body.as_ref()));
975 reads_all.get(v).copied().unwrap_or(0)
976 == in_catch.get(v).copied().unwrap_or(0)
977 })
978 .filter(|v| {
979 let ty = vt.var(*v).ty.erased();
987 let exc = c
988 .exc
989 .first()
990 .cloned()
991 .unwrap_or_else(|| "java/lang/Throwable".into());
992 matches!(&ty, JavaType::Object(o)
993 if o.as_ref() == exc.as_ref()
994 || o.as_ref() == "java/lang/Throwable")
995 })
996 {
997 c.var = v;
1007 fallback_bound.push(v);
1008 } else if let Some(v) =
1009 first_thrown_unassigned_local(c.body.as_ref(), vt, assigned)
1010 .filter(|v| {
1011 let in_catch =
1012 count_locals_stmts(std::slice::from_ref(c.body.as_ref()));
1013 reads_all.get(v).copied().unwrap_or(0)
1014 == in_catch.get(v).copied().unwrap_or(0)
1015 })
1016 .filter(|v| {
1017 let ty = vt.var(*v).ty.erased();
1021 let exc = c
1022 .exc
1023 .first()
1024 .cloned()
1025 .unwrap_or_else(|| "java/lang/Throwable".into());
1026 matches!(&ty, JavaType::Object(o)
1027 if o.as_ref() == exc.as_ref()
1028 || o.as_ref() == "java/lang/Throwable")
1029 })
1030 {
1031 c.var = v;
1037 fallback_bound.push(v);
1038 }
1039 }
1040 if c.var != u32::MAX {
1041 let in_catch =
1056 count_locals_stmts(std::slice::from_ref(c.body.as_ref()));
1057 let mut twins: Vec<u32> = Vec::new();
1058 collect_bare_decls(c.body.as_ref(), &mut twins);
1059 let mut swept: Vec<u32> = Vec::new();
1060 for x in twins {
1061 if x == c.var || assigned.contains(&x) {
1062 continue;
1063 }
1064 if reads_all.get(&x).copied().unwrap_or(0)
1065 != in_catch.get(&x).copied().unwrap_or(0)
1066 {
1067 continue;
1068 }
1069 if !matches!(vt.var(x).ty.erased(),
1070 JavaType::Object(o) if o.as_ref() == "java/lang/Throwable")
1071 {
1072 continue;
1073 }
1074 rewrite_local_refs(c.body.as_mut(), x, c.var);
1075 swept.push(x);
1076 }
1077 if !swept.is_empty() {
1078 remove_bare_decls(c.body.as_mut(), &swept);
1079 }
1080 }
1081 bind_catches_walk(&mut c.body, vt, defined, reads_all, assigned, fallback_bound);
1082 }
1083 if let Some(f) = finally {
1084 bind_catches_walk(f.as_mut(), vt, defined, reads_all, assigned, fallback_bound);
1085 }
1086 }
1087 Stmt::Block(v) => {
1088 for x in v.iter_mut() {
1089 bind_catches_walk(x, vt, defined, reads_all, assigned, fallback_bound);
1090 }
1091 }
1092 Stmt::If {
1093 then_stmt,
1094 else_stmt,
1095 ..
1096 } => {
1097 bind_catches_walk(then_stmt, vt, defined, reads_all, assigned, fallback_bound);
1098 if let Some(e) = else_stmt {
1099 bind_catches_walk(e, vt, defined, reads_all, assigned, fallback_bound);
1100 }
1101 }
1102 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound),
1103 Stmt::For { init, body, .. } => {
1104 for x in init.iter_mut() {
1105 bind_catches_walk(x, vt, defined, reads_all, assigned, fallback_bound);
1106 }
1107 bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound);
1108 }
1109 Stmt::ForEach { body, .. }
1110 | Stmt::Labeled { body, .. }
1111 | Stmt::Synchronized { body, .. } => bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound),
1112 Stmt::Switch { cases, default, .. } => {
1113 for c in cases.iter_mut() {
1114 for x in c.body.iter_mut() {
1115 bind_catches_walk(x, vt, defined, reads_all, assigned, fallback_bound);
1116 }
1117 }
1118 if let Some(d) = default {
1119 bind_catches_walk(d, vt, defined, reads_all, assigned, fallback_bound);
1120 }
1121 }
1122 _ => {}
1123 }
1124}
1125
1126fn rewrite_local_refs(s: &mut Stmt, from: u32, to: u32) {
1127 rewrite_exprs(s, &mut |e| {
1128 deep_rewrite(e, &mut |x| {
1129 if let Expr::Local { var, .. } = x {
1130 if *var == from {
1131 *var = to;
1132 }
1133 }
1134 });
1135 });
1136}
1137
1138pub fn cleanup(s: &mut Stmt) {
1140 jdc_core::ir::stmt::flatten(s);
1141 prune_dead_tails(s);
1142 strip_empty(s);
1143 merge_singleton_stmts(s);
1144}
1145
1146fn seq_terminates(s: &Stmt) -> bool {
1157 match s {
1158 Stmt::Return(_) | Stmt::Throw(_) | Stmt::Break(_) | Stmt::Continue(_) => {
1159 true
1160 }
1161 Stmt::Block(v) => v.iter().any(seq_terminates),
1162 Stmt::If {
1163 then_stmt,
1164 else_stmt,
1165 ..
1166 } => {
1167 seq_terminates(then_stmt)
1168 && else_stmt
1169 .as_ref()
1170 .map(|e| seq_terminates(e))
1171 .unwrap_or(false)
1172 }
1173 Stmt::Try {
1174 body,
1175 catches,
1176 finally,
1177 }
1178 | Stmt::TryWithResources {
1179 body,
1180 catches,
1181 finally,
1182 ..
1183 } => {
1184 if let Some(f) = finally {
1185 if seq_terminates(f) {
1186 return true;
1187 }
1188 }
1189 seq_terminates(body) && catches.iter().all(|c| seq_terminates(&c.body))
1190 }
1191 Stmt::Labeled { body, .. } | Stmt::Synchronized { body, .. } => {
1192 seq_terminates(body)
1193 }
1194 Stmt::While { .. } | Stmt::DoWhile { .. } => {
1199 jdc_core::analysis::dead_end_infinite_while(s)
1200 }
1201 _ => false,
1202 }
1203}
1204
1205fn prune_seq(v: &mut Vec<Stmt>) {
1206 if v.iter()
1207 .any(|x| matches!(x, Stmt::Label(_) | Stmt::Goto(_)))
1208 {
1209 return;
1210 }
1211 if let Some(i) = v.iter().position(seq_terminates) {
1212 v.truncate(i + 1);
1213 }
1214}
1215
1216pub fn prune_dead_tails(s: &mut Stmt) {
1217 match s {
1218 Stmt::Block(v) => {
1219 for x in v.iter_mut() {
1220 prune_dead_tails(x);
1221 }
1222 prune_seq(v);
1223 }
1224 Stmt::If {
1225 then_stmt,
1226 else_stmt,
1227 ..
1228 } => {
1229 prune_dead_tails(then_stmt);
1230 if let Some(e) = else_stmt {
1231 prune_dead_tails(e);
1232 }
1233 }
1234 Stmt::While { body, .. }
1235 | Stmt::DoWhile { body, .. }
1236 | Stmt::ForEach { body, .. }
1237 | Stmt::Labeled { body, .. }
1238 | Stmt::Synchronized { body, .. } => prune_dead_tails(body),
1239 Stmt::For { init, body, .. } => {
1240 for x in init.iter_mut() {
1241 prune_dead_tails(x);
1242 }
1243 prune_seq(init);
1244 prune_dead_tails(body);
1245 }
1246 Stmt::Switch {
1247 cases, default, ..
1248 } => {
1249 for c in cases {
1250 for x in c.body.iter_mut() {
1251 prune_dead_tails(x);
1252 }
1253 prune_seq(&mut c.body);
1254 }
1255 if let Some(d) = default {
1256 prune_dead_tails(d);
1257 }
1258 }
1259 Stmt::Try {
1260 body,
1261 catches,
1262 finally,
1263 }
1264 | Stmt::TryWithResources {
1265 body,
1266 catches,
1267 finally,
1268 ..
1269 } => {
1270 prune_dead_tails(body);
1271 for c in catches {
1272 prune_dead_tails(&mut c.body);
1273 }
1274 if let Some(f) = finally {
1275 prune_dead_tails(f);
1276 }
1277 }
1278 _ => {}
1279 }
1280}
1281
1282pub fn strip_phantom_field_writes(body: &mut Stmt, pool: &DexPool) {
1298 fn phantom(pool: &DexPool, cls: &str, name: &str) -> bool {
1299 let Some(pc) = pool.get(cls) else {
1303 return false;
1304 };
1305 fn declares(pc: &crate::PoolClass, cls: &str, name: &str) -> bool {
1313 pc.static_fields
1314 .iter()
1315 .chain(pc.instance_fields.iter())
1316 .any(|f| {
1317 f.name.as_str() == name
1318 || jdc_core::rename::field_display(cls, &f.name, &f.desc)
1319 .is_some_and(|d| d == name)
1320 })
1321 }
1322 if declares(pc, cls, name) {
1323 return false;
1324 }
1325 let mut cur: Option<String> = pc.super_name.clone();
1335 let mut hops = 0u32;
1336 while let Some(s) = cur {
1337 hops += 1;
1338 if hops > 64 {
1339 return false;
1340 }
1341 if let Some(sup) = pool.get(&s) {
1342 if declares(sup, &s, name) {
1343 return false;
1344 }
1345 cur = sup.super_name.clone();
1346 } else {
1347 if !crate::fwdb::exists(&s) {
1348 return false;
1349 }
1350 let mut found = false;
1351 crate::fwdb::for_each_field(&s, |f, _| {
1352 if f == name {
1353 found = true;
1354 }
1355 });
1356 if found {
1357 return false;
1358 }
1359 cur = crate::fwdb::super_of(&s).map(|x| x.to_string());
1361 }
1362 }
1363 true
1364 }
1365 fn droppable_value(pool: &DexPool, e: &Expr) -> bool {
1366 match e {
1367 Expr::Const(_) | Expr::Local { .. } => true,
1368 Expr::Field { cls, name, .. } => phantom(pool, cls, name),
1369 Expr::Cast { e, .. } => droppable_value(pool, e),
1370 _ => false,
1371 }
1372 }
1373 fn is_phantom_write(pool: &DexPool, st: &Stmt) -> bool {
1374 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = st {
1375 if let Expr::Field { cls, name, .. } = &**target {
1376 return phantom(pool, cls, name) && droppable_value(pool, value);
1377 }
1378 }
1379 false
1380 }
1381 walk_mut_deep(body, &mut |st| match st {
1385 Stmt::Block(v) => v.retain(|x| !is_phantom_write(pool, x)),
1386 Stmt::Switch { cases, .. } => {
1387 for c in cases.iter_mut() {
1388 c.body.retain(|x| !is_phantom_write(pool, x));
1389 }
1390 }
1391 Stmt::For { init, .. } => init.retain(|x| !is_phantom_write(pool, x)),
1392 Stmt::TryWithResources { resources, .. } => {
1393 resources.retain(|x| !is_phantom_write(pool, x))
1394 }
1395 _ => {}
1396 });
1397 if is_phantom_write(pool, body) {
1398 *body = Stmt::Block(vec![]);
1399 }
1400}
1401
1402pub fn init_bare_decls(body: &mut Stmt, vt: &VarTable) {
1414 walk_mut_deep(body, &mut |st| {
1415 if let Stmt::LocalDef { var, init, .. } = st {
1416 if init.is_some() {
1417 return;
1418 }
1419 let lit = match vt.var(*var).ty.erased() {
1420 JavaType::Boolean => "false",
1421 JavaType::Int
1422 | JavaType::Byte
1423 | JavaType::Short
1424 | JavaType::Char => "0",
1425 JavaType::Long => "0L",
1426 JavaType::Float => "0.0f",
1427 JavaType::Double => "0.0d",
1428 JavaType::Void => return,
1429 JavaType::Object(_) | JavaType::Array(_) => "null",
1430 };
1431 *init = Some(Expr::Raw(lit.to_string()));
1432 }
1433 });
1434}
1435
1436fn strip_empty(s: &mut Stmt) {
1437 match s {
1438 Stmt::Block(v) => {
1439 v.retain(|x| !x.is_empty_block());
1440 for x in v.iter_mut() {
1441 strip_empty(x);
1442 }
1443 }
1444 Stmt::If {
1445 then_stmt,
1446 else_stmt,
1447 ..
1448 } => {
1449 strip_empty(then_stmt);
1450 if let Some(e) = else_stmt {
1451 strip_empty(e);
1452 if e.is_empty_block() {
1453 *else_stmt = None;
1454 }
1455 }
1456 }
1457 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => strip_empty(body),
1458 Stmt::For { init, body, .. } => {
1459 init.retain(|x| !x.is_empty_block());
1460 strip_empty(body);
1461 }
1462 Stmt::ForEach { body, .. } => strip_empty(body),
1463 Stmt::Switch { cases, default, .. } => {
1464 for c in cases {
1465 c.body.retain(|x| !x.is_empty_block());
1466 }
1467 if let Some(d) = default {
1468 strip_empty(d);
1469 }
1470 }
1471 Stmt::Try {
1472 body,
1473 catches,
1474 finally,
1475 } => {
1476 strip_empty(body);
1477 for c in catches {
1478 strip_empty(&mut c.body);
1479 }
1480 if let Some(f) = finally {
1481 strip_empty(f);
1482 }
1483 }
1484 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => strip_empty(body),
1485 _ => {}
1486 }
1487}
1488
1489fn merge_singleton_stmts(s: &mut Stmt) {
1490 if let Stmt::Block(v) = s {
1491 for x in v.iter_mut() {
1492 merge_singleton_stmts(x);
1493 }
1494 }
1495}
1496
1497pub fn prune_unreachable(s: &mut Stmt) {
1499 if let Stmt::Block(v) = s {
1500 let mut cut: Option<usize> = None;
1501 for (i, x) in v.iter().enumerate() {
1502 if matches!(x, Stmt::Return(_) | Stmt::Throw(_)) {
1503 cut = Some(i + 1);
1504 break;
1505 }
1506 }
1507 if let Some(c) = cut {
1508 if c < v.len() {
1509 v.truncate(c);
1510 }
1511 }
1512 for x in v.iter_mut() {
1513 prune_unreachable(x);
1514 }
1515 } else {
1516 walk_mut(s, &mut prune_unreachable);
1517 }
1518}
1519
1520fn walk_mut<F: FnMut(&mut Stmt)>(s: &mut Stmt, f: &mut F) {
1521 match s {
1522 Stmt::Block(v) => {
1523 for x in v.iter_mut() {
1524 f(x);
1525 }
1526 }
1527 Stmt::If {
1528 then_stmt,
1529 else_stmt,
1530 ..
1531 } => {
1532 f(then_stmt);
1533 if let Some(e) = else_stmt {
1534 f(e);
1535 }
1536 }
1537 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => f(body),
1538 Stmt::For { init, body, .. } => {
1539 for x in init.iter_mut() {
1540 f(x);
1541 }
1542 f(body);
1543 }
1544 Stmt::ForEach { body, .. } => f(body),
1545 Stmt::Switch { cases, default, .. } => {
1546 for c in cases {
1547 for x in c.body.iter_mut() {
1548 f(x);
1549 }
1550 }
1551 if let Some(d) = default {
1552 f(d);
1553 }
1554 }
1555 Stmt::Try {
1556 body,
1557 catches,
1558 finally,
1559 } => {
1560 f(body);
1561 for c in catches {
1562 f(&mut c.body);
1563 }
1564 if let Some(fl) = finally {
1565 f(fl);
1566 }
1567 }
1568 Stmt::TryWithResources {
1569 resources,
1570 body,
1571 catches,
1572 finally,
1573 } => {
1574 for r in resources.iter_mut() {
1575 f(r);
1576 }
1577 f(body);
1578 for c in catches {
1579 f(&mut c.body);
1580 }
1581 if let Some(fl) = finally {
1582 f(fl);
1583 }
1584 }
1585 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => f(body),
1586 _ => {}
1587 }
1588}
1589
1590pub fn count_localdefs(s: &Stmt, out: &mut usize) {
1592 if matches!(s, Stmt::LocalDef { .. }) {
1593 *out += 1;
1594 }
1595 walk_all(s, &mut |x| {
1596 if matches!(x, Stmt::LocalDef { .. }) {
1597 *out += 1;
1598 }
1599 });
1600}
1601
1602pub fn strip_trailing_void_return(s: &mut Stmt) {
1604 if let Stmt::Block(v) = s {
1605 loop {
1606 match v.last_mut() {
1607 Some(Stmt::Return(None)) => {
1608 v.pop();
1609 }
1610 Some(Stmt::Block(_)) => {
1615 let last = v.last_mut().unwrap();
1616 let before = match last {
1617 Stmt::Block(b) => b.len(),
1618 _ => 0,
1619 };
1620 strip_trailing_void_return(last);
1621 let after = match last {
1622 Stmt::Block(b) => b.len(),
1623 _ => 0,
1624 };
1625 if after == before {
1626 break; }
1628 }
1629 _ => break,
1630 }
1631 }
1632 }
1633}
1634
1635pub fn invert_empty_thens(s: &mut Stmt) {
1640 walk_mut_deep(s, &mut |st| {
1641 if let Stmt::If {
1642 cond,
1643 then_stmt,
1644 else_stmt,
1645 } = st
1646 {
1647 let then_empty = matches!(&**then_stmt, Stmt::Block(v) if v.is_empty());
1648 if then_empty {
1649 if let Some(els) = else_stmt.take() {
1650 let c = std::mem::replace(cond, Expr::Const(ConstVal::Int(0)));
1651 *cond = jdc_core::convert::negate(c);
1652 *then_stmt = els;
1653 }
1654 }
1655 }
1656 });
1657}
1658
1659pub fn fold_short_circuits(s: &mut Stmt) {
1670 for _ in 0..8 {
1671 let mut changed = false;
1672 walk_mut_deep(s, &mut |st| {
1673 if try_fold_diamond(st) {
1674 changed = true;
1675 }
1676 });
1677 if !changed {
1678 break;
1679 }
1680 }
1681}
1682
1683fn try_fold_diamond(st: &mut Stmt) -> bool {
1684 enum Shape {
1685 ThenAnd,
1686 ThenAndNot,
1687 ElseOr,
1688 ElseOrNot,
1689 BareAnd,
1690 BareAndNot,
1691 }
1692 let shape = match &*st {
1696 Stmt::If {
1699 then_stmt,
1700 else_stmt: None,
1701 ..
1702 } => match &**then_stmt {
1703 Stmt::If {
1704 then_stmt: t2,
1705 else_stmt: None,
1706 ..
1707 } if !matches!(&**t2, Stmt::Block(v) if v.is_empty()) => Some(Shape::BareAnd),
1708 Stmt::If {
1709 then_stmt: t2,
1710 else_stmt: Some(_),
1711 ..
1712 } if matches!(&**t2, Stmt::Block(v) if v.is_empty()) => Some(Shape::BareAndNot),
1713 _ => None,
1714 },
1715 Stmt::If {
1716 then_stmt,
1717 else_stmt: Some(els),
1718 ..
1719 } => match (&**then_stmt, &**els) {
1720 (Stmt::If { else_stmt: Some(f2), .. }, _) if f2.as_ref() == els.as_ref() => {
1721 Some(Shape::ThenAnd)
1722 }
1723 (Stmt::If { then_stmt: t2, else_stmt: Some(_), .. }, _)
1724 if t2.as_ref() == els.as_ref() =>
1725 {
1726 Some(Shape::ThenAndNot)
1727 }
1728 (_, Stmt::If { then_stmt: t2, else_stmt: Some(_), .. })
1729 if t2.as_ref() == then_stmt.as_ref() =>
1730 {
1731 Some(Shape::ElseOr)
1732 }
1733 (_, Stmt::If { else_stmt: Some(f2), .. })
1734 if f2.as_ref() == then_stmt.as_ref() =>
1735 {
1736 Some(Shape::ElseOrNot)
1737 }
1738 _ => None,
1739 },
1740 _ => None,
1741 };
1742 let Some(shape) = shape else {
1743 return false;
1744 };
1745
1746 let taken = std::mem::replace(st, Stmt::Block(vec![]));
1747 if matches!(shape, Shape::BareAnd | Shape::BareAndNot) {
1749 let Stmt::If {
1750 cond: c1,
1751 then_stmt,
1752 else_stmt: None,
1753 } = taken
1754 else {
1755 unreachable!()
1756 };
1757 let Stmt::If {
1758 cond: c2,
1759 then_stmt: t2,
1760 else_stmt: f2,
1761 } = *then_stmt
1762 else {
1763 unreachable!()
1764 };
1765 let sc = |op: BinOp, l: Expr, r: Expr| Expr::Bin {
1766 op,
1767 l: Box::new(l),
1768 r: Box::new(r),
1769 ty: None,
1770 };
1771 match shape {
1772 Shape::BareAnd => {
1774 *st = Stmt::If {
1775 cond: sc(BinOp::LogAnd, c1, c2),
1776 then_stmt: t2,
1777 else_stmt: None,
1778 };
1779 }
1780 _ => {
1782 let body = f2.unwrap();
1783 *st = Stmt::If {
1784 cond: sc(
1785 BinOp::LogAnd,
1786 c1,
1787 Expr::Un {
1788 op: UnOp::Not,
1789 e: Box::new(c2),
1790 },
1791 ),
1792 then_stmt: body,
1793 else_stmt: None,
1794 };
1795 }
1796 }
1797 return true;
1798 }
1799 let Stmt::If {
1800 cond: c1,
1801 then_stmt,
1802 else_stmt: Some(els),
1803 } = taken
1804 else {
1805 unreachable!("shape probe guaranteed an If with else")
1806 };
1807 let sc = |op: BinOp, l: Expr, r: Expr| Expr::Bin {
1808 op,
1809 l: Box::new(l),
1810 r: Box::new(r),
1811 ty: None,
1812 };
1813 let not = |e: Expr| Expr::Un {
1814 op: UnOp::Not,
1815 e: Box::new(e),
1816 };
1817 match shape {
1818 Shape::BareAnd | Shape::BareAndNot => unreachable!("handled above"),
1819 Shape::ThenAnd | Shape::ThenAndNot => {
1820 let Stmt::If {
1821 cond: c2,
1822 then_stmt: t2,
1823 else_stmt: Some(f2),
1824 } = *then_stmt
1825 else {
1826 unreachable!()
1827 };
1828 let (nc, body, alt) = match shape {
1829 Shape::ThenAnd => (sc(BinOp::LogAnd, c1, c2), t2, f2),
1831 _ => (sc(BinOp::LogAnd, c1, not(c2)), f2, t2),
1833 };
1834 *st = Stmt::If {
1835 cond: nc,
1836 then_stmt: body,
1837 else_stmt: Some(alt),
1838 };
1839 }
1840 Shape::ElseOr | Shape::ElseOrNot => {
1841 let Stmt::If {
1842 cond: c2,
1843 then_stmt: t2,
1844 else_stmt: Some(f2),
1845 } = *els
1846 else {
1847 unreachable!()
1848 };
1849 let body = then_stmt;
1852 let (nc, alt) = match shape {
1853 Shape::ElseOr => (sc(BinOp::LogOr, c1, c2), f2),
1855 _ => (sc(BinOp::LogOr, c1, not(c2)), t2),
1857 };
1858 *st = Stmt::If {
1859 cond: nc,
1860 then_stmt: body,
1861 else_stmt: Some(alt),
1862 };
1863 }
1864 }
1865 true
1866}
1867
1868pub fn strip_clinit_returns(s: &mut Stmt) {
1874 walk_mut_deep(s, &mut |st| {
1875 if let Stmt::Block(v) = st {
1876 v.retain(|x| !matches!(x, Stmt::Return(None)));
1877 }
1878 });
1879}
1880
1881pub fn prepend_comment(s: &mut Stmt, text: String) {
1882 let stmt = Stmt::Comment(text);
1883 match s {
1884 Stmt::Block(v) => v.insert(0, stmt),
1885 other => {
1886 let inner = std::mem::replace(other, Stmt::Block(vec![]));
1887 *other = Stmt::Block(vec![stmt, inner]);
1888 }
1889 }
1890}
1891
1892fn is_obj_expr(e: &Expr, obj_var: &dyn Fn(u32) -> bool) -> bool {
1900 match e {
1901 Expr::Local { var, .. } => obj_var(*var),
1902 Expr::Const(_) => false,
1903 _ => e.type_ref().erased().is_reference(),
1904 }
1905}
1906
1907fn null_side_rewrite(x: &mut Expr, obj_var: &dyn Fn(u32) -> bool) {
1912 if let Expr::Bin { op, l, r, .. } = x {
1913 if !matches!(op, BinOp::Eq | BinOp::Ne) {
1914 return;
1915 }
1916 let l_zero = matches!(&**l, Expr::Const(ConstVal::Int(0)));
1917 let r_zero = matches!(&**r, Expr::Const(ConstVal::Int(0)));
1918 if l_zero && !r_zero && is_obj_expr(r, obj_var) {
1919 **l = Expr::Const(ConstVal::Null);
1920 } else if r_zero && !l_zero && is_obj_expr(l, obj_var) {
1921 **r = Expr::Const(ConstVal::Null);
1922 }
1923 }
1924}
1925
1926pub fn fused_expr_rewrites(s: &mut Stmt, vt: &VarTable) {
1927 let mut obj_vars: Vec<bool> = Vec::with_capacity(vt.vars.len());
1929 for v in &vt.vars {
1930 obj_vars.push(v.ty.erased().is_reference());
1931 }
1932 rewrite_exprs(s, &mut |e| {
1933 deep_rewrite(e, &mut |x| {
1934 if let Expr::Invokedynamic { name, args, .. } = x {
1936 if name.starts_with('\0') && args.len() == 2 {
1937 let (cls, ty) = match name.as_str() {
1938 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
1939 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
1940 _ => ("java/lang/Double", JavaType::Double),
1941 };
1942 let desc = MethodDescriptor {
1943 args: vec![ty.clone(), ty],
1944 ret: JavaType::Int,
1945 };
1946 *x = Expr::Method {
1947 owner: None,
1948 cls: cls.into(),
1949 name: "compare".into(),
1950 desc: std::sync::Arc::new(desc),
1951 args: args.clone(),
1952 is_static: true,
1953 is_interface: false,
1954 is_special: false,
1955 is_super: false,
1956 is_dynamic: false,
1957 type_args: vec![],
1958 };
1959 return;
1960 }
1961 }
1962 null_side_rewrite(x, &|v| obj_vars.get(v as usize).copied().unwrap_or(false));
1965 if let Expr::Bin { op, l, r, .. } = x {
1967 if matches!(
1968 op,
1969 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
1970 ) {
1971 let l_const = matches!(&**l, Expr::Const(_));
1972 let r_var = matches!(&**r, Expr::Local { .. });
1973 if l_const && r_var {
1974 std::mem::swap(l, r);
1975 }
1976 }
1977 }
1978 });
1979 });
1980 walk_mut_deep(s, &mut |st| {
1988 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = st {
1989 let tgt_is_obj = match &**target {
1990 Expr::Local { var, .. } => {
1991 obj_vars.get(*var as usize).copied().unwrap_or(false)
1992 }
1993 other => other.type_ref().erased().is_reference(),
1994 };
1995 if tgt_is_obj {
1996 if let Expr::Const(ConstVal::Int(0)) = &**value {
1997 **value = Expr::Const(ConstVal::Null);
1998 }
1999 }
2000 } else if let Stmt::LocalDef { var, init, .. } = st {
2001 if obj_vars.get(*var as usize).copied().unwrap_or(false) {
2002 if let Some(iv) = init {
2003 if let Expr::Const(ConstVal::Int(0)) = iv {
2004 *iv = Expr::Const(ConstVal::Null);
2005 }
2006 }
2007 }
2008 }
2009 });
2010}
2011
2012#[allow(dead_code)]
2015pub fn desugar_cmp_residuals(s: &mut Stmt) {
2016 rewrite_exprs(s, &mut |e| {
2017 deep_rewrite(e, &mut |x| {
2018 if let Expr::Invokedynamic { name, args, .. } = x {
2019 if name.starts_with('\0') && args.len() == 2 {
2020 let (cls, ty) = match name.as_str() {
2021 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
2022 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
2023 _ => ("java/lang/Double", JavaType::Double),
2024 };
2025 let desc = jdc_core::types::MethodDescriptor {
2026 args: vec![ty.clone(), ty],
2027 ret: JavaType::Int,
2028 };
2029 *x = Expr::Method {
2030 owner: None,
2031 cls: cls.into(),
2032 name: "compare".into(),
2033 desc: std::sync::Arc::new(desc),
2034 args: args.clone(),
2035 is_static: true,
2036 is_interface: false,
2037 is_special: false,
2038 is_super: false,
2039 is_dynamic: false,
2040 type_args: vec![],
2041 };
2042 }
2043 }
2044 });
2045 });
2046}
2047
2048pub fn ternary_fold(s: &mut Stmt) {
2050 let mut changed = true;
2051 let mut guard = 0;
2052 while changed && guard < 8 {
2053 changed = false;
2054 guard += 1;
2055 ternary_fold_walk(s, &mut changed);
2056 strip_empty(s);
2057 }
2058}
2059
2060fn ternary_fold_walk(s: &mut Stmt, changed: &mut bool) {
2061 if let Stmt::Block(v) = s {
2062 for x in v.iter_mut() {
2063 ternary_fold_walk(x, changed);
2064 }
2065 return;
2066 }
2067 let Stmt::If {
2068 cond,
2069 then_stmt,
2070 else_stmt,
2071 } = s
2072 else {
2073 return;
2074 };
2075 ternary_fold_walk(then_stmt, changed);
2076 if let Some(e) = else_stmt {
2077 ternary_fold_walk(e, changed);
2078 }
2079 let Some(else_b) = else_stmt else { return };
2080 let target_of = |st: &Stmt| -> Option<u32> {
2081 let Stmt::ExprStmt(Expr::Assign { target, .. }) = st else {
2082 return None;
2083 };
2084 if let Expr::Local { var, .. } = &**target {
2085 Some(*var)
2086 } else {
2087 None
2088 }
2089 };
2090 let (Stmt::Block(tv), Stmt::Block(ev)) = (then_stmt.as_mut(), else_b.as_mut()) else {
2091 return;
2092 };
2093 if tv.len() != 1 || ev.len() != 1 {
2094 return;
2095 }
2096 let (Some(vt_), Some(ve)) = (target_of(&tv[0]), target_of(&ev[0])) else {
2097 return;
2098 };
2099 if vt_ != ve {
2100 return;
2101 }
2102 let mut touched = HashSet::default();
2106 let (Stmt::ExprStmt(a_then), Stmt::ExprStmt(a_else)) = (&tv[0], &ev[0]) else {
2107 return;
2108 };
2109 if let Expr::Assign { value, .. } = a_then {
2110 collect_vars(value, &mut touched);
2111 }
2112 if let Expr::Assign { value, .. } = a_else {
2113 collect_vars(value, &mut touched);
2114 }
2115 if touched.contains(&vt_) {
2116 return;
2117 }
2118 let value_then = match a_then {
2119 Expr::Assign { value, .. } => value.clone(),
2120 _ => return,
2121 };
2122 let value_else = match a_else {
2123 Expr::Assign { value, .. } => value.clone(),
2124 _ => return,
2125 };
2126 let cond_e = cond.clone();
2127 let folded = Stmt::ExprStmt(Expr::Assign {
2128 target: Box::new(Expr::Local {
2129 var: vt_,
2130 ty: vt_join_ty(&value_then, &value_else),
2131 }),
2132 op: AssignOp::Plain,
2133 value: Box::new(Expr::Cond {
2134 c: Box::new(cond_e),
2135 t: value_then,
2136 f: value_else,
2137 }),
2138 });
2139 *s = Stmt::Block(vec![folded]);
2140 *changed = true;
2141}
2142
2143fn vt_join_ty(a: &Expr, b: &Expr) -> TypeRef {
2144 let ta = a.type_ref();
2145 let tb = b.type_ref();
2146 if ta.erased() == tb.erased() {
2147 return ta;
2148 }
2149 TypeRef::J(JavaType::Object("java/lang/Object".into()))
2150}
2151
2152pub fn fold_string_builders(s: &mut Stmt, vt: &VarTable) {
2159 let n_vars = vt.vars.len().max(1);
2161 let mut counts = vec![0usize; n_vars];
2162 count_assignments(s, &mut counts);
2163 let mut values: Vec<Option<Expr>> = vec![None; n_vars];
2164 record_assignments(s, &counts, &mut values);
2165
2166 let mut appended_stmts: HashSet<u32> = HashSet::default();
2168 walk_all(s, &mut |st| {
2169 if let Stmt::ExprStmt(Expr::Method {
2170 cls,
2171 name,
2172 owner,
2173 args,
2174 ..
2175 }) = st
2176 {
2177 if name.as_ref() == "append" && is_string_builder(cls) && args.len() == 1 {
2178 if let Some(Expr::Local { var, .. }) = owner.as_deref() {
2179 appended_stmts.insert(*var);
2180 }
2181 }
2182 }
2183 });
2184
2185 rewrite_exprs(s, &mut |e| {
2186 fold_concat_in_expr(e, &values, &appended_stmts);
2187 });
2188
2189 for _ in 0..8 {
2193 let mut reads: HashSet<u32> = HashSet::default();
2194 stmt_collect_vars(s, &mut reads, false);
2195 if drop_unused_assigns(s, &reads) == 0 {
2196 break;
2197 }
2198 }
2199}
2200
2201pub fn count_stmts_deep(s: &Stmt, out: &mut usize) {
2203 *out += 1;
2204 walk_all(s, &mut |_| {
2205 *out += 1;
2206 });
2207}
2208
2209struct VarAnalysis {
2211 assigns: Vec<usize>,
2212 reads: Vec<usize>,
2213 values: Vec<Option<Expr>>,
2215}
2216
2217fn analyze_vars(s: &Stmt, a: &mut VarAnalysis) {
2218 walk_all(s, &mut |st| {
2219 let exprs: Vec<&Expr> = match st {
2220 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
2221 if let Expr::Local { var, .. } = &**target {
2222 grow_to(&mut a.assigns, *var);
2223 let idx = *var as usize;
2224 a.assigns[idx] += 1;
2225 if a.assigns[idx] == 1 {
2226 if idx >= a.values.len() {
2227 a.values.resize(idx + 1, None);
2228 }
2229 a.values[idx] = Some((**value).clone());
2230 } else if idx < a.values.len() {
2231 a.values[idx] = None;
2232 }
2233 }
2234 let mut v: Vec<&Expr> = vec![value];
2235 if !matches!(&**target, Expr::Local { .. }) {
2236 v.push(target);
2237 }
2238 v
2239 }
2240 Stmt::ExprStmt(e @ (Expr::PreIncDec { .. } | Expr::PostIncDec { .. })) => {
2241 let inner = match e {
2246 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => e,
2247 _ => unreachable!(),
2248 };
2249 if let Expr::Local { var, .. } = &**inner {
2250 grow_to(&mut a.assigns, *var);
2251 let idx = *var as usize;
2252 a.assigns[idx] += 1;
2253 if idx < a.values.len() {
2254 a.values[idx] = None;
2255 }
2256 return;
2257 }
2258 vec![e]
2259 }
2260 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
2261 vec![e]
2262 }
2263 Stmt::Return(Some(e)) => vec![e],
2264 Stmt::LocalDef {
2265 var, init: Some(e), ..
2266 } => {
2267 grow_to(&mut a.assigns, *var);
2268 let idx = *var as usize;
2269 a.assigns[idx] += 1;
2270 if a.assigns[idx] == 1 {
2271 if idx >= a.values.len() {
2272 a.values.resize(idx + 1, None);
2273 }
2274 a.values[idx] = Some(e.clone());
2275 } else if idx < a.values.len() {
2276 a.values[idx] = None;
2277 }
2278 vec![e]
2279 }
2280 Stmt::If { cond, .. } => vec![cond],
2281 Stmt::While { cond, .. } => vec![cond],
2282 Stmt::DoWhile { cond, .. } => vec![cond],
2283 _ => Vec::new(),
2284 };
2285 for e in exprs {
2286 visit_exprs(e, &mut |x| {
2287 if let Expr::Local { var, .. } = x {
2288 grow_to(&mut a.reads, *var);
2289 a.reads[*var as usize] += 1;
2290 }
2291 });
2292 }
2293 });
2294 let n = a.assigns.len().max(a.reads.len()).max(a.values.len());
2296 a.assigns.resize(n, 0);
2297 a.reads.resize(n, 0);
2298 a.values.resize(n, None);
2299}
2300
2301fn grow_to(v: &mut Vec<usize>, var: u32) {
2302 if var as usize >= v.len() {
2303 v.resize(var as usize + 1, 0);
2304 }
2305}
2306
2307fn count_assignments(s: &Stmt, counts: &mut Vec<usize>) {
2308 walk_all(s, &mut |st| match st {
2309 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
2310 if let Expr::Local { var, .. } = &**target {
2311 grow_to(counts, *var);
2312 counts[*var as usize] += 1;
2313 }
2314 }
2315 Stmt::LocalDef {
2316 var, init: Some(_), ..
2317 } => {
2318 grow_to(counts, *var);
2319 counts[*var as usize] += 1;
2320 }
2321 _ => {}
2322 });
2323}
2324
2325fn record_assignments(s: &Stmt, counts: &[usize], out: &mut Vec<Option<Expr>>) {
2326 walk_all(s, &mut |st| match st {
2327 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
2328 if let Expr::Local { var, .. } = &**target {
2329 if counts.get(*var as usize).copied().unwrap_or(0) == 1 {
2330 if *var as usize >= out.len() {
2331 out.resize(*var as usize + 1, None);
2332 }
2333 out[*var as usize] = Some((**value).clone());
2334 }
2335 }
2336 }
2337 Stmt::LocalDef {
2338 var, init: Some(e), ..
2339 } if counts.get(*var as usize).copied().unwrap_or(0) == 1 => {
2340 if *var as usize >= out.len() {
2341 out.resize(*var as usize + 1, None);
2342 }
2343 out[*var as usize] = Some(e.clone());
2344 }
2345 _ => {}
2346 });
2347}
2348
2349fn fold_concat_in_expr(e: &mut Expr, values: &[Option<Expr>], appended: &HashSet<u32>) {
2350 deep_rewrite(e, &mut |x| {
2351 if let Expr::Method {
2352 cls,
2353 name,
2354 args,
2355 owner,
2356 ..
2357 } = x
2358 {
2359 if name.as_ref() == "toString" && args.is_empty() && is_string_builder(cls) {
2360 if let Some(o) = owner {
2361 let mut has_stmt_appends = false;
2364 visit_exprs(o, &mut |y| {
2365 if let Expr::Local { var, .. } = y {
2366 if appended.contains(var) {
2367 has_stmt_appends = true;
2368 }
2369 }
2370 });
2371 if has_stmt_appends {
2372 return;
2373 }
2374 let resolved = resolve_local(o, values);
2375 if let Some(parts) = collect_sb_parts(&resolved, values, 0) {
2376 if !parts.is_empty() {
2385 *x = Expr::StringConcat(parts);
2386 }
2387 }
2388 }
2389 }
2390 }
2391 });
2392}
2393
2394fn is_string_builder(cls: &str) -> bool {
2395 cls == "java/lang/StringBuilder" || cls == "java/lang/StringBuffer"
2396}
2397
2398fn resolve_local(e: &Expr, values: &[Option<Expr>]) -> Expr {
2399 match e {
2400 Expr::Local { var, ty } => values
2401 .get(*var as usize)
2402 .and_then(|o| o.clone())
2403 .unwrap_or_else(|| Expr::Local {
2404 var: *var,
2405 ty: ty.clone(),
2406 }),
2407 other => other.clone(),
2408 }
2409}
2410
2411fn collect_sb_parts(e: &Expr, values: &[Option<Expr>], depth: u32) -> Option<Vec<ConcatPart>> {
2413 if depth > 16 {
2414 return None;
2415 }
2416 match e {
2417 Expr::Method {
2418 cls,
2419 name,
2420 args,
2421 owner,
2422 ..
2423 } if name.as_ref() == "append" && is_string_builder(cls) && args.len() == 1 => {
2424 let mut parts =
2425 collect_sb_parts(&resolve_local(owner.as_deref()?, values), values, depth + 1)?;
2426 parts.push(ConcatPart::Str(args[0].clone()));
2427 Some(parts)
2428 }
2429 Expr::New {
2430 cls,
2431 args,
2432 raw: false,
2433 ..
2434 } if is_string_builder(cls) => {
2435 let mut parts = Vec::new();
2436 for a in args {
2437 if let Expr::Const(ConstVal::Str(sv)) = a {
2438 parts.push(ConcatPart::Const(sv.to_string()));
2439 } else {
2440 parts.push(ConcatPart::Str(a.clone()));
2441 }
2442 }
2443 Some(parts)
2444 }
2445 _ => None,
2446 }
2447}
2448
2449fn drop_unused_assigns(s: &mut Stmt, reads: &HashSet<u32>) -> usize {
2450 let mut dropped = 0usize;
2451 walk_mut_deep(s, &mut |st| {
2452 if let Stmt::Block(v) = st {
2453 let before = v.len();
2454 v.retain(|x| match x {
2455 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
2458 Expr::Local { var, .. } => reads.contains(var) || !is_sbish(value),
2459 _ => true,
2460 },
2461 Stmt::LocalDef { var, init, .. } => {
2462 reads.contains(var) || !init.as_ref().map(is_sbish).unwrap_or(false)
2463 }
2464 _ => true,
2465 });
2466 dropped += before - v.len();
2467 }
2468 });
2469 dropped
2470}
2471
2472fn is_sbish(e: &Expr) -> bool {
2473 match e {
2474 Expr::New {
2475 cls, raw: false, ..
2476 } => is_string_builder(cls),
2477 Expr::Method { name, cls, .. } => name.as_ref() == "append" && is_string_builder(cls),
2478 _ => false,
2479 }
2480}
2481
2482fn walk_mut_deep<F: FnMut(&mut Stmt)>(s: &mut Stmt, f: &mut F) {
2483 f(s);
2484 walk_mut(s, &mut |x| walk_mut_deep(x, f));
2485}
2486
2487pub fn drop_dead_raw_news(body: &mut Stmt) {
2499 fn is_orphan(s: &Stmt) -> bool {
2500 matches!(s, Stmt::ExprStmt(Expr::New { raw: true, .. }))
2501 }
2502 walk_mut_deep(body, &mut |st| match st {
2503 Stmt::Block(v) => v.retain(|x| !is_orphan(x)),
2504 Stmt::Switch { cases, .. } => {
2505 for c in cases.iter_mut() {
2506 c.body.retain(|x| !is_orphan(x));
2507 }
2508 }
2509 Stmt::For { init, .. } => init.retain(|x| !is_orphan(x)),
2510 Stmt::TryWithResources { resources, .. } => resources.retain(|x| !is_orphan(x)),
2511 _ => {}
2512 });
2513}
2514
2515pub fn fold_synchronized(s: &mut Stmt) {
2519 fold_sync_walk(s);
2520}
2521
2522fn fold_sync_walk(s: &mut Stmt) {
2523 walk_mut_deep(s, &mut |st| {
2524 let Stmt::Block(v) = st else { return };
2525 let mut i = 0;
2526 while i < v.len() {
2527 if i + 1 < v.len() {
2528 if let Some(sync) = try_sync_at(v, i) {
2529 let _ = v.drain(i..i + 2);
2530 v.insert(i, sync);
2531 continue;
2532 }
2533 }
2534 i += 1;
2535 }
2536 });
2537}
2538
2539fn try_sync_at(v: &[Stmt], i: usize) -> Option<Stmt> {
2540 let Stmt::MonitorEnter(lock) = &v[i] else {
2541 return None;
2542 };
2543 let Stmt::Try {
2544 body,
2545 catches,
2546 finally,
2547 } = &v[i + 1]
2548 else {
2549 return None;
2550 };
2551 if finally.is_some() || catches.len() != 1 {
2552 return None;
2553 }
2554 let c = &catches[0];
2555 if !c.exc.is_empty() {
2556 return None;
2557 }
2558 let Stmt::Block(cv) = c.body.as_ref() else {
2560 return None;
2561 };
2562 let exit_matches = cv.len() >= 2
2563 && matches!(&cv[0], Stmt::MonitorExit(e) if expr_local_var(e) == expr_local_var(lock));
2564 let throws = cv.len() >= 2 && matches!(&cv[1], Stmt::Throw(_));
2565 if !(exit_matches && throws) {
2566 return None;
2567 }
2568 Some(Stmt::Synchronized {
2569 lock: lock.clone(),
2570 body: body.clone(),
2571 })
2572}
2573
2574fn expr_local_var(e: &Expr) -> Option<u32> {
2575 if let Expr::Local { var, .. } = e {
2576 Some(*var)
2577 } else {
2578 None
2579 }
2580}
2581
2582pub fn forward_single_use(s: &mut Stmt, _vt: &VarTable) {
2587 let mut analysis = VarAnalysis {
2591 assigns: Vec::new(),
2592 reads: Vec::new(),
2593 values: Vec::new(),
2594 };
2595 analyze_vars(s, &mut analysis);
2596 let mut assigns = analysis.assigns;
2597 let mut reads = analysis.reads;
2598 let n_vars = assigns.len().max(_vt.vars.len()).max(1);
2601 assigns.resize(n_vars, 0);
2602 reads.resize(n_vars, 0);
2603 let mut values = analysis.values;
2604 values.resize(n_vars, None);
2605
2606 let mut cand = vec![false; n_vars];
2611 let mut edges: Vec<Vec<u32>> = vec![Vec::new(); n_vars];
2612 for (v, cand_v) in cand.iter_mut().enumerate() {
2613 if assigns.get(v).copied().unwrap_or(0) != 1 || reads.get(v).copied().unwrap_or(0) != 1 {
2614 continue;
2615 }
2616 if let Some(val) = values.get(v).and_then(|o| o.as_ref()) {
2617 let mut refs = HashSet::default();
2618 collect_vars(val, &mut refs);
2619 if refs.iter().any(|r| assigns[*r as usize] > 1) {
2620 continue;
2621 }
2622 edges[v] = refs.into_iter().collect();
2626 }
2627 *cand_v = true;
2628 }
2629
2630 let mut color = vec![0u8; n_vars]; let mut bad = vec![false; n_vars];
2648 for root in 0..n_vars {
2649 if color[root] != 0 || edges[root].is_empty() {
2650 continue;
2651 }
2652 color[root] = 1;
2653 let mut stack: Vec<(usize, usize)> = vec![(root, 0)];
2654 while let Some(top) = stack.last_mut() {
2655 let v = top.0;
2656 let i = top.1;
2657 top.1 += 1;
2658 if i < edges[v].len() {
2659 let r = edges[v][i] as usize;
2660 if color[r] == 1 {
2661 for &(pn, _) in &stack {
2662 bad[pn] = true;
2663 }
2664 } else if color[r] == 0 {
2665 color[r] = 1;
2666 stack.push((r, 0));
2667 } else if bad[r] {
2668 for &(pn, _) in &stack {
2669 bad[pn] = true;
2670 }
2671 }
2672 } else {
2673 color[v] = 2;
2674 stack.pop();
2675 }
2676 }
2677 }
2678 let single: Vec<bool> = (0..n_vars).map(|v| cand[v] && !bad[v]).collect();
2679 if !single.iter().any(|&b| b) {
2680 return;
2681 }
2682 let pure: Vec<bool> = (0..n_vars)
2683 .map(|v| {
2684 single[v]
2685 && values
2686 .get(v)
2687 .and_then(|o| o.as_ref())
2688 .map(|e| !has_side_effects(e))
2689 .unwrap_or(false)
2690 })
2691 .collect();
2692 let impure: Vec<bool> = (0..n_vars).map(|v| single[v] && !pure[v]).collect();
2693
2694 if pure.iter().any(|&b| b) {
2696 let vals: Vec<Option<Expr>> = (0..n_vars)
2697 .map(|v| {
2698 if pure[v] {
2699 values.get(v).cloned().flatten()
2700 } else {
2701 None
2702 }
2703 })
2704 .collect();
2705 rewrite_exprs(s, &mut |e| {
2706 deep_rewrite_reads(e, &mut |x| {
2707 if let Expr::Local { var, .. } = x {
2708 if let Some(Some(v)) = vals.get(*var as usize) {
2709 *x = v.clone();
2710 }
2711 }
2712 });
2713 });
2714 drop_defs(s, &pure);
2715 }
2716
2717 if impure.iter().any(|&b| b) {
2720 forward_adjacent_impure(s, &values, &impure);
2721 }
2722}
2723
2724fn count_reads(s: &Stmt, out: &mut Vec<usize>) {
2726 walk_all(s, &mut |st| {
2727 let exprs: Vec<&Expr> = match st {
2728 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
2731 let mut v: Vec<&Expr> = vec![value];
2732 if !matches!(&**target, Expr::Local { .. }) {
2733 v.push(target);
2734 }
2735 v
2736 }
2737 Stmt::ExprStmt(e @ (Expr::PreIncDec { .. } | Expr::PostIncDec { .. })) => {
2740 let inner = match e {
2741 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => e,
2742 _ => unreachable!(),
2743 };
2744 if matches!(**inner, Expr::Local { .. }) {
2745 Vec::new()
2746 } else {
2747 vec![e]
2748 }
2749 }
2750 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
2751 vec![e]
2752 }
2753 Stmt::Return(Some(e)) => vec![e],
2754 Stmt::LocalDef { init: Some(e), .. } => vec![e],
2755 Stmt::If { cond, .. } => vec![cond],
2756 Stmt::While { cond, .. } => vec![cond],
2757 Stmt::DoWhile { cond, .. } => vec![cond],
2758 Stmt::Switch { selector, .. } => vec![selector],
2764 Stmt::ForEach { iterable, .. } => vec![iterable],
2765 Stmt::Synchronized { lock, .. } => vec![lock],
2766 Stmt::For { cond, update, .. } => {
2767 let mut v: Vec<&Expr> = Vec::with_capacity(update.len() + 1);
2768 if let Some(c) = cond {
2769 v.push(c);
2770 }
2771 v.extend(update.iter());
2772 v
2773 }
2774 Stmt::Assert { cond, msg } => {
2775 let mut v: Vec<&Expr> = vec![cond];
2776 if let Some(m) = msg {
2777 v.push(m);
2778 }
2779 v
2780 }
2781 _ => Vec::new(),
2782 };
2783 for e in exprs {
2784 visit_exprs(e, &mut |x| {
2785 if let Expr::Local { var, .. } = x {
2786 grow_to(out, *var);
2787 out[*var as usize] += 1;
2788 }
2789 });
2790 }
2791 });
2792}
2793
2794fn drop_defs(s: &mut Stmt, vars: &[bool]) {
2795 walk_mut_deep(s, &mut |st| {
2796 if let Stmt::Block(v) = st {
2797 v.retain(|x| match x {
2798 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
2799 Expr::Local { var, .. } => !vars.get(*var as usize).copied().unwrap_or(false),
2800 _ => true,
2801 },
2802 Stmt::LocalDef { var, .. } => !vars.get(*var as usize).copied().unwrap_or(false),
2803 _ => true,
2804 });
2805 }
2806 });
2807}
2808
2809fn forward_adjacent_impure(s: &mut Stmt, values: &[Option<Expr>], impure: &[bool]) {
2811 if let Stmt::Block(v) = s {
2812 let mut i = 0;
2813 while i + 1 < v.len() {
2814 let def_var = match &v[i] {
2815 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
2816 Expr::Local { var, .. } => Some(*var),
2817 _ => None,
2818 },
2819 Stmt::LocalDef { var, .. } => Some(*var),
2820 _ => None,
2821 };
2822 if let Some(var) = def_var {
2823 if impure.get(var as usize).copied().unwrap_or(false) {
2824 let mut reads_here = vec![0usize; values.len().max(1)];
2826 count_reads(&v[i + 1], &mut reads_here);
2827 if reads_here.get(var as usize).copied().unwrap_or(0) == 1 {
2828 let val = match &v[i] {
2832 Stmt::ExprStmt(Expr::Assign { value, .. }) => (**value).clone(),
2833 Stmt::LocalDef { init: Some(e), .. } => (*e).clone(),
2834 _ => {
2835 i += 1;
2836 continue;
2837 }
2838 };
2839 rewrite_exprs(&mut v[i + 1], &mut |e| {
2840 deep_rewrite_reads(e, &mut |x| {
2841 if let Expr::Local { var: v2, .. } = x {
2842 if *v2 == var {
2843 *x = val.clone();
2844 }
2845 }
2846 });
2847 });
2848 v.remove(i);
2849 i = i.saturating_sub(1);
2852 continue;
2853 }
2854 }
2855 }
2856 i += 1;
2857 }
2858 }
2859 walk_mut(s, &mut |x| forward_adjacent_impure(x, values, impure));
2860}
2861
2862pub fn infer_types(vt: &mut VarTable, body: &mut Stmt, ret: &JavaType, env: &MethodEnv) {
2868 let n = vt.vars.len();
2869 let mut evidence: Vec<Vec<(JavaType, bool)>> = vec![Vec::new(); n];
2877 let ev = |evidence: &mut Vec<Vec<(JavaType, bool)>>, var: u32, t: JavaType, strong: bool| {
2878 if (var as usize) < evidence.len() {
2879 evidence[var as usize].push((t, strong));
2880 }
2881 };
2882
2883 walk_all(body, &mut |st| match st {
2884 Stmt::LocalDef { var, init, .. } => {
2885 if let Some(e) = init {
2886 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
2887 let strong = match e {
2895 Expr::Const(_) => false,
2896 Expr::Local { var: src, .. } => {
2897 let src_ty = vt.vars.get(*src as usize).map(|v| v.ty.erased());
2898 let tgt_ty = vt.vars.get(*var as usize).map(|v| v.ty.erased());
2899 match (src_ty, tgt_ty) {
2900 (Some(s), Some(g)) => {
2901 (s.is_numeric() && g.is_numeric())
2902 || (s.is_reference() && g.is_reference())
2903 }
2904 _ => false,
2905 }
2906 }
2907 _ => true,
2908 };
2909 ev(&mut evidence, *var, e.type_ref().erased(), strong);
2910 }
2911 }
2912 Stmt::ExprStmt(e) => {
2913 if let Expr::Assign { target, value, .. } = e {
2920 if let Expr::Field { owner: Some(o), cls, is_static: false, .. } = &**target {
2927 if cls.is_empty() {
2928 } else if let Expr::Local { var, .. } = &**o {
2930 ev(
2931 &mut evidence,
2932 *var,
2933 JavaType::Object(cls.clone()),
2934 true,
2935 );
2936 }
2937 }
2938 if let Expr::Local { var, .. } = &**target {
2939 let strong = match &**value {
2940 Expr::Const(_) => false,
2941 Expr::Local { var: src, .. } => {
2942 let src_ty = vt.vars.get(*src as usize).map(|v| v.ty.erased());
2949 let tgt_ty = vt.vars.get(*var as usize).map(|v| v.ty.erased());
2950 match (src_ty, tgt_ty) {
2951 (Some(s), Some(g)) => {
2952 (s.is_numeric() && g.is_numeric())
2953 || (s.is_reference() && g.is_reference())
2954 }
2955 _ => false,
2956 }
2957 }
2958 _ => true,
2959 };
2960 ev(&mut evidence, *var, value.type_ref().erased(), strong);
2961 }
2962 }
2963 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
2964 }
2965 Stmt::Throw(e) => {
2966 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
2967 if let Expr::Local { var, .. } = e {
2968 ev(
2969 &mut evidence,
2970 *var,
2971 JavaType::Object("java/lang/Throwable".into()),
2972 true,
2973 );
2974 }
2975 }
2976 Stmt::Return(Some(e)) => {
2977 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
2978 if let Expr::Local { var, .. } = e {
2979 ev(&mut evidence, *var, ret.clone(), true);
2980 }
2981 }
2982 Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
2983 if let Expr::Local { var, .. } = e {
2984 ev(
2985 &mut evidence,
2986 *var,
2987 JavaType::Object("java/lang/Object".into()),
2988 true,
2989 );
2990 }
2991 }
2992 Stmt::ForEach {
2993 var,
2994 iterable,
2995 is_array,
2996 ..
2997 } => {
2998 if let Expr::Local { var: v0, .. } = iterable {
2999 if *is_array {
3000 ev(&mut evidence, *v0, JavaType::Array(Box::new(JavaType::Int)), true);
3001 } else {
3002 ev(
3003 &mut evidence,
3004 *v0,
3005 JavaType::Object("java/lang/Object".into()),
3006 true,
3007 );
3008 }
3009 }
3010 let _ = var;
3011 }
3012 _ => {}
3013 });
3014 let _ = env;
3015
3016 let mut derefed: Vec<bool> = vec![false; vt.vars.len()];
3026 mark_derefs(body, &mut derefed);
3027 let mut arr_derefed: Vec<bool> = vec![false; vt.vars.len()];
3032 visit_all_exprs(body, &mut |x| {
3033 if let Expr::ArrayIndex { array, .. } = x {
3034 if let Expr::Local { var, .. } = &**array {
3035 if (*var as usize) < arr_derefed.len() {
3036 arr_derefed[*var as usize] = true;
3037 }
3038 }
3039 }
3040 if let Expr::Bin { op, l, r, .. } = x {
3048 if matches!(op, BinOp::Eq | BinOp::Ne)
3049 && (matches!(&**r, Expr::Const(ConstVal::Null))
3050 || matches!(&**l, Expr::Const(ConstVal::Null)))
3051 {
3052 for side in [l, r] {
3053 if let Expr::Local { var, .. } = &**side {
3054 ev(
3055 &mut evidence,
3056 *var,
3057 JavaType::Object("java/lang/Object".into()),
3058 true,
3059 );
3060 }
3061 }
3062 }
3063 }
3064 });
3065 const FINAL_JDK: &[&str] = &[
3066 "java/lang/Boolean",
3067 "java/lang/String",
3068 "java/lang/Integer",
3069 "java/lang/Long",
3070 "java/lang/Short",
3071 "java/lang/Byte",
3072 "java/lang/Character",
3073 "java/lang/Float",
3074 "java/lang/Double",
3075 ];
3076 for (i, evs) in evidence.iter().enumerate() {
3077 let info = &vt.vars[i];
3078 if info.is_param {
3079 continue;
3080 }
3081 let strong: Vec<JavaType> = evs
3082 .iter()
3083 .filter(|(_, s)| *s)
3084 .map(|(t, _)| t.clone())
3085 .collect();
3086 let all: Vec<JavaType> = evs.iter().map(|(t, _)| t.clone()).collect();
3087 if info.ty.erased().is_reference() {
3088 if strong.is_empty() {
3094 let refs: std::collections::HashSet<&str> = all
3095 .iter()
3096 .filter_map(|t| match t {
3097 JavaType::Object(n) if n.as_ref() != "java/lang/Object" => {
3098 Some(n.as_ref())
3099 }
3100 _ => None,
3101 })
3102 .collect();
3103 let mixed = all.iter().any(|t| t.is_numeric());
3104 if refs.len() >= 2 || (mixed && !refs.is_empty()) || (mixed && all.iter().any(|t| matches!(t, JavaType::Array(_)))) {
3105 vt.vars[i].ty =
3106 TypeRef::J(JavaType::Object("java/lang/Object".into()));
3107 continue;
3108 }
3109 }
3110 if arr_derefed[i] {
3113 let arr = strong
3114 .iter()
3115 .chain(all.iter())
3116 .find(|t| matches!(t, JavaType::Array(_)))
3117 .cloned();
3118 if let Some(t) = arr {
3119 vt.vars[i].ty = TypeRef::J(t);
3120 continue;
3121 }
3122 }
3123 let deref_pick = || {
3127 if !derefed[i] {
3128 return None;
3129 }
3130 strong
3131 .iter()
3132 .find(|t| {
3133 matches!(t, JavaType::Object(n)
3134 if !n.is_empty()
3135 && n.as_ref() != "java/lang/Object"
3136 && !FINAL_JDK.contains(&n.as_ref()))
3137 })
3138 .cloned()
3139 };
3140 if let Some(t) = deref_pick()
3141 .or_else(|| pick_object(&strong))
3142 .or_else(|| pick_object(&all))
3143 {
3144 vt.vars[i].ty = TypeRef::J(t);
3145 }
3146 } else if let Some(t) = pick_object(&strong) {
3147 vt.vars[i].ty = TypeRef::J(t);
3151 } else if strong.contains(&JavaType::Boolean) {
3152 vt.vars[i].ty = TypeRef::J(JavaType::Boolean);
3156 } else if !all.is_empty() && all.iter().all(|t| t.is_numeric()) {
3157 if let Some(t) = pick_numeric(&all) {
3158 vt.vars[i].ty = TypeRef::J(t);
3159 }
3160 } else {
3161 let refs = all.iter().filter(|t| t.is_reference()).count();
3165 let nums = all.iter().filter(|t| t.is_numeric()).count();
3166 if refs > 0 && nums > 0 {
3167 vt.vars[i].ty =
3168 TypeRef::J(JavaType::Object("java/lang/Object".into()));
3169 } else if let Some(t) = pick_object(&strong).or_else(|| pick_object(&all)) {
3170 vt.vars[i].ty = TypeRef::J(t);
3171 }
3172 }
3173 }
3174
3175 let types: Vec<&TypeRef> = vt.vars.iter().map(|v| &v.ty).collect();
3180 rewrite_exprs(body, &mut |e| {
3181 deep_rewrite(e, &mut |x| {
3182 if let Expr::Local { var, ty } = x {
3183 if let Some(want) = types.get(*var as usize) {
3184 if ty != *want {
3185 *ty = (*want).clone();
3186 }
3187 }
3188 }
3189 });
3190 });
3191
3192 coerce_num_consts(body, &types);
3195}
3196
3197fn coerce_num_consts(body: &mut Stmt, types: &[&TypeRef]) {
3198 walk_mut_deep(body, &mut |st| {
3199 let (var, val): (u32, &mut Expr) = match st {
3200 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &mut **target {
3201 Expr::Local { var, .. } => (*var, value),
3202 _ => return,
3203 },
3204 Stmt::LocalDef {
3205 var, init: Some(e), ..
3206 } => (*var, e),
3207 _ => return,
3208 };
3209 let want = match types.get(var as usize) {
3210 Some(TypeRef::J(t)) => t.clone(),
3211 _ => return,
3212 };
3213 let rewritten = match &*val {
3214 Expr::Const(ConstVal::Long(l)) => match &want {
3215 JavaType::Double => Some(Expr::Const(ConstVal::Double(*l as f64))),
3216 JavaType::Float => Some(Expr::Const(ConstVal::Float(*l as f32))),
3217 _ => None,
3218 },
3219 Expr::Const(ConstVal::Int(i)) => match &want {
3220 JavaType::Double => Some(Expr::Const(ConstVal::Double(*i as f64))),
3221 JavaType::Float => Some(Expr::Const(ConstVal::Float(*i as f32))),
3222 _ => None,
3223 },
3224 _ => None,
3225 };
3226 if let Some(e) = rewritten {
3227 *val = e;
3228 }
3229 });
3230}
3231
3232fn pick_object(evs: &[JavaType]) -> Option<JavaType> {
3233 evs.iter()
3234 .find(|t| {
3235 t.is_reference()
3236 && !matches!(t, JavaType::Object(n) if n.is_empty() || n.as_ref() == "java/lang/Object")
3237 })
3238 .cloned()
3239}
3240
3241fn pick_numeric(evs: &[JavaType]) -> Option<JavaType> {
3242 if evs.is_empty() {
3243 return None;
3244 }
3245 let mut t = evs[0].clone();
3246 for x in &evs[1..] {
3247 t = join_numeric(&t, x);
3248 }
3249 Some(t)
3250}
3251
3252fn join_numeric(a: &JavaType, b: &JavaType) -> JavaType {
3253 if a == b {
3254 return a.clone();
3255 }
3256 match (a, b) {
3257 (JavaType::Double, _) | (_, JavaType::Double) => JavaType::Double,
3258 (JavaType::Float, _) | (_, JavaType::Float) => JavaType::Float,
3259 (JavaType::Long, _) | (_, JavaType::Long) => JavaType::Long,
3260 (JavaType::Int, x) if x.is_integral() => JavaType::Int,
3261 (x, JavaType::Int) if x.is_integral() => JavaType::Int,
3262 (x, y) if x.is_integral() && y.is_integral() => JavaType::Int,
3263 _ => JavaType::Int,
3264 }
3265}
3266
3267fn expr_evidence<F: FnMut(u32, JavaType)>(e: &Expr, f: &mut F) {
3269 visit_exprs(e, &mut |x| match x {
3270 Expr::Method {
3271 owner,
3272 args,
3273 desc,
3274 is_static,
3275 cls,
3276 ..
3277 } => {
3278 if !*is_static {
3279 if let Some(o) = owner {
3280 if let Expr::Local { var, .. } = &**o {
3281 f(*var, JavaType::Object(cls.clone()));
3282 }
3283 }
3284 }
3285 for (i, a) in args.iter().enumerate() {
3286 if let Expr::Local { var, .. } = a {
3287 if let Some(t) = desc.args.get(i) {
3288 f(*var, t.clone());
3289 }
3290 }
3291 }
3292 }
3293 Expr::Field {
3294 owner,
3295 cls,
3296 ty,
3297 is_static,
3298 ..
3299 } => {
3300 if !*is_static && !cls.is_empty() {
3301 if let Some(o) = owner {
3302 if let Expr::Local { var, .. } = &**o {
3303 f(*var, JavaType::Object(cls.clone()));
3313 }
3314 }
3315 }
3316 let _ = ty;
3317 }
3318 Expr::Bin { op, l, r, .. }
3323 if matches!(op, BinOp::Eq | BinOp::Ne)
3324 && (matches!(&**r, Expr::Const(ConstVal::Null))
3325 || matches!(&**l, Expr::Const(ConstVal::Null))) =>
3326 {
3327 for side in [l, r] {
3328 if let Expr::Local { var, .. } = &**side {
3329 f(*var, JavaType::Object("java/lang/Object".into()));
3330 }
3331 }
3332 }
3333 Expr::ArrayIndex { array, .. } => {
3334 if let Expr::Local { var, ty, .. } = &**array {
3335 let t = ty.erased();
3340 if matches!(t, JavaType::Array(_)) {
3341 f(*var, t);
3342 } else {
3343 f(*var, JavaType::Array(Box::new(JavaType::Int)));
3344 }
3345 }
3346 }
3347 Expr::Assign { .. } => {
3348 }
3351 Expr::Cast { e: inner, .. } => {
3352 let _ = inner;
3358 }
3359 Expr::InstanceOf { e: inner, .. } => {
3360 let _ = inner;
3361 }
3362 _ => {}
3363 });
3364}
3365
3366pub fn mark_field_owner_concrete(vt: &mut VarTable, body: &mut Stmt) {
3378 let mut owners: HashSet<u32> = HashSet::default();
3379 rewrite_exprs(body, &mut |e| {
3380 visit_exprs(e, &mut |x| {
3381 if let Expr::Field {
3382 owner: Some(o),
3383 is_static: false,
3384 ..
3385 } = x
3386 {
3387 if let Expr::Local { var, .. } = &**o {
3388 owners.insert(*var);
3389 }
3390 }
3391 });
3392 });
3393 for var in owners {
3394 let i = var as usize;
3395 if i >= vt.vars.len() {
3396 continue;
3397 }
3398 if let JavaType::Object(cls) = vt.vars[i].ty.erased() {
3399 if let Some(last) = cls.rsplit('$').next() {
3400 if !last.is_empty() && last.chars().all(|c| c.is_ascii_digit()) {
3401 vt.force_concrete_vars.insert(var);
3402 }
3403 }
3404 }
3405 }
3406 let concrete: Vec<u32> = vt
3415 .vars
3416 .iter()
3417 .filter(|v| {
3418 if let JavaType::Object(cls) = v.ty.erased() {
3419 cls.rsplit('$')
3420 .next()
3421 .is_some_and(|l| !l.is_empty() && l.chars().all(|c| c.is_ascii_digit()))
3422 } else {
3423 false
3424 }
3425 })
3426 .map(|v| v.id)
3427 .collect();
3428 for var in concrete {
3429 vt.force_concrete_vars.insert(var);
3430 }
3431}
3432
3433pub fn insert_object_narrowing_casts(
3445 vt: &VarTable,
3446 body: &mut Stmt,
3447 pool: &DexPool,
3448 ret: &JavaType,
3449) {
3450 let value_ty = |e: &Expr| -> JavaType {
3453 match e {
3454 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
3455 other => other.type_ref().erased(),
3456 }
3457 };
3458 let is_top_object = |e: &Expr| -> bool {
3459 value_ty(e) == JavaType::Object("java/lang/Object".into())
3460 };
3461 let specific_ref = |ty: &TypeRef| -> Option<TypeRef> {
3465 match ty.erased() {
3466 JavaType::Object(c) if c.as_ref() != "java/lang/Object" => Some(ty.clone()),
3467 JavaType::Array(_) => Some(ty.clone()),
3468 _ => None,
3469 }
3470 };
3471 let castable = |e: &Expr| -> bool {
3472 is_top_object(e)
3473 && !matches!(e, Expr::Const(_) | Expr::Cast { .. } | Expr::InstanceOf { .. })
3474 };
3475 let needs_downcast = |e: &Expr, tt: &JavaType| -> bool {
3485 if matches!(e, Expr::Const(_) | Expr::Cast { .. } | Expr::InstanceOf { .. }) {
3486 return false;
3487 }
3488 let v = value_ty(e);
3489 let (JavaType::Object(vn), JavaType::Object(tn)) = (&v, tt) else {
3490 return false;
3491 };
3492 if vn.as_ref() == "java/lang/Object" || tn.as_ref() == "java/lang/Object" {
3493 return false;
3494 }
3495 vn != tn && !pool.is_subtype(vn, tn)
3496 };
3497 walk_mut_deep(body, &mut |st| match st {
3498 Stmt::ExprStmt(Expr::Assign {
3499 target,
3500 value,
3501 op: AssignOp::Plain,
3502 ..
3503 }) => {
3504 let tgt = match &**target {
3505 Expr::Local { var, .. } => vt.var(*var).ty.clone(),
3506 Expr::Field { ty, .. } => ty.clone(),
3507 Expr::ArrayIndex { array, .. } => {
3514 let aty = match &**array {
3515 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
3516 other => other.type_ref().erased(),
3517 };
3518 match aty {
3519 JavaType::Array(inner) => TypeRef::J(*inner),
3520 _ => return,
3521 }
3522 }
3523 _ => return,
3524 };
3525 if let Some(t) = specific_ref(&tgt) {
3526 if castable(value) || needs_downcast(value, &tgt.erased()) {
3527 let v = std::mem::replace(value, Box::new(Expr::This));
3528 **value = Expr::Cast { ty: t, e: v };
3529 }
3530 }
3531 }
3532 Stmt::LocalDef { var, init: Some(value), .. } => {
3533 if let Some(t) = specific_ref(&vt.var(*var).ty) {
3534 let tt = vt.var(*var).ty.erased();
3535 if castable(value) || needs_downcast(value, &tt) {
3536 let v = std::mem::replace(value, Expr::This);
3537 *value = Expr::Cast {
3538 ty: t,
3539 e: Box::new(v),
3540 };
3541 }
3542 }
3543 }
3544 Stmt::Return(Some(value)) => {
3545 if let Some(t) = specific_ref(&TypeRef::J(ret.clone())) {
3546 if castable(value) || needs_downcast(value, ret) {
3547 let v = std::mem::replace(value, Expr::This);
3548 *value = Expr::Cast { ty: t, e: Box::new(v) };
3549 }
3550 }
3551 }
3552 _ => {}
3553 });
3554 walk_stmt_exprs(body, &mut |e| {
3561 deep_rewrite(e, &mut |x| {
3562 let (args, arg_tys): (&mut Vec<Expr>, &[JavaType]) = match x {
3563 Expr::Method { desc, args, is_dynamic: false, .. } => {
3564 (args, desc.args.as_slice())
3565 }
3566 Expr::New { arg_tys, args, .. } => (args, arg_tys.as_slice()),
3570 _ => return,
3571 };
3572 for (i, a) in args.iter_mut().enumerate() {
3573 let Some(formal) = arg_tys.get(i) else {
3574 continue;
3575 };
3576 let Some(t) = specific_ref(&TypeRef::J(formal.clone())) else {
3577 continue;
3578 };
3579 if !(castable(a) || needs_downcast(a, formal)) {
3580 continue;
3581 }
3582 let v = std::mem::replace(a, Expr::Const(ConstVal::Null));
3583 *a = Expr::Cast { ty: t, e: Box::new(v) };
3584 }
3585 });
3586 });
3587}
3588
3589pub fn fix_primitive_assign_casts(vt: &VarTable, body: &mut Stmt, ret_ty: &JavaType) {
3598 fn numlike(t: &JavaType) -> bool {
3599 matches!(
3600 t,
3601 JavaType::Byte
3602 | JavaType::Short
3603 | JavaType::Int
3604 | JavaType::Char
3605 | JavaType::Long
3606 | JavaType::Float
3607 | JavaType::Double
3608 )
3609 }
3610 fn val_ty(e: &Expr, vt: &VarTable) -> JavaType {
3611 match e {
3612 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
3613 vt.var(*var).ty.erased()
3614 }
3615 other => other.type_ref().erased(),
3616 }
3617 }
3618 fn wrap(ty: &JavaType, value: &mut Expr) {
3619 if matches!(value, Expr::Cast { .. }) {
3620 return;
3621 }
3622 if val_ty_static(value) == *ty {
3623 return;
3624 }
3625 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
3626 *value = Expr::Cast {
3627 ty: TypeRef::J(ty.clone()),
3628 e: Box::new(taken),
3629 };
3630 }
3631 fn val_ty_static(e: &Expr) -> JavaType {
3632 e.type_ref().erased()
3633 }
3634 fn coerce(tt: &JavaType, value: &mut Expr, vt: &VarTable) {
3642 match (&*value, tt) {
3647 (Expr::Const(ConstVal::Int(b)), JavaType::Float) => {
3648 *value = Expr::Const(ConstVal::Float(f32::from_bits(*b as u32)));
3649 return;
3650 }
3651 (Expr::Const(ConstVal::Long(b)), JavaType::Double) => {
3652 *value = Expr::Const(ConstVal::Double(f64::from_bits(*b as u64)));
3653 return;
3654 }
3655 (Expr::Const(ConstVal::Float(f)), JavaType::Int) => {
3656 *value = Expr::Const(ConstVal::Int(f.to_bits() as i32));
3657 return;
3658 }
3659 (Expr::Const(ConstVal::Double(d)), JavaType::Long) => {
3660 *value = Expr::Const(ConstVal::Long(d.to_bits() as i64));
3661 return;
3662 }
3663 _ => {}
3664 }
3665 let vt_val = val_ty(value, vt);
3666 let tt_bool = matches!(tt, JavaType::Boolean);
3667 let val_bool = matches!(vt_val, JavaType::Boolean);
3668 if tt_bool && !val_bool && numlike(&vt_val) {
3669 if matches!(
3670 value,
3671 Expr::Const(_)
3672 | Expr::Bin { op: BinOp::Eq | BinOp::Ne, .. }
3673 | Expr::Un { op: UnOp::Not, .. }
3674 ) {
3675 return;
3680 }
3681 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
3682 *value = Expr::Bin {
3683 op: BinOp::Ne,
3684 l: Box::new(taken),
3685 r: Box::new(Expr::Const(ConstVal::Int(0))),
3686 ty: Some(TypeRef::J(JavaType::Boolean)),
3687 };
3688 } else if !tt_bool && val_bool && numlike(tt) {
3689 if matches!(value, Expr::Cast { .. }) {
3690 return;
3691 }
3692 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
3693 *value = Expr::Cast {
3694 ty: TypeRef::J(tt.clone()),
3695 e: Box::new(Expr::Cond {
3696 c: Box::new(taken),
3697 t: Box::new(Expr::Const(ConstVal::Int(1))),
3698 f: Box::new(Expr::Const(ConstVal::Int(0))),
3699 }),
3700 };
3701 } else if numlike(tt) && numlike(&vt_val) {
3702 wrap(tt, value);
3703 }
3704 }
3705 walk_mut_deep(body, &mut |st| match st {
3706 Stmt::ExprStmt(Expr::Assign {
3707 target,
3708 op: AssignOp::Plain,
3709 value,
3710 }) => {
3711 let tt = match &**target {
3712 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
3713 vt.var(*var).ty.erased()
3714 }
3715 Expr::Field { ty, .. } => ty.erased(),
3716 Expr::ArrayIndex { array, .. } => match array.type_ref().erased() {
3720 JavaType::Array(inner) => *inner,
3721 _ => return,
3722 },
3723 _ => return,
3724 };
3725 coerce(&tt, value, vt);
3726 }
3727 Stmt::LocalDef {
3728 var,
3729 init: Some(value),
3730 ..
3731 } => {
3732 if (*var as usize) >= vt.vars.len() {
3733 return;
3734 }
3735 let tt = vt.var(*var).ty.erased();
3736 coerce(&tt, value, vt);
3737 }
3738 Stmt::Return(Some(e)) => {
3739 if !numlike(ret_ty) || !numlike(&val_ty(e, vt)) {
3740 return;
3741 }
3742 wrap(ret_ty, e);
3743 }
3744 _ => {}
3745 });
3746}
3747
3748pub fn fix_shadowed_super_fields(
3772 body: &mut Stmt,
3773 vt: &VarTable,
3774 pool: &DexPool,
3775 class_name: &str,
3776) {
3777 fn display(pool: &DexPool, cls: &str, name: &str) -> Option<String> {
3781 let pc = pool.get(cls)?;
3782 let f = pc
3783 .static_fields
3784 .iter()
3785 .chain(pc.instance_fields.iter())
3786 .find(|f| f.name.as_str() == name)?;
3787 Some(
3788 jdc_core::rename::field_display(cls, name, &f.desc)
3789 .map(|d| d.to_string())
3790 .unwrap_or_else(|| name.to_string()),
3791 )
3792 }
3793 fn owner_ct(
3794 o: &Option<Box<Expr>>,
3795 vt: &VarTable,
3796 class_name: &str,
3797 ) -> Option<std::sync::Arc<str>> {
3798 match o.as_deref() {
3799 None | Some(Expr::This) => Some(std::sync::Arc::from(class_name)),
3800 Some(Expr::Local { var, .. }) => match vt.var(*var).ty.erased() {
3801 JavaType::Object(n) => Some(n),
3802 _ => None,
3803 },
3804 Some(other) => match other.type_ref().erased() {
3805 JavaType::Object(n) => Some(n),
3806 _ => None,
3807 },
3808 }
3809 }
3810 walk_stmt_exprs(body, &mut |e| {
3811 deep_rewrite(e, &mut |x| {
3812 let Expr::Field {
3813 owner,
3814 cls,
3815 name,
3816 is_static: false,
3817 ..
3818 } = x
3819 else {
3820 return;
3821 };
3822 let Some(ot) = owner_ct(owner, vt, class_name) else {
3823 return;
3824 };
3825 if cls.as_ref() == ot.as_ref() {
3826 return;
3827 }
3828 let Some(ref_display) = display(pool, cls, name) else {
3829 return;
3830 };
3831 let mut cur = ot.to_string();
3835 let mut shadowed = false;
3836 let mut found = false;
3837 for _ in 0..64 {
3838 if display(pool, &cur, name).as_deref() == Some(ref_display.as_str()) {
3839 shadowed = true;
3840 }
3841 let Some(pc) = pool.get(&cur) else {
3842 return;
3843 };
3844 let Some(sup) = pc.super_name.clone() else {
3845 return;
3846 };
3847 if sup.as_str() == cls.as_ref() {
3848 found = true;
3849 break;
3850 }
3851 cur = sup;
3852 }
3853 if !found || !shadowed {
3854 return;
3855 }
3856 let ty = TypeRef::J(JavaType::Object(cls.clone()));
3857 match owner {
3858 Some(o) => {
3859 if let Expr::Cast { ty: ct, .. } = &mut **o {
3860 *ct = ty;
3861 } else {
3862 let taken = std::mem::replace(&mut **o, Expr::This);
3863 **o = Expr::Cast {
3864 ty,
3865 e: Box::new(taken),
3866 };
3867 }
3868 }
3869 None => {
3870 *owner = Some(Box::new(Expr::Cast {
3871 ty,
3872 e: Box::new(Expr::This),
3873 }))
3874 }
3875 }
3876 });
3877 });
3878}
3879
3880pub fn fix_field_owner_downcasts(body: &mut Stmt, vt: &VarTable, pool: &DexPool) {
3899 fn owner_internal(o: &Expr, vt: &VarTable) -> Option<std::sync::Arc<str>> {
3900 let t = match o {
3901 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
3902 other => other.type_ref().erased(),
3903 };
3904 match t {
3905 JavaType::Object(n) => Some(n),
3906 _ => None,
3907 }
3908 }
3909 fn declares_up(pool: &DexPool, class: &str, name: &str) -> bool {
3910 let mut cur = Some(class.to_string());
3911 let mut hops = 0;
3912 while let Some(c) = cur {
3913 hops += 1;
3914 if hops > 64 {
3915 return false;
3916 }
3917 let Some(pc) = pool.get(&c) else { return false };
3918 if pc
3919 .instance_fields
3920 .iter()
3921 .chain(pc.static_fields.iter())
3922 .any(|f| f.name.as_str() == name)
3923 {
3924 return true;
3925 }
3926 cur = pc.super_name.clone();
3927 }
3928 false
3929 }
3930 walk_stmt_exprs(body, &mut |e| {
3931 deep_rewrite(e, &mut |x| {
3932 if let Expr::Field {
3933 owner: Some(o),
3934 cls,
3935 name,
3936 is_static: false,
3937 ..
3938 } = x
3939 {
3940 let Some(ot) = owner_internal(o, vt) else {
3941 return;
3942 };
3943 if ot.as_ref() == cls.as_ref() || !pool.is_subtype(cls, &ot) {
3944 return;
3945 }
3946 if declares_up(pool, &ot, name) || !declares_up(pool, cls, name) {
3947 return;
3948 }
3949 let ty = TypeRef::J(JavaType::Object(cls.clone()));
3950 if let Expr::Cast { ty: ct, .. } = &mut **o {
3951 *ct = ty;
3952 } else {
3953 let taken = std::mem::replace(&mut **o, Expr::This);
3954 **o = Expr::Cast { ty, e: Box::new(taken) };
3955 }
3956 }
3957 let (tgt_ty, value) = match x {
3959 Expr::Assign { target, op: AssignOp::Plain, value } => {
3960 let t = match &**target {
3961 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
3962 Expr::Field { ty, .. } => ty.erased(),
3963 _ => return,
3964 };
3965 (t, value)
3966 }
3967 _ => return,
3968 };
3969 let JavaType::Object(sn) = tgt_ty else { return };
3970 if let Expr::Cast { ty, .. } = &mut **value {
3971 if let TypeRef::J(JavaType::Object(tn)) = ty {
3972 if tn.as_ref() != sn.as_ref() && pool.is_subtype(&sn, tn) {
3973 *ty = TypeRef::J(JavaType::Object(sn.clone()));
3974 }
3975 }
3976 }
3977 });
3978 });
3979 walk_mut_deep(body, &mut |st| {
3981 if let Stmt::LocalDef { var, init: Some(value), .. } = st {
3982 let tt = if (*var as usize) < vt.vars.len() {
3983 vt.vars[*var as usize].ty.erased()
3984 } else {
3985 return;
3986 };
3987 let JavaType::Object(sn) = tt else { return };
3988 if let Expr::Cast { ty, .. } = value {
3989 if let TypeRef::J(JavaType::Object(tn)) = ty {
3990 if tn.as_ref() != sn.as_ref() && pool.is_subtype(&sn, tn) {
3991 *ty = TypeRef::J(JavaType::Object(sn.clone()));
3992 }
3993 }
3994 }
3995 }
3996 });
3997}
3998
3999pub fn fix_incomparable_equality(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
4006 fn side_ty(e: &Expr, vt: &VarTable) -> JavaType {
4007 match e {
4008 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
4009 vt.var(*var).ty.erased()
4010 }
4011 other => other.type_ref().erased(),
4012 }
4013 }
4014 let incomparable = |a: &JavaType, b: &JavaType| -> bool {
4015 use JavaType::{Array, Object};
4016 match (a, b) {
4017 (Object(x), Object(y)) => {
4018 if x == y || x.as_ref() == "java/lang/Object" || y.as_ref() == "java/lang/Object"
4019 {
4020 return false;
4021 }
4022 let xi = pool.get(x).map(|c| c.is_interface()).unwrap_or(true);
4023 let yi = pool.get(y).map(|c| c.is_interface()).unwrap_or(true);
4024 if xi || yi {
4028 return false;
4029 }
4030 !pool.is_subtype(x, y) && !pool.is_subtype(y, x)
4031 }
4032 (Array(_), Object(y)) | (Object(y), Array(_)) => {
4033 y.as_ref() != "java/lang/Object"
4034 && y.as_ref() != "java/lang/Cloneable"
4035 && y.as_ref() != "java/io/Serializable"
4036 && !pool.get(y).map(|c| c.is_interface()).unwrap_or(false)
4037 }
4038 _ => false,
4039 }
4040 };
4041 let is_ref = |t: &JavaType| matches!(t, JavaType::Object(_) | JavaType::Array(_));
4042 walk_stmt_exprs(body, &mut |e| {
4043 deep_rewrite(e, &mut |x| {
4044 if let Expr::Bin {
4045 op: BinOp::Eq | BinOp::Ne,
4046 l,
4047 r,
4048 ..
4049 } = x
4050 {
4051 let lt = side_ty(l, vt);
4052 let rt = side_ty(r, vt);
4053 if is_ref(<) && matches!(&**r, Expr::Const(ConstVal::Int(0))) {
4059 **r = Expr::Const(ConstVal::Null);
4060 return;
4061 }
4062 if is_ref(&rt) && matches!(&**l, Expr::Const(ConstVal::Int(0))) {
4063 **l = Expr::Const(ConstVal::Null);
4064 return;
4065 }
4066 if incomparable(<, &rt) {
4067 let taken = std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
4068 **l = Expr::Cast {
4069 ty: TypeRef::J(JavaType::Object("java/lang/Object".into())),
4070 e: taken,
4071 };
4072 }
4073 }
4074 });
4075 });
4076}
4077
4078pub fn fix_primitive_arg_bridges(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
4092 fn val_ty(e: &Expr, vt: &VarTable) -> JavaType {
4093 match e {
4094 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4095 other => other.type_ref().erased(),
4096 }
4097 }
4098 #[allow(clippy::type_complexity)]
4099 let is_bool = |t: &JavaType| matches!(t, JavaType::Boolean);
4100 let is_num = |t: &JavaType| {
4101 matches!(
4102 t,
4103 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
4104 | JavaType::Long | JavaType::Float | JavaType::Double
4105 )
4106 };
4107 fn bridge(actual: &JavaType, formal: &JavaType, arg: &Expr) -> Option<Expr> {
4112 match (arg, formal) {
4122 (Expr::Const(ConstVal::Int(b)), JavaType::Float) => {
4123 return Some(Expr::Const(ConstVal::Float(f32::from_bits(*b as u32))));
4124 }
4125 (Expr::Const(ConstVal::Long(b)), JavaType::Double) => {
4126 return Some(Expr::Const(ConstVal::Double(f64::from_bits(*b as u64))));
4127 }
4128 (Expr::Const(ConstVal::Float(f)), JavaType::Int) => {
4129 return Some(Expr::Const(ConstVal::Int(f.to_bits() as i32)));
4130 }
4131 (Expr::Const(ConstVal::Double(d)), JavaType::Long) => {
4132 return Some(Expr::Const(ConstVal::Long(d.to_bits() as i64)));
4133 }
4134 _ => {}
4135 }
4136 if matches!(actual, JavaType::Boolean)
4137 && matches!(
4138 formal,
4139 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
4140 | JavaType::Long | JavaType::Float | JavaType::Double
4141 )
4142 {
4143 Some(Expr::Cast {
4144 ty: TypeRef::J(formal.clone()),
4145 e: Box::new(Expr::Cond {
4146 c: Box::new(arg.clone()),
4147 t: Box::new(Expr::Const(ConstVal::Int(1))),
4148 f: Box::new(Expr::Const(ConstVal::Int(0))),
4149 }),
4150 })
4151 } else if matches!(formal, JavaType::Boolean)
4152 && matches!(
4153 actual,
4154 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
4155 | JavaType::Long | JavaType::Float | JavaType::Double
4156 )
4157 {
4158 Some(Expr::Bin {
4159 op: BinOp::Ne,
4160 l: Box::new(arg.clone()),
4161 r: Box::new(Expr::Const(ConstVal::Int(0))),
4162 ty: Some(TypeRef::J(JavaType::Boolean)),
4163 })
4164 } else if matches!(
4165 actual,
4166 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
4167 | JavaType::Long | JavaType::Float | JavaType::Double
4168 ) && matches!(
4169 formal,
4170 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
4171 | JavaType::Long | JavaType::Float | JavaType::Double
4172 ) && !implicit_widening(actual, formal)
4173 {
4174 Some(Expr::Cast {
4183 ty: TypeRef::J(formal.clone()),
4184 e: Box::new(arg.clone()),
4185 })
4186 } else {
4187 None
4188 }
4189 }
4190 fn fw_resolvable(n: &str) -> bool {
4206 crate::fwdb::exists(n)
4207 }
4208 fn null_arg_cast(a: &mut Expr, f: &JavaType, pool: &DexPool) -> bool {
4209 if !matches!(&*a, Expr::Const(ConstVal::Null)) {
4210 return false;
4211 }
4212 if let JavaType::Object(n) = f {
4213 let resolvable = pool.get(n).is_some() || fw_resolvable(n);
4219 if n.as_ref() != "java/lang/Object" && resolvable {
4220 *a = Expr::Cast {
4221 ty: TypeRef::J(f.clone()),
4222 e: Box::new(Expr::Const(ConstVal::Null)),
4223 };
4224 return true;
4225 }
4226 } else if let JavaType::Array(_) = f {
4227 let mut e: &JavaType = f;
4234 while let JavaType::Array(inner) = e {
4235 e = &**inner;
4236 }
4237 let resolvable = match e {
4238 JavaType::Object(n) => pool.get(n).is_some() || fw_resolvable(n),
4239 _ => true, };
4241 if resolvable {
4242 *a = Expr::Cast {
4243 ty: TypeRef::J(f.clone()),
4244 e: Box::new(Expr::Const(ConstVal::Null)),
4245 };
4246 return true;
4247 }
4248 }
4249 false
4252 }
4253 fn implicit_widening(a: &JavaType, f: &JavaType) -> bool {
4256 use JavaType::*;
4257 match (a, f) {
4258 (Byte, Short) | (Byte, Int) | (Byte, Long) | (Byte, Float) | (Byte, Double) => true,
4259 (Short, Int) | (Short, Long) | (Short, Float) | (Short, Double) => true,
4260 (Char, Int) | (Char, Long) | (Char, Float) | (Char, Double) => true,
4261 (Int, Long) | (Int, Float) | (Int, Double) => true,
4262 (Long, Float) | (Long, Double) => true,
4263 (Float, Double) => true,
4264 (x, y) => x == y,
4265 }
4266 }
4267 let overload_competes = |cls: &str, name: &str, args: &[JavaType]| -> bool {
4276 if let Some(c) = pool.get(cls) {
4277 return c.all_methods().any(|m| {
4278 &*m.name == name
4279 && m.access & (crate::access::ACC_BRIDGE | crate::access::ACC_SYNTHETIC) == 0
4285 && m.parsed_desc()
4286 .is_some_and(|d| d.args.len() == args.len() && d.args != args)
4287 });
4288 }
4289 let _ = args;
4298 false
4299 };
4300 let ctor_formals = |cls: &str, n: usize| -> Option<Vec<JavaType>> {
4303 let c = pool.get(cls)?;
4304 let mut out: Option<Vec<JavaType>> = None;
4305 for m in c.all_methods() {
4306 if &*m.name != "<init>" {
4307 continue;
4308 }
4309 let Some(d) = m.parsed_desc() else { continue };
4310 if d.args.len() != n {
4311 continue;
4312 }
4313 match &out {
4314 None => out = Some(d.args.clone()),
4315 Some(prev) if *prev != d.args => return None,
4316 Some(_) => {}
4317 }
4318 }
4319 out
4320 };
4321 walk_stmt_exprs(body, &mut |e| {
4322 deep_rewrite(e, &mut |x| match x {
4323 Expr::Method {
4324 cls,
4325 name,
4326 desc,
4327 args,
4328 is_static,
4329 ..
4330 } => {
4331 if args.len() != desc.args.len() {
4332 return;
4333 }
4334 let _ = is_static;
4335 let mut competes: Option<bool> = None;
4336 for (a, f) in args.iter_mut().zip(desc.args.iter()) {
4337 if null_arg_cast(a, f, pool) {
4338 continue;
4339 }
4340 if matches!(a, Expr::Cast { .. }) {
4341 continue;
4342 }
4343 let subtype_arg = match (&val_ty(a, vt), f) {
4354 (JavaType::Object(an), JavaType::Object(fn_)) => {
4355 (an != fn_ && pool.is_subtype(an, fn_))
4360 || (fn_.as_ref() == "java/lang/Object"
4361 && an.as_ref() != "java/lang/Object")
4362 }
4363 (JavaType::Array(_), JavaType::Object(fn_))
4364 if fn_.as_ref() == "java/lang/Object" =>
4365 {
4366 true
4367 }
4368 _ => false,
4369 };
4370 let null_obj =
4384 matches!(a, Expr::Const(ConstVal::Null)) && matches!(f, JavaType::Object(_));
4385 let framework_owner = pool.get(cls.as_ref()).is_none();
4386 let fw_null_ambiguous = framework_owner
4387 && matches!(
4388 (cls.as_ref(), name.as_ref()),
4389 ("java/lang/StringBuilder", "append")
4390 | ("java/lang/StringBuffer", "append")
4391 | ("java/io/PrintStream", "println")
4392 | ("java/io/PrintStream", "print")
4393 | ("java/io/PrintWriter", "println")
4394 | ("java/io/PrintWriter", "print")
4395 | ("java/lang/String", "valueOf")
4396 );
4397 let fw_subtype_pinnable = framework_owner
4408 && subtype_arg
4409 && !matches!(f, JavaType::Object(fn_) if fn_.as_ref() == "java/lang/Object")
4410 && matches!(
4411 (cls.as_ref(), name.as_ref()),
4412 ("android/content/Intent", "putExtra")
4413 | ("java/util/concurrent/ScheduledExecutorService", "schedule")
4414 | ("java/util/concurrent/ExecutorService", "submit")
4415 | ("java/util/concurrent/ExecutorService", "invokeAll")
4416 );
4417 if fw_subtype_pinnable
4418 || (subtype_arg
4419 && *competes.get_or_insert_with(|| {
4420 overload_competes(cls, name, &desc.args)
4421 }))
4422 || (null_obj
4423 && (fw_null_ambiguous
4424 || *competes.get_or_insert_with(|| {
4425 overload_competes(cls, name, &desc.args)
4426 })))
4427 {
4428 let old =
4429 std::mem::replace(a, Expr::Const(ConstVal::Null));
4430 *a = Expr::Cast {
4431 ty: TypeRef::J(f.clone()),
4432 e: Box::new(old),
4433 };
4434 continue;
4435 }
4436 if !is_bool(&val_ty(a, vt)) && !is_num(&val_ty(a, vt)) {
4437 continue;
4438 }
4439 if !is_bool(f) && !is_num(f) {
4440 continue;
4441 }
4442 if let Some(b) = bridge(&val_ty(a, vt), f, a) {
4443 *a = b;
4444 }
4445 }
4446 }
4447 Expr::New { cls, args, arg_tys, .. } => {
4448 let formals_owned = if arg_tys.len() == args.len() && !arg_tys.is_empty() {
4449 Some(arg_tys.clone())
4450 } else {
4451 ctor_formals(cls, args.len())
4452 };
4453 if let Some(formals) = formals_owned {
4454 for (a, f) in args.iter_mut().zip(formals.iter()) {
4455 if null_arg_cast(a, f, pool) {
4456 continue;
4457 }
4458 if matches!(a, Expr::Cast { .. }) {
4459 continue;
4460 }
4461 if !is_bool(f) && !is_num(f) {
4462 continue;
4463 }
4464 if let Some(b) = bridge(&val_ty(a, vt), f, a) {
4465 *a = b;
4466 }
4467 }
4468 }
4469 }
4470 _ => {}
4471 });
4472 });
4473}
4474
4475pub fn deshadow_locals(vt: &mut VarTable, pool: &crate::DexPool, class_name: &str, body: &Stmt) {
4487 let segs = pool.root_pkg_segs();
4488 let obscured = crate::classdec::obscured_simples_snapshot();
4492 let self_pkg = class_name.rfind('/').map(|i| &class_name[..i]).unwrap_or("");
4499 let mut same_pkg_simples: jdc_core::FxHashSet<String> = jdc_core::FxHashSet::default();
4500 visit_all_exprs(body, &mut |x| {
4501 let cls = match x {
4502 Expr::Field { cls, is_static: true, .. } => Some(cls.as_ref()),
4503 Expr::Method { cls, is_static: true, .. } => Some(cls.as_ref()),
4504 _ => None,
4505 };
4506 if let Some(c) = cls {
4507 let pkg = c.rfind('/').map(|i| &c[..i]).unwrap_or("");
4508 if pkg == self_pkg {
4509 let simple = &c[c.rfind('/').map(|i| i + 1).unwrap_or(0)..];
4510 let head = simple.split('$').next().unwrap_or(simple);
4511 if !head.is_empty() {
4512 same_pkg_simples.insert(head.to_string());
4513 }
4514 }
4515 }
4516 });
4517 let hit = |name: &str| {
4518 segs.contains(name) || obscured.contains(name) || same_pkg_simples.contains(name)
4519 };
4520 if (segs.is_empty() && obscured.is_empty() && same_pkg_simples.is_empty())
4521 || !vt.vars.iter().any(|v| hit(&v.name))
4522 {
4523 return;
4524 }
4525 let mut taken: jdc_core::FxHashSet<String> =
4526 vt.vars.iter().map(|v| v.name.clone()).collect();
4527 for v in vt.vars.iter_mut() {
4528 if !hit(&v.name) {
4529 continue;
4530 }
4531 let mut k = 0u32;
4532 let cand = loop {
4533 k += 1;
4534 let c = if k == 1 {
4535 format!("{}x", v.name)
4536 } else {
4537 format!("{}x{}", v.name, k)
4538 };
4539 if !taken.contains(&c) {
4540 break c;
4541 }
4542 };
4543 taken.insert(cand.clone());
4544 v.name = cand;
4545 }
4546}
4547
4548pub fn fix_ctor_this_aliases(body: &mut Stmt, vt: &VarTable) {
4574 let Some(this_var) = vt
4575 .vars
4576 .iter()
4577 .find(|v| v.is_param && v.name == "this")
4578 .map(|v| v.id)
4579 else {
4580 return;
4581 };
4582 let mut analysis = VarAnalysis {
4583 assigns: Vec::new(),
4584 reads: Vec::new(),
4585 values: Vec::new(),
4586 };
4587 analyze_vars(body, &mut analysis);
4588 enum Src {
4599 This,
4600 Loc(u32),
4601 Other,
4602 }
4603 let mut sources: jdc_core::FxHashMap<u32, Vec<Src>> = jdc_core::FxHashMap::default();
4604 walk_all(body, &mut |st| {
4605 let (tgt, val): (u32, &Expr) = match st {
4606 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
4607 Expr::Local { var, .. } => (*var, &**value),
4608 _ => return,
4609 },
4610 Stmt::LocalDef { var, init: Some(value), .. } => (*var, value),
4611 _ => return,
4612 };
4613 let s = match val {
4614 Expr::This => Src::This,
4615 Expr::Local { var: src, .. } if *src == this_var => Src::This,
4616 Expr::Local { var: src, .. } => Src::Loc(*src),
4617 _ => Src::Other,
4618 };
4619 sources.entry(tgt).or_default().push(s);
4620 });
4621 let n = vt
4622 .vars
4623 .len()
4624 .max(analysis.assigns.len())
4625 .max(sources.keys().map(|k| *k as usize + 1).max().unwrap_or(0));
4626 let mut alias = vec![false; n];
4627 loop {
4628 let mut changed = false;
4629 for (v, srcs) in &sources {
4630 let vi = *v as usize;
4631 if vi >= n || alias[vi] || srcs.is_empty() {
4632 continue;
4633 }
4634 let ok = srcs.iter().all(|s| match s {
4635 Src::This => true,
4636 Src::Loc(w) => (*w as usize) < n && alias[*w as usize],
4637 Src::Other => false,
4638 });
4639 if ok {
4640 alias[vi] = true;
4641 changed = true;
4642 }
4643 }
4644 if !changed {
4645 break;
4646 }
4647 }
4648 if !alias.iter().any(|&b| b) {
4649 return;
4650 }
4651 walk_stmt_exprs(body, &mut |e| {
4652 deep_rewrite_reads(e, &mut |x| {
4653 if let Expr::Local { var, .. } = x {
4654 if (*var as usize) < n && alias[*var as usize] {
4655 *x = Expr::This;
4656 }
4657 }
4658 });
4659 });
4660}
4661
4662pub fn fix_this0_owners(body: &mut Stmt, class_name: &str) {
4663 walk_stmt_exprs(body, &mut |e| {
4664 deep_rewrite(e, &mut |x| {
4665 if let Expr::Field { owner: Some(_), cls, name, .. } = x {
4666 if name.as_ref() == "this$0" && cls.as_ref() == class_name {
4667 if let Expr::Field { owner, .. } = x {
4668 *owner = None;
4669 }
4670 }
4671 }
4672 });
4673 });
4674}
4675
4676pub fn rescue_primitive_receivers(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
4677 fn prim(t: &JavaType) -> bool {
4678 match t {
4679 JavaType::Int
4680 | JavaType::Long
4681 | JavaType::Short
4682 | JavaType::Byte
4683 | JavaType::Char
4684 | JavaType::Float
4685 | JavaType::Double
4686 | JavaType::Boolean => true,
4687 JavaType::Object(n) => matches!(
4691 n.as_ref(),
4692 "java/lang/Boolean"
4693 | "java/lang/String"
4694 | "java/lang/Integer"
4695 | "java/lang/Long"
4696 | "java/lang/Short"
4697 | "java/lang/Byte"
4698 | "java/lang/Character"
4699 | "java/lang/Float"
4700 | "java/lang/Double"
4701 ),
4702 _ => false,
4703 }
4704 }
4705 walk_stmt_exprs(body, &mut |e| {
4706 deep_rewrite(e, &mut |x| {
4707 let (recv_var, recv_ty) = {
4709 let o: &Expr = match &*x {
4710 Expr::Method { owner: Some(o), .. } => o,
4711 Expr::Field { owner: Some(o), is_static: false, .. } => o,
4712 _ => return,
4713 };
4714 let Expr::Local { var, ty } = o else {
4715 return;
4716 };
4717 let vt_ty = if (*var as usize) < vt.vars.len() {
4718 vt.var(*var).ty.erased()
4719 } else {
4720 ty.erased()
4721 };
4722 if !prim(&vt_ty) {
4723 return;
4724 }
4725 (*var, vt_ty)
4726 };
4727 let cls: std::sync::Arc<str> = match &*x {
4728 Expr::Method { cls, .. } | Expr::Field { cls, .. } => cls.clone(),
4729 _ => return,
4730 };
4731 if let JavaType::Object(n) = &recv_ty {
4740 if n.as_ref() == cls.as_ref() || pool.is_subtype(n.as_ref(), cls.as_ref()) {
4741 return;
4742 }
4743 }
4744 let mut cands: Vec<u32> = Vec::new();
4746 for v in &vt.vars {
4747 if let JavaType::Object(n) = v.ty.erased() {
4748 if n.as_ref() == cls.as_ref()
4749 || pool.is_subtype(n.as_ref(), cls.as_ref())
4750 {
4751 cands.push(v.id);
4752 }
4753 }
4754 }
4755 let id = if cands.len() == 1 {
4756 cands[0]
4757 } else if cands.len() > 1 {
4758 let slot = if (recv_var as usize) < vt.vars.len() {
4763 Some(vt.var(recv_var).slot)
4764 } else {
4765 None
4766 };
4767 let mut hits: Vec<u32> = match slot {
4768 Some(sl) => cands
4769 .iter()
4770 .copied()
4771 .filter(|&c| {
4772 c != recv_var
4773 && (c as usize) < vt.vars.len()
4774 && vt.var(c).slot == sl
4775 })
4776 .collect(),
4777 None => Vec::new(),
4778 };
4779 if hits.is_empty() {
4780 hits = cands
4783 .iter()
4784 .copied()
4785 .filter(|&c| c != recv_var)
4786 .collect();
4787 }
4788 hits.sort_unstable();
4789 hits.dedup();
4790 if hits.is_empty() {
4791 return;
4792 }
4793 *hits.last().unwrap()
4794 } else {
4795 if cands.is_empty() {
4802 return;
4803 }
4804 let slot = if (recv_var as usize) < vt.vars.len() {
4805 vt.var(recv_var).slot
4806 } else {
4807 return;
4808 };
4809 let mut hits: Vec<u32> = cands
4810 .into_iter()
4811 .filter(|&c| {
4812 c != recv_var
4813 && (c as usize) < vt.vars.len()
4814 && vt.var(c).slot == slot
4815 })
4816 .collect();
4817 hits.sort_unstable();
4818 hits.dedup();
4819 if hits.len() != 1 {
4820 return;
4821 }
4822 hits[0]
4823 };
4824 let o: &mut Box<Expr> = match x {
4826 Expr::Method { owner: Some(o), .. } => o,
4827 Expr::Field { owner: Some(o), is_static: false, .. } => o,
4828 _ => return,
4829 };
4830 if let Expr::Local { var, ty } = &mut **o {
4831 if *var == recv_var {
4832 *var = id;
4833 *ty = vt.vars[id as usize].ty.clone();
4834 }
4835 }
4836 });
4837 });
4838
4839}
4840
4841pub fn rescue_arg_return_swaps(
4847 body: &mut Stmt,
4848 vt: &VarTable,
4849 pool: &crate::DexPool,
4850 ret_ty: &JavaType,
4851) {
4852 let mut top_ids: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
4860 for v in &vt.vars {
4861 if v.is_param {
4862 top_ids.insert(v.id);
4863 }
4864 }
4865 if let Stmt::Block(stmts) = body {
4866 for st in stmts.iter() {
4867 if let Stmt::LocalDef { var, .. } = st {
4868 top_ids.insert(*var);
4869 }
4870 }
4871 }
4872 let ret_wants_ref = match ret_ty {
4873 JavaType::Object(n) => n.as_ref() != "java/lang/Object",
4874 JavaType::Array(_) => true,
4875 _ => false,
4876 };
4877 let try_swap = |a: &mut Expr, want: &JavaType| {
4878 let Expr::Local { var, ty } = &mut *a else {
4879 return;
4880 };
4881 if (*var as usize) >= vt.vars.len() {
4882 return;
4883 }
4884 let avt = vt.var(*var).ty.erased();
4885 let avt_ref = matches!(avt, JavaType::Object(_) | JavaType::Array(_));
4886 let want_arr = matches!(want, JavaType::Array(_));
4887 if avt_ref && !(avt == JavaType::Object("java/lang/Object".into())) {
4890 return;
4891 }
4892 if avt_ref && !matches!(want, JavaType::Object(_)) {
4893 return;
4894 }
4895 let slot = vt.var(*var).slot;
4896 let mut hits: Vec<u32> = Vec::new();
4897 for v in &vt.vars {
4898 if v.slot != slot || !top_ids.contains(&v.id) || v.id == *var {
4899 continue;
4900 }
4901 let t = v.ty.erased();
4902 let ok = match (&t, want) {
4903 (JavaType::Object(n), JavaType::Object(w)) => {
4904 n.as_ref() == w.as_ref()
4905 || pool.is_subtype(n.as_ref(), w.as_ref())
4906 }
4907 (JavaType::Array(_), JavaType::Array(_)) => want_arr,
4908 _ => false,
4909 };
4910 if ok {
4911 hits.push(v.id);
4912 }
4913 }
4914 hits.sort_unstable();
4915 hits.dedup();
4916 if hits.is_empty() {
4917 return;
4918 }
4919 let id = *hits.last().unwrap();
4925 *var = id;
4926 *ty = vt.vars[id as usize].ty.clone();
4927 };
4928 walk_stmt_exprs(body, &mut |e| {
4929 deep_rewrite(e, &mut |x| {
4930 match x {
4931 Expr::Method { desc, args, is_dynamic: false, .. } => {
4932 for (i, a) in args.iter_mut().enumerate() {
4933 if let Some(f) = desc.args.get(i) {
4934 if matches!(f, JavaType::Object(n) if n.as_ref() != "java/lang/Object")
4935 || matches!(f, JavaType::Array(_))
4936 {
4937 try_swap(a, f);
4938 }
4939 }
4940 }
4941 }
4942 Expr::New { arg_tys, args, .. } => {
4943 for (i, a) in args.iter_mut().enumerate() {
4944 if let Some(f) = arg_tys.get(i) {
4945 if matches!(f, JavaType::Object(n) if n.as_ref() != "java/lang/Object")
4946 || matches!(f, JavaType::Array(_))
4947 {
4948 try_swap(a, f);
4949 }
4950 }
4951 }
4952 }
4953 _ => {}
4954 }
4955 });
4956 });
4957 if ret_wants_ref {
4958 walk_mut_deep(body, &mut |st| {
4959 if let Stmt::Return(Some(e)) = st {
4960 try_swap(e, ret_ty);
4961 }
4962 });
4963 }
4964}
4965
4966pub fn fix_ref_null_assigns(vt: &VarTable, body: &mut Stmt) {
4981 let is_ref_var = |var: u32| -> bool {
4982 (var as usize) < vt.vars.len()
4983 && matches!(
4984 vt.vars[var as usize].ty.erased(),
4985 JavaType::Object(_) | JavaType::Array(_)
4986 )
4987 };
4988 walk_mut_deep(body, &mut |st| match st {
4989 Stmt::ExprStmt(Expr::Assign {
4990 target,
4991 value,
4992 op: AssignOp::Plain,
4993 ..
4994 }) => {
4995 if let Expr::Local { var, .. } = &**target {
4996 if is_ref_var(*var) && matches!(&**value, Expr::Const(ConstVal::Int(0))) {
4997 **value = Expr::Const(ConstVal::Null);
4998 }
4999 }
5000 }
5001 Stmt::LocalDef {
5002 var,
5003 init: Some(e),
5004 ..
5005 } => {
5006 if is_ref_var(*var) && matches!(e, Expr::Const(ConstVal::Int(0))) {
5007 *e = Expr::Const(ConstVal::Null);
5008 }
5009 }
5010 _ => {}
5011 });
5012}
5013
5014pub fn fix_ref_array_null_consts(body: &mut Stmt) {
5015 walk_stmt_exprs(body, &mut |e| {
5016 deep_rewrite(e, &mut |x| {
5017 if let Expr::NewArray { elem, init: Some(list), .. } = x {
5018 if matches!(elem.erased(), JavaType::Object(_) | JavaType::Array(_)) {
5019 for slot in list.iter_mut() {
5020 if matches!(&*slot, Expr::Const(ConstVal::Int(0))) {
5021 *slot = Expr::Const(ConstVal::Null);
5022 }
5023 }
5024 }
5025 }
5026 });
5027 });
5028}
5029
5030pub fn fix_null_sentinels(body: &mut Stmt, vt: &VarTable, ret: &JavaType) {
5069 #[derive(Clone, Copy, PartialEq)]
5070 enum WK {
5071 Zero,
5072 Copy(u32),
5073 Other,
5074 }
5075 fn is_ref_jt(t: &JavaType) -> bool {
5076 matches!(t, JavaType::Object(_) | JavaType::Array(_))
5077 }
5078 fn cls_write(e: &Expr) -> WK {
5079 match e {
5080 Expr::Const(ConstVal::Int(0)) => WK::Zero,
5081 Expr::Local { var, .. } => WK::Copy(*var),
5082 _ => WK::Other,
5083 }
5084 }
5085 fn expr_writes(e: &Expr, writes: &mut jdc_core::FxHashMap<u32, Vec<WK>>) {
5086 visit_exprs(e, &mut |x| match x {
5087 Expr::Assign { target, op, value } => {
5088 if let Expr::Local { var, .. } = &**target {
5089 let k = if matches!(op, AssignOp::Plain) {
5090 cls_write(value)
5091 } else {
5092 WK::Other
5093 };
5094 writes.entry(*var).or_default().push(k);
5095 }
5096 }
5097 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
5098 if let Expr::Local { var, .. } = &**inner {
5099 writes.entry(*var).or_default().push(WK::Other);
5100 }
5101 }
5102 _ => {}
5103 });
5104 }
5105 fn collect_writes(
5106 s: &Stmt,
5107 writes: &mut jdc_core::FxHashMap<u32, Vec<WK>>,
5108 has_zero: &mut bool,
5109 ) {
5110 fn note(e: &Expr, has_zero: &mut bool, writes: &mut jdc_core::FxHashMap<u32, Vec<WK>>) {
5111 visit_exprs(e, &mut |x| {
5112 if let Expr::Const(ConstVal::Int(0)) = x {
5113 *has_zero = true;
5114 }
5115 });
5116 expr_writes(e, writes);
5117 }
5118 match s {
5119 Stmt::Block(v) => {
5120 for x in v {
5121 collect_writes(x, writes, has_zero);
5122 }
5123 }
5124 Stmt::LocalDef { var, init, .. } => {
5125 if let Some(e) = init {
5126 if matches!(e, Expr::Const(ConstVal::Int(0))) {
5127 *has_zero = true;
5128 }
5129 writes.entry(*var).or_default().push(cls_write(e));
5130 }
5131 }
5132 Stmt::ExprStmt(e) => note(e, has_zero, writes),
5133 Stmt::Return(e) => {
5134 if let Some(x) = e {
5135 note(x, has_zero, writes);
5136 }
5137 }
5138 Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
5139 note(e, has_zero, writes);
5140 }
5141 Stmt::TernaryValue { e } => note(e, has_zero, writes),
5142 Stmt::If { cond, then_stmt, else_stmt } => {
5143 note(cond, has_zero, writes);
5144 collect_writes(then_stmt, writes, has_zero);
5145 if let Some(b) = else_stmt {
5146 collect_writes(b, writes, has_zero);
5147 }
5148 }
5149 Stmt::While { cond, body } | Stmt::DoWhile { body, cond } => {
5150 note(cond, has_zero, writes);
5151 collect_writes(body, writes, has_zero);
5152 }
5153 Stmt::For { init, cond, update, body } => {
5154 for x in init {
5155 collect_writes(x, writes, has_zero);
5156 }
5157 if let Some(c) = cond {
5158 note(c, has_zero, writes);
5159 }
5160 for u in update {
5161 note(u, has_zero, writes);
5162 }
5163 collect_writes(body, writes, has_zero);
5164 }
5165 Stmt::ForEach { var, iterable, body, .. } => {
5166 writes.entry(*var).or_default().push(WK::Other);
5167 note(iterable, has_zero, writes);
5168 collect_writes(body, writes, has_zero);
5169 }
5170 Stmt::Switch { selector, cases, default, .. } => {
5171 note(selector, has_zero, writes);
5172 for c in cases {
5173 if let Some(g) = &c.guard {
5174 note(g, has_zero, writes);
5175 }
5176 for x in &c.body {
5177 collect_writes(x, writes, has_zero);
5178 }
5179 }
5180 if let Some(d) = default {
5181 collect_writes(d, writes, has_zero);
5182 }
5183 }
5184 Stmt::Try { body, catches, finally } => {
5185 collect_writes(body, writes, has_zero);
5186 for c in catches {
5187 writes.entry(c.var).or_default().push(WK::Other);
5188 collect_writes(&c.body, writes, has_zero);
5189 }
5190 if let Some(f) = finally {
5191 collect_writes(f, writes, has_zero);
5192 }
5193 }
5194 Stmt::TryWithResources { resources, body, catches, finally, .. } => {
5195 for r in resources {
5196 collect_writes(r, writes, has_zero);
5197 }
5198 collect_writes(body, writes, has_zero);
5199 for c in catches {
5200 writes.entry(c.var).or_default().push(WK::Other);
5201 collect_writes(&c.body, writes, has_zero);
5202 }
5203 if let Some(f) = finally {
5204 collect_writes(f, writes, has_zero);
5205 }
5206 }
5207 Stmt::Assert { cond, msg } => {
5208 note(cond, has_zero, writes);
5209 if let Some(m) = msg {
5210 note(m, has_zero, writes);
5211 }
5212 }
5213 Stmt::Synchronized { lock, body } => {
5214 note(lock, has_zero, writes);
5215 collect_writes(body, writes, has_zero);
5216 }
5217 Stmt::Labeled { body, .. } => collect_writes(body, writes, has_zero),
5218 _ => {}
5219 }
5220 }
5221
5222 let mut writes: jdc_core::FxHashMap<u32, Vec<WK>> =
5223 jdc_core::FxHashMap::default();
5224 let mut has_zero = false;
5225 collect_writes(body, &mut writes, &mut has_zero);
5226
5227 let mut eligible: HashSet<u32> = HashSet::default();
5229 let int_typed = |v: u32| {
5230 (v as usize) < vt.vars.len() && vt.vars[v as usize].ty.erased() == JavaType::Int
5231 };
5232 loop {
5233 let mut changed = false;
5234 for (&v, ws) in writes.iter() {
5235 if eligible.contains(&v) || ws.is_empty() || !int_typed(v) {
5236 continue;
5237 }
5238 if ws.iter().all(|k| match k {
5239 WK::Zero => true,
5240 WK::Copy(u) => *u != v && eligible.contains(u),
5241 WK::Other => false,
5242 }) {
5243 eligible.insert(v);
5244 changed = true;
5245 }
5246 }
5247 if !changed {
5248 break;
5249 }
5250 }
5251 if eligible.is_empty() && !has_zero {
5252 return; }
5254
5255 #[derive(Clone, Copy, PartialEq)]
5256 enum Mode {
5257 Scan,
5258 Rewrite,
5259 }
5260 struct St<'a> {
5261 mode: Mode,
5262 vt: &'a VarTable,
5263 eligible: &'a HashSet<u32>,
5264 ref_read: HashSet<u32>,
5265 dead: HashSet<u32>,
5266 sentinels: HashSet<u32>,
5267 n_lit: usize,
5268 n_var: usize,
5269 }
5270 fn arg_ctx(t: &JavaType) -> Option<JavaType> {
5274 match t {
5275 JavaType::Object(n) if n.as_ref() == "java/lang/Object" => None,
5276 t if is_ref_jt(t) => Some(t.clone()),
5277 _ => None,
5278 }
5279 }
5280 fn walk_expr(e: &mut Expr, ctx: Option<JavaType>, st: &mut St) {
5281 walk_expr_c(e, ctx, false, st);
5282 }
5283 fn walk_expr_c(e: &mut Expr, ctx: Option<JavaType>, cast_arg: bool, st: &mut St) {
5289 let slot_ref = ctx.as_ref().is_some_and(is_ref_jt);
5290 fn mk_null(ctx: &Option<JavaType>, cast_arg: bool) -> Expr {
5291 match (cast_arg, ctx) {
5292 (true, Some(t)) => Expr::Cast {
5293 ty: TypeRef::J(t.clone()),
5294 e: Box::new(Expr::Const(ConstVal::Null)),
5295 },
5296 _ => Expr::Const(ConstVal::Null),
5297 }
5298 }
5299 match e {
5300 Expr::Const(ConstVal::Int(0)) if slot_ref => {
5301 if st.mode == Mode::Rewrite {
5302 *e = mk_null(&ctx, cast_arg);
5303 st.n_lit += 1;
5304 }
5305 return;
5306 }
5307 Expr::Local { var, .. } => {
5308 if st.eligible.contains(var) {
5309 if slot_ref {
5310 st.ref_read.insert(*var);
5311 } else {
5312 st.dead.insert(*var);
5313 }
5314 }
5315 if st.mode == Mode::Rewrite && slot_ref && st.sentinels.contains(var) {
5316 *e = mk_null(&ctx, cast_arg);
5317 st.n_var += 1;
5318 }
5319 return;
5320 }
5321 _ => {}
5322 }
5323 match e {
5324 Expr::Method { owner, desc, args, .. } => {
5325 if let Some(o) = owner {
5326 walk_expr(o, None, st);
5327 }
5328 for (i, a) in args.iter_mut().enumerate() {
5329 let c = desc.args.get(i).and_then(arg_ctx);
5330 walk_expr_c(a, c, true, st);
5331 }
5332 }
5333 Expr::Invokedynamic { desc, args, .. } => {
5334 for (i, a) in args.iter_mut().enumerate() {
5335 let c = desc.args.get(i).and_then(arg_ctx);
5336 walk_expr_c(a, c, true, st);
5337 }
5338 }
5339 Expr::New { args, arg_tys, .. } => {
5340 for (i, a) in args.iter_mut().enumerate() {
5341 let c = arg_tys.get(i).and_then(arg_ctx);
5342 walk_expr_c(a, c, true, st);
5343 }
5344 }
5345 Expr::AnonNew { args, .. } => {
5346 for a in args.iter_mut() {
5347 walk_expr(a, None, st);
5348 }
5349 }
5350 Expr::Assign { target, value, .. } => {
5351 if let Expr::Local { var: u, .. } = &**target {
5355 if st.eligible.contains(u) {
5356 return;
5357 }
5358 }
5359 let vctx = match &**target {
5360 Expr::Field { ty, .. } => {
5361 let t = ty.erased();
5362 is_ref_jt(&t).then_some(t)
5363 }
5364 Expr::ArrayIndex { array, .. } => match array.type_ref().erased() {
5365 JavaType::Array(inner) if is_ref_jt(&inner) => Some(*inner),
5366 _ => None,
5367 },
5368 _ => None,
5369 };
5370 walk_expr(target, None, st);
5371 walk_expr(value, vctx, st);
5372 }
5373 Expr::Cast { ty, e: inner } => {
5374 let t = ty.erased();
5375 let c = is_ref_jt(&t).then_some(t);
5376 walk_expr(inner, c, st);
5377 }
5378 Expr::InstanceOf { e: inner, .. } => {
5379 walk_expr(inner, Some(JavaType::Object("java/lang/Object".into())), st);
5380 }
5381 Expr::Cond { c, t, f } => {
5382 walk_expr(c, None, st);
5383 let tc = ctx.clone();
5384 walk_expr(t, tc, st);
5385 walk_expr(f, ctx, st);
5386 }
5387 Expr::NewArray { elem, dims, init, .. } => {
5388 for d in dims.iter_mut() {
5389 walk_expr(d, None, st);
5390 }
5391 if let Some(list) = init {
5392 let et = elem.erased();
5393 let c = is_ref_jt(&et).then_some(et);
5394 for slot in list.iter_mut() {
5395 walk_expr(slot, c.clone(), st);
5396 }
5397 }
5398 }
5399 Expr::NewMultiArray { dims, .. } => {
5400 for d in dims.iter_mut() {
5401 walk_expr(d, None, st);
5402 }
5403 }
5404 Expr::ArrayIndex { array, index } => {
5405 walk_expr(array, None, st);
5406 walk_expr(index, None, st);
5407 }
5408 Expr::Field { owner, .. } => {
5409 if let Some(o) = owner {
5410 walk_expr(o, None, st);
5411 }
5412 }
5413 Expr::Bin { l, r, .. } => {
5414 walk_expr(l, None, st);
5415 walk_expr(r, None, st);
5416 }
5417 Expr::Un { e: inner, .. } => walk_expr(inner, None, st),
5418 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
5419 walk_expr(inner, None, st);
5420 }
5421 Expr::StringConcat(parts) => {
5422 for p in parts.iter_mut() {
5423 if let ConcatPart::Str(x) = p {
5424 walk_expr(x, None, st);
5425 }
5426 }
5427 }
5428 Expr::Lambda(l) => {
5429 for c in l.captures.iter_mut() {
5430 walk_expr(c, None, st);
5431 }
5432 }
5433 _ => {}
5434 }
5435 }
5436 fn walk_stmt(s: &mut Stmt, st: &mut St, ret: &JavaType) {
5437 match s {
5438 Stmt::Block(v) => {
5439 for x in v.iter_mut() {
5440 walk_stmt(x, st, ret);
5441 }
5442 }
5443 Stmt::ExprStmt(e) => walk_expr(e, None, st),
5444 Stmt::LocalDef { var, init, .. } => {
5445 if let Some(e) = init {
5446 let ctx = if (*var as usize) < st.vt.vars.len() {
5447 let t = st.vt.vars[*var as usize].ty.erased();
5448 is_ref_jt(&t).then_some(t)
5449 } else {
5450 None
5451 };
5452 walk_expr(e, ctx, st);
5453 }
5454 }
5455 Stmt::Return(e) => {
5456 if let Some(x) = e {
5457 let c = is_ref_jt(ret).then(|| ret.clone());
5458 walk_expr(x, c, st);
5459 }
5460 }
5461 Stmt::Throw(e) => {
5462 walk_expr(e, Some(JavaType::Object("java/lang/Throwable".into())), st);
5463 }
5464 Stmt::If { cond, then_stmt, else_stmt } => {
5465 walk_expr(cond, None, st);
5466 walk_stmt(then_stmt, st, ret);
5467 if let Some(b) = else_stmt {
5468 walk_stmt(b, st, ret);
5469 }
5470 }
5471 Stmt::While { cond, body } => {
5472 walk_expr(cond, None, st);
5473 walk_stmt(body, st, ret);
5474 }
5475 Stmt::DoWhile { body, cond } => {
5476 walk_stmt(body, st, ret);
5477 walk_expr(cond, None, st);
5478 }
5479 Stmt::For { init, cond, update, body } => {
5480 for x in init.iter_mut() {
5481 walk_stmt(x, st, ret);
5482 }
5483 if let Some(c) = cond {
5484 walk_expr(c, None, st);
5485 }
5486 for u in update.iter_mut() {
5487 walk_expr(u, None, st);
5488 }
5489 walk_stmt(body, st, ret);
5490 }
5491 Stmt::ForEach { iterable, body, .. } => {
5492 walk_expr(iterable, None, st);
5493 walk_stmt(body, st, ret);
5494 }
5495 Stmt::Switch { selector, cases, default, .. } => {
5496 walk_expr(selector, None, st);
5497 for c in cases.iter_mut() {
5498 if let Some(g) = &mut c.guard {
5499 walk_expr(g, None, st);
5500 }
5501 for x in c.body.iter_mut() {
5502 walk_stmt(x, st, ret);
5503 }
5504 }
5505 if let Some(d) = default {
5506 walk_stmt(d, st, ret);
5507 }
5508 }
5509 Stmt::Try { body, catches, finally } => {
5510 walk_stmt(body, st, ret);
5511 for c in catches.iter_mut() {
5512 walk_stmt(&mut c.body, st, ret);
5513 }
5514 if let Some(f) = finally {
5515 walk_stmt(f, st, ret);
5516 }
5517 }
5518 Stmt::TryWithResources { resources, body, catches, finally, .. } => {
5519 for r in resources.iter_mut() {
5520 walk_stmt(r, st, ret);
5521 }
5522 walk_stmt(body, st, ret);
5523 for c in catches.iter_mut() {
5524 walk_stmt(&mut c.body, st, ret);
5525 }
5526 if let Some(f) = finally {
5527 walk_stmt(f, st, ret);
5528 }
5529 }
5530 Stmt::Assert { cond, msg } => {
5531 walk_expr(cond, None, st);
5532 if let Some(m) = msg {
5533 walk_expr(m, None, st);
5534 }
5535 }
5536 Stmt::Synchronized { lock, body } => {
5537 walk_expr(lock, Some(JavaType::Object("java/lang/Object".into())), st);
5538 walk_stmt(body, st, ret);
5539 }
5540 Stmt::TernaryValue { e } => walk_expr(e, None, st),
5541 Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
5542 walk_expr(e, Some(JavaType::Object("java/lang/Object".into())), st);
5543 }
5544 Stmt::Labeled { body, .. } => walk_stmt(body, st, ret),
5545 _ => {}
5546 }
5547 }
5548
5549 let mut st = St {
5550 mode: Mode::Scan,
5551 vt,
5552 eligible: &eligible,
5553 ref_read: HashSet::default(),
5554 dead: HashSet::default(),
5555 sentinels: HashSet::default(),
5556 n_lit: 0,
5557 n_var: 0,
5558 };
5559 if !eligible.is_empty() {
5560 walk_stmt(body, &mut st, ret);
5561 st.sentinels = eligible
5562 .iter()
5563 .copied()
5564 .filter(|v| st.ref_read.contains(v) && !st.dead.contains(v))
5565 .collect();
5566 }
5567 if st.sentinels.is_empty() && !has_zero {
5568 return;
5569 }
5570 st.mode = Mode::Rewrite;
5571 walk_stmt(body, &mut st, ret);
5572 if std::env::var_os("DDC_SENT").is_some() {
5573 eprintln!(
5574 "[sent] elig={} confirmed={} lit={} var={}",
5575 eligible.len(),
5576 st.sentinels.len(),
5577 st.n_lit,
5578 st.n_var
5579 );
5580 }
5581}
5582
5583pub fn fix_int_operand_bridges(body: &mut Stmt, vt: &VarTable) {
5591 fn side_bool(e: &Expr, vt: &VarTable) -> bool {
5592 match e {
5593 Expr::Local { var, .. } => {
5594 matches!(vt.var(*var).ty.erased(), JavaType::Boolean)
5595 }
5596 Expr::Bin {
5606 op: BinOp::And | BinOp::Or | BinOp::Xor,
5607 l,
5608 r,
5609 ..
5610 } => side_bool(l, vt) && side_bool(r, vt),
5611 other => matches!(other.type_ref().erased(), JavaType::Boolean),
5612 }
5613 }
5614 fn side_int(e: &Expr, vt: &VarTable) -> bool {
5615 if side_bool(e, vt) {
5619 return false;
5620 }
5621 matches!(
5622 match e {
5623 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
5624 other => other.type_ref().erased(),
5625 },
5626 JavaType::Int | JavaType::Short | JavaType::Byte | JavaType::Char | JavaType::Long
5627 )
5628 }
5629 fn wrap(e: Box<Expr>) -> Box<Expr> {
5630 Box::new(Expr::Cond {
5631 c: e,
5632 t: Box::new(Expr::Const(ConstVal::Int(1))),
5633 f: Box::new(Expr::Const(ConstVal::Int(0))),
5634 })
5635 }
5636 walk_stmt_exprs(body, &mut |e| {
5637 deep_rewrite(e, &mut |x| {
5638 if let Expr::Assign { target, value, op: AssignOp::Plain } = x {
5645 let tgt_ty: Option<JavaType> = match &**target {
5646 Expr::Field { ty, .. } => Some(ty.erased()),
5647 Expr::ArrayIndex { array, .. } => {
5648 match array.type_ref().erased() {
5649 JavaType::Array(el) => Some(el.as_ref().clone()),
5650 _ => None,
5651 }
5652 }
5653 Expr::Local { var, .. } => {
5658 if (*var as usize) < vt.vars.len() {
5659 Some(vt.var(*var).ty.erased())
5660 } else {
5661 None
5662 }
5663 }
5664 _ => None,
5665 };
5666 if let Some(t) = tgt_ty {
5667 let tgt_int = matches!(
5668 t,
5669 JavaType::Int
5670 | JavaType::Short
5671 | JavaType::Byte
5672 | JavaType::Char
5673 | JavaType::Long
5674 );
5675 if tgt_int && side_bool(value, vt) && !side_int(value, vt) {
5676 let taken = std::mem::replace(
5677 value,
5678 Box::new(Expr::Const(ConstVal::Null)),
5679 );
5680 let mut wrapped = wrap(taken);
5681 if !matches!(t, JavaType::Int | JavaType::Long) {
5682 wrapped = Box::new(Expr::Cast {
5683 ty: TypeRef::J(t.clone()),
5684 e: wrapped,
5685 });
5686 }
5687 *value = wrapped;
5688 } else if matches!(t, JavaType::Boolean)
5689 && side_int(value, vt)
5690 && !side_bool(value, vt)
5691 {
5692 let taken = std::mem::replace(
5693 value,
5694 Box::new(Expr::Const(ConstVal::Null)),
5695 );
5696 **value = Expr::Bin {
5697 op: BinOp::Ne,
5698 l: taken,
5699 r: Box::new(Expr::Const(ConstVal::Int(0))),
5700 ty: Some(TypeRef::J(JavaType::Boolean)),
5701 };
5702 }
5703 }
5704 }
5705 if let Expr::Cast { ty, e } = x {
5711 let t = ty.erased();
5712 let t_num = matches!(
5713 t,
5714 JavaType::Int
5715 | JavaType::Short
5716 | JavaType::Byte
5717 | JavaType::Char
5718 | JavaType::Long
5719 | JavaType::Float
5720 | JavaType::Double
5721 );
5722 if t_num && side_bool(e, vt) && !side_int(e, vt) {
5723 let taken = std::mem::replace(e, Box::new(Expr::Const(ConstVal::Null)));
5724 **e = Expr::Cond {
5725 c: taken,
5726 t: Box::new(Expr::Const(ConstVal::Int(1))),
5727 f: Box::new(Expr::Const(ConstVal::Int(0))),
5728 };
5729 } else if matches!(t, JavaType::Boolean)
5730 && side_int(e, vt)
5731 && !side_bool(e, vt)
5732 {
5733 let taken = std::mem::replace(e, Box::new(Expr::Const(ConstVal::Null)));
5734 *x = Expr::Bin {
5735 op: BinOp::Ne,
5736 l: taken,
5737 r: Box::new(Expr::Const(ConstVal::Int(0))),
5738 ty: Some(TypeRef::J(JavaType::Boolean)),
5739 };
5740 }
5741 }
5742 if let Expr::Bin { op, l, r, .. } = x {
5751 if matches!(op, BinOp::Eq | BinOp::Ne) {
5752 if matches!(&**r, Expr::Const(ConstVal::Null))
5753 && side_int(l, vt)
5754 && !side_bool(l, vt)
5755 {
5756 **r = Expr::Const(ConstVal::Int(0));
5757 } else if matches!(&**l, Expr::Const(ConstVal::Null))
5758 && side_int(r, vt)
5759 && !side_bool(r, vt)
5760 {
5761 **l = Expr::Const(ConstVal::Int(0));
5762 } else if matches!(&**r, Expr::Const(ConstVal::Null))
5763 && side_bool(l, vt)
5764 {
5765 let taken =
5766 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
5767 *x = if matches!(op, BinOp::Eq) {
5768 Expr::Un { op: UnOp::Not, e: taken }
5769 } else {
5770 *taken
5771 };
5772 } else if matches!(&**l, Expr::Const(ConstVal::Null))
5773 && side_bool(r, vt)
5774 {
5775 let taken =
5776 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
5777 *x = if matches!(op, BinOp::Eq) {
5778 Expr::Un { op: UnOp::Not, e: taken }
5779 } else {
5780 *taken
5781 };
5782 }
5783 }
5784 }
5785 if let Expr::ArrayIndex { index, .. } = x {
5789 if side_bool(index, vt) && !side_int(index, vt) {
5790 let taken =
5791 std::mem::replace(index, Box::new(Expr::Const(ConstVal::Null)));
5792 *index = wrap(taken);
5793 }
5794 }
5795 if let Expr::NewArray { dims, .. } = x {
5801 for d in dims.iter_mut() {
5802 if side_bool(d, vt) && !side_int(d, vt) {
5803 let taken =
5804 std::mem::replace(d, Expr::Const(ConstVal::Null));
5805 *d = *wrap(Box::new(taken));
5806 }
5807 }
5808 }
5809 if let Expr::Bin { op, l, r, .. } = x {
5810 let int_kind = matches!(
5811 op,
5812 BinOp::Or
5813 | BinOp::And
5814 | BinOp::Xor
5815 | BinOp::Add
5816 | BinOp::Sub
5817 | BinOp::Mul
5818 | BinOp::Div
5819 | BinOp::Rem
5820 | BinOp::Shl
5821 | BinOp::Shr
5822 | BinOp::Ushr
5823 | BinOp::Eq
5824 | BinOp::Ne
5825 | BinOp::Lt
5826 | BinOp::Ge
5827 | BinOp::Gt
5828 | BinOp::Le
5829 );
5830 if !int_kind {
5831 return;
5832 }
5833 if matches!(
5846 op,
5847 BinOp::Or
5848 | BinOp::And
5849 | BinOp::Xor
5850 | BinOp::Add
5851 | BinOp::Sub
5852 | BinOp::Mul
5853 | BinOp::Div
5854 | BinOp::Rem
5855 | BinOp::Shl
5856 | BinOp::Shr
5857 | BinOp::Ushr
5858 ) {
5859 if matches!(&**r, Expr::Const(ConstVal::Null))
5860 && (side_bool(l, vt) || side_int(l, vt))
5861 {
5862 **r = Expr::Const(ConstVal::Int(0));
5863 } else if matches!(&**l, Expr::Const(ConstVal::Null))
5864 && (side_bool(r, vt) || side_int(r, vt))
5865 {
5866 **l = Expr::Const(ConstVal::Int(0));
5867 }
5868 }
5869 let arith = matches!(
5879 op,
5880 BinOp::Add
5881 | BinOp::Sub
5882 | BinOp::Mul
5883 | BinOp::Div
5884 | BinOp::Rem
5885 | BinOp::Shl
5886 | BinOp::Shr
5887 | BinOp::Ushr
5888 );
5889 let ordered = matches!(op, BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le);
5890 if arith || ordered {
5891 let side_str = |e: &Expr| {
5892 matches!(
5893 match e {
5894 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
5895 other => other.type_ref().erased(),
5896 },
5897 JavaType::Object(ref s) if s.as_ref() == "java/lang/String"
5898 )
5899 };
5900 let concat = arith && (side_str(l) || side_str(r));
5901 if !concat {
5902 if side_bool(l, vt) {
5903 let taken =
5904 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
5905 *l = wrap(taken);
5906 }
5907 if side_bool(r, vt) {
5908 let taken =
5909 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
5910 *r = wrap(taken);
5911 }
5912 }
5913 return;
5914 }
5915 if side_bool(l, vt) && side_int(r, vt) && !side_bool(r, vt) {
5918 let taken = std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
5919 *l = wrap(taken);
5920 } else if side_bool(r, vt) && side_int(l, vt) && !side_bool(l, vt) {
5921 let taken = std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
5922 *r = wrap(taken);
5923 }
5924 }
5925 });
5926 });
5927}
5928
5929pub fn fold_value_diamonds(body: &mut Stmt, vt: &VarTable) {
5967 let Stmt::Block(stmts) = body else { return };
5968 fold_diamonds_in(stmts, vt);
5969}
5970
5971fn diamond_types_ok(a: &Expr, b: &Expr) -> bool {
5972 fn num(t: &JavaType) -> bool {
5973 matches!(
5974 t,
5975 JavaType::Byte
5976 | JavaType::Short
5977 | JavaType::Int
5978 | JavaType::Char
5979 | JavaType::Long
5980 | JavaType::Float
5981 | JavaType::Double
5982 )
5983 }
5984 if let Some(other) = if matches!(a, Expr::Const(ConstVal::Null)) {
5992 Some(b)
5993 } else if matches!(b, Expr::Const(ConstVal::Null)) {
5994 Some(a)
5995 } else {
5996 None
5997 } {
5998 return !num(&other.type_ref().erased());
5999 }
6000 let ta = a.type_ref().erased();
6001 let tb = b.type_ref().erased();
6002 ta == tb || (num(&ta) && num(&tb))
6003}
6004
6005fn single_local_assign(st: &Stmt) -> Option<(u32, Expr, Expr)> {
6013 let mut inner = st;
6014 while let Stmt::Block(v) = inner {
6015 if v.len() != 1 {
6016 return None;
6017 }
6018 inner = &v[0];
6019 }
6020 match inner {
6021 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain }) => {
6022 match &**target {
6023 Expr::Local { var, .. } => Some((*var, *target.clone(), *value.clone())),
6024 _ => None,
6025 }
6026 }
6027 _ => None,
6028 }
6029}
6030
6031fn arm_assign_inlined(st: &Stmt) -> Option<(u32, Expr, Expr)> {
6040 if let Some(r) = single_local_assign(st) {
6041 return Some(r);
6042 }
6043 let mut inner = st;
6044 while let Stmt::Block(v) = inner {
6045 if v.len() == 1 {
6046 inner = &v[0];
6047 } else {
6048 break;
6049 }
6050 }
6051 let Stmt::Block(v) = inner else { return None };
6052 if v.len() != 2 {
6053 return None;
6054 }
6055 let Stmt::LocalDef {
6056 var: x,
6057 init: Some(init),
6058 ..
6059 } = &v[0]
6060 else {
6061 return None;
6062 };
6063 let (var, tgt, val) = single_local_assign(&v[1])?;
6064 let mut count = 0usize;
6065 let mut c = val.clone();
6066 deep_rewrite(&mut c, &mut |e| {
6067 if let Expr::Local { var: lv, .. } = e {
6068 if lv == x {
6069 count += 1;
6070 }
6071 }
6072 });
6073 if count != 1 {
6074 return None;
6075 }
6076 let mut out = val.clone();
6077 deep_rewrite(&mut out, &mut |e| {
6078 if let Expr::Local { var: lv, .. } = e {
6079 if lv == x {
6080 *e = init.clone();
6081 }
6082 }
6083 });
6084 Some((var, tgt, out))
6085}
6086
6087fn mk_ternary_assign(tgt: Expr, c: Expr, a: Expr, b: Expr) -> Stmt {
6088 Stmt::ExprStmt(Expr::Assign {
6089 target: Box::new(tgt),
6090 op: AssignOp::Plain,
6091 value: Box::new(Expr::Cond {
6092 c: Box::new(c),
6093 t: Box::new(a),
6094 f: Box::new(b),
6095 }),
6096 })
6097}
6098
6099fn same_render_var(v1: u32, v2: u32, vt: &VarTable) -> bool {
6104 let (a, b) = (vt.vars.get(v1 as usize), vt.vars.get(v2 as usize));
6105 match (a, b) {
6106 (Some(x), Some(y)) => x.name == y.name && x.ty.erased() == y.ty.erased(),
6107 _ => false,
6108 }
6109}
6110
6111fn canon_stmts_eq(a: &[Stmt], b: &[Stmt], vt: &VarTable) -> bool {
6118 if a.len() != b.len() {
6119 return false;
6120 }
6121 let mut map: std::collections::HashMap<String, u32> = std::collections::HashMap::default();
6122 let canon = |s: &Stmt, map: &mut std::collections::HashMap<String, u32>| -> Stmt {
6123 let mut c = s.clone();
6124 walk_stmt_exprs(&mut c, &mut |e| {
6125 deep_rewrite(e, &mut |x| {
6126 if let Expr::Local { var, .. } = x {
6127 let key = match vt.vars.get(*var as usize) {
6128 Some(vi) => format!("{}#{:?}", vi.name, vi.ty.erased()),
6129 None => format!("v{}", var),
6130 };
6131 let id = *map.entry(key).or_insert(*var);
6132 *x = Expr::Local {
6133 var: id,
6134 ty: vt.var(id).ty.clone(),
6135 };
6136 }
6137 });
6138 });
6139 c
6140 };
6141 a.iter()
6142 .zip(b.iter())
6143 .all(|(x, y)| canon(x, &mut map) == canon(y, &mut map))
6144}
6145
6146fn fold_diamonds_in(stmts: &mut Vec<Stmt>, vt: &VarTable) {
6147 let mut i = 0usize;
6148 while i < stmts.len() {
6149 match &mut stmts[i] {
6150 Stmt::Block(v) => fold_diamonds_in(v, vt),
6151 Stmt::If { then_stmt, else_stmt, .. } => {
6152 if let Stmt::Block(v) = &mut **then_stmt {
6153 fold_diamonds_in(v, vt);
6154 }
6155 if let Some(e) = else_stmt {
6156 if let Stmt::Block(v) = &mut **e {
6157 fold_diamonds_in(v, vt);
6158 }
6159 }
6160 }
6161 _ => {}
6162 }
6163 let shape1 = if let Stmt::If { cond, then_stmt, else_stmt: Some(el), .. } = &stmts[i] {
6165 match (arm_assign_inlined(then_stmt), arm_assign_inlined(el)) {
6166 (Some((v1, tgt, a)), Some((v2, _, b)))
6167 if v1 == v2 && diamond_types_ok(&a, &b) =>
6168 {
6169 Some(mk_ternary_assign(tgt, cond.clone(), a, b))
6170 }
6171 _ => None,
6172 }
6173 } else {
6174 None
6175 };
6176 if let Some(f) = shape1 {
6177 stmts[i] = f;
6178 i += 1;
6179 continue;
6180 }
6181 let shape3 = if let Stmt::If {
6194 cond,
6195 then_stmt,
6196 else_stmt: Some(el),
6197 ..
6198 } = &stmts[i]
6199 {
6200 let arm = |st: &Stmt| -> Option<((u32, Expr, Expr), Vec<Stmt>)> {
6201 let Stmt::Block(v) = st else { return None };
6202 if v.len() < 2 {
6203 return None;
6204 }
6205 let first = single_local_assign(&v[0])?;
6206 Some((first, v[1..].to_vec()))
6207 };
6208 match (arm(then_stmt), arm(el)) {
6209 (Some(((v1, tgt1, a), mut rest_t)), Some(((v2, _t2, b), rest_e)))
6210 if !rest_t.is_empty()
6211 && is_bare_ctor_call(first_leaf_stmt(&rest_t[0]))
6212 && stmts_count_writes(&rest_t, v1) == 0
6213 && stmts_count_writes(&rest_t, v2) == 0
6214 && diamond_types_ok(&a, &b) =>
6215 {
6216 if v1 == v2 || same_render_var(v1, v2, vt) {
6217 if canon_stmts_eq(&rest_t, &rest_e, vt) {
6219 Some((mk_ternary_assign(tgt1, cond.clone(), a, b), rest_t))
6220 } else {
6221 None
6222 }
6223 } else if stmts_read_var(&rest_t, v1) == 1
6224 && stmts_read_var(&rest_e, v2) == 1
6225 && stmts_read_var(&rest_t, v2) == 0
6226 && stmts_read_var(&rest_e, v1) == 0
6227 {
6228 let mut unified = rest_e.clone();
6236 for st in unified.iter_mut() {
6237 let mut c2 = st.clone();
6238 walk_stmt_exprs(&mut c2, &mut |e| {
6239 deep_rewrite(e, &mut |x| {
6240 if let Expr::Local { var, .. } = x {
6241 if *var == v2 {
6242 *x = Expr::Local {
6243 var: v1,
6244 ty: vt.var(v1).ty.clone(),
6245 };
6246 }
6247 }
6248 });
6249 });
6250 *st = c2;
6251 }
6252 if unified != rest_t {
6253 None
6254 } else {
6255 let tern = Expr::Cond {
6256 c: Box::new(cond.clone()),
6257 t: Box::new(a.clone()),
6258 f: Box::new(b.clone()),
6259 };
6260 for st in rest_t.iter_mut() {
6261 let mut c2 = st.clone();
6262 walk_stmt_exprs(&mut c2, &mut |e| {
6263 deep_rewrite(e, &mut |x| {
6264 if let Expr::Local { var, .. } = x {
6265 if *var == v1 {
6266 *x = tern.clone();
6267 }
6268 }
6269 });
6270 });
6271 *st = c2;
6272 }
6273 let first = rest_t.remove(0);
6277 Some((first, rest_t))
6278 }
6279 } else {
6280 None
6281 }
6282 }
6283 _ => None,
6284 }
6285 } else {
6286 None
6287 };
6288 if let Some((folded, rest)) = shape3 {
6289 stmts[i] = folded;
6290 stmts.splice(i + 1..i + 1, rest);
6291 i += 1;
6292 continue;
6293 }
6294 if i + 1 < stmts.len() {
6304 let shape2 = {
6324 let s_i = single_local_assign(&stmts[i]);
6325 if let Stmt::If {
6326 cond,
6327 then_stmt,
6328 else_stmt,
6329 ..
6330 } = &stmts[i + 1]
6331 {
6332 let then_a = single_local_assign(then_stmt);
6333 let else_a = else_stmt.as_deref().and_then(single_local_assign);
6334 let then_empty =
6335 matches!(&**then_stmt, Stmt::Block(v) if v.is_empty());
6336 let else_empty = else_stmt
6337 .as_deref()
6338 .map(|e| matches!(e, Stmt::Block(v) if v.is_empty()))
6339 .unwrap_or(true);
6340 let try_fold = |over: Option<(u32, Expr, Expr)>,
6341 keep_over: bool|
6342 -> Option<Stmt> {
6343 let (v2, tgt2, a) = over?;
6344 let (v1, _t1, b) = s_i.as_ref()?;
6345 if !(*v1 == v2 || same_render_var(*v1, v2, vt)) {
6346 return None;
6347 }
6348 let keep = if *v1 == v2 {
6349 b.clone()
6350 } else {
6351 Expr::Local {
6352 var: *v1,
6353 ty: vt.var(*v1).ty.clone(),
6354 }
6355 };
6356 if !diamond_types_ok(&a, &keep) {
6357 return None;
6358 }
6359 if keep_over {
6360 Some(mk_ternary_assign(tgt2, cond.clone(), keep, a))
6361 } else {
6362 Some(mk_ternary_assign(tgt2, cond.clone(), a, keep))
6363 }
6364 };
6365 if then_a.is_some() && else_empty {
6366 try_fold(then_a, false)
6367 } else if else_a.is_some() && then_empty {
6368 try_fold(else_a, true)
6369 } else {
6370 None
6371 }
6372 } else {
6373 None
6374 }
6375 };
6376 let mut lead_fold: Option<(Stmt, Stmt)> = None;
6385 if shape2.is_none() && i + 1 < stmts.len() {
6386 let s_i2 = single_local_assign(&stmts[i]);
6387 let leading = |st: &Stmt| -> Option<((u32, Expr, Expr), Vec<Stmt>)> {
6388 let Stmt::Block(v) = st else { return None };
6389 if v.len() < 2 {
6390 return None;
6391 }
6392 let first = single_local_assign(&v[0])?;
6393 Some((first, v[1..].to_vec()))
6394 };
6395 if let (Some((v1, _t1, b)), Stmt::If { cond, then_stmt, else_stmt, .. }) =
6396 (&s_i2, &stmts[i + 1])
6397 {
6398 let else_absent = else_stmt.is_none()
6399 || else_stmt
6400 .as_deref()
6401 .is_some_and(|e| matches!(e, Stmt::Block(v) if v.is_empty()));
6402 let then_absent = matches!(&**then_stmt, Stmt::Block(v) if v.is_empty());
6403 let try_lead = |over: Option<((u32, Expr, Expr), Vec<Stmt>)>,
6404 keep_over: bool|
6405 -> Option<(Stmt, Stmt)> {
6406 let ((v2, tgt2, a), rest) = over?;
6407 if !(*v1 == v2 || same_render_var(*v1, v2, vt)) {
6408 return None;
6409 }
6410 if stmts_count_writes(&rest, *v1) != 0
6411 || stmts_count_writes(&rest, v2) != 0
6412 {
6413 return None;
6414 }
6415 let keep = if *v1 == v2 {
6416 b.clone()
6417 } else {
6418 Expr::Local {
6419 var: *v1,
6420 ty: vt.var(*v1).ty.clone(),
6421 }
6422 };
6423 if !diamond_types_ok(&a, &keep) {
6424 return None;
6425 }
6426 let t = if keep_over {
6427 mk_ternary_assign(tgt2, cond.clone(), keep, a)
6428 } else {
6429 mk_ternary_assign(tgt2, cond.clone(), a, keep)
6430 };
6431 let mut new_if = stmts[i + 1].clone();
6432 if let Stmt::If { then_stmt, else_stmt, .. } = &mut new_if {
6433 if keep_over {
6434 *else_stmt = Some(Box::new(Stmt::Block(rest)));
6435 } else {
6436 **then_stmt = Stmt::Block(rest);
6437 }
6438 }
6439 Some((t, new_if))
6440 };
6441 if else_absent {
6442 lead_fold = try_lead(leading(then_stmt), false);
6443 } else if then_absent {
6444 if let Some(e) = else_stmt {
6445 lead_fold = try_lead(leading(e), true);
6446 }
6447 }
6448 }
6449 }
6450 if let Some((folded, new_if)) = lead_fold {
6451 stmts[i] = folded;
6452 stmts[i + 1] = new_if;
6453 continue;
6454 }
6455 if let Some(f) = shape2 {
6456 stmts[i] = f;
6457 stmts.remove(i + 1);
6458 continue; }
6460 }
6461 i += 1;
6462 }
6463}
6464
6465pub fn fold_bool_value_diamonds(body: &mut Stmt, vt: &VarTable) {
6466 fn is_bool_var(v: u32, vt: &VarTable) -> bool {
6467 (v as usize) < vt.vars.len()
6468 && matches!(vt.vars[v as usize].ty.erased(), JavaType::Boolean)
6469 }
6470 fn bool_const(e: &Expr) -> Option<bool> {
6471 fn cint(e: &Expr) -> Option<i32> {
6472 match e {
6473 Expr::Const(ConstVal::Int(n)) => Some(*n),
6474 _ => None,
6475 }
6476 }
6477 match e {
6478 Expr::Const(ConstVal::Int(0)) => Some(false),
6479 Expr::Const(ConstVal::Int(1)) => Some(true),
6480 Expr::Bin { op: op @ (BinOp::Eq | BinOp::Ne), l, r, .. } => {
6484 let (a, b) = (cint(l)?, cint(r)?);
6485 let eq = a == b;
6486 Some(if matches!(op, BinOp::Eq) { eq } else { !eq })
6487 }
6488 _ => None,
6489 }
6490 }
6491 fn reads_var(e: &Expr, v: u32) -> bool {
6492 let mut hit = false;
6493 visit_exprs(e, &mut |x| {
6494 if let Expr::Local { var, .. } = x {
6495 if *var == v {
6496 hit = true;
6497 }
6498 }
6499 });
6500 hit
6501 }
6502 fn bool_valued(e: &Expr, vt: &VarTable) -> bool {
6506 fn side(e: &Expr, vt: &VarTable) -> bool {
6507 bool_const(e).is_some() || bool_valued(e, vt)
6508 }
6509 match e {
6510 Expr::Local { var, .. } => is_bool_var(*var, vt),
6511 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
6512 Expr::InstanceOf { .. } => true,
6513 Expr::Un { op: UnOp::Not, e: x, .. } => bool_valued(x, vt) || bool_const(x).is_some(),
6514 Expr::Bin { op, l, r, .. } => match op {
6515 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le => true,
6516 BinOp::LogAnd | BinOp::LogOr => {
6519 side(l, vt) && side(r, vt)
6520 }
6521 _ => false,
6522 },
6523 Expr::Cond { t, f, .. } => side(t, vt) && side(f, vt),
6524 _ => false,
6525 }
6526 }
6527 fn lone_assign(
6536 b: &Stmt,
6537 v: u32,
6538 vt: &VarTable,
6539 an: &VarAnalysis,
6540 consumed: &mut HashSet<u32>,
6541 ) -> Option<Expr> {
6542 let l = flat_list(b);
6543 if l.len() == 2 {
6544 let (u, e): (u32, &Expr) = match l[0] {
6545 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e),
6546 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
6547 match &**target {
6548 Expr::Local { var, .. } => (*var, &**value),
6549 _ => return None,
6550 }
6551 }
6552 _ => return None,
6553 };
6554 if u == v || consumed.contains(&u) {
6555 return None;
6556 }
6557 if an.assigns.get(u as usize).copied().unwrap_or(0) != 1
6564 || an.reads.get(u as usize).copied().unwrap_or(0) != 1
6565 {
6566 return None;
6567 }
6568 let (tv, rv) = match l[1] {
6569 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
6570 match (&**target, &**value) {
6571 (Expr::Local { var: t, .. }, Expr::Local { var: r, .. }) => (*t, *r),
6572 _ => return None,
6573 }
6574 }
6575 Stmt::LocalDef { var, init: Some(Expr::Local { var: r, .. }), .. } => (*var, *r),
6576 _ => return None,
6577 };
6578 if tv != v || rv != u {
6579 return None;
6580 }
6581 if reads_var(e, v) || !(bool_valued(e, vt) || bool_const(e).is_some()) {
6582 return None;
6583 }
6584 consumed.insert(u);
6585 return Some(e.clone());
6586 }
6587 if l.len() != 1 {
6588 return None;
6589 }
6590 let (target, value) = match l[0] {
6591 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => (target, value),
6592 Stmt::LocalDef { var, init: Some(value), .. } if *var == v => {
6593 if reads_var(value, v) || !(bool_valued(value, vt) || bool_const(value).is_some()) {
6594 return None;
6595 }
6596 return Some(value.clone());
6597 }
6598 _ => return None,
6599 };
6600 let Expr::Local { var: tv, .. } = &**target else { return None };
6601 if *tv != v || reads_var(value, v) {
6602 return None;
6603 }
6604 if !(bool_valued(value, vt) || bool_const(value).is_some()) {
6605 return None;
6606 }
6607 Some((**value).clone())
6608 }
6609 fn not(e: Expr) -> Expr {
6610 Expr::Un { op: UnOp::Not, e: Box::new(e) }
6611 }
6612 fn simplify_bool(e: Expr) -> Expr {
6617 match e {
6618 Expr::Un { op: UnOp::Not, e: inner, .. } => match *inner {
6619 Expr::Bin { op: BinOp::Ne, l, r, ty } => {
6624 simplify_bool(Expr::Bin { op: BinOp::Eq, l, r, ty })
6625 }
6626 Expr::Bin { op: BinOp::Eq, l, r, ty } => {
6627 simplify_bool(Expr::Bin { op: BinOp::Ne, l, r, ty })
6628 }
6629 other => Expr::Un { op: UnOp::Not, e: Box::new(simplify_bool(other)) },
6630 },
6631 Expr::Bin { op: op @ (BinOp::Eq | BinOp::Ne), l, r, ty } => {
6632 fn cond01(e: &Expr) -> Option<Expr> {
6634 if let Expr::Cond { c, t, f } = e {
6635 if matches!(&**t, Expr::Const(ConstVal::Int(1)))
6636 && matches!(&**f, Expr::Const(ConstVal::Int(0)))
6637 {
6638 return Some((**c).clone());
6639 }
6640 }
6641 None
6642 }
6643 let (lc, rc) = (cond01(&l), cond01(&r));
6644 match (lc, rc) {
6645 (Some(c), None) => match (&*r, op) {
6646 (Expr::Const(ConstVal::Int(1)), BinOp::Eq) => c,
6647 (Expr::Const(ConstVal::Int(1)), BinOp::Ne) => not(c),
6648 (Expr::Const(ConstVal::Int(0)), BinOp::Eq) => not(c),
6649 (Expr::Const(ConstVal::Int(0)), BinOp::Ne) => c,
6650 _ => Expr::Bin { op, l: Box::new(c), r, ty },
6651 },
6652 (None, Some(c)) => match (&*l, op) {
6653 (Expr::Const(ConstVal::Int(1)), BinOp::Eq) => c,
6654 (Expr::Const(ConstVal::Int(1)), BinOp::Ne) => not(c),
6655 (Expr::Const(ConstVal::Int(0)), BinOp::Eq) => not(c),
6656 (Expr::Const(ConstVal::Int(0)), BinOp::Ne) => c,
6657 _ => Expr::Bin { op, l, r: Box::new(c), ty },
6658 },
6659 _ => Expr::Bin { op, l, r, ty },
6660 }
6661 }
6662 other => other,
6663 }
6664 }
6665 fn bin(op: BinOp, l: Expr, r: Expr) -> Expr {
6666 Expr::Bin { op, l: Box::new(l), r: Box::new(r), ty: None }
6667 }
6668
6669 for _round in 0..4 {
6670 let mut changed = false;
6671 let mut analysis = VarAnalysis {
6672 assigns: Vec::new(),
6673 reads: Vec::new(),
6674 values: Vec::new(),
6675 };
6676 analyze_vars(body, &mut analysis);
6677 let mut consumed: HashSet<u32> = HashSet::default();
6678 walk_mut_deep(body, &mut |st| {
6679 let Stmt::Block(list) = st else { return };
6680 let mut i = 0usize;
6681 while i + 1 < list.len() {
6682 let folded = try_at(list, i, vt, &is_bool_var, &bool_const, &reads_var, &lone_assign, &analysis, &mut consumed);
6683 match folded {
6684 Some(expr) => {
6685 let is_def = matches!(&list[i], Stmt::LocalDef { .. });
6686 let var = match &list[i] {
6687 Stmt::LocalDef { var, .. } => *var,
6688 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
6689 match &**target { Expr::Local { var, .. } => *var, _ => unreachable!() }
6690 }
6691 _ => unreachable!(),
6692 };
6693 let (is_final, force_type) = match &list[i] {
6694 Stmt::LocalDef { is_final, force_type, .. } => (*is_final, *force_type),
6695 _ => (false, false),
6696 };
6697 list[i] = if is_def {
6698 Stmt::LocalDef { var, init: Some(expr), is_final, force_type }
6699 } else {
6700 Stmt::ExprStmt(Expr::Assign {
6701 target: Box::new(Expr::Local {
6702 var,
6703 ty: TypeRef::J(JavaType::Boolean),
6704 }),
6705 op: AssignOp::Plain,
6706 value: Box::new(expr),
6707 })
6708 };
6709 list.remove(i + 1);
6710 changed = true;
6711 }
6714 None => i += 1,
6715 }
6716 }
6717 });
6718 if !changed {
6719 break;
6720 }
6721 }
6722
6723 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
6724 fn try_at(
6725 list: &[Stmt],
6726 i: usize,
6727 vt: &VarTable,
6728 is_bool_var: &dyn Fn(u32, &VarTable) -> bool,
6729 bool_const: &dyn Fn(&Expr) -> Option<bool>,
6730 reads_var: &dyn Fn(&Expr, u32) -> bool,
6731 lone_assign: &dyn Fn(&Stmt, u32, &VarTable, &VarAnalysis, &mut HashSet<u32>) -> Option<Expr>,
6732 an: &VarAnalysis,
6733 consumed: &mut HashSet<u32>,
6734 ) -> Option<Expr> {
6735 let (v, base) = match &list[i] {
6737 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
6738 match &**target {
6739 Expr::Local { var, .. } => {
6740 let b = bool_const(value)?;
6743 if !is_bool_var(*var, vt) { return None; }
6744 (*var, b)
6745 }
6746 _ => return None,
6747 }
6748 }
6749 Stmt::LocalDef { var, init: Some(e), .. } => {
6750 let b = bool_const(e)?;
6751 if !is_bool_var(*var, vt) { return None; }
6752 (*var, b)
6753 }
6754 _ => return None,
6755 };
6756 let Stmt::If { cond, then_stmt, else_stmt } = &list[i + 1] else {
6757 return None;
6758 };
6759 if reads_var(cond, v) {
6760 return None;
6761 }
6762 let mut touches_v = false;
6763 visit_exprs(cond, &mut |x| {
6764 if let Expr::Assign { target, .. } = x {
6765 if let Expr::Local { var, .. } = &**target {
6766 if *var == v { touches_v = true; }
6767 }
6768 }
6769 });
6770 if touches_v {
6771 return None;
6772 }
6773 let (cond_n, then_arm, else_arm): (Expr, &Stmt, Option<&Stmt>) =
6776 if flat_list(then_stmt).is_empty() && else_stmt.is_some() {
6777 (not(cond.clone()), else_stmt.as_ref().unwrap(), None)
6778 } else {
6779 (cond.clone(), then_stmt.as_ref(), else_stmt.as_deref())
6780 };
6781 let e1 = lone_assign(then_arm, v, vt, an, consumed)?;
6782 let e2 = match else_arm {
6783 None => None,
6784 Some(b) => Some(lone_assign(b, v, vt, an, consumed)?),
6785 };
6786 let e1c = bool_const(&e1);
6787 let c = simplify_bool(cond_n);
6788 Some(match e2 {
6789 None => {
6790 if !base {
6791 match e1c {
6792 Some(true) => c,
6793 Some(false) => return None, None => bin(BinOp::LogAnd, c, e1),
6795 }
6796 } else {
6797 match e1c {
6798 Some(false) => not(c),
6799 Some(true) => return None,
6800 None => bin(BinOp::LogOr, not(c), e1),
6801 }
6802 }
6803 }
6804 Some(e2) => {
6805 if e1 == e2 {
6807 return None; }
6809 Expr::Cond { c: Box::new(c), t: Box::new(e1), f: Box::new(e2) }
6810 }
6811 })
6812 }
6813}
6814
6815pub fn idiom_compounds(body: &mut Stmt, vt: &VarTable) {
6816 walk_mut_deep(body, &mut |st| {
6817 let Stmt::ExprStmt(e) = st else { return };
6818 let Expr::Assign { target, op: AssignOp::Plain, value } = &mut *e else {
6819 return;
6820 };
6821 enum Tgt {
6824 Loc(u32),
6825 Fld,
6826 }
6827 let tgt = match &**target {
6828 Expr::Local { var, .. } => Tgt::Loc(*var),
6829 Expr::Field { owner, .. }
6830 if owner.is_none()
6831 || matches!(&**owner.as_ref().unwrap(), Expr::This) =>
6832 {
6833 Tgt::Fld
6834 }
6835 _ => return,
6836 };
6837 let Expr::Bin { op, l, r, .. } = &mut **value else {
6838 return;
6839 };
6840 match (&tgt, &**l) {
6842 (Tgt::Loc(v), Expr::Local { var, .. }) if *var == *v => {}
6843 (Tgt::Fld, Expr::Field { cls: c2, name: n2, .. }) => {
6844 let Expr::Field { cls: c1, name: n1, .. } = &**target else {
6845 return;
6846 };
6847 if c1 != c2 || n1 != n2 {
6848 return;
6849 }
6850 }
6851 _ => return,
6852 }
6853 let one = match &**r {
6855 Expr::Const(ConstVal::Int(1)) => Some(1i64),
6856 Expr::Const(ConstVal::Int(-1)) => Some(-1),
6857 Expr::Const(ConstVal::Long(1)) => Some(1),
6858 Expr::Const(ConstVal::Long(-1)) => Some(-1),
6859 _ => None,
6860 };
6861 if *op == BinOp::Add || *op == BinOp::Sub {
6862 if let Some(mut d) = one {
6863 if *op == BinOp::Sub {
6864 d = -d;
6865 }
6866 let tgt_ty = match &tgt {
6867 Tgt::Loc(v) => vt.var(*v).ty.erased(),
6868 Tgt::Fld => {
6869 let Expr::Field { ty, .. } = &**target else {
6870 return;
6871 };
6872 ty.erased()
6873 }
6874 };
6875 let numeric = matches!(
6876 tgt_ty,
6877 JavaType::Int
6878 | JavaType::Long
6879 | JavaType::Short
6880 | JavaType::Byte
6881 | JavaType::Char
6882 | JavaType::Float
6883 | JavaType::Double
6884 );
6885 if numeric {
6886 let wide = matches!(tgt_ty, JavaType::Long);
6887 let inner = match &tgt {
6888 Tgt::Loc(v) => Expr::Local {
6889 var: *v,
6890 ty: vt.var(*v).ty.clone(),
6891 },
6892 Tgt::Fld => (**target).clone(),
6893 };
6894 *e = Expr::PostIncDec {
6895 e: Box::new(inner),
6896 delta: d,
6897 wide,
6898 };
6899 return;
6900 }
6901 }
6902 }
6903 let cop = match op {
6904 BinOp::Add => AssignOp::Add,
6905 BinOp::Sub => AssignOp::Sub,
6906 BinOp::Mul => AssignOp::Mul,
6907 BinOp::Div => AssignOp::Div,
6908 BinOp::Rem => AssignOp::Rem,
6909 BinOp::Shl => AssignOp::Shl,
6910 BinOp::Shr => AssignOp::Shr,
6911 BinOp::Ushr => AssignOp::Ushr,
6912 BinOp::And => AssignOp::And,
6913 BinOp::Or => AssignOp::Or,
6914 BinOp::Xor => AssignOp::Xor,
6915 _ => return,
6916 };
6917 let rhs = std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
6918 let new_target = match &tgt {
6919 Tgt::Loc(v) => Box::new(Expr::Local {
6920 var: *v,
6921 ty: vt.var(*v).ty.clone(),
6922 }),
6923 Tgt::Fld => target.clone(),
6924 };
6925 *e = Expr::Assign {
6926 target: new_target,
6927 op: cop,
6928 value: rhs,
6929 };
6930 });
6931}
6932
6933pub fn fix_bool_xor(body: &mut Stmt, vt: &VarTable, ret_bool: bool) {
6934 fn is_one(e: &Expr) -> bool {
6935 matches!(e, Expr::Const(ConstVal::Int(1)))
6936 }
6937 fn bty(e: &Expr, vt: &VarTable) -> bool {
6938 match e {
6939 Expr::Local { var, .. } => {
6940 matches!(vt.var(*var).ty.erased(), JavaType::Boolean)
6941 }
6942 Expr::Bin {
6951 op: BinOp::And | BinOp::Or | BinOp::Xor,
6952 l,
6953 r,
6954 ..
6955 } => bty(l, vt) && bty(r, vt),
6956 other => matches!(other.type_ref().erased(), JavaType::Boolean),
6957 }
6958 }
6959 fn inv_of_xor(x: &Expr, vt: &VarTable) -> Option<Expr> {
6964 if let Expr::Bin { op: BinOp::Xor, l, r, .. } = x {
6965 let (int_side, one_side) = if is_one(r) {
6966 (l, r)
6967 } else if is_one(l) {
6968 (r, l)
6969 } else {
6970 return None;
6971 };
6972 let _ = one_side;
6973 if !bty(int_side, vt) {
6974 return Some(Expr::Bin {
6975 op: BinOp::Eq,
6976 l: int_side.clone(),
6977 r: Box::new(Expr::Const(ConstVal::Int(0))),
6978 ty: Some(TypeRef::J(JavaType::Boolean)),
6979 });
6980 }
6981 }
6982 None
6983 }
6984 let tgt_bool = |target: &Expr, vt: &VarTable| -> bool {
6985 match target {
6986 Expr::Local { var, .. } => matches!(vt.var(*var).ty.erased(), JavaType::Boolean),
6987 Expr::Field { ty, .. } => matches!(ty.erased(), JavaType::Boolean),
6988 _ => false,
6989 }
6990 };
6991 walk_mut_deep(body, &mut |st| match st {
6992 Stmt::Return(Some(e)) if ret_bool => {
6993 if let Some(r) = inv_of_xor(e, vt) {
6994 *e = r;
6995 }
6996 }
6997 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain, .. }) => {
6998 if tgt_bool(target, vt) {
6999 if let Some(r) = inv_of_xor(value, vt) {
7000 **value = r;
7001 }
7002 }
7003 }
7004 Stmt::LocalDef { var, init: Some(value), .. }
7005 if matches!(vt.var(*var).ty.erased(), JavaType::Boolean) =>
7006 {
7007 if let Some(r) = inv_of_xor(value, vt) {
7008 *value = r;
7009 }
7010 }
7011 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
7012 if let Some(r) = inv_of_xor(cond, vt) {
7013 *cond = r;
7014 }
7015 }
7016 _ => {}
7017 });
7018 fn ity(e: &Expr, vt: &VarTable) -> bool {
7019 matches!(
7020 match e {
7021 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
7022 other => other.type_ref().erased(),
7023 },
7024 JavaType::Int | JavaType::Short | JavaType::Byte | JavaType::Char
7025 )
7026 }
7027 fn mixed_rewrite(e: &mut Expr, vt: &VarTable) {
7036 deep_rewrite(e, &mut |x| {
7037 if let Expr::Bin { op, l, r, ty } = x {
7038 match op {
7039 BinOp::Xor => {
7040 if is_one(r) && bty(l, vt) {
7041 let taken =
7042 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
7043 *x = Expr::Un { op: UnOp::Not, e: taken };
7044 } else if is_one(l) && bty(r, vt) {
7045 let taken =
7046 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
7047 *x = Expr::Un { op: UnOp::Not, e: taken };
7048 }
7049 }
7050 BinOp::Or | BinOp::And => {
7051 if bty(l, vt) && ity(r, vt) {
7052 let taken =
7053 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
7054 **r = Expr::Bin {
7055 op: BinOp::Ne,
7056 l: taken,
7057 r: Box::new(Expr::Const(ConstVal::Int(0))),
7058 ty: Some(TypeRef::J(JavaType::Boolean)),
7059 };
7060 *ty = Some(TypeRef::J(JavaType::Boolean));
7061 } else if ity(l, vt) && bty(r, vt) {
7062 let taken =
7063 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
7064 **l = Expr::Bin {
7065 op: BinOp::Ne,
7066 l: taken,
7067 r: Box::new(Expr::Const(ConstVal::Int(0))),
7068 ty: Some(TypeRef::J(JavaType::Boolean)),
7069 };
7070 *ty = Some(TypeRef::J(JavaType::Boolean));
7071 }
7072 }
7073 _ => {}
7074 }
7075 }
7076 });
7077 }
7078 walk_mut_deep(body, &mut |st| match st {
7081 Stmt::Return(Some(e)) if ret_bool => mixed_rewrite(e, vt),
7082 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain, .. }) => {
7083 if tgt_bool(target, vt) {
7084 mixed_rewrite(value, vt);
7085 }
7086 }
7087 Stmt::LocalDef { var, init: Some(e), .. }
7088 if matches!(vt.var(*var).ty.erased(), JavaType::Boolean) =>
7089 {
7090 mixed_rewrite(e, vt);
7091 }
7092 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
7093 mixed_rewrite(cond, vt);
7094 }
7095 _ => {}
7096 });
7097 walk_stmt_exprs(body, &mut |e| {
7100 deep_rewrite(e, &mut |x| {
7101 if let Expr::Cond { c, .. } = x {
7102 mixed_rewrite(c, vt);
7103 }
7104 });
7105 });
7106}
7107
7108pub fn booleanize(vt: &mut VarTable, body: &mut Stmt, ret_bool: bool) -> usize {
7114 let n0 = vt.vars.len();
7121 let mut derefed = vec![false; n0];
7122 mark_derefs(body, &mut derefed);
7123 let mut total = 0usize;
7124 for _ in 0..4 {
7125 let n = booleanize_round(vt, body, ret_bool, &derefed);
7126 if n == 0 {
7127 break;
7128 }
7129 total += n;
7130 }
7131 total
7132}
7133
7134fn mark_deref_expr(e: &Expr, derefed: &mut [bool]) {
7138 match e {
7139 Expr::Field { owner: Some(o), is_static: false, .. }
7140 | Expr::Method { owner: Some(o), .. } => {
7141 if let Expr::Local { var, .. } = &**o {
7142 if (*var as usize) < derefed.len() {
7143 derefed[*var as usize] = true;
7144 }
7145 }
7146 }
7147 Expr::ArrayIndex { array, .. } => {
7148 if let Expr::Local { var, .. } = &**array {
7149 if (*var as usize) < derefed.len() {
7150 derefed[*var as usize] = true;
7151 }
7152 }
7153 }
7154 _ => {}
7155 }
7156 for_each_child(e, &mut |c| mark_deref_expr(c, derefed));
7157}
7158
7159fn mark_derefs(body: &Stmt, derefed: &mut [bool]) {
7160 walk_all(body, &mut |st| {
7161 match st {
7162 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
7163 mark_deref_expr(e, derefed);
7164 }
7165 Stmt::Return(Some(e)) => mark_deref_expr(e, derefed),
7166 Stmt::LocalDef { init: Some(e), .. } => mark_deref_expr(e, derefed),
7167 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
7168 mark_deref_expr(cond, derefed);
7169 }
7170 Stmt::For { init, .. } => {
7171 for x in init {
7172 mark_derefs(x, derefed);
7173 }
7174 }
7175 Stmt::Switch { selector, .. } => mark_deref_expr(selector, derefed),
7176 Stmt::ForEach { iterable, .. } => mark_deref_expr(iterable, derefed),
7177 _ => {}
7178 }
7179 });
7180}
7181
7182fn booleanize_round(
7183 vt: &mut VarTable,
7184 body: &mut Stmt,
7185 ret_bool: bool,
7186 derefed: &[bool],
7187) -> usize {
7188 let n = vt.vars.len();
7189 let mut in_cond = vec![false; n];
7190
7191 walk_all(body, &mut |st| {
7194 let cond: Option<&Expr> = match st {
7195 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
7196 Some(cond)
7197 }
7198 _ => None,
7199 };
7200 if let Some(c) = cond {
7201 if let Expr::Bin {
7202 op: BinOp::Eq | BinOp::Ne,
7203 l,
7204 r,
7205 ..
7206 } = c
7207 {
7208 for (x, other) in [(l, r), (r, l)] {
7209 if let (Expr::Local { var, .. }, Expr::Const(ConstVal::Int(0))) =
7210 (&**x, &**other)
7211 {
7212 if (*var as usize) < n {
7213 in_cond[*var as usize] = true;
7214 }
7215 }
7216 }
7217 }
7218 }
7219 if ret_bool {
7224 if let Stmt::Return(Some(Expr::Local { var, .. })) = st {
7225 if (*var as usize) < n {
7226 in_cond[*var as usize] = true;
7227 }
7228 }
7229 }
7230 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = st {
7237 if let Expr::Field { ty, .. } = &**target {
7238 if ty.erased() == JavaType::Boolean {
7239 if let Expr::Local { var, .. } = &**value {
7240 if (*var as usize) < n {
7241 in_cond[*var as usize] = true;
7242 }
7243 }
7244 }
7245 }
7246 }
7247 let val: Option<&Expr> = match st {
7248 Stmt::ExprStmt(Expr::Assign { value, .. }) => Some(value),
7249 Stmt::ExprStmt(e) => Some(e),
7252 Stmt::LocalDef { init: Some(e), .. } => Some(e),
7253 Stmt::Return(Some(e)) => Some(e),
7254 _ => None,
7255 };
7256 if let Some(v) = val {
7257 visit_exprs(v, &mut |x| {
7258 if let Expr::Method { desc, args, .. } = x {
7264 for (i, a) in args.iter().enumerate() {
7265 if desc.args.get(i) == Some(&JavaType::Boolean) {
7266 if let Expr::Local { var, .. } = a {
7267 if (*var as usize) < n {
7268 in_cond[*var as usize] = true;
7269 }
7270 }
7271 }
7272 }
7273 }
7274 if let Expr::Bin { op, l, r, .. } = x {
7275 if matches!(
7276 op,
7277 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
7278 ) {
7279 for side in [l, r] {
7280 if let Expr::Local { var, .. } = &**side {
7281 if (*var as usize) < n {
7282 in_cond[*var as usize] = true;
7283 }
7284 }
7285 }
7286 }
7287 if matches!(op, BinOp::Or | BinOp::And) {
7293 let l_bool = l.type_ref().erased() == JavaType::Boolean;
7294 let r_bool = r.type_ref().erased() == JavaType::Boolean;
7295 if l_bool != r_bool {
7296 let side = if l_bool { r } else { l };
7297 if let Expr::Local { var, .. } = &**side {
7298 if (*var as usize) < n {
7299 in_cond[*var as usize] = true;
7300 }
7301 }
7302 }
7303 }
7304 }
7305 });
7306 }
7307 });
7308
7309 let mut assigned_any = vec![false; n];
7316 let mut all_bool = vec![true; n];
7317 let mut edges: Vec<(usize, usize)> = Vec::new();
7318 walk_all(body, &mut |st| {
7319 let (var, value) = match st {
7320 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
7321 Expr::Local { var, .. } => (var, &**value),
7322 _ => return,
7323 },
7324 Stmt::LocalDef {
7325 var, init: Some(e), ..
7326 } => (var, e),
7327 _ => return,
7328 };
7329 let i = *var as usize;
7330 if i < n {
7331 assigned_any[i] = true;
7332 if !is_boolean_valued(value, vt) {
7333 all_bool[i] = false;
7334 }
7335 if let Expr::Local { var: src, .. } = value {
7341 if (*src as usize) < n {
7342 edges.push((i, *src as usize));
7343 }
7344 }
7345 }
7346 });
7347 loop {
7352 let mut changed = false;
7353 for &(tgt, src) in &edges {
7354 if in_cond[tgt] && !in_cond[src] && all_bool[src] {
7355 in_cond[src] = true;
7356 changed = true;
7357 }
7358 if in_cond[src] && !in_cond[tgt] && all_bool[tgt] {
7364 in_cond[tgt] = true;
7365 changed = true;
7366 }
7367 }
7368 if !changed {
7369 break;
7370 }
7371 }
7372 let reads = count_locals_stmts(std::slice::from_ref(body));
7373 let mut boolean_vars: HashSet<u32> = HashSet::default();
7374 for i in 0..n {
7375 if vt.vars[i].is_param {
7376 continue;
7377 }
7378 if derefed[i] {
7379 continue;
7380 }
7381 if !in_cond[i] && !(reads.get(&(i as u32)).copied().unwrap_or(0) == 0) {
7382 continue;
7383 }
7384 if assigned_any[i] && all_bool[i] {
7385 boolean_vars.insert(i as u32);
7386 }
7387 }
7388 if boolean_vars.is_empty() {
7389 return 0;
7390 }
7391 for v in &boolean_vars {
7392 vt.vars[*v as usize].ty = TypeRef::J(JavaType::Boolean);
7393 }
7394 let types: Vec<&TypeRef> = vt.vars.iter().map(|v| &v.ty).collect();
7395 rewrite_exprs(body, &mut |e| {
7396 deep_rewrite(e, &mut |x| {
7397 if let Expr::Local { var, ty } = x {
7398 if let Some(want) = types.get(*var as usize) {
7399 if ty != *want {
7400 *ty = (*want).clone();
7401 }
7402 }
7403 }
7404 });
7405 });
7406
7407 fold_bool_conditions(body, &boolean_vars);
7409 boolean_vars.len()
7410}
7411
7412fn is_boolean_valued(e: &Expr, vt: &VarTable) -> bool {
7413 match e {
7414 Expr::Const(ConstVal::Int(0)) | Expr::Const(ConstVal::Int(1)) => true,
7415 Expr::Bin { op: BinOp::Or | BinOp::And, l, r, .. } => {
7422 is_boolean_valued(l, vt) || is_boolean_valued(r, vt)
7423 }
7424 Expr::Bin {
7432 op: BinOp::Xor,
7433 l,
7434 r,
7435 ..
7436 } => is_boolean_valued(l, vt) && is_boolean_valued(r, vt),
7437 Expr::Bin { op, .. } => matches!(
7438 op,
7439 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
7440 ),
7441 Expr::InstanceOf { .. } => true,
7442 Expr::Local { var, ty } => {
7448 let t = if (*var as usize) < vt.vars.len() {
7449 vt.var(*var).ty.erased()
7450 } else {
7451 ty.erased()
7452 };
7453 t == JavaType::Boolean
7454 }
7455 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
7456 Expr::Cond { t, f, .. } => is_boolean_valued(t, vt) && is_boolean_valued(f, vt),
7457 Expr::Un { op: UnOp::Not, .. } => true,
7458 Expr::Field { ty, .. } => ty.erased() == JavaType::Boolean,
7462 _ => false,
7463 }
7464}
7465
7466fn fold_bool_conditions(s: &mut Stmt, bv: &HashSet<u32>) {
7467 let fold = |e: &mut Expr| {
7468 deep_rewrite(e, &mut |x| {
7469 if let Expr::Bin { op, l, r, .. } = x {
7470 let (var_side, const_side) = match (&**l, &**r) {
7471 (Expr::Local { .. }, Expr::Const(ConstVal::Int(_))) => (l, 0),
7472 (Expr::Const(ConstVal::Int(_)), Expr::Local { .. }) => (r, 1),
7473 _ => return,
7474 };
7475 let var = match &**var_side {
7476 Expr::Local { var, .. } => *var,
7477 _ => return,
7478 };
7479 if !bv.contains(&var) {
7480 return;
7481 }
7482 let _ = const_side;
7483 match op {
7484 BinOp::Ne => {
7485 let inner = var_side.clone();
7486 *x = *inner;
7487 }
7488 BinOp::Eq => {
7489 let inner = var_side.clone();
7490 *x = Expr::Un {
7491 op: UnOp::Not,
7492 e: Box::new(*inner),
7493 };
7494 }
7495 _ => {}
7496 }
7497 }
7498 });
7499 };
7500 match s {
7501 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => fold(cond),
7502 _ => {}
7503 }
7504 walk_mut_deep(s, &mut |st| {
7505 let cond = match st {
7506 Stmt::If { cond, .. } => Some(cond),
7507 Stmt::While { cond, .. } => Some(cond),
7508 Stmt::DoWhile { cond, .. } => Some(cond),
7509 _ => None,
7510 };
7511 if let Some(c) = cond {
7512 fold(c);
7513 }
7514 });
7515}
7516
7517pub fn flip_const_compares(s: &mut Stmt) {
7519 rewrite_exprs(s, &mut |e| {
7520 deep_rewrite(e, &mut |x| {
7521 if let Expr::Bin { op, l, r, .. } = x {
7522 if matches!(
7523 op,
7524 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
7525 ) {
7526 let l_const = matches!(&**l, Expr::Const(_));
7527 let r_var = matches!(&**r, Expr::Local { .. });
7528 if l_const && r_var {
7529 std::mem::swap(l, r);
7530 }
7531 }
7532 }
7533 });
7534 });
7535}
7536
7537pub fn null_compares(vt: &VarTable, body: &mut Stmt) {
7539 let obj_vars: HashSet<u32> = vt
7540 .vars
7541 .iter()
7542 .filter(|v| v.ty.erased().is_reference())
7543 .map(|v| v.id)
7544 .collect();
7545 rewrite_exprs(body, &mut |e| {
7546 deep_rewrite(e, &mut |x| {
7547 null_side_rewrite(x, &|v| obj_vars.contains(&v));
7548 });
7549 });
7550}
7551
7552pub fn drop_dead_locals(body: &mut Stmt) {
7561 loop {
7562 let mut reads: Vec<usize> = Vec::new();
7563 count_reads(body, &mut reads);
7564 let mut dropped = 0usize;
7565 walk_mut_deep(body, &mut |st| match st {
7566 Stmt::Block(items) => prune_dead_items(items, &reads, &mut dropped),
7567 Stmt::Switch { cases, .. } => {
7577 for c in cases {
7578 prune_dead_items(&mut c.body, &reads, &mut dropped);
7579 }
7580 }
7581 Stmt::For { init, .. } => prune_dead_items(init, &reads, &mut dropped),
7582 _ => {}
7583 });
7584 if dropped == 0 {
7585 break;
7586 }
7587 }
7588}
7589
7590fn prune_dead_items(items: &mut Vec<Stmt>, reads: &[usize], dropped: &mut usize) {
7595 let dead = |v: u32| reads.get(v as usize).copied().unwrap_or(0) == 0;
7596 items.retain_mut(|x| match x {
7597 Stmt::ExprStmt(Expr::Assign { target, value, op, .. }) => {
7598 let dead_target = matches!(&**target, Expr::Local { var, .. } if dead(*var));
7599 if dead_target && matches!(*op, AssignOp::Plain) {
7600 *dropped += 1;
7601 if has_side_effects(value) {
7602 let e = std::mem::replace(value, Box::new(Expr::This));
7603 *x = Stmt::ExprStmt(*e);
7604 true
7605 } else {
7606 false
7607 }
7608 } else {
7609 true
7610 }
7611 }
7612 Stmt::LocalDef { var, init, .. } if dead(*var) => {
7613 *dropped += 1;
7614 if let Some(e) = init.take() {
7615 if has_side_effects(&e) {
7616 *x = Stmt::ExprStmt(e);
7617 return true;
7618 }
7619 }
7620 false
7621 }
7622 _ => true,
7623 });
7624}
7625
7626
7627fn stmt_shallow_vars<F: FnMut(u32)>(st: &Stmt, out: &mut F) {
7632 fn ex<F: FnMut(u32)>(e: &Expr, out: &mut F) {
7633 visit_exprs(e, &mut |x| {
7634 if let Expr::Local { var, .. } = x {
7635 out(*var);
7636 }
7637 });
7638 }
7639 match st {
7640 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
7641 ex(e, out)
7642 }
7643 Stmt::TernaryValue { e } => ex(e, out),
7644 Stmt::Return(Some(e)) => ex(e, out),
7645 Stmt::LocalDef { var, init, .. } => {
7646 out(*var);
7647 if let Some(e) = init {
7648 ex(e, out);
7649 }
7650 }
7651 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
7652 ex(cond, out)
7653 }
7654 Stmt::For { cond, update, .. } => {
7655 if let Some(c) = cond {
7656 ex(c, out);
7657 }
7658 for u in update {
7659 ex(u, out);
7660 }
7661 }
7662 Stmt::ForEach { iterable, .. } => ex(iterable, out),
7663 Stmt::Switch { selector, cases, .. } => {
7664 ex(selector, out);
7665 for cg in cases {
7666 if let Some(g) = &cg.guard {
7667 ex(g, out);
7668 }
7669 }
7670 }
7671 Stmt::Assert { cond, msg } => {
7672 ex(cond, out);
7673 if let Some(m) = msg {
7674 ex(m, out);
7675 }
7676 }
7677 Stmt::Synchronized { lock, .. } => ex(lock, out),
7678 _ => {}
7679 }
7680}
7681
7682fn scope_pass_a(st: &Stmt, cur: u32, next: &mut u32, def_block: &mut [u32], sizes: &mut Vec<u32>) {
7694 if let Stmt::LocalDef { var, .. } = st {
7695 let i = *var as usize;
7696 if i < def_block.len() && def_block[i] == u32::MAX {
7697 def_block[i] = cur;
7698 }
7699 }
7700 fn scoped(c: &Stmt, next: &mut u32, def_block: &mut [u32], sizes: &mut Vec<u32>) {
7701 let id = *next;
7702 *next += 1;
7703 sizes.push(0);
7704 scope_pass_a(c, id, next, def_block, sizes);
7705 sizes[id as usize] = *next - id;
7706 }
7707 match st {
7708 Stmt::Block(v) => {
7709 for x in v {
7710 scope_pass_a(x, cur, next, def_block, sizes);
7711 }
7712 }
7713 Stmt::If {
7714 then_stmt,
7715 else_stmt,
7716 ..
7717 } => {
7718 scoped(then_stmt, next, def_block, sizes);
7719 if let Some(e) = else_stmt {
7720 scoped(e, next, def_block, sizes);
7721 }
7722 }
7723 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => {
7724 scoped(body, next, def_block, sizes)
7725 }
7726 Stmt::For { init, body, .. } => {
7727 let id = *next;
7730 *next += 1;
7731 sizes.push(0);
7732 for x in init {
7733 scope_pass_a(x, id, next, def_block, sizes);
7734 }
7735 scoped(body, next, def_block, sizes);
7736 sizes[id as usize] = *next - id;
7737 }
7738 Stmt::ForEach { body, .. } => scoped(body, next, def_block, sizes),
7739 Stmt::Switch { cases, default, .. } => {
7740 for c in cases {
7741 let id = *next;
7746 *next += 1;
7747 sizes.push(0);
7748 for x in &c.body {
7749 if matches!(x, Stmt::Block(_)) {
7750 scoped(x, next, def_block, sizes);
7751 } else {
7752 scope_pass_a(x, id, next, def_block, sizes);
7753 }
7754 }
7755 sizes[id as usize] = *next - id;
7756 }
7757 if let Some(d) = default {
7758 scoped(d, next, def_block, sizes);
7759 }
7760 }
7761 Stmt::Try {
7762 body,
7763 catches,
7764 finally,
7765 } => {
7766 scoped(body, next, def_block, sizes);
7767 for c in catches {
7768 scoped(&c.body, next, def_block, sizes);
7769 }
7770 if let Some(f) = finally {
7771 scoped(f, next, def_block, sizes);
7772 }
7773 }
7774 Stmt::TryWithResources {
7775 resources,
7776 body,
7777 catches,
7778 finally,
7779 } => {
7780 let id = *next;
7782 *next += 1;
7783 sizes.push(0);
7784 for r in resources {
7785 scope_pass_a(r, id, next, def_block, sizes);
7786 }
7787 scoped(body, next, def_block, sizes);
7788 for c in catches {
7789 scoped(&c.body, next, def_block, sizes);
7790 }
7791 if let Some(f) = finally {
7792 scoped(f, next, def_block, sizes);
7793 }
7794 sizes[id as usize] = *next - id;
7795 }
7796 Stmt::Synchronized { body, .. } => scoped(body, next, def_block, sizes),
7797 Stmt::Labeled { body, .. } => scope_pass_a(body, cur, next, def_block, sizes),
7798 _ => {}
7799 }
7800}
7801
7802fn scope_pass_b(
7807 st: &Stmt,
7808 cur: u32,
7809 next: &mut u32,
7810 def_block: &[u32],
7811 sizes: &[u32],
7812 within: &mut [u32],
7813 total: &mut [u32],
7814) {
7815 fn cnt(
7816 v: u32,
7817 at: u32,
7818 def_block: &[u32],
7819 sizes: &[u32],
7820 within: &mut [u32],
7821 total: &mut [u32],
7822 ) {
7823 let i = v as usize;
7824 if i < total.len() {
7825 total[i] += 1;
7826 let d = def_block[i];
7827 if d != u32::MAX && d <= at && at < d + sizes[d as usize] {
7828 within[i] += 1;
7829 }
7830 }
7831 }
7832 fn scoped(
7833 c: &Stmt,
7834 next: &mut u32,
7835 def_block: &[u32],
7836 sizes: &[u32],
7837 within: &mut [u32],
7838 total: &mut [u32],
7839 ) {
7840 let id = *next;
7841 *next += 1;
7842 scope_pass_b(c, id, next, def_block, sizes, within, total);
7843 }
7844 fn ex_at(
7845 e: &Expr,
7846 at: u32,
7847 def_block: &[u32],
7848 sizes: &[u32],
7849 within: &mut [u32],
7850 total: &mut [u32],
7851 ) {
7852 visit_exprs(e, &mut |x| {
7853 if let Expr::Local { var, .. } = x {
7854 cnt(*var, at, def_block, sizes, within, total);
7855 }
7856 });
7857 }
7858 match st {
7859 Stmt::For {
7860 init,
7861 cond,
7862 update,
7863 body,
7864 } => {
7865 let id = *next;
7866 *next += 1;
7867 for x in init {
7868 scope_pass_b(x, id, next, def_block, sizes, within, total);
7869 }
7870 if let Some(c) = cond {
7871 ex_at(c, id, def_block, sizes, within, total);
7872 }
7873 for u in update {
7874 ex_at(u, id, def_block, sizes, within, total);
7875 }
7876 scoped(body, next, def_block, sizes, within, total);
7877 }
7878 Stmt::TryWithResources {
7879 resources,
7880 body,
7881 catches,
7882 finally,
7883 } => {
7884 let id = *next;
7885 *next += 1;
7886 for r in resources {
7887 scope_pass_b(r, id, next, def_block, sizes, within, total);
7888 }
7889 scoped(body, next, def_block, sizes, within, total);
7890 for c in catches {
7891 scoped(&c.body, next, def_block, sizes, within, total);
7892 }
7893 if let Some(f) = finally {
7894 scoped(f, next, def_block, sizes, within, total);
7895 }
7896 }
7897 _ => {
7898 stmt_shallow_vars(st, &mut |v| cnt(v, cur, def_block, sizes, within, total));
7899 match st {
7900 Stmt::Block(v) => {
7901 for x in v {
7902 scope_pass_b(x, cur, next, def_block, sizes, within, total);
7903 }
7904 }
7905 Stmt::If {
7906 then_stmt,
7907 else_stmt,
7908 ..
7909 } => {
7910 scoped(then_stmt, next, def_block, sizes, within, total);
7911 if let Some(e) = else_stmt {
7912 scoped(e, next, def_block, sizes, within, total);
7913 }
7914 }
7915 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => {
7916 scoped(body, next, def_block, sizes, within, total)
7917 }
7918 Stmt::ForEach { body, .. } => {
7919 scoped(body, next, def_block, sizes, within, total)
7920 }
7921 Stmt::Switch { cases, default, .. } => {
7922 for c in cases {
7923 let id = *next;
7924 *next += 1;
7925 for x in &c.body {
7926 if matches!(x, Stmt::Block(_)) {
7927 scoped(x, next, def_block, sizes, within, total);
7928 } else {
7929 scope_pass_b(x, id, next, def_block, sizes, within, total);
7930 }
7931 }
7932 }
7933 if let Some(d) = default {
7934 scoped(d, next, def_block, sizes, within, total);
7935 }
7936 }
7937 Stmt::Try {
7938 body,
7939 catches,
7940 finally,
7941 } => {
7942 scoped(body, next, def_block, sizes, within, total);
7943 for c in catches {
7944 scoped(&c.body, next, def_block, sizes, within, total);
7945 }
7946 if let Some(f) = finally {
7947 scoped(f, next, def_block, sizes, within, total);
7948 }
7949 }
7950 Stmt::Synchronized { body, .. } => {
7951 scoped(body, next, def_block, sizes, within, total)
7952 }
7953 Stmt::Labeled { body, .. } => {
7954 scope_pass_b(body, cur, next, def_block, sizes, within, total)
7955 }
7956 _ => {}
7957 }
7958 }
7959 }
7960}
7961
7962pub fn strip_enum_const_inits(s: &mut Stmt, class: &crate::PoolClass) {
7971 let const_fields: jdc_core::FxHashSet<&str> = class
7972 .static_fields
7973 .iter()
7974 .filter(|f| f.access & crate::access::ACC_ENUM != 0)
7975 .map(|f| f.name.as_str())
7976 .collect();
7977 if const_fields.is_empty() {
7978 return;
7979 }
7980 if let Stmt::Block(stmts) = s {
7981 stmts.retain(|st| {
7982 !matches!(
7983 st,
7984 Stmt::ExprStmt(Expr::Assign { target, value, .. })
7985 if matches!(&**target,
7986 Expr::Field { cls, name, is_static: true, .. }
7987 if cls.as_ref() == class.name
7988 && const_fields.contains(&**name))
7989 && matches!(&**value, Expr::New { cls: ncls, .. } if ncls.as_ref() == class.name)
7990 )
7991 });
7992 }
7993}
7994
7995fn contains_this_access(e: &Expr) -> bool {
8004 let mut hit = false;
8005 visit_exprs(e, &mut |x| {
8006 if hit {
8007 return;
8008 }
8009 match x {
8010 Expr::This => hit = true,
8011 Expr::Field { owner, is_static: false, .. } => {
8012 if owner.is_none() || matches!(&**owner.as_ref().unwrap(), Expr::This) {
8013 hit = true;
8014 }
8015 }
8016 Expr::Method { owner, is_static: false, .. } => {
8017 if owner.is_none() || matches!(&**owner.as_ref().unwrap(), Expr::This) {
8018 hit = true;
8019 }
8020 }
8021 _ => {}
8022 }
8023 });
8024 hit
8025}
8026
8027fn var_occurs_in(stmts: &[Stmt], var: u32) -> bool {
8042 stmts.iter().any(|st| {
8043 let mut hit = false;
8044 visit_stmt_exprs_ro(st, &mut |e| {
8045 if !hit {
8046 visit_exprs(e, &mut |x| {
8047 if let Expr::Local { var: v, .. } = x {
8048 if *v == var {
8049 hit = true;
8050 }
8051 }
8052 });
8053 }
8054 });
8055 hit
8056 })
8057}
8058
8059pub struct CtorHelper {
8069 pub name: String,
8070 pub ret: JavaType,
8071 pub params: Vec<(JavaType, String)>,
8072 pub body: Stmt,
8073 pub vt: VarTable,
8074}
8075
8076fn ctor_helpers() -> &'static std::sync::Mutex<std::collections::HashMap<String, Vec<CtorHelper>>> {
8077 static H: std::sync::OnceLock<std::sync::Mutex<std::collections::HashMap<String, Vec<CtorHelper>>>> =
8078 std::sync::OnceLock::new();
8079 H.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::default()))
8080}
8081
8082pub struct CtorRelay {
8087 pub formals: Vec<JavaType>,
8089 pub is_super: bool,
8090}
8091
8092type RelayMap = std::collections::HashMap<String, Vec<(String, CtorRelay)>>;
8093
8094fn ctor_relays() -> &'static std::sync::Mutex<RelayMap> {
8095 static H: std::sync::OnceLock<std::sync::Mutex<RelayMap>> = std::sync::OnceLock::new();
8096 H.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::default()))
8097}
8098
8099pub fn take_ctor_relays(class: &str) -> Vec<(String, CtorRelay)> {
8102 let mut m = ctor_relays().lock().unwrap_or_else(|e| e.into_inner());
8103 let mut v = m.remove(class).unwrap_or_default();
8104 v.sort_by(|a, b| a.0.cmp(&b.0));
8105 v
8106}
8107
8108fn array_init(args: Vec<Expr>) -> Expr {
8109 Expr::NewArray {
8110 elem: TypeRef::J(JavaType::Object(std::sync::Arc::from("java/lang/Object"))),
8111 dims: Vec::new(),
8112 trailing_dims: 0,
8113 init: Some(args),
8114 }
8115}
8116
8117fn helper_name(class: &str, desc: &str) -> String {
8120 let mut h: u64 = 0xcbf29ce484222325;
8121 for b in class.as_bytes().iter().chain(b"::").chain(desc.as_bytes()) {
8122 h ^= *b as u64;
8123 h = h.wrapping_mul(0x100000001b3);
8124 }
8125 format!("resolve${:x}", (h >> 16) & 0xffff_ffff)
8126}
8127
8128pub fn take_ctor_helpers(class: &str) -> Vec<CtorHelper> {
8131 let mut m = ctor_helpers().lock().unwrap_or_else(|e| e.into_inner());
8132 let mut v = m.remove(class).unwrap_or_default();
8133 v.sort_by(|a, b| a.name.cmp(&b.name));
8134 v
8135}
8136
8137fn strip_delegations_deep(stmts: &mut Vec<Stmt>) {
8138 stmts.retain(|s| !is_bare_ctor_call(s));
8139 for s in stmts.iter_mut() {
8140 strip_delegation_one(s);
8141 }
8142}
8143
8144fn strip_delegation_one(st: &mut Stmt) {
8145 if is_bare_ctor_call(st) {
8146 *st = Stmt::Block(Vec::new());
8147 return;
8148 }
8149 match st {
8150 Stmt::Block(v) => strip_delegations_deep(v),
8151 Stmt::If { then_stmt, else_stmt, .. } => {
8152 strip_delegation_one(then_stmt);
8153 if let Some(e) = else_stmt {
8154 strip_delegation_one(e);
8155 }
8156 }
8157 Stmt::While { body, .. }
8158 | Stmt::DoWhile { body, .. }
8159 | Stmt::ForEach { body, .. }
8160 | Stmt::Synchronized { body, .. }
8161 | Stmt::Labeled { body, .. } => strip_delegation_one(body),
8162 Stmt::For { init, body, .. } => {
8163 strip_delegations_deep(init);
8164 strip_delegation_one(body);
8165 }
8166 Stmt::Switch { cases, default, .. } => {
8167 for c in cases.iter_mut() {
8168 strip_delegations_deep(&mut c.body);
8169 }
8170 if let Some(d) = default {
8171 strip_delegation_one(d);
8172 }
8173 }
8174 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
8175 strip_delegation_one(body);
8176 for c in catches.iter_mut() {
8177 strip_delegation_one(&mut c.body);
8178 }
8179 if let Some(f) = finally {
8180 strip_delegation_one(f);
8181 }
8182 }
8183 _ => {}
8184 }
8185}
8186
8187#[allow(clippy::ptr_arg)]
8188fn rewrite_bare_returns(stmts: &mut Vec<Stmt>, val: &Expr) {
8189 for s in stmts.iter_mut() {
8190 rewrite_bare_returns_one(s, val);
8191 }
8192}
8193
8194fn rewrite_bare_returns_one(st: &mut Stmt, val: &Expr) {
8195 match st {
8196 Stmt::Return(None) => *st = Stmt::Return(Some(val.clone())),
8197 Stmt::Block(v) => rewrite_bare_returns(v, val),
8198 Stmt::If { then_stmt, else_stmt, .. } => {
8199 rewrite_bare_returns_one(then_stmt, val);
8200 if let Some(e) = else_stmt {
8201 rewrite_bare_returns_one(e, val);
8202 }
8203 }
8204 Stmt::While { body, .. }
8205 | Stmt::DoWhile { body, .. }
8206 | Stmt::ForEach { body, .. }
8207 | Stmt::Synchronized { body, .. }
8208 | Stmt::Labeled { body, .. } => rewrite_bare_returns_one(body, val),
8209 Stmt::For { init, body, .. } => {
8210 rewrite_bare_returns(init, val);
8211 rewrite_bare_returns_one(body, val);
8212 }
8213 Stmt::Switch { cases, default, .. } => {
8214 for c in cases.iter_mut() {
8215 rewrite_bare_returns(&mut c.body, val);
8216 }
8217 if let Some(d) = default {
8218 rewrite_bare_returns_one(d, val);
8219 }
8220 }
8221 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
8222 rewrite_bare_returns_one(body, val);
8223 for c in catches.iter_mut() {
8224 rewrite_bare_returns_one(&mut c.body, val);
8225 }
8226 if let Some(f) = finally {
8227 rewrite_bare_returns_one(f, val);
8228 }
8229 }
8230 _ => {}
8231 }
8232}
8233
8234fn collect_def_vars(stmts: &[Stmt], out: &mut jdc_core::FxHashSet<u32>) {
8235 for st in stmts {
8236 match st {
8237 Stmt::LocalDef { var, .. } => {
8238 out.insert(*var);
8239 }
8240 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
8241 if let Expr::Local { var, .. } = &**target {
8242 out.insert(*var);
8243 }
8244 }
8245 Stmt::Block(v) => collect_def_vars(v, out),
8246 Stmt::If { then_stmt, else_stmt, .. } => {
8247 collect_def_vars(std::slice::from_ref(then_stmt.as_ref()), out);
8248 if let Some(e) = else_stmt {
8249 collect_def_vars(std::slice::from_ref(e.as_ref()), out);
8250 }
8251 }
8252 Stmt::While { body, .. }
8253 | Stmt::DoWhile { body, .. }
8254 | Stmt::Synchronized { body, .. }
8255 | Stmt::Labeled { body, .. } => {
8256 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
8257 }
8258 Stmt::ForEach { var, body, .. } => {
8259 out.insert(*var);
8260 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
8261 }
8262 Stmt::For { init, body, .. } => {
8263 collect_def_vars(init, out);
8264 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
8265 }
8266 Stmt::Switch { cases, default, .. } => {
8267 for c in cases {
8268 collect_def_vars(&c.body, out);
8269 }
8270 if let Some(d) = default {
8271 collect_def_vars(std::slice::from_ref(d.as_ref()), out);
8272 }
8273 }
8274 Stmt::Try { body, catches, finally }
8275 | Stmt::TryWithResources { body, catches, finally, .. } => {
8276 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
8277 for c in catches {
8278 out.insert(c.var);
8279 collect_def_vars(std::slice::from_ref(c.body.as_ref()), out);
8280 }
8281 if let Some(f) = finally {
8282 collect_def_vars(std::slice::from_ref(f.as_ref()), out);
8283 }
8284 }
8285 _ => {}
8286 }
8287 }
8288}
8289
8290fn extraction_shape_ok(stmts: &[Stmt]) -> bool {
8293 stmts.iter().all(|st| match st {
8294 Stmt::Block(v) => extraction_shape_ok(v),
8295 Stmt::LocalDef { .. }
8296 | Stmt::ExprStmt(_)
8297 | Stmt::Return(_)
8298 | Stmt::Throw(_)
8299 | Stmt::Assert { .. }
8300 | Stmt::Break(_)
8301 | Stmt::Continue(_) => true,
8302 Stmt::If { then_stmt, else_stmt, .. } => {
8303 ok_one(then_stmt)
8304 && else_stmt.as_deref().map(ok_one).unwrap_or(true)
8305 }
8306 Stmt::While { body, .. }
8307 | Stmt::DoWhile { body, .. }
8308 | Stmt::ForEach { body, .. }
8309 | Stmt::Synchronized { body, .. } => ok_one(body),
8310 Stmt::Labeled { body, .. } => ok_one(body),
8311 Stmt::For { init, body, .. } => extraction_shape_ok(init) && ok_one(body),
8312 Stmt::Switch { cases, default, .. } => {
8313 cases.iter().all(|c| extraction_shape_ok(&c.body))
8314 && default.as_deref().map(ok_one).unwrap_or(true)
8315 }
8316 Stmt::Try { body, catches, finally }
8317 | Stmt::TryWithResources { body, catches, finally, .. } => {
8318 ok_one(body)
8319 && catches.iter().all(|c| ok_one(&c.body))
8320 && finally.as_deref().map(ok_one).unwrap_or(true)
8321 }
8322 _ => false,
8325 })
8326}
8327
8328fn ok_one(st: &Stmt) -> bool {
8329 extraction_shape_ok(std::slice::from_ref(st))
8330}
8331
8332fn static_safe(stmts: &[Stmt], this_id: Option<u32>) -> bool {
8333 let mut ok = true;
8334 for st in stmts {
8335 visit_stmt_exprs_ro(st, &mut |e| {
8336 visit_exprs(e, &mut |x| {
8337 match x {
8338 Expr::This => ok = false,
8339 Expr::Local { var, .. } => {
8345 if Some(*var) == this_id {
8346 ok = false;
8347 }
8348 }
8349 Expr::Field { owner: None, is_static: false, .. } => ok = false,
8351 Expr::Method { owner: None, is_static: false, .. } => ok = false,
8352 _ => {}
8353 }
8354 });
8355 });
8356 }
8357 ok
8358}
8359
8360pub fn strip_clinit_hotfix_guard(body: &mut Stmt) {
8371 if let Stmt::Block(vs) = body {
8375 flatten_top_blocks(vs);
8376 }
8377 let Stmt::Block(stmts) = body else { return };
8378 let mut gi = 0usize;
8381 while gi < stmts.len() && matches!(&stmts[gi], Stmt::LocalDef { init: None, .. }) {
8382 gi += 1;
8383 }
8384 let Some(first) = stmts.get(gi) else { return };
8385 let Stmt::If { then_stmt, else_stmt, .. } = first else {
8386 return;
8387 };
8388 if else_stmt.is_some() {
8389 return;
8390 }
8391 let mut has_ret = false;
8392 let mut has_proxy = false;
8393 let c = then_stmt.as_ref();
8394 walk_all(c, &mut |st| {
8395 if matches!(st, Stmt::Return(None)) {
8396 has_ret = true;
8397 }
8398 });
8399 let mut cc = c.clone();
8400 walk_stmt_exprs(&mut cc, &mut |e| {
8401 deep_rewrite(e, &mut |x| {
8402 if let Expr::Method { cls, .. } = x {
8403 if cls.contains("PatchProxy") {
8404 has_proxy = true;
8405 }
8406 }
8407 });
8408 });
8409 if has_ret && has_proxy {
8410 stmts.remove(gi);
8411 }
8412}
8413
8414pub(crate) fn first_leaf_stmt(s: &Stmt) -> &Stmt {
8435 match s {
8436 Stmt::Block(v) if !v.is_empty() => first_leaf_stmt(&v[0]),
8437 other => other,
8438 }
8439}
8440
8441pub fn strip_ctor_hotfix_guards(body: &mut Stmt) {
8460 fn marker_hit(e: &Expr) -> bool {
8461 let has = |n: &str| {
8462 let l = n.to_ascii_lowercase();
8463 l.contains("hotfix")
8464 || l.contains("instantrun")
8465 || l.contains("robust")
8466 || l.contains("titan")
8467 || l.contains("patchproxy")
8468 || l.contains("changequickredirect")
8469 || l.contains("constructorcode")
8470 };
8471 let mut hit = false;
8472 visit_exprs(e, &mut |x| {
8473 match x {
8474 Expr::Method { cls, .. } | Expr::New { cls, .. } | Expr::Field { cls, .. } => {
8475 if has(cls) {
8476 hit = true;
8477 }
8478 }
8479 _ => {}
8480 }
8481 if let Expr::Field { ty, .. } = x {
8482 if let TypeRef::J(JavaType::Object(n)) = ty {
8483 if has(n) {
8484 hit = true;
8485 }
8486 }
8487 }
8488 });
8489 hit
8490 }
8491 fn is_guard(st: &Stmt) -> bool {
8495 let mut cur = st;
8496 loop {
8497 match cur {
8498 Stmt::Block(v) if v.len() == 1 => cur = &v[0],
8499 Stmt::If { .. } => break,
8500 _ => return false,
8501 }
8502 }
8503 let mut has_del = false;
8504 walk_all(cur, &mut |s2| {
8505 if is_bare_ctor_call(s2) {
8506 has_del = true;
8507 }
8508 });
8509 if !has_del {
8510 return false;
8511 }
8512 let mut has_marker = false;
8513 visit_stmt_exprs_ro(cur, &mut |e| {
8514 if marker_hit(e) {
8515 has_marker = true;
8516 }
8517 });
8518 has_marker
8519 }
8520 fn strip_in(stmts: &mut Vec<Stmt>) {
8531 stmts.retain(|st| !is_guard(st));
8532 for st in stmts.iter_mut() {
8533 if let Stmt::Block(v) = st {
8534 strip_in(v);
8535 }
8536 }
8537 }
8538 let Stmt::Block(stmts) = body else { return };
8539 strip_in(stmts);
8540}
8541
8542pub fn hoist_branch_delegations(body: &mut Stmt, vt: &VarTable) {
8555 let Stmt::Block(stmts) = body else { return };
8556 if stmts.first().is_some_and(is_bare_ctor_call) {
8557 return; }
8559 let mut dels: Vec<Expr> = Vec::new();
8560 for st in stmts.iter() {
8561 visit_stmt_exprs_ro(st, &mut |e| {
8562 if is_delegation_expr(e) {
8563 dels.push(e.clone());
8564 }
8565 });
8566 }
8567 if dels.is_empty() {
8568 return;
8569 }
8570 if !dels.iter().all(|d| *d == dels[0]) {
8571 return;
8572 }
8573 let mut bad = false;
8574 visit_exprs(&dels[0], &mut |x| {
8575 if let Expr::Local { var, .. } = x {
8576 if (*var as usize) >= vt.vars.len() || !vt.vars[*var as usize].is_param {
8577 bad = true;
8578 }
8579 }
8580 });
8581 if bad {
8582 return;
8583 }
8584 let del = dels[0].clone();
8585 strip_delegations_deep(stmts);
8586 stmts.insert(0, Stmt::ExprStmt(del));
8587}
8588
8589pub fn extract_branched_delegation_helper(
8623 body: &mut Stmt,
8624 vt: &VarTable,
8625 pool: &DexPool,
8626 class: &crate::PoolClass,
8627 desc_str: &str,
8628) {
8629 if class.is_enum() {
8630 return;
8631 }
8632 if class.nesting.enclosing_class.is_some() && !crate::ctx::nested_is_static(pool, class) {
8637 return;
8638 }
8639 let Stmt::Block(stmts) = body else { return };
8640 let mut dels: Vec<Expr> = Vec::new();
8641 for st in stmts.iter() {
8642 visit_stmt_exprs_ro(st, &mut |e| {
8643 if is_delegation_expr(e) {
8644 dels.push(e.clone());
8645 }
8646 });
8647 }
8648 if dels.is_empty() {
8649 return;
8650 }
8651 if dels.len() == 1 && stmts.first().is_some_and(is_bare_ctor_call) {
8652 return; }
8654 let template = dels[0].clone();
8655 let Expr::Method { args: t_args, .. } = &template else {
8656 return;
8657 };
8658 enum Mode {
8662 Single(u32),
8663 Position(usize),
8664 Full,
8671 }
8672 let full_gate = (|| -> Option<()> {
8679 let Expr::Method {
8680 args: a0,
8681 desc: d0,
8682 is_super: s0,
8683 cls: c0,
8684 ..
8685 } = &dels[0]
8686 else {
8687 return None;
8688 };
8689 if a0.is_empty() {
8690 return None;
8691 }
8692 for d in &dels[1..] {
8693 match d {
8694 Expr::Method {
8695 args,
8696 desc,
8697 is_super,
8698 cls,
8699 ..
8700 } if args.len() == a0.len()
8701 && **desc == **d0
8702 && is_super == s0
8703 && cls == c0 => {}
8704 _ => return None,
8705 }
8706 }
8707 if class
8708 .all_methods()
8709 .any(|m| &*m.name == "<init>" && &*m.desc == "([Ljava/lang/Object;)V")
8710 {
8711 return None;
8712 }
8713 Some(())
8714 })()
8715 .is_some();
8716 let mode = if dels.iter().all(|d| *d == dels[0]) {
8717 let mut carrier: Option<u32> = None;
8718 let mut reads = 0usize;
8719 let mut multi = false;
8720 for a in t_args.iter() {
8721 let c = a.clone();
8722 visit_exprs(&c, &mut |x| {
8723 if let Expr::Local { var, .. } = x {
8724 if (*var as usize) < vt.vars.len() && !vt.vars[*var as usize].is_param {
8725 reads += 1;
8726 match carrier {
8727 Some(v) if v == *var => {}
8728 Some(_) => multi = true,
8729 None => carrier = Some(*var),
8730 }
8731 }
8732 }
8733 });
8734 }
8735 match carrier {
8736 Some(cv) if !multi && reads == 1 => Mode::Single(cv),
8737 _ if full_gate => Mode::Full,
8738 _ => return,
8739 }
8740 } else {
8741 let positioned: Option<Mode> = (|| {
8742 let arity = t_args.len();
8743 if dels
8747 .iter()
8748 .any(|d| !matches!(d, Expr::Method { args, .. } if args.len() == arity))
8749 {
8750 return None;
8751 }
8752 let mut diff: Vec<usize> = Vec::new();
8753 for j in 0..arity {
8754 if dels.iter().any(|d| match d {
8755 Expr::Method { args, .. } => args.get(j) != t_args.get(j),
8756 _ => true,
8757 }) {
8758 diff.push(j);
8759 }
8760 }
8761 if diff.len() != 1 {
8762 return None;
8763 }
8764 let k = diff[0];
8765 let mut base0 = dels[0].clone();
8769 if let Expr::Method { args, .. } = &mut base0 {
8770 args[k] = Expr::Const(ConstVal::Null);
8771 }
8772 for d in dels.iter() {
8773 let mut c = d.clone();
8774 if let Expr::Method { args, .. } = &mut c {
8775 args[k] = Expr::Const(ConstVal::Null);
8776 }
8777 if c != base0 {
8778 return None;
8779 }
8780 }
8781 let mut carrier_ty: Option<JavaType> = None;
8784 for d in dels.iter() {
8785 let Expr::Method { args, .. } = d else {
8786 return None;
8787 };
8788 let Expr::Local { var, .. } = &args[k] else {
8789 return None;
8790 };
8791 if (*var as usize) >= vt.vars.len() || vt.vars[*var as usize].is_param {
8792 return None;
8793 }
8794 let ty = vt.vars[*var as usize].ty.erased();
8795 match &carrier_ty {
8796 Some(t) if *t == ty => {}
8797 Some(_) => return None,
8798 None => carrier_ty = Some(ty),
8799 }
8800 for (j, a) in args.iter().enumerate() {
8806 if j == k {
8807 continue;
8808 }
8809 let mut bad = false;
8810 visit_exprs(a, &mut |x| {
8811 if let Expr::Local { var: v2, .. } = x {
8812 if (*v2 as usize) >= vt.vars.len() || !vt.vars[*v2 as usize].is_param {
8813 bad = true;
8814 }
8815 }
8816 });
8817 if bad {
8818 return None;
8819 }
8820 }
8821 }
8822 Some(Mode::Position(k))
8823 })();
8824 match positioned {
8825 Some(m) => m,
8826 None if full_gate => Mode::Full,
8827 None => return,
8828 }
8829 };
8830 let l = stmts.iter().rposition(is_bare_ctor_call);
8831 if l == Some(0) {
8832 return;
8833 }
8834 let (mut extracted, mut tail): (Vec<Stmt>, Vec<Stmt>) = match l {
8835 Some(l) => (stmts[..l].to_vec(), stmts[l + 1..].to_vec()),
8836 None => (stmts.clone(), Vec::new()),
8837 };
8838 if !extraction_shape_ok(&extracted) {
8839 return;
8840 }
8841 let mut site_tails: Vec<Vec<Stmt>> = Vec::new();
8848 {
8849 let mut probe = extracted.clone();
8850 site_tails_walk(&mut probe, &mut site_tails, false);
8851 }
8852 let hoist = !site_tails.is_empty() && {
8853 let n0 = normalize_tail(&site_tails[0]);
8854 site_tails.iter().all(|t| normalize_tail(t) == n0)
8855 && (l.is_none() || normalize_tail(&tail) == n0)
8856 };
8857 if hoist {
8858 site_tails_walk(&mut extracted, &mut Vec::new(), true);
8859 if l.is_none() {
8860 tail = normalize_tail(&site_tails[0]);
8861 }
8862 }
8863 let primary_del: Expr = match l {
8866 Some(l) => match &stmts[l] {
8867 Stmt::ExprStmt(e) => e.clone(),
8868 _ => return,
8869 },
8870 None => dels[0].clone(),
8871 };
8872 let ret = match mode {
8873 Mode::Single(cv) => vt.var(cv).ty.erased(),
8874 Mode::Position(k) => match &primary_del {
8875 Expr::Method { args, .. } => match args.get(k) {
8876 Some(Expr::Local { var, .. }) => vt.var(*var).ty.erased(),
8877 _ => return,
8878 },
8879 _ => return,
8880 },
8881 Mode::Full => JavaType::Array(Box::new(JavaType::Object(std::sync::Arc::from(
8882 "java/lang/Object",
8883 )))),
8884 };
8885 match mode {
8886 Mode::Single(cv) => {
8887 strip_delegations_deep(&mut extracted);
8888 let cv_local = Expr::Local {
8889 var: cv,
8890 ty: vt.var(cv).ty.clone(),
8891 };
8892 rewrite_bare_returns(&mut extracted, &cv_local);
8893 if !always_returns(&extracted) {
8894 extracted.push(Stmt::Return(Some(cv_local)));
8895 }
8896 }
8897 Mode::Position(k) => {
8898 fill_returns_by_position(&mut extracted, &|args: &[Expr]| args.get(k).cloned());
8902 let fallback = match &primary_del {
8903 Expr::Method { args, .. } => args[k].clone(),
8904 _ => return,
8905 };
8906 rewrite_bare_returns(&mut extracted, &fallback);
8907 if !always_returns(&extracted) {
8908 extracted.push(Stmt::Return(Some(fallback)));
8909 }
8910 }
8911 Mode::Full => {
8912 fill_returns_by_position(&mut extracted, &|args: &[Expr]| {
8919 Some(array_init(args.to_vec()))
8920 });
8921 if l.is_none() {
8922 if !always_returns(&extracted) {
8929 let cut = (1..=extracted.len())
8930 .find(|&i| always_returns(&extracted[..i]));
8931 match cut {
8932 Some(i) => extracted.truncate(i),
8933 None => return,
8934 }
8935 }
8936 }
8937 let fallback_args = match &primary_del {
8938 Expr::Method { args, .. } => args.clone(),
8939 _ => return,
8940 };
8941 let fallback = array_init(fallback_args);
8942 rewrite_bare_returns(&mut extracted, &fallback);
8943 if !always_returns(&extracted) {
8944 extracted.push(Stmt::Return(Some(fallback)));
8945 }
8946 }
8947 }
8948 let this_id = vt
8949 .vars
8950 .iter()
8951 .find(|v| v.is_param && v.name == "this")
8952 .map(|v| v.id);
8953 if !static_safe(&extracted, this_id) {
8954 return;
8955 }
8956 let mut defs: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
8957 collect_def_vars(&extracted, &mut defs);
8958 if defs.iter().any(|v| var_occurs_in(&tail, *v)) {
8959 return;
8960 }
8961 let params: Vec<(u32, JavaType, String)> = vt
8962 .vars
8963 .iter()
8964 .filter(|v| v.is_param && v.name != "this")
8965 .map(|v| (v.id, v.ty.erased(), v.name.clone()))
8966 .collect();
8967 let name = helper_name(&class.name, desc_str);
8968 let call = Expr::Method {
8969 owner: None,
8970 cls: std::sync::Arc::from(class.name.as_str()),
8971 name: std::sync::Arc::from(name.as_str()),
8972 desc: std::sync::Arc::new(jdc_core::types::MethodDescriptor {
8973 ret: ret.clone(),
8974 args: params.iter().map(|p| p.1.clone()).collect(),
8975 }),
8976 args: params
8977 .iter()
8978 .map(|(id, ty, _)| Expr::Local {
8979 var: *id,
8980 ty: TypeRef::J(ty.clone()),
8981 })
8982 .collect(),
8983 is_static: true,
8984 is_interface: false,
8985 is_special: false,
8986 is_super: false,
8987 is_dynamic: false,
8988 type_args: Vec::new(),
8989 };
8990 let mut new_del = primary_del.clone();
8991 match mode {
8992 Mode::Single(cv) => {
8993 deep_rewrite(&mut new_del, &mut |x| {
8994 if let Expr::Local { var, .. } = x {
8995 if *var == cv {
8996 *x = call.clone();
8997 }
8998 }
8999 });
9000 }
9001 Mode::Position(k) => {
9002 let Expr::Method { args, .. } = &mut new_del else {
9003 return;
9004 };
9005 if k >= args.len() {
9006 return;
9007 }
9008 args[k] = call.clone();
9009 }
9010 Mode::Full => {
9011 new_del = Expr::Method {
9015 owner: None,
9016 cls: std::sync::Arc::from(class.name.as_str()),
9017 name: "<init>".into(),
9018 desc: std::sync::Arc::new(jdc_core::types::MethodDescriptor {
9019 args: vec![JavaType::Array(Box::new(JavaType::Object(
9020 std::sync::Arc::from("java/lang/Object"),
9021 )))],
9022 ret: JavaType::Void,
9023 }),
9024 args: vec![call.clone()],
9025 is_static: false,
9026 is_interface: false,
9027 is_special: true,
9028 is_super: false,
9029 is_dynamic: false,
9030 type_args: Vec::new(),
9031 };
9032 }
9033 }
9034 {
9035 let mut m = ctor_helpers().lock().unwrap_or_else(|e| e.into_inner());
9036 let v = m.entry(class.name.clone()).or_default();
9037 if !v.iter().any(|h| h.name == name) {
9038 let mut hbody = Stmt::Block(extracted);
9042 invert_empty_thens(&mut hbody);
9043 v.push(CtorHelper {
9044 name,
9045 ret: ret.clone(),
9046 params: params.into_iter().map(|(_, ty, nm)| (ty, nm)).collect(),
9047 body: hbody,
9048 vt: vt.clone(),
9049 });
9050 if matches!(mode, Mode::Full) {
9051 let Expr::Method {
9052 desc: d0,
9053 is_super: s0,
9054 ..
9055 } = &primary_del
9056 else {
9057 return;
9058 };
9059 let mut rm = ctor_relays().lock().unwrap_or_else(|e| e.into_inner());
9060 let rv = rm.entry(class.name.clone()).or_default();
9061 if !rv.iter().any(|(d, _)| d == desc_str) {
9062 rv.push((
9063 desc_str.to_string(),
9064 CtorRelay {
9065 formals: d0.args.clone(),
9066 is_super: *s0,
9067 },
9068 ));
9069 }
9070 }
9071 } else {
9072 return; }
9074 }
9075 *stmts = std::iter::once(Stmt::ExprStmt(new_del)).chain(tail).collect();
9076}
9077
9078fn fill_returns_by_position(
9085 stmts: &mut Vec<Stmt>,
9086 pick: &dyn Fn(&[Expr]) -> Option<Expr>,
9087) {
9088 let mut i = 0;
9089 while i < stmts.len() {
9090 if is_bare_ctor_call(&stmts[i]) {
9091 let karg = match &stmts[i] {
9092 Stmt::ExprStmt(Expr::Method { args, .. }) => pick(args),
9093 _ => None,
9094 };
9095 if let Some(v) = karg {
9096 let mut j = i + 1;
9097 let mut found = None;
9098 while j < stmts.len() {
9099 if matches!(stmts[j], Stmt::Return(None)) {
9100 found = Some(j);
9101 break;
9102 }
9103 if is_bare_ctor_call(&stmts[j]) {
9104 break; }
9106 j += 1;
9107 }
9108 match found {
9109 Some(j) => {
9110 stmts[j] = Stmt::Return(Some(v));
9111 stmts.remove(i);
9112 continue;
9113 }
9114 None => {
9115 stmts[i] = Stmt::Return(Some(v));
9116 stmts.truncate(i + 1);
9117 break;
9118 }
9119 }
9120 }
9121 }
9122 fill_returns_one(&mut stmts[i], pick);
9123 i += 1;
9124 }
9125}
9126
9127fn fill_returns_one(st: &mut Stmt, pick: &dyn Fn(&[Expr]) -> Option<Expr>) {
9128 match st {
9129 Stmt::Block(v) => fill_returns_by_position(v, pick),
9130 Stmt::If { then_stmt, else_stmt, .. } => {
9131 fill_returns_stmt(then_stmt, pick);
9132 if let Some(e) = else_stmt {
9133 fill_returns_stmt(e, pick);
9134 }
9135 }
9136 Stmt::While { body, .. }
9137 | Stmt::DoWhile { body, .. }
9138 | Stmt::ForEach { body, .. }
9139 | Stmt::Synchronized { body, .. }
9140 | Stmt::Labeled { body, .. } => fill_returns_stmt(body, pick),
9141 Stmt::For { init, body, .. } => {
9142 fill_returns_by_position(init, pick);
9143 fill_returns_stmt(body, pick);
9144 }
9145 Stmt::Switch { cases, default, .. } => {
9146 for c in cases.iter_mut() {
9147 fill_returns_by_position(&mut c.body, pick);
9148 }
9149 if let Some(d) = default {
9150 fill_returns_stmt(d, pick);
9151 }
9152 }
9153 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
9154 fill_returns_stmt(body, pick);
9155 for c in catches.iter_mut() {
9156 fill_returns_stmt(&mut c.body, pick);
9157 }
9158 if let Some(f) = finally {
9159 fill_returns_stmt(f, pick);
9160 }
9161 }
9162 _ => {}
9163 }
9164}
9165
9166fn fill_returns_stmt(st: &mut Stmt, pick: &dyn Fn(&[Expr]) -> Option<Expr>) {
9167 if is_bare_ctor_call(st) {
9168 if let Stmt::ExprStmt(Expr::Method { args, .. }) = st {
9171 if let Some(v) = pick(args) {
9172 *st = Stmt::Return(Some(v));
9173 return;
9174 }
9175 }
9176 }
9177 fill_returns_one(st, pick);
9178}
9179
9180fn normalize_tail(t: &[Stmt]) -> Vec<Stmt> {
9181 let mut v = t.to_vec();
9182 while matches!(v.last(), Some(Stmt::Return(None))) {
9183 v.pop();
9184 }
9185 v
9186}
9187
9188fn site_tails_walk(stmts: &mut Vec<Stmt>, out: &mut Vec<Vec<Stmt>>, carve: bool) {
9193 let mut i = 0;
9194 while i < stmts.len() {
9195 if is_bare_ctor_call(&stmts[i]) {
9196 let mut j = i + 1;
9197 let mut end = stmts.len();
9198 while j < stmts.len() {
9199 if matches!(stmts[j], Stmt::Return(None)) {
9200 end = j;
9201 break;
9202 }
9203 if is_bare_ctor_call(&stmts[j]) {
9204 end = j;
9205 break;
9206 }
9207 j += 1;
9208 }
9209 out.push(stmts[i + 1..end].to_vec());
9210 if carve {
9211 stmts.splice(i + 1..end, []);
9212 }
9213 i += 1;
9214 continue;
9215 }
9216 site_tails_one(&mut stmts[i], out, carve);
9217 i += 1;
9218 }
9219}
9220
9221fn site_tails_one(st: &mut Stmt, out: &mut Vec<Vec<Stmt>>, carve: bool) {
9222 if is_bare_ctor_call(st) {
9223 out.push(Vec::new());
9224 return;
9225 }
9226 match st {
9227 Stmt::Block(v) => site_tails_walk(v, out, carve),
9228 Stmt::If { then_stmt, else_stmt, .. } => {
9229 site_tails_one(then_stmt, out, carve);
9230 if let Some(e) = else_stmt {
9231 site_tails_one(e, out, carve);
9232 }
9233 }
9234 Stmt::While { body, .. }
9235 | Stmt::DoWhile { body, .. }
9236 | Stmt::ForEach { body, .. }
9237 | Stmt::Synchronized { body, .. }
9238 | Stmt::Labeled { body, .. } => site_tails_one(body, out, carve),
9239 Stmt::For { init, body, .. } => {
9240 site_tails_walk(init, out, carve);
9241 site_tails_one(body, out, carve);
9242 }
9243 Stmt::Switch { cases, default, .. } => {
9244 for c in cases.iter_mut() {
9245 site_tails_walk(&mut c.body, out, carve);
9246 }
9247 if let Some(d) = default {
9248 site_tails_one(d, out, carve);
9249 }
9250 }
9251 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
9252 site_tails_one(body, out, carve);
9253 for c in catches.iter_mut() {
9254 site_tails_one(&mut c.body, out, carve);
9255 }
9256 if let Some(f) = finally {
9257 site_tails_one(f, out, carve);
9258 }
9259 }
9260 _ => {}
9261 }
9262}
9263
9264fn always_returns(stmts: &[Stmt]) -> bool {
9270 let Some(last) = stmts
9271 .iter()
9272 .rev()
9273 .find(|s| !matches!(s, Stmt::Block(v) if v.is_empty()))
9274 else {
9275 return false;
9276 };
9277 match last {
9278 Stmt::Return(Some(_)) | Stmt::Throw(_) => true,
9279 Stmt::Block(v) => always_returns(v),
9280 Stmt::If { then_stmt, else_stmt, .. } => match else_stmt {
9281 Some(e) => always_returns_one(then_stmt) && always_returns_one(e),
9282 None => false,
9283 },
9284 Stmt::Labeled { body, .. } | Stmt::Synchronized { body, .. } => {
9285 always_returns_one(body)
9286 }
9287 Stmt::Switch { cases, default, .. } => {
9288 default.as_ref().is_some_and(|d| always_returns_one(d))
9289 && cases.iter().all(|c| always_returns(&c.body))
9290 }
9291 Stmt::Try { body, catches, finally }
9292 | Stmt::TryWithResources { body, catches, finally, .. } => {
9293 finally.as_ref().is_some_and(|f| always_returns_one(f))
9294 || (always_returns_one(body)
9295 && catches.iter().all(|c| always_returns_one(&c.body)))
9296 }
9297 _ => false,
9298 }
9299}
9300
9301fn always_returns_one(st: &Stmt) -> bool {
9302 always_returns(std::slice::from_ref(st))
9303}
9304
9305fn inline_ctor_arg_defs(
9306 call: &mut Stmt,
9307 prefix: &[&Stmt],
9308 vt: &VarTable,
9309 consumed: &mut Vec<(u32, usize)>,
9310) -> bool {
9311 let Stmt::ExprStmt(cexpr) = call else {
9312 return false;
9313 };
9314 fn subst(e: &mut Expr, v: u32, val: &Option<Expr>) -> bool {
9317 let mut ok = true;
9318 deep_rewrite(e, &mut |x| {
9319 if let Expr::Local { var, .. } = x {
9320 if *var == v {
9321 match val {
9322 Some(e2) => *x = e2.clone(),
9323 None => ok = false,
9324 }
9325 }
9326 }
9327 });
9328 ok
9329 }
9330 let mut seen: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
9331 for _ in 0..32 {
9332 let mut need: Option<u32> = None;
9333 visit_exprs(cexpr, &mut |x| {
9334 if need.is_none() {
9335 if let Expr::Local { var, .. } = x {
9336 if (*var as usize) < vt.vars.len() && !vt.vars[*var as usize].is_param {
9337 need = Some(*var);
9338 }
9339 }
9340 }
9341 });
9342 let Some(v) = need else { break };
9343 if !seen.insert(v) {
9344 return false; }
9346 let mut val: Option<Expr> = None;
9358 let mut ok = true;
9359 for (si, st) in prefix.iter().enumerate() {
9360 match st {
9361 Stmt::LocalDef { var, init: None, .. } if *var == v => {}
9365 Stmt::LocalDef { var, init, .. } if *var == v => {
9366 let Some(e) = init else { ok = false; break };
9367 let mut e2 = (*e).clone();
9368 if !subst(&mut e2, v, &val) {
9369 ok = false;
9370 break;
9371 }
9372 val = Some(e2);
9373 consumed.push((v, si));
9374 }
9375 Stmt::ExprStmt(Expr::Assign { target, op, .. })
9376 if matches!(&**target, Expr::Local { var: tv, .. } if *tv == v) =>
9377 {
9378 if !matches!(op, AssignOp::Plain) {
9379 ok = false; break;
9381 }
9382 let Stmt::ExprStmt(Expr::Assign { value, .. }) = st else {
9383 unreachable!()
9384 };
9385 let mut e2 = value.as_ref().clone();
9386 if !subst(&mut e2, v, &val) {
9387 ok = false;
9388 break;
9389 }
9390 val = Some(e2);
9391 consumed.push((v, si));
9392 }
9393 _ => {
9394 let mut touched = false;
9395 visit_all_exprs(st, &mut |x| {
9396 if let Expr::Local { var, .. } = x {
9397 if *var == v {
9398 touched = true;
9399 }
9400 }
9401 });
9402 if touched {
9403 ok = false;
9404 break;
9405 }
9406 }
9407 }
9408 }
9409 if !ok {
9410 return false;
9411 }
9412 let Some(de) = val else { return false };
9413 if contains_this_access(&de) {
9414 return false;
9415 }
9416 let mut de_nodes = 0usize;
9418 visit_exprs(&de, &mut |_| de_nodes += 1);
9419 if de_nodes > 256 {
9420 return false;
9421 }
9422 deep_rewrite(cexpr, &mut |x| {
9423 if let Expr::Local { var, .. } = x {
9424 if *var == v {
9425 *x = de.clone();
9426 }
9427 }
9428 });
9429 let mut nodes = 0usize;
9434 visit_exprs(cexpr, &mut |_| nodes += 1);
9435 if nodes > 2_048 {
9436 return false;
9437 }
9438 }
9439 true
9440}
9441
9442fn fold_sb_chain(stmts: &mut Vec<Stmt>, pos: usize) -> usize {
9455 let mut sbv: Option<u32> = None;
9457 let mut def_i: Option<usize> = None;
9458 let mut def_init: Option<Expr> = None;
9459 for (i, st) in stmts[..pos].iter().enumerate() {
9460 let cand: Option<(u32, &Expr)> = match st {
9461 Stmt::LocalDef {
9462 var,
9463 init: Some(e),
9464 ..
9465 } => Some((*var, e)),
9466 Stmt::ExprStmt(Expr::Assign {
9467 target,
9468 value,
9469 op: AssignOp::Plain,
9470 ..
9471 }) => match &**target {
9472 Expr::Local { var, .. } => Some((*var, value.as_ref())),
9473 _ => None,
9474 },
9475 _ => None,
9476 };
9477 if let Some((v, init)) = cand {
9478 if let Expr::New { cls, .. } = init {
9479 if cls.as_ref() == "java/lang/StringBuilder" {
9480 sbv = Some(v);
9481 def_i = Some(i);
9482 def_init = Some(init.clone());
9483 break;
9484 }
9485 }
9486 }
9487 }
9488 let (Some(sbv), Some(def_i), Some(init)) = (sbv, def_i, def_init) else {
9489 return pos;
9490 };
9491 if contains_this_access(&init) {
9492 return pos;
9493 }
9494 let mut appends: Vec<usize> = Vec::new();
9496 for (i, st) in stmts[..pos].iter().enumerate() {
9497 if let Stmt::ExprStmt(Expr::Method {
9498 owner: Some(o),
9499 cls,
9500 name,
9501 ..
9502 }) = st
9503 {
9504 if matches!(&**o, Expr::Local { var, .. } if *var == sbv)
9505 && cls.as_ref() == "java/lang/StringBuilder"
9506 && &**name == "append"
9507 {
9508 appends.push(i);
9509 }
9510 }
9511 }
9512 let mut post_read = false;
9514 for st in stmts.iter().skip(pos + 1) {
9515 visit_all_exprs(st, &mut |e| {
9516 if let Expr::Local { var, .. } = e {
9517 if *var == sbv {
9518 post_read = true;
9519 }
9520 }
9521 });
9522 }
9523 if post_read {
9524 return pos;
9525 }
9526 for (i, st) in stmts[..pos].iter().enumerate() {
9529 if i == def_i || appends.contains(&i) {
9530 if let Stmt::ExprStmt(Expr::Method { args, .. }) = st {
9532 if args.iter().any(contains_this_access) {
9533 return pos;
9534 }
9535 }
9536 continue;
9537 }
9538 let mut leak = false;
9539 visit_all_exprs(st, &mut |e| {
9540 if let Expr::Local { var, .. } = e {
9541 if *var == sbv {
9542 leak = true;
9543 }
9544 }
9545 });
9546 if leak {
9547 return pos;
9548 }
9549 }
9550 let mut local_reads = 0usize;
9552 let mut tostring_reads = 0usize;
9553 visit_all_exprs(&stmts[pos], &mut |e| match e {
9554 Expr::Local { var, .. } if *var == sbv => local_reads += 1,
9555 Expr::Method {
9556 owner: Some(o),
9557 name,
9558 args,
9559 ..
9560 } if args.is_empty()
9561 && &**name == "toString"
9562 && matches!(&**o, Expr::Local { var, .. } if *var == sbv) =>
9563 {
9564 tostring_reads += 1;
9565 }
9566 _ => {}
9567 });
9568 if local_reads != 1 || tostring_reads != 1 {
9569 return pos;
9570 }
9571 let mut chain = init;
9573 for ai in &appends {
9574 if let Stmt::ExprStmt(Expr::Method {
9575 owner: _,
9576 cls,
9577 name,
9578 desc,
9579 args,
9580 is_static,
9581 is_interface,
9582 is_special,
9583 is_super,
9584 is_dynamic,
9585 type_args,
9586 ..
9587 }) = &stmts[*ai]
9588 {
9589 chain = Expr::Method {
9590 owner: Some(Box::new(chain)),
9591 cls: cls.clone(),
9592 name: name.clone(),
9593 desc: desc.clone(),
9594 args: args.clone(),
9595 is_static: *is_static,
9596 is_interface: *is_interface,
9597 is_special: *is_special,
9598 is_super: *is_super,
9599 is_dynamic: *is_dynamic,
9600 type_args: type_args.clone(),
9601 };
9602 }
9603 }
9604 let mut replaced = false;
9605 walk_stmt_exprs(&mut stmts[pos], &mut |e| {
9606 deep_rewrite(e, &mut |x| {
9607 if let Expr::Method {
9608 owner: Some(o),
9609 name,
9610 args,
9611 ..
9612 } = x
9613 {
9614 if args.is_empty()
9615 && &**name == "toString"
9616 && matches!(&**o, Expr::Local { var, .. } if *var == sbv)
9617 {
9618 **o = chain.clone();
9619 replaced = true;
9620 }
9621 }
9622 });
9623 });
9624 if !replaced {
9625 return pos; }
9627 let mut removed: Vec<usize> = appends.clone();
9629 removed.push(def_i);
9630 removed.sort_unstable();
9631 removed.dedup();
9632 for i in removed.iter().rev() {
9633 stmts.remove(*i);
9634 }
9635 pos - removed.len()
9636}
9637
9638pub fn fix_ctor_super_first(body: &mut Stmt, vt: &VarTable) {
9639 let Stmt::Block(stmts) = body else { return };
9640 if stmts.is_empty() || is_bare_ctor_call(stmts.first().unwrap()) {
9641 return;
9642 }
9643 if let Some(pos0) = stmts.iter().position(is_bare_ctor_call) {
9645 let pos = fold_sb_chain(stmts, pos0);
9646 let mut call = stmts[pos].clone();
9647 let prefix: Vec<&Stmt> = stmts[..pos].iter().collect();
9648 let mut consumed: Vec<(u32, usize)> = Vec::new();
9649 if !inline_ctor_arg_defs(&mut call, &prefix, vt, &mut consumed) {
9650 return;
9651 }
9652 stmts.remove(pos);
9653 let mut drop_idx: Vec<usize> = consumed
9660 .iter()
9661 .filter(|(v, _)| !var_occurs_in(&stmts[pos..], *v))
9662 .map(|(_, k)| *k)
9663 .collect();
9664 drop_idx.sort_unstable();
9665 drop_idx.dedup();
9666 for k in drop_idx.iter().rev() {
9667 stmts.remove(*k);
9668 }
9669 stmts.insert(0, call);
9670 return;
9671 }
9672 let mut found: Option<(usize, usize)> = None;
9682 for (i, st) in stmts.iter().enumerate() {
9683 if let Stmt::Block(inner) = st {
9684 if let Some(p) = inner.iter().position(is_bare_ctor_call) {
9685 if inner[..p]
9686 .iter()
9687 .all(|s| matches!(s, Stmt::LocalDef { .. } | Stmt::ExprStmt(_)))
9688 {
9689 found = Some((i, p));
9690 break;
9691 }
9692 }
9693 }
9694 }
9695 if let Some((i, p)) = found {
9696 let mut call = match &stmts[i] {
9697 Stmt::Block(inner) => inner[p].clone(),
9698 _ => unreachable!(),
9699 };
9700 let mut prefix: Vec<&Stmt> = stmts[..i].iter().collect();
9701 if let Stmt::Block(inner) = &stmts[i] {
9702 prefix.extend(inner[..p].iter());
9703 }
9704 let mut consumed: Vec<(u32, usize)> = Vec::new();
9705 if !inline_ctor_arg_defs(&mut call, &prefix, vt, &mut consumed) {
9706 return;
9707 }
9708 let mut ks: Vec<usize> = Vec::new();
9715 {
9716 let inner_tail: &[Stmt] = match &stmts[i] {
9717 Stmt::Block(inner) if p < inner.len() => &inner[p + 1..],
9718 _ => &[],
9719 };
9720 for (v, k) in &consumed {
9721 if !var_occurs_in(inner_tail, *v) && !var_occurs_in(&stmts[i + 1..], *v) {
9722 ks.push(*k);
9723 }
9724 }
9725 }
9726 ks.sort_unstable();
9727 ks.dedup();
9728 if let Stmt::Block(inner) = &mut stmts[i] {
9729 inner.remove(p);
9730 for k in ks.iter().rev().filter(|k| **k >= i) {
9731 inner.remove(k - i);
9732 }
9733 }
9734 for k in ks.iter().rev().filter(|k| **k < i) {
9735 stmts.remove(*k);
9736 }
9737 stmts.insert(0, call);
9738 }
9739}
9740
9741fn is_delegation_expr(e: &Expr) -> bool {
9750 matches!(
9751 e,
9752 Expr::Method { name, is_special: true, owner, .. }
9753 if &**name == "<init>"
9754 && (owner.is_none() || matches!(owner.as_deref(), Some(Expr::This)))
9755 )
9756}
9757
9758pub(crate) fn is_bare_ctor_call(s: &Stmt) -> bool {
9759 matches!(s, Stmt::ExprStmt(e) if is_delegation_expr(e))
9760}
9761
9762pub(crate) fn local_is_written(body: &Stmt, var: u32) -> bool {
9766 let mut hit = false;
9767 visit_stmt_exprs_ro(body, &mut |e| {
9768 if hit {
9769 return;
9770 }
9771 visit_exprs(e, &mut |x| {
9772 if hit {
9773 return;
9774 }
9775 match x {
9776 Expr::Assign { target, .. }
9777 if matches!(&**target, Expr::Local { var: v, .. } if *v == var) =>
9778 {
9779 hit = true;
9780 }
9781 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. }
9782 if matches!(&**e, Expr::Local { var: v, .. } if *v == var) =>
9783 {
9784 hit = true;
9785 }
9786 _ => {}
9787 }
9788 });
9789 });
9790 hit
9791}
9792
9793pub(crate) fn rewrite_inner_ctor_outer_param(
9803 body: &mut Stmt,
9804 param0: u32,
9805 outer_display: &str,
9806 outer_ty: &TypeRef,
9807 eligible: &[String],
9808) {
9809 walk_stmt_exprs(body, &mut |e| {
9813 if let Expr::Method { name, cls, args, .. } = e {
9814 if &**name == "<init>"
9815 && eligible.iter().any(|c| c.as_str() == cls.as_ref())
9816 && args.first().is_some_and(|a| matches!(a, Expr::Local { var: v, .. } if *v == param0))
9817 {
9818 args.remove(0);
9819 }
9820 }
9821 });
9822 let text = format!("{outer_display}.this");
9824 let ty = outer_ty.clone();
9825 walk_stmt_exprs(body, &mut |e| {
9826 deep_rewrite_reads(e, &mut |x| {
9827 if let Expr::Local { var: v, .. } = x {
9828 if *v == param0 {
9829 *x = Expr::RawT(text.clone(), ty.clone());
9830 }
9831 }
9832 });
9833 });
9834}
9835
9836fn stmts_count_writes(stmts: &[Stmt], var: u32) -> usize {
9884 let mut n = 0usize;
9885 for s in stmts {
9886 visit_stmt_exprs_ro(s, &mut |e| {
9887 visit_exprs(e, &mut |x| match x {
9888 Expr::Assign { target, .. }
9889 if matches!(&**target, Expr::Local { var: v, .. } if *v == var) =>
9890 {
9891 n += 1;
9892 }
9893 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. }
9894 if matches!(&**e, Expr::Local { var: v, .. } if *v == var) =>
9895 {
9896 n += 1;
9897 }
9898 _ => {}
9899 });
9900 });
9901 }
9902 n
9903}
9904
9905fn prefix_written_vars(stmts: &[Stmt]) -> jdc_core::FxHashSet<u32> {
9909 let mut set = jdc_core::FxHashSet::default();
9910 for s in stmts {
9911 let mut c = s.clone();
9912 walk_stmt_exprs(&mut c, &mut |e| {
9913 deep_rewrite(e, &mut |x| match x {
9914 Expr::Assign { target, .. } => {
9915 if let Expr::Local { var, .. } = &**target {
9916 set.insert(*var);
9917 }
9918 }
9919 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => {
9920 if let Expr::Local { var, .. } = &**e {
9921 set.insert(*var);
9922 }
9923 }
9924 _ => {}
9925 });
9926 });
9927 if let Stmt::LocalDef { var, .. } = s {
9928 set.insert(*var);
9929 }
9930 }
9931 set
9932}
9933
9934fn pre_this_safe(e: &Expr, prefix_vars: &jdc_core::FxHashSet<u32>) -> bool {
9938 match e {
9939 Expr::Local { var, .. } => !prefix_vars.contains(var),
9940 Expr::Const(_) => true,
9941 Expr::Cast { e, .. } => pre_this_safe(e, prefix_vars),
9942 Expr::Bin { l, r, .. } => {
9943 pre_this_safe(l, prefix_vars) && pre_this_safe(r, prefix_vars)
9944 }
9945 Expr::Cond { c, t, f } => {
9946 pre_this_safe(c, prefix_vars)
9947 && pre_this_safe(t, prefix_vars)
9948 && pre_this_safe(f, prefix_vars)
9949 }
9950 Expr::Field { owner, is_static, .. } => {
9951 *is_static
9952 && owner
9953 .as_ref()
9954 .is_none_or(|o| pre_this_safe(o, prefix_vars))
9955 }
9956 Expr::Method {
9957 owner,
9958 args,
9959 is_static,
9960 ..
9961 } => {
9962 args.iter().all(|a| pre_this_safe(a, prefix_vars))
9963 && match owner {
9964 Some(o) => pre_this_safe(o, prefix_vars),
9965 None => *is_static,
9966 }
9967 }
9968 Expr::New { args, .. } => args.iter().all(|a| pre_this_safe(a, prefix_vars)),
9976 Expr::NewArray { dims, init, .. } => {
9977 dims.iter().all(|d| pre_this_safe(d, prefix_vars))
9978 && init
9979 .as_ref()
9980 .map(|l| l.iter().all(|x| pre_this_safe(x, prefix_vars)))
9981 .unwrap_or(true)
9982 }
9983 Expr::Un { e, .. } | Expr::InstanceOf { e, .. } => pre_this_safe(e, prefix_vars),
9985 Expr::ArrayIndex { array, index } => {
9986 pre_this_safe(array, prefix_vars) && pre_this_safe(index, prefix_vars)
9987 }
9988 _ => false,
9989 }
9990}
9991
9992fn single_assign_to_v(s: &Stmt, v: u32) -> Option<Expr> {
9996 match s {
9997 Stmt::ExprStmt(Expr::Assign {
9998 target,
9999 op: AssignOp::Plain,
10000 value,
10001 }) if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v) => {
10002 Some((**value).clone())
10003 }
10004 Stmt::Block(xs) if xs.len() == 1 => single_assign_to_v(&xs[0], v),
10005 _ => None,
10006 }
10007}
10008
10009pub(crate) fn flatten_top_blocks(stmts: &mut Vec<Stmt>) {
10031 fn rec(s: Stmt, out: &mut Vec<Stmt>) {
10032 match s {
10033 Stmt::Block(inner) => {
10034 for x in inner {
10035 rec(x, out);
10036 }
10037 }
10038 other => out.push(other),
10039 }
10040 }
10041 if !stmts.iter().any(|s| matches!(s, Stmt::Block(_))) {
10042 return;
10043 }
10044 let mut out: Vec<Stmt> = Vec::with_capacity(stmts.len());
10045 for s in std::mem::take(stmts) {
10046 rec(s, &mut out);
10047 }
10048 *stmts = out;
10049}
10050
10051fn fold_default_arg_bridge(stmts: &mut Vec<Stmt>) -> bool {
10052 flatten_top_blocks(stmts);
10053 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
10054 return false;
10055 };
10056 if pos == 0 {
10057 return false;
10058 }
10059 let Stmt::ExprStmt(Expr::Method { args, .. }) = &stmts[pos] else {
10060 return false;
10061 };
10062 let mut arg_vars: Vec<u32> = Vec::new();
10063 let mut reads = 0usize;
10064 for a in args {
10065 let mut c = a.clone();
10066 deep_rewrite_reads(&mut c, &mut |x| {
10067 if let Expr::Local { var, .. } = x {
10068 reads += 1;
10069 if !arg_vars.contains(var) {
10070 arg_vars.push(*var);
10071 }
10072 }
10073 });
10074 }
10075 if arg_vars.is_empty() || reads != arg_vars.len() {
10076 return false; }
10078 for &v in &arg_vars {
10081 if stmts_read_var(&stmts[pos + 1..], v) != 0
10082 || stmts_count_writes(&stmts[pos + 1..], v) != 0
10083 {
10084 return false;
10085 }
10086 }
10087 let prefix_vars = prefix_written_vars(&stmts[..pos]);
10088 let mut repl: Vec<(u32, Expr)> = Vec::new();
10089 let mut drop: Vec<usize> = Vec::new();
10090 for &v in &arg_vars {
10091 let mut base: Option<(usize, Expr)> = None;
10093 let mut decl: Option<usize> = None;
10094 let mut condf: Option<(usize, Expr, Option<Expr>, Option<Expr>)> = None;
10095 let mut ok = true;
10096 for (i, s) in stmts[..pos].iter().enumerate() {
10097 match s {
10098 Stmt::LocalDef { var, init, .. } if *var == v => match init {
10099 None => {
10100 if decl.is_some() {
10101 ok = false;
10102 break;
10103 }
10104 decl = Some(i);
10105 }
10106 Some(e) => {
10107 if base.is_some() {
10108 ok = false;
10109 break;
10110 }
10111 base = Some((i, e.clone()));
10112 }
10113 },
10114 Stmt::ExprStmt(Expr::Assign {
10115 target,
10116 op: AssignOp::Plain,
10117 value,
10118 }) if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v) => {
10119 if base.is_some() {
10120 ok = false;
10121 break;
10122 }
10123 base = Some((i, (**value).clone()));
10124 }
10125 Stmt::If {
10126 cond,
10127 then_stmt,
10128 else_stmt,
10129 } => {
10130 let t = single_assign_to_v(then_stmt, v);
10131 let f = else_stmt.as_ref().and_then(|b| single_assign_to_v(b, v));
10132 let then_touches = t.is_some()
10133 || stmts_read_var(std::slice::from_ref(&**then_stmt), v) != 0
10134 || stmts_count_writes(std::slice::from_ref(&**then_stmt), v) != 0;
10135 let else_touches = else_stmt.as_ref().is_some_and(|b| {
10136 f.is_some()
10137 || stmts_read_var(std::slice::from_ref(b), v) != 0
10138 || stmts_count_writes(std::slice::from_ref(b), v) != 0
10139 });
10140 if !then_touches && !else_touches {
10141 continue; }
10143 if condf.is_some()
10148 || base.is_none()
10149 || (then_touches && t.is_none())
10150 || (else_touches && f.is_none())
10151 {
10152 ok = false;
10153 break;
10154 }
10155 let cond_stmt = Stmt::ExprStmt(cond.clone());
10156 if stmts_read_var(std::slice::from_ref(&cond_stmt), v) != 0 {
10157 ok = false;
10158 break;
10159 }
10160 condf = Some((i, cond.clone(), t, f));
10161 }
10162 _ => {}
10163 }
10164 }
10165 if !ok {
10166 continue;
10167 }
10168 let Some((bi, be)) = base else { continue };
10169 let want_writes = 1
10172 + condf
10173 .as_ref()
10174 .map(|(_, _, t, f)| t.is_some() as usize + f.is_some() as usize)
10175 .unwrap_or(0);
10176 if stmts_count_writes(&stmts[..pos], v) != want_writes
10177 || stmts_read_var(&stmts[..pos], v) != 0
10178 {
10179 continue;
10180 }
10181 let rep = match condf {
10182 Some((ci, c, t, f)) => {
10183 drop.push(ci);
10184 match (t, f) {
10185 (Some(t), Some(f)) => Expr::Cond {
10186 c: Box::new(c),
10187 t: Box::new(t),
10188 f: Box::new(f),
10189 },
10190 (Some(t), None) => Expr::Cond {
10191 c: Box::new(c),
10192 t: Box::new(t),
10193 f: Box::new(be.clone()),
10194 },
10195 (None, Some(f)) => Expr::Cond {
10196 c: Box::new(c),
10197 t: Box::new(be.clone()),
10198 f: Box::new(f),
10199 },
10200 (None, None) => unreachable!("condf requires a branch assign"),
10201 }
10202 }
10203 None => be.clone(),
10204 };
10205 if !pre_this_safe(&rep, &prefix_vars) {
10206 continue;
10207 }
10208 drop.push(bi);
10209 if let Some(d) = decl {
10210 drop.push(d);
10211 }
10212 repl.push((v, rep));
10213 }
10214 if repl.is_empty() {
10215 return false;
10216 }
10217 if let Stmt::ExprStmt(Expr::Method { args, .. }) = &mut stmts[pos] {
10218 for a in args.iter_mut() {
10219 deep_rewrite_reads(a, &mut |x| {
10220 if let Expr::Local { var, .. } = x {
10221 if let Some((_, e)) = repl.iter().find(|(vv, _)| vv == var) {
10222 *x = e.clone();
10223 }
10224 }
10225 });
10226 }
10227 }
10228 drop.sort_unstable();
10229 drop.dedup();
10230 for i in drop.into_iter().rev() {
10231 stmts.remove(i);
10232 }
10233 true
10234}
10235
10236pub fn rewrite_kotlin_facades(body: &mut Stmt, pool: &crate::DexPool) {
10244 let map = pool.kotlin_facade_map();
10245 if map.is_empty() {
10246 return;
10247 }
10248 walk_stmt_exprs(body, &mut |e| {
10249 match e {
10250 Expr::Method {
10251 cls,
10252 name,
10253 desc,
10254 is_static,
10255 ..
10256 } if *is_static => {
10257 if let Some((base, cover)) = map.get(cls.as_ref()) {
10258 let hit = match cover {
10259 None => true,
10260 Some(c) => c.0.contains(&(name.to_string(), desc.to_string())),
10261 };
10262 if hit {
10263 *cls = std::sync::Arc::from(base.as_str());
10264 }
10265 }
10266 }
10267 Expr::Field {
10268 cls,
10269 name,
10270 is_static,
10271 ..
10272 } if *is_static => {
10273 if let Some((base, cover)) = map.get(cls.as_ref()) {
10274 let hit = match cover {
10275 None => true,
10276 Some(c) => c.1.contains(name.as_ref()),
10277 };
10278 if hit {
10279 *cls = std::sync::Arc::from(base.as_str());
10280 }
10281 }
10282 }
10283 _ => {}
10284 }
10285 });
10286}
10287
10288pub fn fold_enum_default_arg_bridge(body: &mut Stmt) {
10296 let Stmt::Block(stmts) = body else { return };
10297 fold_default_arg_bridge(stmts);
10298}
10299
10300pub fn fix_ctor_delegation_arg_defs(body: &mut Stmt) {
10301 let Stmt::Block(stmts) = body else { return };
10302 fold_default_arg_bridge(stmts);
10305 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
10309 return;
10310 };
10311 if pos == 0 {
10312 return;
10313 }
10314 let mut arg_vars: Vec<u32> = Vec::new();
10317 let mut total_reads = 0usize;
10318 {
10319 let Stmt::ExprStmt(Expr::Method { args, .. }) = &stmts[pos] else {
10320 return;
10321 };
10322 for a in args {
10323 let mut c = a.clone();
10324 deep_rewrite_reads(&mut c, &mut |x| {
10325 if let Expr::Local { var: v, .. } = x {
10326 total_reads += 1;
10327 if !arg_vars.contains(v) {
10328 arg_vars.push(*v);
10329 }
10330 }
10331 });
10332 }
10333 }
10334 if arg_vars.is_empty() || total_reads != arg_vars.len() {
10335 return;
10336 }
10337 let prefix_defs: Vec<u32> = stmts[..pos]
10340 .iter()
10341 .filter_map(|s| match s {
10342 Stmt::LocalDef { var, init: Some(_), .. } => Some(*var),
10343 _ => None,
10344 })
10345 .collect();
10346 let mut plan: Vec<(u32, usize, Option<usize>)> = Vec::new();
10351 for v in arg_vars {
10352 let defs: Vec<usize> = stmts[..pos]
10353 .iter()
10354 .enumerate()
10355 .filter(|(i, _)| {
10356 matches!(&stmts[*i], Stmt::LocalDef { var, init: Some(_), .. } if *var == v)
10357 })
10358 .map(|(i, _)| i)
10359 .collect();
10360 if defs.len() > 1 {
10361 return; }
10363 let Some(&q) = defs.first() else {
10364 continue; };
10366 let init = match &stmts[q] {
10367 Stmt::LocalDef { init: Some(e), .. } => e.clone(),
10368 _ => return,
10369 };
10370 if stmts_read_var(&stmts[q + 1..pos], v) != 0 {
10371 return; }
10373 let mut init_vars: Vec<u32> = Vec::new();
10376 deep_rewrite_reads(&mut { init.clone() }, &mut |x| {
10377 if let Expr::Local { var: vv, .. } = x {
10378 if !init_vars.contains(vv) {
10379 init_vars.push(*vv);
10380 }
10381 }
10382 });
10383 if init_vars.iter().any(|iv| prefix_defs.contains(iv)) {
10384 return;
10385 }
10386 match stmts[pos + 1..]
10387 .iter()
10388 .position(|s| {
10389 matches!(s, Stmt::ExprStmt(Expr::Assign { target, .. })
10390 if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v))
10391 }) {
10392 Some(w) => {
10393 if stmts_read_var(&stmts[pos + 1..pos + 1 + w], v) != 0 {
10395 return;
10396 }
10397 plan.push((v, q, Some(pos + 1 + w)));
10398 }
10399 None => {
10400 if stmts_read_var(&stmts[pos + 1..], v) != 0 {
10402 return;
10403 }
10404 plan.push((v, q, None));
10405 }
10406 }
10407 }
10408 if plan.is_empty() {
10409 return;
10410 }
10411 let values: Vec<(u32, Expr)> = plan
10413 .iter()
10414 .map(|(_v, q, _)| match &stmts[*q] {
10415 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e.clone()),
10416 _ => unreachable!("plan entries carry a LocalDef with init"),
10417 })
10418 .collect();
10419 if let Stmt::ExprStmt(Expr::Method { args, .. }) = &mut stmts[pos] {
10420 for a in args {
10421 deep_rewrite_reads(a, &mut |x| {
10422 if let Expr::Local { var: v, .. } = x {
10423 if let Some((_, e)) = values.iter().find(|(vv, _)| vv == v) {
10424 *x = e.clone();
10425 }
10426 }
10427 });
10428 }
10429 }
10430 for (v, _, w) in &plan {
10434 if let Some(w) = w {
10435 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = &mut stmts[*w] {
10436 if matches!(&**target, Expr::Local { var: vv, .. } if vv == v) {
10437 let taken = std::mem::replace(&mut **value, Expr::Const(ConstVal::Null));
10438 stmts[*w] = Stmt::LocalDef {
10439 var: *v,
10440 init: Some(taken),
10441 is_final: false,
10442 force_type: false,
10443 };
10444 }
10445 }
10446 }
10447 }
10448 let mut order: Vec<usize> = plan.iter().map(|(_, q, _)| *q).collect();
10449 order.sort_unstable();
10450 order.reverse();
10451 for q in order {
10452 stmts.remove(q);
10453 }
10454}
10455
10456pub fn fix_bool_returns(vt: &VarTable, body: &mut Stmt) {
10467 walk_mut_deep(body, &mut |st| {
10468 if let Stmt::Return(Some(e)) = st {
10469 let numeric = match &*e {
10470 Expr::Local { var, ty } => {
10471 let t = if (*var as usize) < vt.vars.len() {
10472 vt.var(*var).ty.erased()
10473 } else {
10474 ty.erased()
10475 };
10476 matches!(
10477 t,
10478 JavaType::Int
10479 | JavaType::Long
10480 | JavaType::Short
10481 | JavaType::Byte
10482 | JavaType::Char
10483 )
10484 }
10485 other => matches!(
10486 other.type_ref().erased(),
10487 JavaType::Int | JavaType::Long | JavaType::Short | JavaType::Byte
10488 | JavaType::Char
10489 ),
10490 };
10491 if numeric {
10492 let taken = e.clone();
10493 *e = Expr::Bin {
10494 op: BinOp::Ne,
10495 l: Box::new(taken),
10496 r: Box::new(Expr::Const(ConstVal::Int(0))),
10497 ty: Some(TypeRef::J(JavaType::Boolean)),
10498 };
10499 }
10500 }
10501 });
10502}
10503
10504pub fn fix_int_returns(vt: &VarTable, body: &mut Stmt) {
10505 walk_mut_deep(body, &mut |st| {
10506 if let Stmt::Return(Some(e)) = st {
10507 let is_bool = match e {
10508 Expr::Local { var, .. } => {
10509 (*var as usize) < vt.vars.len()
10510 && vt.vars[*var as usize].ty.erased() == JavaType::Boolean
10511 }
10512 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
10513 _ => false,
10514 };
10515 if is_bool {
10516 *e = Expr::Cond {
10517 c: Box::new(e.clone()),
10518 t: Box::new(Expr::Const(ConstVal::Int(1))),
10519 f: Box::new(Expr::Const(ConstVal::Int(0))),
10520 };
10521 }
10522 }
10523 });
10524}
10525
10526pub fn split_generations(vt: &mut VarTable, body: &mut Stmt, pool: &DexPool) {
10542 let mut counter: u32 = 0;
10543 let mut gen: std::collections::HashMap<u32, u32> = std::collections::HashMap::default();
10544 split_walk_stmt(body, vt, &mut gen, &mut counter, pool);
10545}
10546
10547fn split_needed(vt: &VarTable, target: u32, value: &Expr, pool: &DexPool) -> bool {
10549 let n = vt.vars.len();
10550 if target as usize >= n {
10551 return false;
10552 }
10553 let declared = vt.vars[target as usize].ty.erased();
10554 let vt_is_ref = |t: &JavaType| matches!(t, JavaType::Object(_) | JavaType::Array(_));
10555 fn side_bool(e: &Expr, vt: &VarTable) -> bool {
10564 match e {
10565 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
10566 matches!(vt.var(*var).ty.erased(), JavaType::Boolean)
10567 }
10568 Expr::Const(ConstVal::Int(0 | 1)) => true,
10569 Expr::Bin { op: BinOp::Or | BinOp::And | BinOp::Xor, l, r, .. } => {
10570 side_bool(l, vt) && side_bool(r, vt)
10571 }
10572 other => matches!(other.type_ref().erased(), JavaType::Boolean),
10573 }
10574 }
10575 let value_ty = match value {
10576 Expr::Local { var, ty } if (*var as usize) < vt.vars.len() => vt.var(*var).ty.erased(),
10577 Expr::Bin {
10578 op: BinOp::Or | BinOp::And | BinOp::Xor,
10579 l,
10580 r,
10581 ..
10582 } if side_bool(l, vt) && side_bool(r, vt) => JavaType::Boolean,
10583 other => other.type_ref().erased(),
10584 };
10585 if vt_is_ref(&declared) && vt_is_ref(&value_ty) {
10586 let obj = JavaType::Object("java/lang/Object".into());
10596 if declared == value_ty || declared == obj || value_ty == obj {
10597 return false;
10598 }
10599 if let (JavaType::Object(v), JavaType::Object(d)) = (&value_ty, &declared) {
10612 if matches!(value, Expr::New { .. })
10613 && (pool.get(v).is_none() || pool.get(d).is_none())
10614 {
10615 return false;
10616 }
10617 }
10618 true
10619 } else {
10620 if vt_is_ref(&declared) != vt_is_ref(&value_ty) {
10624 return true;
10625 }
10626 let d_bool = matches!(declared, JavaType::Boolean);
10634 let v_bool = matches!(value_ty, JavaType::Boolean);
10635 let zero_one = matches!(value, Expr::Const(ConstVal::Int(0 | 1)));
10636 d_bool != v_bool && !zero_one
10637 }
10638}
10639
10640fn rewrite_gen_reads(e: &mut Expr, gen: &std::collections::HashMap<u32, u32>, vt: &VarTable) {
10641 deep_rewrite(e, &mut |x| {
10642 if let Expr::Local { var, ty } = x {
10643 if let Some(&to) = gen.get(var) {
10644 if to != *var {
10645 *var = to;
10646 *ty = vt.var(to).ty.clone();
10647 }
10648 }
10649 }
10650 });
10651}
10652
10653fn do_split(
10654 vt: &mut VarTable,
10655 target: &mut Expr,
10656 old: u32,
10657 value_ty: &JavaType,
10658 gen: &mut std::collections::HashMap<u32, u32>,
10659 counter: &mut u32,
10660) {
10661 *counter += 1;
10662 let info = &vt.vars[old as usize];
10663 let id = vt.vars.len() as u32;
10664 let name = format!("{}_g{}", info.name, counter);
10665 let slot = info.slot;
10666 let is_param = false;
10674 vt.vars.push(jdc_core::var::VarInfo {
10675 id,
10676 slot,
10677 name,
10678 ty: TypeRef::J(value_ty.clone()),
10679 is_param,
10680 range_start: 0,
10681 range_end: u16::MAX,
10682 synthetic_name: true,
10683 });
10684 while vt.by_slot.len() <= slot as usize {
10685 vt.by_slot.push(Vec::new());
10686 }
10687 vt.by_slot[slot as usize].push((0, u16::MAX, id));
10688 gen.insert(old, id);
10689 if let Expr::Local { var, ty, .. } = target {
10690 *var = id;
10691 *ty = TypeRef::J(value_ty.clone());
10692 }
10693}
10694
10695fn rename_gen_back(s: &mut Stmt, from: u32, to: u32, vt: &VarTable) {
10707 if from == to || (to as usize) >= vt.vars.len() {
10708 return;
10709 }
10710 let to_ty = vt.vars[to as usize].ty.clone();
10711 walk_mut_deep(s, &mut |st| {
10712 if let Stmt::LocalDef { var, .. } = st {
10713 if *var == from {
10714 *var = to;
10715 }
10716 }
10717 });
10718 rewrite_exprs(s, &mut |e| {
10719 deep_rewrite(e, &mut |x| {
10720 if let Expr::Local { var, ty } = x {
10721 if *var == from {
10722 *var = to;
10723 *ty = to_ty.clone();
10724 }
10725 }
10726 });
10727 });
10728}
10729
10730fn drop_gen_slot(vt: &mut VarTable, dead: u32) {
10734 if (dead as usize) >= vt.vars.len() {
10735 return;
10736 }
10737 let slot = vt.vars[dead as usize].slot as usize;
10738 if let Some(seg) = vt.by_slot.get_mut(slot) {
10739 seg.retain(|&(_, _, id)| id != dead);
10740 }
10741}
10742
10743fn undo_type_safe(vt: &VarTable, k: u32, v: u32) -> bool {
10752 if (k as usize) >= vt.vars.len() || (v as usize) >= vt.vars.len() {
10753 return false;
10754 }
10755 let kt = vt.vars[k as usize].ty.erased();
10756 let vt_ty = vt.vars[v as usize].ty.erased();
10757 if kt == vt_ty {
10758 return true;
10759 }
10760 let is_prim = |t: &JavaType| {
10761 matches!(
10762 t,
10763 JavaType::Int
10764 | JavaType::Long
10765 | JavaType::Short
10766 | JavaType::Byte
10767 | JavaType::Char
10768 | JavaType::Float
10769 | JavaType::Double
10770 | JavaType::Boolean
10771 )
10772 };
10773 is_prim(&kt) && is_prim(&vt_ty)
10774}
10775
10776fn split_walk_stmt(
10777 st: &mut Stmt,
10778 vt: &mut VarTable,
10779 gen: &mut std::collections::HashMap<u32, u32>,
10780 counter: &mut u32,
10781 pool: &DexPool,
10782) {
10783 match st {
10784 Stmt::Block(v) => {
10785 for x in v.iter_mut() {
10786 split_walk_stmt(x, vt, gen, counter, pool);
10787 }
10788 }
10789 Stmt::ExprStmt(e) => {
10790 rewrite_gen_reads(e, gen, vt);
10791 if let Expr::Assign {
10792 target,
10793 value,
10794 op: AssignOp::Plain,
10795 ..
10796 } = e
10797 {
10798 if let Expr::Local { var, .. } = &**target {
10799 let old = *var;
10800 if split_needed(vt, old, value, pool) {
10801 let value_ty = value.type_ref().erased();
10802 do_split(vt, target, old, &value_ty, gen, counter);
10803 }
10804 }
10805 }
10806 }
10807 Stmt::LocalDef { var, init, .. } => {
10808 if let Some(e) = init {
10809 rewrite_gen_reads(e, gen, vt);
10810 if split_needed(vt, *var, e, pool) {
10811 let value_ty = e.type_ref().erased();
10815 let old = *var;
10816 let mut fake = Expr::Local {
10817 var: old,
10818 ty: vt.vars[old as usize].ty.clone(),
10819 };
10820 do_split(vt, &mut fake, old, &value_ty, gen, counter);
10821 if let Expr::Local { var: nv, .. } = fake {
10822 *var = nv;
10823 }
10824 }
10825 }
10826 }
10827 Stmt::If {
10828 cond,
10829 then_stmt,
10830 else_stmt,
10831 ..
10832 } => {
10833 rewrite_gen_reads(cond, gen, vt);
10834 let then_floor = vt.vars.len() as u32;
10835 let mut g_then = gen.clone();
10836 split_walk_stmt(then_stmt, vt, &mut g_then, counter, pool);
10837 let else_floor = vt.vars.len() as u32;
10838 let mut g_else = gen.clone();
10839 if let Some(e) = else_stmt {
10840 split_walk_stmt(e, vt, &mut g_else, counter, pool);
10841 }
10842 let mut undo_then: Vec<(u32, u32)> = g_then
10866 .iter()
10867 .filter(|(&k, &v)| {
10868 v >= then_floor
10869 && v < else_floor
10870 && g_else.get(&k).copied() == gen.get(&k).copied()
10871 && undo_type_safe(vt, k, v)
10872 })
10873 .map(|(&k, &v)| (v, k))
10874 .collect();
10875 if else_floor > then_floor && !undo_then.is_empty() {
10876 let vars: Vec<u32> = undo_then.iter().map(|&(v, _)| v).collect();
10877 let mut hits = vec![false; vars.len()];
10878 mark_read_vars(std::slice::from_ref(&**then_stmt), &vars, &mut hits);
10879 let mut i = 0usize;
10880 undo_then.retain(|_| {
10881 let keep = !hits[i];
10882 i += 1;
10883 keep
10884 });
10885 }
10886 undo_then.sort_unstable_by_key(|&(v, _)| std::cmp::Reverse(v));
10887 for &(v, k) in &undo_then {
10888 rename_gen_back(then_stmt, v, k, vt);
10889 drop_gen_slot(vt, v);
10890 }
10891 if let Some(e) = else_stmt.as_deref_mut() {
10892 let mut undo_else: Vec<(u32, u32)> = g_else
10893 .iter()
10894 .filter(|(&k, &v)| {
10895 v >= else_floor
10896 && g_then.get(&k).copied() == gen.get(&k).copied()
10897 && undo_type_safe(vt, k, v)
10898 })
10899 .map(|(&k, &v)| (v, k))
10900 .collect();
10901 if (vt.vars.len() as u32) > else_floor && !undo_else.is_empty() {
10902 let vars: Vec<u32> = undo_else.iter().map(|&(v, _)| v).collect();
10903 let mut hits = vec![false; vars.len()];
10904 mark_read_vars(std::slice::from_ref(&*e), &vars, &mut hits);
10905 let mut i = 0usize;
10906 undo_else.retain(|_| {
10907 let keep = !hits[i];
10908 i += 1;
10909 keep
10910 });
10911 }
10912 undo_else.sort_unstable_by_key(|&(v, _)| std::cmp::Reverse(v));
10913 for &(v, k) in &undo_else {
10914 rename_gen_back(e, v, k, vt);
10915 drop_gen_slot(vt, v);
10916 }
10917 }
10918 gen.retain(|k, v| g_then.get(k) == Some(v) && g_else.get(k) == Some(v));
10921 }
10922 Stmt::While { cond, body, .. } | Stmt::DoWhile { cond, body, .. } => {
10923 let entry = gen.clone();
10927 for _ in 0..3 {
10928 rewrite_gen_reads(cond, gen, vt);
10929 split_walk_stmt(body, vt, gen, counter, pool);
10930 }
10931 *gen = entry;
10934 }
10935 Stmt::For { init, body, .. } => {
10936 for x in init.iter_mut() {
10937 split_walk_stmt(x, vt, gen, counter, pool);
10938 }
10939 let entry = gen.clone();
10940 for _ in 0..3 {
10941 split_walk_stmt(body, vt, gen, counter, pool);
10942 }
10943 *gen = entry;
10944 }
10945 Stmt::ForEach { body, .. } => {
10946 let entry = gen.clone();
10947 for _ in 0..3 {
10948 split_walk_stmt(body, vt, gen, counter, pool);
10949 }
10950 *gen = entry;
10951 }
10952 Stmt::Switch { cases, default, .. } => {
10953 let entry = gen.clone();
10954 for c in cases.iter_mut() {
10955 let mut g_case = entry.clone();
10956 for x in c.body.iter_mut() {
10957 split_walk_stmt(x, vt, &mut g_case, counter, pool);
10958 }
10959 gen.retain(|k, v| g_case.get(k) == Some(v));
10960 }
10961 if let Some(d) = default {
10962 let mut g_def = entry.clone();
10963 split_walk_stmt(d, vt, &mut g_def, counter, pool);
10964 gen.retain(|k, v| g_def.get(k) == Some(v));
10965 }
10966 }
10967 Stmt::Try {
10968 body,
10969 catches,
10970 finally,
10971 } => {
10972 let entry = gen.clone();
10973 let mut g_try = entry.clone();
10974 split_walk_stmt(body, vt, &mut g_try, counter, pool);
10975 let mut g_all = g_try.clone();
10976 for c in catches.iter_mut() {
10977 let mut g_c = entry.clone();
10980 split_walk_stmt(&mut c.body, vt, &mut g_c, counter, pool);
10981 g_all.retain(|k, v| g_c.get(k) == Some(v));
10982 }
10983 *gen = g_all;
10984 if let Some(f) = finally {
10985 split_walk_stmt(f, vt, gen, counter, pool);
10986 }
10987 }
10988 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => {
10989 split_walk_stmt(body, vt, gen, counter, pool);
10990 }
10991 Stmt::Return(Some(e)) | Stmt::Throw(e) => {
10992 rewrite_gen_reads(e, gen, vt);
10993 }
10994 _ => {}
10995 }
10996}
10997
10998fn fw_super(c: &str) -> Option<&'static str> {
11005 crate::fwdb::super_of(c)
11006}
11007
11008fn exc_subtype(sub: &str, sup: &str, pool: &DexPool) -> bool {
11014 if sub == sup {
11015 return true;
11016 }
11017 if pool.is_subtype(sub, sup) {
11018 return true;
11019 }
11020 let mut cur = sub;
11021 for _ in 0..24 {
11022 let Some(next) = fw_super(cur) else { break };
11023 if next == sup {
11024 return true;
11025 }
11026 cur = next;
11027 }
11028 false
11029}
11030
11031pub fn dedupe_multicatch(s: &mut Stmt, pool: &DexPool) {
11037 dedupe_multicatch_walk(s, pool);
11038}
11039
11040fn dedupe_multicatch_walk(s: &mut Stmt, pool: &DexPool) {
11041 match s {
11042 Stmt::Try {
11043 body,
11044 catches,
11045 finally,
11046 }
11047 | Stmt::TryWithResources {
11048 body,
11049 catches,
11050 finally,
11051 ..
11052 } => {
11053 dedupe_multicatch_walk(body, pool);
11054 for c in catches.iter_mut() {
11055 if c.exc.len() > 1 {
11056 let mut keep: Vec<std::sync::Arc<str>> = Vec::with_capacity(c.exc.len());
11057 for e in c.exc.iter() {
11058 let shadowed = c.exc.iter().any(|o| {
11059 o.as_ref() != e.as_ref() && exc_subtype(e, o, pool)
11060 });
11061 if !shadowed && !keep.iter().any(|k| k.as_ref() == e.as_ref()) {
11062 keep.push(e.clone());
11063 }
11064 }
11065 c.exc = keep;
11066 }
11067 dedupe_multicatch_walk(&mut c.body, pool);
11068 }
11069 if let Some(f) = finally {
11070 dedupe_multicatch_walk(f, pool);
11071 }
11072 }
11073 Stmt::Block(v) => {
11074 for x in v.iter_mut() {
11075 dedupe_multicatch_walk(x, pool);
11076 }
11077 }
11078 Stmt::If {
11079 then_stmt,
11080 else_stmt,
11081 ..
11082 } => {
11083 dedupe_multicatch_walk(then_stmt, pool);
11084 if let Some(e) = else_stmt {
11085 dedupe_multicatch_walk(e, pool);
11086 }
11087 }
11088 Stmt::While { body, .. }
11089 | Stmt::DoWhile { body, .. }
11090 | Stmt::For { body, .. }
11091 | Stmt::ForEach { body, .. }
11092 | Stmt::Labeled { body, .. }
11093 | Stmt::Synchronized { body, .. } => dedupe_multicatch_walk(body, pool),
11094 Stmt::Switch { cases, default, .. } => {
11095 for c in cases.iter_mut() {
11096 for x in c.body.iter_mut() {
11097 dedupe_multicatch_walk(x, pool);
11098 }
11099 }
11100 if let Some(d) = default {
11101 dedupe_multicatch_walk(d, pool);
11102 }
11103 }
11104 _ => {}
11105 }
11106}
11107
11108pub fn resolve_dangling_gotos(s: &mut Stmt) {
11120 let mut labels: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
11121 walk_all(s, &mut |st| {
11124 if let Stmt::Label(id) = st {
11125 labels.insert(*id);
11126 }
11127 });
11128 resolve_gotos_walk(s, &labels, 0);
11129}
11130
11131fn resolve_gotos_walk(s: &mut Stmt, labels: &jdc_core::FxHashSet<u32>, loop_depth: u32) {
11132 let depth = match s {
11133 Stmt::While { .. } | Stmt::DoWhile { .. } | Stmt::For { .. } | Stmt::ForEach { .. } => {
11134 loop_depth + 1
11135 }
11136 _ => loop_depth,
11137 };
11138 match s {
11139 Stmt::Goto(id) => {
11140 if labels.contains(id) {
11141 return; }
11143 if depth > 0 {
11144 *s = Stmt::Break(None);
11145 } else {
11146 *s = Stmt::Comment(format!(
11147 "$DDC: unresolved jump to block {} (irreducible control flow)",
11148 id
11149 ));
11150 }
11151 }
11152 Stmt::Block(v) => {
11153 let mut i = 0usize;
11154 while i < v.len() {
11155 let dangling_here =
11160 depth == 0 && matches!(&v[i], Stmt::Goto(id) if !labels.contains(id));
11161 resolve_gotos_walk(&mut v[i], labels, depth);
11162 if dangling_here {
11163 v.truncate(i + 1);
11164 break;
11165 }
11166 i += 1;
11167 }
11168 }
11169 Stmt::If { then_stmt, else_stmt, .. } => {
11170 resolve_gotos_walk(then_stmt, labels, depth);
11171 if let Some(e) = else_stmt {
11172 resolve_gotos_walk(e, labels, depth);
11173 }
11174 }
11175 Stmt::While { body, .. }
11176 | Stmt::DoWhile { body, .. }
11177 | Stmt::For { body, .. }
11178 | Stmt::ForEach { body, .. }
11179 | Stmt::Labeled { body, .. }
11180 | Stmt::Synchronized { body, .. } => resolve_gotos_walk(body, labels, depth),
11181 Stmt::Try {
11182 body,
11183 catches,
11184 finally,
11185 } => {
11186 resolve_gotos_walk(body, labels, depth);
11187 for c in catches.iter_mut() {
11188 resolve_gotos_walk(&mut c.body, labels, depth);
11189 }
11190 if let Some(f) = finally {
11191 resolve_gotos_walk(f, labels, depth);
11192 }
11193 }
11194 Stmt::Switch { cases, default, .. } => {
11195 for c in cases.iter_mut() {
11196 for x in c.body.iter_mut() {
11197 resolve_gotos_walk(x, labels, depth);
11198 }
11199 }
11200 if let Some(d) = default {
11201 resolve_gotos_walk(d, labels, depth);
11202 }
11203 }
11204 _ => {}
11205 }
11206}
11207
11208fn mark_read_vars(stmts: &[Stmt], vars: &[u32], hits: &mut [bool]) {
11220 let mut remaining = vars.len();
11221 for s in stmts {
11222 if remaining == 0 {
11223 break;
11224 }
11225 visit_stmt_exprs_ro(s, &mut |e| {
11226 if remaining == 0 {
11227 return;
11228 }
11229 visit_exprs_reads(e, &mut |x| {
11230 if remaining == 0 {
11231 return;
11232 }
11233 if let Expr::Local { var, .. } = x {
11234 for (i, v) in vars.iter().enumerate() {
11235 if *v == *var && !hits[i] {
11236 hits[i] = true;
11237 remaining -= 1;
11238 }
11239 }
11240 }
11241 });
11242 });
11243 }
11244}
11245
11246fn stmts_read_var(stmts: &[Stmt], v: u32) -> usize {
11247 let mut n = 0usize;
11248 for s in stmts {
11249 visit_stmt_exprs_ro(s, &mut |e| {
11250 visit_exprs_reads(e, &mut |x| {
11251 if let Expr::Local { var: vv, .. } = x {
11252 if *vv == v {
11253 n += 1;
11254 }
11255 }
11256 });
11257 });
11258 }
11259 n
11260}
11261
11262pub fn fix_ctor_conditional_super(body: &mut Stmt) {
11263 if !matches!(body, Stmt::Block(_)) {
11266 let inner = std::mem::replace(body, Stmt::Block(Vec::new()));
11267 *body = Stmt::Block(vec![inner]);
11268 }
11269 let Stmt::Block(stmts) = body else { return };
11270 while stmts.len() == 1 {
11274 let Stmt::Block(inner) = &stmts[0] else { break };
11275 *stmts = inner.clone();
11276 }
11277 if stmts.is_empty() {
11278 return;
11279 }
11280 if is_bare_ctor_call(stmts.first().unwrap()) {
11281 dedupe_ctor_delegations(body);
11282 return;
11283 }
11284 if try_linear_super_inline(stmts) {
11285 return;
11286 }
11287 try_merge_branched_super(stmts);
11288}
11289
11290pub fn strip_secondary_delegations(body: &mut Stmt) {
11314 let Stmt::Block(stmts) = body else { return };
11315 if !stmts.first().is_some_and(is_bare_ctor_call) {
11316 return;
11317 }
11318 fn strip_in(s: &mut Stmt) {
11319 if is_bare_ctor_call(s) {
11320 *s = Stmt::Block(Vec::new());
11321 return;
11322 }
11323 match s {
11324 Stmt::Block(v) => v.iter_mut().for_each(strip_in),
11325 Stmt::If {
11326 then_stmt,
11327 else_stmt,
11328 ..
11329 } => {
11330 strip_in(then_stmt);
11331 if let Some(e) = else_stmt {
11332 strip_in(e);
11333 }
11334 }
11335 Stmt::While { body, .. }
11336 | Stmt::DoWhile { body, .. }
11337 | Stmt::ForEach { body, .. }
11338 | Stmt::Synchronized { body, .. }
11339 | Stmt::Labeled { body, .. } => strip_in(body),
11340 Stmt::For { init, body, .. } => {
11341 init.iter_mut().for_each(strip_in);
11342 strip_in(body);
11343 }
11344 Stmt::Switch { cases, default, .. } => {
11345 for c in cases.iter_mut() {
11346 c.body.iter_mut().for_each(strip_in);
11347 }
11348 if let Some(d) = default {
11349 strip_in(d);
11350 }
11351 }
11352 Stmt::Try {
11353 body,
11354 catches,
11355 finally,
11356 }
11357 | Stmt::TryWithResources {
11358 body,
11359 catches,
11360 finally,
11361 ..
11362 } => {
11363 strip_in(body);
11364 for c in catches.iter_mut() {
11365 strip_in(&mut c.body);
11366 }
11367 if let Some(f) = finally {
11368 strip_in(f);
11369 }
11370 }
11371 _ => {}
11372 }
11373 }
11374 for st in stmts.iter_mut().skip(1) {
11375 strip_in(st);
11376 }
11377}
11378
11379pub fn dedupe_ctor_delegations(body: &mut Stmt) {
11380 let Stmt::Block(stmts) = body else { return };
11381 let Some(d0) = stmts.first().and_then(|s| match s {
11382 Stmt::ExprStmt(e) if is_delegation_expr(e) => Some(e.clone()),
11383 _ => None,
11384 }) else {
11385 return;
11386 };
11387 fn rec(s: &mut Stmt, d0: &Expr) {
11388 match s {
11389 Stmt::Block(v) => {
11390 v.retain(|x| {
11391 !matches!(x, Stmt::ExprStmt(e) if is_delegation_expr(e) && same_delegation(e, d0))
11392 });
11393 for x in v.iter_mut() {
11394 rec(x, d0);
11395 }
11396 }
11397 Stmt::If { then_stmt, else_stmt, .. } => {
11398 rec(then_stmt, d0);
11399 if let Some(e) = else_stmt {
11400 rec(e, d0);
11401 }
11402 }
11403 _ => {}
11404 }
11405 }
11406 {
11410 let d0r = &d0;
11411 let mut idx = 0usize;
11412 stmts.retain(|s| {
11413 idx += 1;
11414 idx == 1
11415 || !(matches!(s, Stmt::ExprStmt(e)
11416 if is_delegation_expr(e) && same_delegation(e, d0r)))
11417 });
11418 }
11419 for s in stmts.iter_mut().skip(1) {
11420 rec(s, &d0);
11421 }
11422}
11423
11424fn same_delegation(a: &Expr, b: &Expr) -> bool {
11428 match (a, b) {
11429 (
11430 Expr::Method { name: n1, cls: c1, is_super: s1, args: a1, .. },
11431 Expr::Method { name: n2, cls: c2, is_super: s2, args: a2, .. },
11432 ) => {
11433 &**n1 == "<init>"
11434 && &**n2 == "<init>"
11435 && c1 == c2
11436 && s1 == s2
11437 && (a1 == a2 || (a1.is_empty() && a2.is_empty()))
11438 }
11439 _ => false,
11440 }
11441}
11442
11443fn ctor_call_expr(s: &Stmt) -> Option<&Expr> {
11445 match s {
11446 Stmt::ExprStmt(e) if is_delegation_expr(e) => Some(e),
11447 _ => None,
11448 }
11449}
11450
11451fn same_ctor_kind(a: &Expr, b: &Expr) -> bool {
11453 match (a, b) {
11454 (
11455 Expr::Method { cls: c1, is_super: s1, owner: o1, .. },
11456 Expr::Method { cls: c2, is_super: s2, owner: o2, .. },
11457 ) => c1 == c2 && s1 == s2 && o1 == o2,
11458 _ => false,
11459 }
11460}
11461
11462fn flat_list(s: &Stmt) -> Vec<&Stmt> {
11467 fn rec<'a>(s: &'a Stmt, out: &mut Vec<&'a Stmt>) {
11468 match s {
11469 Stmt::Block(v) => {
11470 for x in v {
11471 rec(x, out);
11472 }
11473 }
11474 other => out.push(other),
11475 }
11476 }
11477 let mut out = Vec::new();
11478 rec(s, &mut out);
11479 out
11480}
11481
11482fn strip_lost_alloc_owners(e: &mut Expr) {
11488 deep_rewrite(e, &mut |x| {
11489 if let Expr::Method { name, is_special: true, owner: Some(o), .. } = x {
11490 if &**name == "<init>" && !matches!(**o, Expr::This) {
11491 **o = Expr::Const(jdc_core::ir::expr::ConstVal::Int(0));
11492 }
11493 }
11494 });
11495}
11496
11497fn count_locals_expr(e: &Expr) -> jdc_core::FxHashMap<u32, usize> {
11500 let mut m = jdc_core::FxHashMap::default();
11501 count_expr_ro(e, &mut m);
11502 m
11503}
11504
11505pub(crate) fn count_locals_stmts(ss: &[Stmt]) -> jdc_core::FxHashMap<u32, usize> {
11506 let mut m = jdc_core::FxHashMap::default();
11507 for s in ss {
11508 visit_stmt_exprs_ro(s, &mut |e| count_expr_ro(e, &mut m));
11509 }
11510 m
11511}
11512
11513fn cnt(m: &jdc_core::FxHashMap<u32, usize>, v: u32) -> usize {
11514 m.get(&v).copied().unwrap_or(0)
11515}
11516
11517fn inline_locals(
11521 e: &Expr,
11522 map: &std::collections::HashMap<u32, Expr>,
11523 depth: u32,
11524) -> Option<Expr> {
11525 if depth > 4 {
11526 return None;
11527 }
11528 let mut out = e.clone();
11529 let mut fail = false;
11530 deep_rewrite(&mut out, &mut |x| {
11531 if let Expr::Local { var, .. } = x {
11532 if let Some(def) = map.get(var) {
11533 match inline_locals(def, map, depth + 1) {
11534 Some(r) => *x = r,
11535 None => fail = true,
11536 }
11537 }
11538 }
11539 });
11540 if fail {
11541 None
11542 } else {
11543 Some(out)
11544 }
11545}
11546
11547fn def_of(s: &Stmt) -> Option<(u32, &Expr)> {
11549 match s {
11550 Stmt::LocalDef { var, init: Some(e), .. } => Some((*var, e)),
11551 Stmt::ExprStmt(Expr::Assign {
11552 target,
11553 value,
11554 op: jdc_core::ir::expr::AssignOp::Plain,
11555 ..
11556 }) => match &**target {
11557 Expr::Local { var, .. } => Some((*var, value)),
11558 _ => None,
11559 },
11560 _ => None,
11561 }
11562}
11563
11564fn try_linear_super_inline(stmts: &mut Vec<Stmt>) -> bool {
11566 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
11567 return false;
11568 };
11569 if pos == 0 {
11570 return false; }
11572 if !stmts[..pos]
11574 .iter()
11575 .all(|s| matches!(s, Stmt::LocalDef { .. } | Stmt::ExprStmt(_)))
11576 {
11577 return false;
11578 }
11579 let Some(call) = ctor_call_expr(&stmts[pos]) else {
11580 return false;
11581 };
11582 let Expr::Method { args, .. } = call else {
11583 return false;
11584 };
11585 let mut map: std::collections::HashMap<u32, Expr> =
11587 std::collections::HashMap::new();
11588 let mut def_idx: jdc_core::FxHashMap<u32, usize> =
11589 jdc_core::FxHashMap::default();
11590 for (i, s) in stmts[..pos].iter().enumerate() {
11591 if let Some((v, e)) = def_of(s) {
11592 map.insert(v, e.clone());
11593 def_idx.insert(v, i);
11594 }
11595 }
11596 let mut needed: Vec<u32> = Vec::new();
11598 for a in args {
11599 let mut probe = a.clone();
11600 strip_lost_alloc_owners(&mut probe);
11601 deep_rewrite(&mut probe, &mut |x| {
11602 if let Expr::Local { var, .. } = x {
11603 if map.contains_key(var) && !needed.contains(var) {
11604 needed.push(*var);
11605 }
11606 }
11607 });
11608 }
11609 if needed.is_empty() {
11610 return false; }
11612 let mut consumed: Vec<usize> = Vec::new();
11615 let use_counts = count_locals_stmts(stmts);
11616 for &v in &needed {
11617 let def = &map[&v];
11618 let uses = cnt(&use_counts, v);
11619 if jdc_core::ir::build::has_side_effects(def) && uses != 1 {
11620 return false;
11621 }
11622 if uses == 1 {
11623 consumed.push(def_idx[&v]);
11624 }
11625 }
11626 let mut new_args: Vec<Expr> = Vec::with_capacity(args.len());
11628 for a in args {
11629 match inline_locals(a, &map, 0) {
11630 Some(r) => new_args.push(r),
11631 None => return false,
11632 }
11633 }
11634 let mut call_stmt = stmts.remove(pos);
11637 if let Stmt::ExprStmt(Expr::Method { args: slot, .. }) = &mut call_stmt {
11638 *slot = new_args;
11639 }
11640 let mut rest: Vec<Stmt> = Vec::with_capacity(stmts.len() + 1);
11641 rest.push(call_stmt);
11642 for (i, s) in stmts.drain(..).enumerate() {
11643 let orig = if i >= pos { i + 1 } else { i };
11646 if !consumed.contains(&orig) {
11647 rest.push(s);
11648 }
11649 }
11650 *stmts = rest;
11651 true
11652}
11653
11654fn try_merge_branched_super(stmts: &mut Vec<Stmt>) -> bool {
11656 let if_positions: Vec<usize> = stmts
11660 .iter()
11661 .enumerate()
11662 .filter(|(_, s)| matches!(s, Stmt::If { .. }))
11663 .map(|(i, _)| i)
11664 .collect();
11665 for if_pos in if_positions {
11666 let gate = match &stmts[if_pos] {
11672 Stmt::If { then_stmt, else_stmt, .. } => {
11673 contains_delegation(then_stmt)
11674 || else_stmt.as_ref().is_some_and(|e| contains_delegation(e))
11675 }
11676 _ => false,
11677 };
11678 if gate && merge_at(stmts, if_pos) {
11679 return true;
11680 }
11681 }
11682 false
11683}
11684
11685fn contains_delegation(s: &Stmt) -> bool {
11688 match s {
11689 Stmt::ExprStmt(e) => is_delegation_expr(e),
11690 Stmt::Block(v) => v.iter().any(contains_delegation),
11691 Stmt::If { then_stmt, else_stmt, .. } => {
11692 contains_delegation(then_stmt)
11693 || else_stmt.as_ref().is_some_and(|e| contains_delegation(e))
11694 }
11695 _ => false,
11696 }
11697}
11698
11699
11700
11701fn merge_at(stmts: &mut Vec<Stmt>, if_pos: usize) -> bool {
11714 enum Br {
11719 Call(usize, Vec<(u32, Expr)>, Vec<usize>),
11721 Clean,
11722 Dirty,
11723 }
11724 fn split_branch(list: &[&Stmt]) -> Br {
11725 let call_pos = list.iter().position(|s| ctor_call_expr(s).is_some());
11726 let Some(c) = call_pos else {
11727 return if list.iter().any(|s| contains_delegation(s)) {
11730 Br::Dirty
11731 } else {
11732 Br::Clean
11733 };
11734 };
11735 if list.iter().skip(c + 1).any(|s| ctor_call_expr(s).is_some()) {
11736 return Br::Dirty; }
11738 let mut defs: Vec<(u32, Expr)> = Vec::new();
11739 let mut def_idxs: Vec<usize> = Vec::new();
11740 for (i, s) in list.iter().enumerate().take(c) {
11741 match def_of(s) {
11742 Some((v, e)) => {
11743 defs.push((v, e.clone()));
11744 def_idxs.push(i);
11745 }
11746 None => match s {
11747 Stmt::ExprStmt(_) | Stmt::LocalDef { .. } => {}
11750 _ => return Br::Dirty, },
11752 }
11753 }
11754 Br::Call(c, defs, def_idxs)
11755 }
11756 fn cond_def_of(s: &Stmt, scope: &[Stmt]) -> Option<(u32, Expr)> {
11758 let Stmt::If { cond, then_stmt, else_stmt: Some(e), .. } = s else {
11759 return None;
11760 };
11761 fn one<'x>(b: &'x Stmt, scope: &[Stmt]) -> Option<(u32, &'x Expr)> {
11762 let l = flat_list(b);
11763 if l.len() == 1 {
11764 return def_of(l[0]);
11765 }
11766 if l.len() == 2 {
11775 let (w, ex) = def_of(l[0])?;
11776 let Stmt::ExprStmt(Expr::Assign {
11777 target,
11778 op: AssignOp::Plain,
11779 value,
11780 }) = l[1]
11781 else {
11782 return None;
11783 };
11784 let Expr::Local { var: tv, .. } = &**target else {
11785 return None;
11786 };
11787 let Expr::Local { var: rv, .. } = &**value else {
11788 return None;
11789 };
11790 if *rv != w || *tv == w {
11791 return None;
11792 }
11793 let mut ldef_writes = 0usize;
11797 for st in scope {
11798 walk_all(st, &mut |x| {
11799 if let Stmt::LocalDef {
11800 var,
11801 init: Some(_),
11802 ..
11803 } = x
11804 {
11805 if *var == w {
11806 ldef_writes += 1;
11807 }
11808 }
11809 });
11810 }
11811 if stmts_read_var(scope, w) != 1
11812 || stmts_count_writes(scope, w) + ldef_writes != 1
11813 {
11814 return None;
11815 }
11816 return Some((*tv, ex));
11817 }
11818 None
11819 }
11820 let (v1, e1) = one(then_stmt, scope)?;
11821 let (v2, e2) = one(e, scope)?;
11822 if v1 != v2 {
11823 return None;
11824 }
11825 Some((
11826 v1,
11827 Expr::Cond {
11828 c: Box::new(cond.clone()),
11829 t: Box::new(e1.clone()),
11830 f: Box::new(e2.clone()),
11831 },
11832 ))
11833 }
11834
11835 let mut pmap: std::collections::HashMap<u32, Expr> =
11837 std::collections::HashMap::new();
11838 let mut pidx: jdc_core::FxHashMap<u32, usize> =
11839 jdc_core::FxHashMap::default();
11840 for (i, s) in stmts[..if_pos].iter().enumerate() {
11841 match s {
11842 Stmt::LocalDef { .. } | Stmt::ExprStmt(_) => {
11843 if let Some((v, e)) = def_of(s) {
11844 pmap.insert(v, e.clone());
11845 pidx.insert(v, i);
11846 }
11847 }
11848 Stmt::If { .. } => match cond_def_of(s, stmts) {
11849 Some((v, e)) => {
11850 pmap.insert(v, e);
11851 pidx.insert(v, i);
11852 }
11853 None => return false,
11854 },
11855 _ => return false,
11856 }
11857 }
11858 let (cond, then_v, else_v, had_else) = match &stmts[if_pos] {
11859 Stmt::If { cond, then_stmt, else_stmt, .. } => (
11860 cond.clone(),
11861 then_stmt.as_ref().clone(),
11862 else_stmt.as_ref().map_or_else(|| Stmt::Block(Vec::new()), |e| e.as_ref().clone()),
11863 else_stmt.is_some(),
11864 ),
11865 _ => return false,
11866 };
11867 fn normalize_branch(b: &mut Stmt) {
11873 let slot: &mut Stmt = match b {
11878 Stmt::Block(v) if v.len() == 1 => &mut v[0],
11879 other => other,
11880 };
11881 if !matches!(slot, Stmt::If { .. }) || !contains_delegation(slot) {
11882 return;
11883 }
11884 let taken = std::mem::replace(slot, Stmt::Block(Vec::new()));
11885 let mut tmp = vec![taken];
11886 if merge_at(&mut tmp, 0) {
11887 *slot = Stmt::Block(tmp);
11888 } else {
11889 *slot = tmp.pop().unwrap();
11890 }
11891 }
11892 let mut then_v = then_v;
11893 let mut else_v = else_v;
11894 normalize_branch(&mut then_v);
11895 normalize_branch(&mut else_v);
11896 let then_list = flat_list(&then_v);
11897 let else_list = flat_list(&else_v);
11898
11899 enum Side {
11902 Call {
11903 at: usize,
11904 defs: Vec<(u32, Expr)>,
11905 def_idxs: Vec<usize>,
11906 },
11907 Clean,
11908 }
11909 let (t_side, e_side, two_sided) = match (split_branch(&then_list), split_branch(&else_list)) {
11910 (Br::Call(tc, td, ti), Br::Call(ec, ed, ei)) => (
11911 Side::Call { at: tc, defs: td, def_idxs: ti },
11912 Side::Call { at: ec, defs: ed, def_idxs: ei },
11913 true,
11914 ),
11915 (Br::Call(tc, td, ti), Br::Clean) => (
11916 Side::Call { at: tc, defs: td, def_idxs: ti },
11917 Side::Clean,
11918 false,
11919 ),
11920 (Br::Clean, Br::Call(ec, ed, ei)) => (
11921 Side::Clean,
11922 Side::Call { at: ec, defs: ed, def_idxs: ei },
11923 false,
11924 ),
11925 _ => return false,
11926 };
11927 let (t_call, e_call) = match (&t_side, &e_side) {
11929 (Side::Call { at: ta, .. }, Side::Call { at: ea, .. }) => (
11930 ctor_call_expr(then_list[*ta]),
11931 ctor_call_expr(else_list[*ea]),
11932 ),
11933 (Side::Call { at: ta, .. }, Side::Clean) => {
11934 (ctor_call_expr(then_list[*ta]), None)
11935 }
11936 (Side::Clean, Side::Call { at: ea, .. }) => {
11937 (None, ctor_call_expr(else_list[*ea]))
11938 }
11939 _ => return false,
11940 };
11941 let (call_list, call_at) = match &t_side {
11943 Side::Call { at, .. } => (&then_list, *at),
11944 Side::Clean => match &e_side {
11945 Side::Call { at, .. } => (&else_list, *at),
11946 Side::Clean => return false,
11947 },
11948 };
11949 let (base_args, other_args) = if two_sided {
11950 let (Some(tc), Some(ec)) = (t_call, e_call) else {
11951 return false;
11952 };
11953 if !same_ctor_kind(tc, ec) {
11954 return false;
11955 }
11956 let (Expr::Method { args: a1, .. }, Expr::Method { args: a2, .. }) = (tc, ec) else {
11957 return false;
11958 };
11959 if a1.len() != a2.len() {
11960 return false;
11961 }
11962 (a1, Some(a2))
11963 } else {
11964 let c = t_call.or(e_call);
11965 let Some(c) = c else { return false };
11966 let Expr::Method { args, .. } = c else {
11967 return false;
11968 };
11969 (args, None)
11970 };
11971
11972 let mut tmap: std::collections::HashMap<u32, Expr> = pmap.clone();
11974 let mut emap: std::collections::HashMap<u32, Expr> = pmap.clone();
11975 if let Side::Call { defs, .. } = &t_side {
11976 for (v, e) in defs {
11977 tmap.insert(*v, e.clone());
11978 }
11979 }
11980 if let Side::Call { defs, .. } = &e_side {
11981 for (v, e) in defs {
11982 emap.insert(*v, e.clone());
11983 }
11984 }
11985 let Some(cond_i) = inline_locals(&cond, &pmap, 0) else {
11986 return false;
11987 };
11988 let mut then_args: Vec<Expr> = Vec::with_capacity(base_args.len());
11990 let mut else_args: Vec<Expr> = Vec::new();
11991 if two_sided {
11992 for a in base_args {
11993 match inline_locals(a, &tmap, 0) {
11994 Some(r) => then_args.push(r),
11995 None => return false,
11996 }
11997 }
11998 for a in other_args.unwrap() {
11999 match inline_locals(a, &emap, 0) {
12000 Some(r) => else_args.push(r),
12001 None => return false,
12002 }
12003 }
12004 } else {
12005 let from_then = matches!(&t_side, Side::Call { .. });
12008 for a in base_args {
12009 let r = if from_then {
12010 inline_locals(a, &tmap, 0)
12011 } else {
12012 inline_locals(a, &emap, 0)
12013 };
12014 match r {
12015 Some(r) => then_args.push(r),
12016 None => return false,
12017 }
12018 }
12019 }
12020 let primary_args = base_args;
12021 let mut merged: Vec<Expr> = Vec::with_capacity(then_args.len());
12023 let mut differ = 0usize;
12024 for (j, t) in then_args.iter().enumerate() {
12025 if two_sided {
12026 let e = &else_args[j];
12027 if t == e {
12028 merged.push(t.clone());
12029 } else {
12030 differ += 1;
12031 merged.push(Expr::Cond {
12032 c: Box::new(cond_i.clone()),
12033 t: Box::new(t.clone()),
12034 f: Box::new(e.clone()),
12035 });
12036 }
12037 } else {
12038 merged.push(t.clone());
12039 }
12040 }
12041
12042 let vars_in = |e: &Expr| -> Vec<u32> {
12044 let mut vs = Vec::new();
12045 let mut probe = e.clone();
12046 strip_lost_alloc_owners(&mut probe);
12047 deep_rewrite(&mut probe, &mut |x| {
12048 if let Expr::Local { var, .. } = x {
12049 if !vs.contains(var) {
12050 vs.push(*var);
12051 }
12052 }
12053 });
12054 vs
12055 };
12056 let branch_remainder = |list: &[&Stmt], side: &Side| -> Vec<Stmt> {
12064 match side {
12065 Side::Clean => list.iter().map(|s| (*s).clone()).collect(),
12066 Side::Call { at, def_idxs, .. } => {
12067 list.iter()
12068 .enumerate()
12069 .filter(|(i, _)| *i != *at && !def_idxs.contains(i))
12070 .map(|(_, s)| (*s).clone())
12071 .collect()
12072 }
12073 }
12074 };
12075 let tail_then_v = branch_remainder(&then_list, &t_side);
12076 let tail_else_v = branch_remainder(&else_list, &e_side);
12077 let post = &stmts[if_pos + 1..];
12078 let mut inlined: Vec<u32> = vars_in(&cond);
12079 for a in primary_args.iter().chain(else_args.iter()) {
12080 for v in vars_in(a) {
12081 if !inlined.contains(&v) {
12082 inlined.push(v);
12083 }
12084 }
12085 }
12086 inlined.retain(|v| pmap.contains_key(v) || tmap.contains_key(v) || emap.contains_key(v));
12087 for &v in &inlined {
12089 let in_branch = match (&t_side, &e_side) {
12090 (Side::Call { defs: td, .. }, Side::Call { defs: ed, .. }) => {
12091 td.iter().any(|(d, _)| *d == v) || ed.iter().any(|(d, _)| *d == v)
12092 }
12093 (Side::Call { defs, .. }, Side::Clean)
12094 | (Side::Clean, Side::Call { defs, .. }) => {
12095 defs.iter().any(|(d, _)| *d == v)
12096 }
12097 _ => false,
12098 };
12099 if pmap.contains_key(&v) && in_branch {
12100 return false;
12101 }
12102 }
12103 type Counts = jdc_core::FxHashMap<u32, usize>;
12104 type GuardCounts = (
12105 Counts,
12106 Vec<Counts>,
12107 Vec<Counts>,
12108 Counts,
12109 Counts,
12110 Counts,
12111 Vec<(u32, Counts)>,
12112 );
12113 let (cond_counts, a1_counts, a2_counts, tail1_counts, tail2_counts, post_counts, def_counts): GuardCounts =
12114 if inlined.is_empty() {
12115 Default::default()
12116 } else {
12117 let all_defs: Vec<(u32, &Expr)> = pmap
12118 .iter()
12119 .map(|(k, v)| (*k, v))
12120 .chain(match &t_side {
12121 Side::Call { defs, .. } => defs.iter().map(|(k, v)| (*k, v)).collect(),
12122 Side::Clean => Vec::new(),
12123 })
12124 .chain(match &e_side {
12125 Side::Call { defs, .. } => defs.iter().map(|(k, v)| (*k, v)).collect(),
12126 Side::Clean => Vec::new(),
12127 })
12128 .collect();
12129 (
12130 count_locals_expr(&cond),
12131 primary_args.iter().map(count_locals_expr).collect(),
12132 else_args.iter().map(count_locals_expr).collect(),
12133 count_locals_stmts(&tail_then_v),
12134 count_locals_stmts(&tail_else_v),
12135 count_locals_stmts(post),
12136 all_defs.into_iter().map(|(v, e)| (v, count_locals_expr(e))).collect(),
12137 )
12138 };
12139 for &v in &inlined {
12140 let in_t = matches!(&t_side, Side::Call { defs, .. } if defs.iter().any(|(d, _)| *d == v));
12141 let in_e = matches!(&e_side, Side::Call { defs, .. } if defs.iter().any(|(d, _)| *d == v));
12142 let effectful = [
12143 in_t.then(|| tmap.get(&v)).flatten(),
12144 in_e.then(|| emap.get(&v)).flatten(),
12145 pmap.get(&v),
12146 ]
12147 .into_iter()
12148 .flatten()
12149 .any(jdc_core::ir::build::has_side_effects);
12150 if !effectful {
12151 continue;
12152 }
12153 let u_cond = cnt(&cond_counts, v);
12154 let u_post = cnt(&post_counts, v);
12155 let u_t1 = cnt(&tail1_counts, v);
12156 let u_t2 = cnt(&tail2_counts, v);
12157 let mut u_odef = 0usize;
12158 for (dv, dc) in &def_counts {
12159 if *dv != v && inlined.contains(dv) {
12160 u_odef += cnt(dc, v);
12161 }
12162 }
12163 let mut emit = u_cond * (1 + differ);
12164 let mut uses_args = 0usize;
12165 for j in 0..primary_args.len() {
12166 let c1 = cnt(&a1_counts[j], v);
12167 let c2 = if two_sided && j < a2_counts.len() {
12168 cnt(&a2_counts[j], v)
12169 } else {
12170 0
12171 };
12172 uses_args += c1 + c2;
12173 emit += if two_sided && then_args[j] != else_args[j] {
12174 c1 + c2
12175 } else {
12176 c1.max(c2)
12177 };
12178 }
12179 let ok = if in_t || in_e {
12180 u_cond == 0
12181 && u_post == 0
12182 && u_t1 == 0
12183 && u_t2 == 0
12184 && u_odef == 0
12185 && a1_counts.iter().map(|c| cnt(c, v)).sum::<usize>() <= 1
12186 && a2_counts.iter().map(|c| cnt(c, v)).sum::<usize>() <= 1
12187 && emit <= 2
12188 && (in_t || a1_counts.iter().all(|c| cnt(c, v) == 0))
12189 && (in_e || a2_counts.iter().all(|c| cnt(c, v) == 0))
12190 } else {
12191 u_post == 0
12192 && u_t1 == 0
12193 && u_t2 == 0
12194 && u_odef == 0
12195 && ((u_cond == 1 && uses_args == 0 && emit <= 2)
12196 || (u_cond == 0 && emit <= 1))
12197 };
12198 if !ok {
12199 return false;
12200 }
12201 }
12202
12203 let mut call_stmt = call_list[call_at].clone();
12205 if let Stmt::ExprStmt(Expr::Method { args: slot, .. }) = &mut call_stmt {
12206 *slot = merged;
12207 }
12208 let residual = |v: u32| -> usize {
12209 cnt(&tail1_counts, v) + cnt(&tail2_counts, v) + cnt(&post_counts, v)
12210 };
12211 let mut kept_prelude: Vec<Stmt> = Vec::new();
12212 for (i, s) in stmts[..if_pos].iter().enumerate() {
12213 let consumed = match s {
12214 Stmt::LocalDef { var, .. } => Some(*var),
12215 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
12216 Expr::Local { var, .. } => Some(*var),
12217 _ => None,
12218 },
12219 Stmt::If { .. } => cond_def_of(s, stmts).map(|(v, _)| v),
12220 _ => None,
12221 };
12222 if let Some(v) = consumed {
12223 if inlined.contains(&v) && pidx.get(&v) == Some(&i) && residual(v) == 0 {
12224 continue;
12225 }
12226 }
12227 kept_prelude.push(s.clone());
12228 }
12229 let build_keep = |list: &[&Stmt], side: &Side| -> Vec<Stmt> {
12230 match side {
12231 Side::Clean => list.iter().map(|s| (*s).clone()).collect(),
12232 Side::Call { at, .. } => {
12233 let mut out = Vec::new();
12234 for (i, s) in list.iter().enumerate() {
12235 if i == *at {
12236 continue;
12237 }
12238 if i < *at {
12239 if let Some((v, _)) = def_of(s) {
12240 if inlined.contains(&v) && residual(v) == 0 {
12241 continue;
12242 }
12243 }
12244 }
12245 out.push((*s).clone());
12246 }
12247 out
12248 }
12249 }
12250 };
12251 let then_keep = build_keep(&then_list, &t_side);
12252 let else_keep = build_keep(&else_list, &e_side);
12253 let else_part = if else_keep.is_empty() && !had_else {
12254 None
12255 } else {
12256 Some(Box::new(Stmt::Block(else_keep)))
12257 };
12258 let new_if = Stmt::If {
12259 cond: cond_i,
12260 then_stmt: Box::new(Stmt::Block(then_keep)),
12261 else_stmt: else_part,
12262 };
12263 let if_empty = match &new_if {
12264 Stmt::If { then_stmt, else_stmt, .. } => {
12265 let be = |s: &Stmt| matches!(s, Stmt::Block(v) if v.is_empty());
12266 be(then_stmt) && else_stmt.as_ref().is_none_or(|e| be(e))
12267 }
12268 _ => false,
12269 };
12270 let mut out: Vec<Stmt> = Vec::with_capacity(stmts.len());
12271 out.push(call_stmt);
12272 out.extend(kept_prelude);
12273 if !if_empty {
12274 out.push(new_if);
12275 }
12276 out.extend(stmts[if_pos + 1..].to_vec());
12277 *stmts = out;
12278 true
12279}
12280
12281
12282pub fn strip_enum_ctor_super(body: &mut Stmt) {
12283 fn is_enum_super(s: &Stmt) -> bool {
12284 matches!(
12285 s,
12286 Stmt::ExprStmt(Expr::Method { name, is_super: true, .. }) if &**name == "<init>"
12287 )
12288 }
12289 fn strip_deep(stmts: &mut Vec<Stmt>) {
12299 stmts.retain(|s| !is_enum_super(s));
12300 for st in stmts.iter_mut() {
12301 strip_one(st);
12302 }
12303 }
12304 fn strip_one(st: &mut Stmt) {
12305 match st {
12306 Stmt::Block(v) => strip_deep(v),
12307 Stmt::If { then_stmt, else_stmt, .. } => {
12308 strip_one(then_stmt);
12309 if let Some(e) = else_stmt {
12310 strip_one(e);
12311 }
12312 }
12313 Stmt::While { body, .. }
12314 | Stmt::DoWhile { body, .. }
12315 | Stmt::ForEach { body, .. }
12316 | Stmt::Synchronized { body, .. }
12317 | Stmt::Labeled { body, .. } => strip_one(body),
12318 Stmt::For { init, body, .. } => {
12319 strip_deep(init);
12320 strip_one(body);
12321 }
12322 Stmt::Switch { cases, default, .. } => {
12323 for c in cases.iter_mut() {
12324 strip_deep(&mut c.body);
12325 }
12326 if let Some(d) = default {
12327 strip_one(d);
12328 }
12329 }
12330 Stmt::Try { body, catches, finally }
12331 | Stmt::TryWithResources { body, catches, finally, .. } => {
12332 strip_one(body);
12333 for c in catches.iter_mut() {
12334 strip_one(&mut c.body);
12335 }
12336 if let Some(f) = finally {
12337 strip_one(f);
12338 }
12339 }
12340 _ => {}
12341 }
12342 }
12343 let Stmt::Block(stmts) = body else {
12344 return;
12345 };
12346 strip_deep(stmts);
12347}
12348
12349pub fn ensure_declared(body: &mut Stmt, vt: &VarTable) {
12350 let n = vt.vars.len().max(1);
12354
12355 let mut seen: HashSet<u32> = HashSet::default();
12358 demote_dup_decls(body, &mut seen, vt);
12359
12360 let mut def_block: Vec<u32> = vec![u32::MAX; n];
12368 let mut sizes: Vec<u32> = vec![0];
12369 let mut next_id: u32 = 1;
12370 scope_pass_a(body, 0, &mut next_id, &mut def_block, &mut sizes);
12371 sizes[0] = next_id;
12372 let mut within: Vec<u32> = vec![0; n];
12373 let mut total: Vec<u32> = vec![0; n];
12374 let mut next_b: u32 = 1;
12375 scope_pass_b(body, 0, &mut next_b, &def_block, &sizes, &mut within, &mut total);
12376 let mut hoist: HashSet<u32> = HashSet::default();
12377 for v in 0..n {
12378 if def_block[v] != u32::MAX && def_block[v] != 0 && within[v] < total[v] {
12379 hoist.insert(v as u32);
12380 }
12381 }
12382 if !hoist.is_empty() {
12383 let mut seed: HashSet<u32> = hoist.iter().copied().collect();
12384 demote_dup_decls(body, &mut seed, vt);
12385 }
12386
12387 let mut declared: Vec<bool> = vec![false; n];
12388 let mut is_param: Vec<bool> = vec![false; n];
12389 for v in &vt.vars {
12390 if v.is_param && (v.id as usize) < n {
12391 is_param[v.id as usize] = true;
12392 }
12393 }
12394 let mut is_catch: Vec<bool> = vec![false; n];
12400 walk_all(body, &mut |st| {
12401 if let Stmt::Try { catches, .. } = st {
12402 for c in catches {
12403 if (c.var as usize) < n {
12404 is_catch[c.var as usize] = true;
12405 }
12406 }
12407 }
12408 });
12409 walk_all(body, &mut |st| {
12410 if let Stmt::LocalDef { var, .. } = st {
12411 if (*var as usize) < n {
12412 declared[*var as usize] = true;
12413 }
12414 }
12415 });
12416 let mut used: HashSet<u32> = HashSet::default();
12417 stmt_collect_vars(body, &mut used, true);
12421
12422 let mut needs_decl: Vec<u32> = used
12425 .into_iter()
12426 .filter(|v| {
12427 let i = *v as usize;
12428 i >= n
12429 || ((!declared[i] && !is_param[i] && !is_catch[i]) || hoist.contains(v))
12430 })
12431 .collect();
12432 needs_decl.sort_unstable();
12433 needs_decl.dedup();
12434
12435 if !needs_decl.is_empty() {
12436 let mut decls: Vec<Stmt> = needs_decl
12437 .into_iter()
12438 .map(|v| Stmt::LocalDef {
12439 var: v,
12440 init: None,
12441 is_final: false,
12442 force_type: true,
12443 })
12444 .collect();
12445 decls.reverse();
12446 match body {
12447 Stmt::Block(v) => {
12448 for d in decls {
12449 v.insert(0, d);
12450 }
12451 }
12452 other => {
12453 let inner = std::mem::replace(other, Stmt::Block(vec![]));
12454 *other = Stmt::Block({
12455 let mut vs = decls;
12456 vs.push(inner);
12457 vs
12458 });
12459 }
12460 }
12461 }
12462}
12463
12464fn demote_dup_decls(s: &mut Stmt, seen: &mut HashSet<u32>, vt: &VarTable) {
12465 match s {
12466 Stmt::Block(v) => {
12469 for x in v.iter_mut() {
12470 demote_dup_decls(x, seen, vt);
12471 }
12472 }
12473 Stmt::LocalDef { var, init, .. } => {
12474 if seen.contains(var) {
12475 let ty = vt.var(*var).ty.clone();
12476 if let Some(e) = init.take() {
12477 *s = Stmt::ExprStmt(Expr::Assign {
12478 target: Box::new(Expr::Local { var: *var, ty }),
12479 op: AssignOp::Plain,
12480 value: Box::new(e),
12481 });
12482 } else {
12483 *s = Stmt::Block(vec![]);
12484 }
12485 } else {
12486 seen.insert(*var);
12487 }
12488 }
12489 other => {
12490 walk_mut(other, &mut |x| demote_dup_decls(x, seen, vt));
12491 }
12492 }
12493}
12494
12495#[allow(dead_code)]
12496fn unused(_: &Vec<CaseGroup>, _: &Catch) {}
12497
12498pub fn apply_local_names(vt: &mut VarTable, body: &Stmt) {
12512 use std::collections::{HashMap, HashSet};
12513
12514 let mut by_call: HashMap<u32, Vec<String>> = HashMap::new();
12517 let mut by_intrinsics: HashMap<u32, String> = HashMap::new();
12518
12519 visit_all_exprs(body, &mut |e| {
12520 if let Expr::Method { cls, name, args, .. } = e {
12521 if (cls.as_ref() == "kotlin/jvm/internal/Intrinsics"
12522 || cls.as_ref() == "kotlin/jvm/internal/IntrinsicsKt")
12523 && matches!(name.as_ref(), "checkNotNullParameter" | "checkParameterIsNotNull")
12524 && args.len() == 2
12525 {
12526 if let (Expr::Local { var, .. }, Expr::Const(ConstVal::Str(s))) = (&args[0], &args[1])
12527 {
12528 by_intrinsics
12529 .entry(*var)
12530 .or_insert_with(|| sanitize_name(s).unwrap_or_default());
12531 }
12532 }
12533 }
12534 });
12535
12536 walk_all(body, &mut |st| {
12537 let (var, value) = match st {
12538 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e),
12539 Stmt::ExprStmt(Expr::Assign { target, value, .. })
12540 if matches!(&**target, Expr::Local { .. }) =>
12541 {
12542 let Expr::Local { var, .. } = &**target else { return };
12543 (*var, value.as_ref())
12544 }
12545 _ => return,
12546 };
12547 if let Some(n) = defining_call_name(value) {
12548 by_call.entry(var).or_default().push(n);
12549 }
12550 });
12551
12552 let sanit = |n: &str| crate::classdec::java_ident(n).into_owned();
12563 let mut taken: HashSet<String> = HashSet::default();
12564 for v in vt.vars.iter() {
12569 if v.synthetic_name {
12574 taken.insert(sanit(&v.name));
12575 }
12576 }
12577 for v in vt.vars.iter_mut() {
12578 if v.synthetic_name {
12579 continue;
12580 }
12581 if !taken.insert(sanit(&v.name)) {
12582 let base = sanit(&v.name);
12583 for i in 2.. {
12584 let cand = format!("{base}{i}");
12585 if taken.insert(cand.clone()) {
12586 v.name = cand;
12587 break;
12588 }
12589 }
12590 }
12591 }
12592 let claim = |taken: &mut HashSet<String>, want: &str| -> String {
12593 let w = crate::classdec::java_ident(want).into_owned();
12594 if taken.insert(w.clone()) {
12595 return w;
12596 }
12597 for i in 2.. {
12598 let cand = format!("{w}{i}");
12599 if taken.insert(cand.clone()) {
12600 return cand;
12601 }
12602 }
12603 w
12604 };
12605
12606 for info in vt.vars.iter_mut() {
12607 if !info.synthetic_name {
12608 continue;
12609 }
12610 if let Some(n) = by_intrinsics.get(&info.id) {
12613 if !n.is_empty() {
12614 info.name = claim(&mut taken, n);
12615 continue;
12616 }
12617 }
12618 if let Some(names) = by_call.get(&info.id) {
12620 let unique: HashSet<&String> = names.iter().collect();
12621 if unique.len() == 1 {
12622 let n = unique.into_iter().next().unwrap();
12623 if !is_java_keyword(n) {
12624 info.name = claim(&mut taken, n);
12625 continue;
12626 }
12627 }
12628 }
12629 let want = type_alias(&info.ty).map(str::to_string).or_else(|| simple_type_name(&info.ty));
12632 if let Some(n) = want {
12633 info.name = claim(&mut taken, &n);
12634 }
12635 }
12636
12637 let mut seen: HashSet<String> = HashSet::default();
12642 for v in vt.vars.iter_mut() {
12643 if !seen.insert(sanit(&v.name)) {
12644 let base = sanit(&v.name);
12645 for i in 2.. {
12646 let cand = format!("{base}{i}");
12647 if seen.insert(cand.clone()) {
12648 v.name = cand;
12649 break;
12650 }
12651 }
12652 }
12653 }
12654}
12655
12656fn simple_type_name(ty: &TypeRef) -> Option<String> {
12658 let TypeRef::J(JavaType::Object(n)) = ty else {
12659 return None;
12660 };
12661 let simple = n.rsplit('/').next().unwrap_or(n);
12662 let simple = simple.rsplit('$').next().unwrap_or(simple);
12663 let mut chars = simple.chars();
12666 let mut out = String::with_capacity(simple.len());
12667 let mut lowered = false;
12668 for c in chars.by_ref() {
12669 if c.is_ascii_alphabetic() && !lowered {
12670 out.push(c.to_ascii_lowercase());
12671 lowered = true;
12672 } else {
12673 out.push(c);
12674 }
12675 if lowered {
12676 break;
12677 }
12678 }
12679 out.extend(chars);
12680 sanitize_name(&out)
12681}
12682
12683fn defining_call_name(e: &Expr) -> Option<String> {
12687 match e {
12688 Expr::Method { name, .. } => {
12689 let base = name
12690 .strip_prefix("get")
12691 .or_else(|| name.strip_prefix("is"))
12692 .filter(|rest| rest.chars().next().is_some_and(|c| c.is_ascii_uppercase()))
12693 .unwrap_or(name);
12694 let first = base.chars().next()?;
12695 let mut out = String::with_capacity(base.len());
12696 out.push(first.to_ascii_lowercase());
12697 out.extend(base.chars().skip(1));
12698 sanitize_name(&out)
12699 }
12700 Expr::New { cls, .. } => {
12701 let simple = cls.rsplit('/').next().unwrap_or(cls);
12702 let simple = simple.rsplit('$').next().unwrap_or(simple);
12703 let first = simple.chars().next()?;
12704 let mut out = String::with_capacity(simple.len());
12705 out.push(first.to_ascii_lowercase());
12706 out.extend(simple.chars().skip(1));
12707 sanitize_name(&out)
12708 }
12709 _ => None,
12710 }
12711}
12712
12713fn sanitize_name(s: &str) -> Option<String> {
12715 let mut out = String::with_capacity(s.len());
12716 for c in s.chars() {
12717 if c.is_ascii_alphanumeric() || c == '_' || c == '$' {
12718 out.push(c);
12719 } else {
12720 out.push('_');
12721 }
12722 }
12723 if out.len() < 2
12724 || out.chars().next().is_some_and(|c| c.is_ascii_digit())
12725 || is_java_keyword(&out)
12726 {
12727 return None;
12728 }
12729 Some(out)
12730}
12731
12732fn type_alias(ty: &TypeRef) -> Option<&'static str> {
12735 let TypeRef::J(JavaType::Object(n)) = ty else {
12736 return None;
12737 };
12738 Some(match n.as_ref() {
12739 "java/lang/String" | "kotlin/String" => "str",
12740 "java/lang/Class" => "cls",
12741 "java/lang/Throwable" => "th",
12742 "java/lang/Object" | "kotlin/Any" => "obj",
12743 "java/util/Iterator" | "kotlin/collections/Iterator" => "it",
12744 "java/lang/Boolean" => "bool",
12745 "java/lang/Integer" => "num",
12746 "java/lang/Character" => "ch",
12747 "java/lang/Byte" => "b",
12748 "java/lang/Short" => "sh",
12749 "java/lang/Float" => "f",
12750 "java/lang/Double" => "d",
12751 "java/lang/Long" => "i",
12752 "java/lang/StringBuilder" => "sb",
12753 "java/util/ArrayList" => "list",
12754 "java/util/HashMap" => "map",
12755 "android/content/Context" => "context",
12756 "android/content/Intent" => "intent",
12757 "android/os/Bundle" => "bundle",
12758 "android/view/View" => "view",
12759 "android/graphics/Bitmap" => "bitmap",
12760 "android/view/ViewGroup" => "viewGroup",
12761 _ => return None,
12762 })
12763}
12764
12765pub(crate) fn visit_all_exprs<F: FnMut(&Expr)>(s: &Stmt, f: &mut F) {
12767 walk_all(s, &mut |st| {
12768 let exprs: Vec<&Expr> = match st {
12769 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
12770 let mut v: Vec<&Expr> = vec![value];
12771 if !matches!(&**target, Expr::Local { .. }) {
12772 v.push(target);
12773 }
12774 v
12775 }
12776 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
12777 vec![e]
12778 }
12779 Stmt::Return(Some(e)) => vec![e],
12780 Stmt::LocalDef { init: Some(e), .. } => vec![e],
12781 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
12782 vec![cond]
12783 }
12784 _ => Vec::new(),
12785 };
12786 for e in exprs {
12787 visit_exprs(e, f);
12788 }
12789 });
12790}
12791
12792pub fn platform_constants(body: &mut Stmt) {
12797 if !crate::platform::installed() {
12798 return;
12799 }
12800 rewrite_exprs(body, &mut |e| {
12801 if let Expr::Method { cls, name, desc, args, .. } = e {
12802 for a in args.iter_mut() {
12803 let (value, is_long) = match a {
12804 Expr::Const(ConstVal::Int(v)) => (*v as i64, false),
12805 Expr::Const(ConstVal::Long(v)) => (*v, true),
12806 _ => continue,
12807 };
12808 if let Some(named) =
12809 crate::platform::named_constant(cls, name, &desc_to_string(desc), 0, value)
12810 {
12811 let _ = is_long;
12812 *a = Expr::Raw(named);
12813 }
12814 }
12815 }
12816 });
12817}
12818
12819fn desc_to_string(d: &jdc_core::types::MethodDescriptor) -> String {
12821 let mut out = String::from("(");
12822 for a in &d.args {
12823 type_to_desc(a, &mut out);
12824 }
12825 out.push(')');
12826 type_to_desc(&d.ret, &mut out);
12827 out
12828}
12829
12830fn type_to_desc(t: &jdc_core::types::JavaType, out: &mut String) {
12831 match t {
12832 JavaType::Void => out.push('V'),
12833 JavaType::Boolean => out.push('Z'),
12834 JavaType::Byte => out.push('B'),
12835 JavaType::Char => out.push('C'),
12836 JavaType::Short => out.push('S'),
12837 JavaType::Int => out.push('I'),
12838 JavaType::Float => out.push('F'),
12839 JavaType::Long => out.push('J'),
12840 JavaType::Double => out.push('D'),
12841 JavaType::Object(n) => {
12842 out.push('L');
12843 out.push_str(n);
12844 out.push(';');
12845 }
12846 JavaType::Array(inner) => {
12847 out.push('[');
12848 type_to_desc(inner, out);
12849 }
12850 }
12851}
12852
12853pub fn remove_kotlin_checks(s: &mut Stmt) {
12859 walk_mut_deep(s, &mut |st| {
12860 let Stmt::Block(v) = st else { return };
12861 v.retain(|x| !is_kotlin_check(x));
12862 });
12863}
12864
12865fn is_kotlin_check(s: &Stmt) -> bool {
12866 let Stmt::ExprStmt(e) = s else { return false };
12867 let Expr::Method { cls, name, .. } = e else { return false };
12868 (cls.as_ref() == "kotlin/jvm/internal/Intrinsics" || cls.as_ref() == "kotlin/jvm/internal/IntrinsicsKt__Jdk7Kt")
12869 && matches!(
12870 name.as_ref(),
12871 "checkNotNullParameter"
12872 | "checkParameterIsNotNull"
12873 | "checkExpressionValueIsNotNull"
12874 | "checkNotNullExpressionValue"
12875 | "checkReturnedValueIsNotNull"
12876 | "checkFieldIsNotNull"
12877 | "checkNotNull"
12878 )
12879}
12880
12881fn inline_ref_ok(pool: &DexPool, host: &str, target: &str, member: Option<(&str, bool)>) -> bool {
12901 let hpkg = match host.rfind('/') {
12902 Some(i) => &host[..i],
12903 None => "",
12904 };
12905 let mut cur = target;
12906 loop {
12907 let tpkg = match cur.rfind('/') {
12908 Some(i) => &cur[..i],
12909 None => "",
12910 };
12911 if tpkg != hpkg {
12912 if let Some(pc) = pool.get(cur) {
12913 if pc.access & crate::access::ACC_PUBLIC == 0 {
12914 return false;
12915 }
12916 if cur == target {
12917 if let Some((mname, is_method)) = member {
12918 let ok = if is_method {
12919 pc.all_methods().any(|m| {
12920 &*m.name == mname
12921 && m.access & crate::access::ACC_PUBLIC != 0
12922 })
12923 } else {
12924 pc.static_fields
12925 .iter()
12926 .chain(pc.instance_fields.iter())
12927 .any(|f| {
12928 f.name == mname
12929 && f.access & crate::access::ACC_PUBLIC != 0
12930 })
12931 };
12932 if !ok
12937 && (if is_method {
12938 pc.all_methods().any(|m| &*m.name == mname)
12939 } else {
12940 pc.static_fields
12941 .iter()
12942 .chain(pc.instance_fields.iter())
12943 .any(|f| f.name == mname)
12944 })
12945 {
12946 return false;
12947 }
12948 }
12949 }
12950 }
12951 }
12952 match cur.rfind('$') {
12953 Some(i) if i > 0 => cur = &cur[..i],
12954 _ => break,
12955 }
12956 }
12957 true
12958}
12959
12960pub fn inline_accessors(s: &mut Stmt, pool: &DexPool, host: &str) {
12961 rewrite_exprs(s, &mut |e| {
12962 let Expr::Method { cls, name, desc, args, is_static, .. } = e else { return };
12963 if !*is_static || args.is_empty() {
12964 return;
12965 }
12966 let Some(target) = pool.get(cls) else { return };
12967 let desc_s = desc_to_string(desc);
12968 let Some(m) = target.find_method(name, &desc_s) else { return };
12969 if !(m.is_static() && m.access & access::ACC_SYNTHETIC != 0) {
12970 return;
12971 }
12972 let Some(dex) = pool.dex(m.dex_idx) else { return };
12973 let code = match pool.accessor_code(m.dex_idx, m.code_off) {
12978 Some(bytes) => ddc_dex::CodeItem::parse(&bytes, 0),
12979 None => dex.code_at(m.code_off),
12980 };
12981 let Some(code) = code else { return };
12982 let insns: Vec<&Insn> =
12983 code.insns.iter().filter(|i| !matches!(i.kind, InsnKind::Nop)).collect();
12984 let core: Vec<&Insn> = insns
12986 .iter()
12987 .copied()
12988 .filter(|i| !is_const_only_trace(&i.kind, &insns))
12989 .collect();
12990 match accessor_shape(&dex, &core, &code) {
12991 Some(Shape::Identity(arg_i)) => {
12992 if let Some(a) = args.get(arg_i) {
12993 *e = a.clone();
12994 }
12995 }
12996 Some(Shape::FieldRead { arg, cls: field_cls, field, field_ty }) => {
12997 if !inline_ref_ok(pool, host, &field_cls, Some((&field, false))) {
12998 return;
12999 }
13000 if let Some(owner) = args.get(arg).cloned().map(Box::new) {
13001 let ty = TypeRef::J(desc_type(&field_ty));
13002 let name = jdc_core::rename::field_display(
13008 &field_cls,
13009 &field,
13010 &field_ty,
13011 )
13012 .map(std::sync::Arc::from)
13013 .unwrap_or_else(|| field.into());
13014 *e = Expr::Field {
13015 owner: Some(owner),
13016 cls: field_cls.into(),
13017 name,
13018 ty,
13019 is_static: false,
13020 };
13021 }
13022 }
13023 Some(Shape::Forward { cls: tcls, name: tname, desc: tdesc, instance }) => {
13024 if !inline_ref_ok(pool, host, &tcls, Some((&tname, true))) {
13025 return;
13026 }
13027 let disp = jdc_core::rename::field_display(
13031 &tcls,
13032 &tname,
13033 &tdesc.to_string(),
13034 )
13035 .map(std::sync::Arc::from)
13036 .unwrap_or_else(|| tname.into());
13037 *cls = tcls.into();
13038 *name = disp;
13039 *desc = std::sync::Arc::new(tdesc);
13040 if instance && !args.is_empty() {
13041 let recv = args.remove(0);
13043 if let Expr::Method { owner, .. } = e {
13044 *owner = Some(Box::new(recv));
13045 }
13046 }
13047 }
13048 None => {}
13049 }
13050 });
13051}
13052
13053enum Shape {
13055 Identity(usize),
13057 FieldRead { arg: usize, cls: String, field: String, field_ty: String },
13059 Forward { cls: String, name: String, desc: MethodDescriptor, instance: bool },
13062}
13063
13064fn field_of(dex: &ddc_dex::DexFile, field_idx: u32) -> Option<(String, String, String)> {
13066 let f = dex.field(field_idx);
13067 Some((
13068 dex.class_name(f.class_idx),
13071 dex.string(f.name_idx).to_string(),
13072 dex.type_name(f.type_idx).to_string(),
13074 ))
13075}
13076
13077fn param_index(code: &ddc_dex::CodeItem, reg: u16) -> Option<usize> {
13080 let rs = code.registers_size as usize;
13081 let ins = code.ins_size as usize;
13082 let r = reg as usize;
13083 let first = rs.checked_sub(ins)?;
13084 (r >= first && r < rs).then(|| r - first)
13085}
13086
13087fn accessor_shape(
13088 dex: &ddc_dex::DexFile,
13089 core: &[&Insn],
13090 code: &ddc_dex::CodeItem,
13091) -> Option<Shape> {
13092 let reads = |rest: &[&Insn], r: u16| {
13102 rest.iter()
13103 .any(|i| crate::lift::src_regs(&i.kind).contains(&r))
13104 };
13105 let core: &[&Insn] = match core.split_first() {
13106 Some((first, rest))
13107 if matches!(first.kind, InsnKind::Const { dst, .. } if !reads(rest, dst)) =>
13108 {
13109 rest
13110 }
13111 _ => core,
13112 };
13113 let core: &[&Insn] = match core.split_last() {
13114 Some((last, rest))
13115 if matches!(last.kind, InsnKind::Const { dst, .. } if !reads(rest, dst)) =>
13116 {
13117 rest
13118 }
13119 _ => core,
13120 };
13121 match core {
13122 [Insn { kind: InsnKind::Return { src }, .. }] => {
13124 let idx = param_index(code, *src)?;
13125 Some(Shape::Identity(idx))
13126 }
13127 [
13129 Insn { kind: InsnKind::IGet { dst, obj, field_idx }, .. },
13130 Insn { kind: InsnKind::Return { src }, .. },
13131 ] if dst == src => {
13132 let arg = param_index(code, *obj)?;
13133 let (cls, field, field_ty) = field_of(dex, *field_idx)?;
13134 Some(Shape::FieldRead { arg, cls, field, field_ty })
13135 }
13136 [Insn { kind: InsnKind::Invoke { method_idx, regs, kind, .. }, .. }, rest @ ..] if rest.len() <= 2 => {
13138 if !regs.iter().all(|r| param_index(code, *r).is_some()) || regs.is_empty() {
13140 return None;
13141 }
13142 if rest.len() == 2 {
13145 let ok = matches!(rest[0].kind, InsnKind::MoveResult { .. })
13146 && matches!(rest[1].kind, InsnKind::Return { .. } | InsnKind::ReturnVoid);
13147 if !ok {
13148 return None;
13149 }
13150 }
13151 let mid = dex.method(*method_idx);
13152 let cls = dex.class_name(mid.class_idx);
13155 let name = dex.string(mid.name_idx).to_string();
13156 let mut s = String::from("(");
13157 for t in dex.proto_params(mid.proto_idx) {
13158 s.push_str(dex.type_name(*t));
13159 }
13160 s.push(')');
13161 s.push_str(dex.type_name(dex.proto(mid.proto_idx).return_type_idx));
13162 let desc = parse_method_descriptor(&s)?;
13163 Some(Shape::Forward { cls, name, desc, instance: !matches!(kind, InvokeKind::Static) })
13164 }
13165 _ => None,
13166 }
13167}
13168
13169
13170fn is_const_only_trace(kind: &InsnKind, insns: &[&Insn]) -> bool {
13176 let InsnKind::Invoke { kind, regs, .. } = kind else { return false };
13177 if !matches!(kind, InvokeKind::Static) || regs.is_empty() {
13178 return false;
13179 }
13180 regs.iter().all(|r| const_loaded(*r, insns, 0))
13181}
13182
13183fn const_loaded(r: u16, insns: &[&Insn], depth: u8) -> bool {
13186 if depth > 3 {
13187 return false;
13188 }
13189 insns.iter().any(|i| match &i.kind {
13190 InsnKind::Const { dst, .. } => *dst == r,
13191 InsnKind::Move { dst, src } => *dst == r && const_loaded(*src, insns, depth + 1),
13192 _ => false,
13193 })
13194}