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 std::collections::HashMap<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: &std::collections::HashMap<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 strip_empty(s);
1142 merge_singleton_stmts(s);
1143}
1144
1145fn strip_empty(s: &mut Stmt) {
1146 match s {
1147 Stmt::Block(v) => {
1148 v.retain(|x| !x.is_empty_block());
1149 for x in v.iter_mut() {
1150 strip_empty(x);
1151 }
1152 }
1153 Stmt::If {
1154 then_stmt,
1155 else_stmt,
1156 ..
1157 } => {
1158 strip_empty(then_stmt);
1159 if let Some(e) = else_stmt {
1160 strip_empty(e);
1161 if e.is_empty_block() {
1162 *else_stmt = None;
1163 }
1164 }
1165 }
1166 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => strip_empty(body),
1167 Stmt::For { init, body, .. } => {
1168 init.retain(|x| !x.is_empty_block());
1169 strip_empty(body);
1170 }
1171 Stmt::ForEach { body, .. } => strip_empty(body),
1172 Stmt::Switch { cases, default, .. } => {
1173 for c in cases {
1174 c.body.retain(|x| !x.is_empty_block());
1175 }
1176 if let Some(d) = default {
1177 strip_empty(d);
1178 }
1179 }
1180 Stmt::Try {
1181 body,
1182 catches,
1183 finally,
1184 } => {
1185 strip_empty(body);
1186 for c in catches {
1187 strip_empty(&mut c.body);
1188 }
1189 if let Some(f) = finally {
1190 strip_empty(f);
1191 }
1192 }
1193 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => strip_empty(body),
1194 _ => {}
1195 }
1196}
1197
1198fn merge_singleton_stmts(s: &mut Stmt) {
1199 if let Stmt::Block(v) = s {
1200 for x in v.iter_mut() {
1201 merge_singleton_stmts(x);
1202 }
1203 }
1204}
1205
1206pub fn prune_unreachable(s: &mut Stmt) {
1208 if let Stmt::Block(v) = s {
1209 let mut cut: Option<usize> = None;
1210 for (i, x) in v.iter().enumerate() {
1211 if matches!(x, Stmt::Return(_) | Stmt::Throw(_)) {
1212 cut = Some(i + 1);
1213 break;
1214 }
1215 }
1216 if let Some(c) = cut {
1217 if c < v.len() {
1218 v.truncate(c);
1219 }
1220 }
1221 for x in v.iter_mut() {
1222 prune_unreachable(x);
1223 }
1224 } else {
1225 walk_mut(s, &mut prune_unreachable);
1226 }
1227}
1228
1229fn walk_mut<F: FnMut(&mut Stmt)>(s: &mut Stmt, f: &mut F) {
1230 match s {
1231 Stmt::Block(v) => {
1232 for x in v.iter_mut() {
1233 f(x);
1234 }
1235 }
1236 Stmt::If {
1237 then_stmt,
1238 else_stmt,
1239 ..
1240 } => {
1241 f(then_stmt);
1242 if let Some(e) = else_stmt {
1243 f(e);
1244 }
1245 }
1246 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => f(body),
1247 Stmt::For { init, body, .. } => {
1248 for x in init.iter_mut() {
1249 f(x);
1250 }
1251 f(body);
1252 }
1253 Stmt::ForEach { body, .. } => f(body),
1254 Stmt::Switch { cases, default, .. } => {
1255 for c in cases {
1256 for x in c.body.iter_mut() {
1257 f(x);
1258 }
1259 }
1260 if let Some(d) = default {
1261 f(d);
1262 }
1263 }
1264 Stmt::Try {
1265 body,
1266 catches,
1267 finally,
1268 } => {
1269 f(body);
1270 for c in catches {
1271 f(&mut c.body);
1272 }
1273 if let Some(fl) = finally {
1274 f(fl);
1275 }
1276 }
1277 Stmt::TryWithResources {
1278 resources,
1279 body,
1280 catches,
1281 finally,
1282 } => {
1283 for r in resources.iter_mut() {
1284 f(r);
1285 }
1286 f(body);
1287 for c in catches {
1288 f(&mut c.body);
1289 }
1290 if let Some(fl) = finally {
1291 f(fl);
1292 }
1293 }
1294 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => f(body),
1295 _ => {}
1296 }
1297}
1298
1299pub fn count_localdefs(s: &Stmt, out: &mut usize) {
1301 if matches!(s, Stmt::LocalDef { .. }) {
1302 *out += 1;
1303 }
1304 walk_all(s, &mut |x| {
1305 if matches!(x, Stmt::LocalDef { .. }) {
1306 *out += 1;
1307 }
1308 });
1309}
1310
1311pub fn strip_trailing_void_return(s: &mut Stmt) {
1313 if let Stmt::Block(v) = s {
1314 loop {
1315 match v.last_mut() {
1316 Some(Stmt::Return(None)) => {
1317 v.pop();
1318 }
1319 Some(Stmt::Block(_)) => {
1324 let last = v.last_mut().unwrap();
1325 let before = match last {
1326 Stmt::Block(b) => b.len(),
1327 _ => 0,
1328 };
1329 strip_trailing_void_return(last);
1330 let after = match last {
1331 Stmt::Block(b) => b.len(),
1332 _ => 0,
1333 };
1334 if after == before {
1335 break; }
1337 }
1338 _ => break,
1339 }
1340 }
1341 }
1342}
1343
1344pub fn invert_empty_thens(s: &mut Stmt) {
1349 walk_mut_deep(s, &mut |st| {
1350 if let Stmt::If {
1351 cond,
1352 then_stmt,
1353 else_stmt,
1354 } = st
1355 {
1356 let then_empty = matches!(&**then_stmt, Stmt::Block(v) if v.is_empty());
1357 if then_empty {
1358 if let Some(els) = else_stmt.take() {
1359 let c = std::mem::replace(cond, Expr::Const(ConstVal::Int(0)));
1360 *cond = jdc_core::convert::negate(c);
1361 *then_stmt = els;
1362 }
1363 }
1364 }
1365 });
1366}
1367
1368pub fn fold_short_circuits(s: &mut Stmt) {
1379 for _ in 0..8 {
1380 let mut changed = false;
1381 walk_mut_deep(s, &mut |st| {
1382 if try_fold_diamond(st) {
1383 changed = true;
1384 }
1385 });
1386 if !changed {
1387 break;
1388 }
1389 }
1390}
1391
1392fn try_fold_diamond(st: &mut Stmt) -> bool {
1393 enum Shape {
1394 ThenAnd,
1395 ThenAndNot,
1396 ElseOr,
1397 ElseOrNot,
1398 BareAnd,
1399 BareAndNot,
1400 }
1401 let shape = match &*st {
1405 Stmt::If {
1408 then_stmt,
1409 else_stmt: None,
1410 ..
1411 } => match &**then_stmt {
1412 Stmt::If {
1413 then_stmt: t2,
1414 else_stmt: None,
1415 ..
1416 } if !matches!(&**t2, Stmt::Block(v) if v.is_empty()) => Some(Shape::BareAnd),
1417 Stmt::If {
1418 then_stmt: t2,
1419 else_stmt: Some(_),
1420 ..
1421 } if matches!(&**t2, Stmt::Block(v) if v.is_empty()) => Some(Shape::BareAndNot),
1422 _ => None,
1423 },
1424 Stmt::If {
1425 then_stmt,
1426 else_stmt: Some(els),
1427 ..
1428 } => match (&**then_stmt, &**els) {
1429 (Stmt::If { else_stmt: Some(f2), .. }, _) if f2.as_ref() == els.as_ref() => {
1430 Some(Shape::ThenAnd)
1431 }
1432 (Stmt::If { then_stmt: t2, else_stmt: Some(_), .. }, _)
1433 if t2.as_ref() == els.as_ref() =>
1434 {
1435 Some(Shape::ThenAndNot)
1436 }
1437 (_, Stmt::If { then_stmt: t2, else_stmt: Some(_), .. })
1438 if t2.as_ref() == then_stmt.as_ref() =>
1439 {
1440 Some(Shape::ElseOr)
1441 }
1442 (_, Stmt::If { else_stmt: Some(f2), .. })
1443 if f2.as_ref() == then_stmt.as_ref() =>
1444 {
1445 Some(Shape::ElseOrNot)
1446 }
1447 _ => None,
1448 },
1449 _ => None,
1450 };
1451 let Some(shape) = shape else {
1452 return false;
1453 };
1454
1455 let taken = std::mem::replace(st, Stmt::Block(vec![]));
1456 if matches!(shape, Shape::BareAnd | Shape::BareAndNot) {
1458 let Stmt::If {
1459 cond: c1,
1460 then_stmt,
1461 else_stmt: None,
1462 } = taken
1463 else {
1464 unreachable!()
1465 };
1466 let Stmt::If {
1467 cond: c2,
1468 then_stmt: t2,
1469 else_stmt: f2,
1470 } = *then_stmt
1471 else {
1472 unreachable!()
1473 };
1474 let sc = |op: BinOp, l: Expr, r: Expr| Expr::Bin {
1475 op,
1476 l: Box::new(l),
1477 r: Box::new(r),
1478 ty: None,
1479 };
1480 match shape {
1481 Shape::BareAnd => {
1483 *st = Stmt::If {
1484 cond: sc(BinOp::LogAnd, c1, c2),
1485 then_stmt: t2,
1486 else_stmt: None,
1487 };
1488 }
1489 _ => {
1491 let body = f2.unwrap();
1492 *st = Stmt::If {
1493 cond: sc(
1494 BinOp::LogAnd,
1495 c1,
1496 Expr::Un {
1497 op: UnOp::Not,
1498 e: Box::new(c2),
1499 },
1500 ),
1501 then_stmt: body,
1502 else_stmt: None,
1503 };
1504 }
1505 }
1506 return true;
1507 }
1508 let Stmt::If {
1509 cond: c1,
1510 then_stmt,
1511 else_stmt: Some(els),
1512 } = taken
1513 else {
1514 unreachable!("shape probe guaranteed an If with else")
1515 };
1516 let sc = |op: BinOp, l: Expr, r: Expr| Expr::Bin {
1517 op,
1518 l: Box::new(l),
1519 r: Box::new(r),
1520 ty: None,
1521 };
1522 let not = |e: Expr| Expr::Un {
1523 op: UnOp::Not,
1524 e: Box::new(e),
1525 };
1526 match shape {
1527 Shape::BareAnd | Shape::BareAndNot => unreachable!("handled above"),
1528 Shape::ThenAnd | Shape::ThenAndNot => {
1529 let Stmt::If {
1530 cond: c2,
1531 then_stmt: t2,
1532 else_stmt: Some(f2),
1533 } = *then_stmt
1534 else {
1535 unreachable!()
1536 };
1537 let (nc, body, alt) = match shape {
1538 Shape::ThenAnd => (sc(BinOp::LogAnd, c1, c2), t2, f2),
1540 _ => (sc(BinOp::LogAnd, c1, not(c2)), f2, t2),
1542 };
1543 *st = Stmt::If {
1544 cond: nc,
1545 then_stmt: body,
1546 else_stmt: Some(alt),
1547 };
1548 }
1549 Shape::ElseOr | Shape::ElseOrNot => {
1550 let Stmt::If {
1551 cond: c2,
1552 then_stmt: t2,
1553 else_stmt: Some(f2),
1554 } = *els
1555 else {
1556 unreachable!()
1557 };
1558 let body = then_stmt;
1561 let (nc, alt) = match shape {
1562 Shape::ElseOr => (sc(BinOp::LogOr, c1, c2), f2),
1564 _ => (sc(BinOp::LogOr, c1, not(c2)), t2),
1566 };
1567 *st = Stmt::If {
1568 cond: nc,
1569 then_stmt: body,
1570 else_stmt: Some(alt),
1571 };
1572 }
1573 }
1574 true
1575}
1576
1577pub fn strip_clinit_returns(s: &mut Stmt) {
1583 walk_mut_deep(s, &mut |st| {
1584 if let Stmt::Block(v) = st {
1585 v.retain(|x| !matches!(x, Stmt::Return(None)));
1586 }
1587 });
1588}
1589
1590pub fn prepend_comment(s: &mut Stmt, text: String) {
1591 let stmt = Stmt::Comment(text);
1592 match s {
1593 Stmt::Block(v) => v.insert(0, stmt),
1594 other => {
1595 let inner = std::mem::replace(other, Stmt::Block(vec![]));
1596 *other = Stmt::Block(vec![stmt, inner]);
1597 }
1598 }
1599}
1600
1601fn is_obj_expr(e: &Expr, obj_var: &dyn Fn(u32) -> bool) -> bool {
1609 match e {
1610 Expr::Local { var, .. } => obj_var(*var),
1611 Expr::Const(_) => false,
1612 _ => e.type_ref().erased().is_reference(),
1613 }
1614}
1615
1616fn null_side_rewrite(x: &mut Expr, obj_var: &dyn Fn(u32) -> bool) {
1621 if let Expr::Bin { op, l, r, .. } = x {
1622 if !matches!(op, BinOp::Eq | BinOp::Ne) {
1623 return;
1624 }
1625 let l_zero = matches!(&**l, Expr::Const(ConstVal::Int(0)));
1626 let r_zero = matches!(&**r, Expr::Const(ConstVal::Int(0)));
1627 if l_zero && !r_zero && is_obj_expr(r, obj_var) {
1628 **l = Expr::Const(ConstVal::Null);
1629 } else if r_zero && !l_zero && is_obj_expr(l, obj_var) {
1630 **r = Expr::Const(ConstVal::Null);
1631 }
1632 }
1633}
1634
1635pub fn fused_expr_rewrites(s: &mut Stmt, vt: &VarTable) {
1636 let mut obj_vars: Vec<bool> = Vec::with_capacity(vt.vars.len());
1638 for v in &vt.vars {
1639 obj_vars.push(v.ty.erased().is_reference());
1640 }
1641 rewrite_exprs(s, &mut |e| {
1642 deep_rewrite(e, &mut |x| {
1643 if let Expr::Invokedynamic { name, args, .. } = x {
1645 if name.starts_with('\0') && args.len() == 2 {
1646 let (cls, ty) = match name.as_str() {
1647 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
1648 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
1649 _ => ("java/lang/Double", JavaType::Double),
1650 };
1651 let desc = MethodDescriptor {
1652 args: vec![ty.clone(), ty],
1653 ret: JavaType::Int,
1654 };
1655 *x = Expr::Method {
1656 owner: None,
1657 cls: cls.into(),
1658 name: "compare".into(),
1659 desc: std::sync::Arc::new(desc),
1660 args: args.clone(),
1661 is_static: true,
1662 is_interface: false,
1663 is_special: false,
1664 is_super: false,
1665 is_dynamic: false,
1666 type_args: vec![],
1667 };
1668 return;
1669 }
1670 }
1671 null_side_rewrite(x, &|v| obj_vars.get(v as usize).copied().unwrap_or(false));
1674 if let Expr::Bin { op, l, r, .. } = x {
1676 if matches!(
1677 op,
1678 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
1679 ) {
1680 let l_const = matches!(&**l, Expr::Const(_));
1681 let r_var = matches!(&**r, Expr::Local { .. });
1682 if l_const && r_var {
1683 std::mem::swap(l, r);
1684 }
1685 }
1686 }
1687 });
1688 });
1689 walk_mut_deep(s, &mut |st| {
1697 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = st {
1698 let tgt_is_obj = match &**target {
1699 Expr::Local { var, .. } => {
1700 obj_vars.get(*var as usize).copied().unwrap_or(false)
1701 }
1702 other => other.type_ref().erased().is_reference(),
1703 };
1704 if tgt_is_obj {
1705 if let Expr::Const(ConstVal::Int(0)) = &**value {
1706 **value = Expr::Const(ConstVal::Null);
1707 }
1708 }
1709 } else if let Stmt::LocalDef { var, init, .. } = st {
1710 if obj_vars.get(*var as usize).copied().unwrap_or(false) {
1711 if let Some(iv) = init {
1712 if let Expr::Const(ConstVal::Int(0)) = iv {
1713 *iv = Expr::Const(ConstVal::Null);
1714 }
1715 }
1716 }
1717 }
1718 });
1719}
1720
1721#[allow(dead_code)]
1724pub fn desugar_cmp_residuals(s: &mut Stmt) {
1725 rewrite_exprs(s, &mut |e| {
1726 deep_rewrite(e, &mut |x| {
1727 if let Expr::Invokedynamic { name, args, .. } = x {
1728 if name.starts_with('\0') && args.len() == 2 {
1729 let (cls, ty) = match name.as_str() {
1730 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
1731 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
1732 _ => ("java/lang/Double", JavaType::Double),
1733 };
1734 let desc = jdc_core::types::MethodDescriptor {
1735 args: vec![ty.clone(), ty],
1736 ret: JavaType::Int,
1737 };
1738 *x = Expr::Method {
1739 owner: None,
1740 cls: cls.into(),
1741 name: "compare".into(),
1742 desc: std::sync::Arc::new(desc),
1743 args: args.clone(),
1744 is_static: true,
1745 is_interface: false,
1746 is_special: false,
1747 is_super: false,
1748 is_dynamic: false,
1749 type_args: vec![],
1750 };
1751 }
1752 }
1753 });
1754 });
1755}
1756
1757pub fn ternary_fold(s: &mut Stmt) {
1759 let mut changed = true;
1760 let mut guard = 0;
1761 while changed && guard < 8 {
1762 changed = false;
1763 guard += 1;
1764 ternary_fold_walk(s, &mut changed);
1765 strip_empty(s);
1766 }
1767}
1768
1769fn ternary_fold_walk(s: &mut Stmt, changed: &mut bool) {
1770 if let Stmt::Block(v) = s {
1771 for x in v.iter_mut() {
1772 ternary_fold_walk(x, changed);
1773 }
1774 return;
1775 }
1776 let Stmt::If {
1777 cond,
1778 then_stmt,
1779 else_stmt,
1780 } = s
1781 else {
1782 return;
1783 };
1784 ternary_fold_walk(then_stmt, changed);
1785 if let Some(e) = else_stmt {
1786 ternary_fold_walk(e, changed);
1787 }
1788 let Some(else_b) = else_stmt else { return };
1789 let target_of = |st: &Stmt| -> Option<u32> {
1790 let Stmt::ExprStmt(Expr::Assign { target, .. }) = st else {
1791 return None;
1792 };
1793 if let Expr::Local { var, .. } = &**target {
1794 Some(*var)
1795 } else {
1796 None
1797 }
1798 };
1799 let (Stmt::Block(tv), Stmt::Block(ev)) = (then_stmt.as_mut(), else_b.as_mut()) else {
1800 return;
1801 };
1802 if tv.len() != 1 || ev.len() != 1 {
1803 return;
1804 }
1805 let (Some(vt_), Some(ve)) = (target_of(&tv[0]), target_of(&ev[0])) else {
1806 return;
1807 };
1808 if vt_ != ve {
1809 return;
1810 }
1811 let mut touched = HashSet::default();
1815 let (Stmt::ExprStmt(a_then), Stmt::ExprStmt(a_else)) = (&tv[0], &ev[0]) else {
1816 return;
1817 };
1818 if let Expr::Assign { value, .. } = a_then {
1819 collect_vars(value, &mut touched);
1820 }
1821 if let Expr::Assign { value, .. } = a_else {
1822 collect_vars(value, &mut touched);
1823 }
1824 if touched.contains(&vt_) {
1825 return;
1826 }
1827 let value_then = match a_then {
1828 Expr::Assign { value, .. } => value.clone(),
1829 _ => return,
1830 };
1831 let value_else = match a_else {
1832 Expr::Assign { value, .. } => value.clone(),
1833 _ => return,
1834 };
1835 let cond_e = cond.clone();
1836 let folded = Stmt::ExprStmt(Expr::Assign {
1837 target: Box::new(Expr::Local {
1838 var: vt_,
1839 ty: vt_join_ty(&value_then, &value_else),
1840 }),
1841 op: AssignOp::Plain,
1842 value: Box::new(Expr::Cond {
1843 c: Box::new(cond_e),
1844 t: value_then,
1845 f: value_else,
1846 }),
1847 });
1848 *s = Stmt::Block(vec![folded]);
1849 *changed = true;
1850}
1851
1852fn vt_join_ty(a: &Expr, b: &Expr) -> TypeRef {
1853 let ta = a.type_ref();
1854 let tb = b.type_ref();
1855 if ta.erased() == tb.erased() {
1856 return ta;
1857 }
1858 TypeRef::J(JavaType::Object("java/lang/Object".into()))
1859}
1860
1861pub fn fold_string_builders(s: &mut Stmt, vt: &VarTable) {
1868 let n_vars = vt.vars.len().max(1);
1870 let mut counts = vec![0usize; n_vars];
1871 count_assignments(s, &mut counts);
1872 let mut values: Vec<Option<Expr>> = vec![None; n_vars];
1873 record_assignments(s, &counts, &mut values);
1874
1875 let mut appended_stmts: HashSet<u32> = HashSet::default();
1877 walk_all(s, &mut |st| {
1878 if let Stmt::ExprStmt(Expr::Method {
1879 cls,
1880 name,
1881 owner,
1882 args,
1883 ..
1884 }) = st
1885 {
1886 if name.as_ref() == "append" && is_string_builder(cls) && args.len() == 1 {
1887 if let Some(Expr::Local { var, .. }) = owner.as_deref() {
1888 appended_stmts.insert(*var);
1889 }
1890 }
1891 }
1892 });
1893
1894 rewrite_exprs(s, &mut |e| {
1895 fold_concat_in_expr(e, &values, &appended_stmts);
1896 });
1897
1898 for _ in 0..8 {
1902 let mut reads: HashSet<u32> = HashSet::default();
1903 stmt_collect_vars(s, &mut reads, false);
1904 if drop_unused_assigns(s, &reads) == 0 {
1905 break;
1906 }
1907 }
1908}
1909
1910pub fn count_stmts_deep(s: &Stmt, out: &mut usize) {
1912 *out += 1;
1913 walk_all(s, &mut |_| {
1914 *out += 1;
1915 });
1916}
1917
1918struct VarAnalysis {
1920 assigns: Vec<usize>,
1921 reads: Vec<usize>,
1922 values: Vec<Option<Expr>>,
1924}
1925
1926fn analyze_vars(s: &Stmt, a: &mut VarAnalysis) {
1927 walk_all(s, &mut |st| {
1928 let exprs: Vec<&Expr> = match st {
1929 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
1930 if let Expr::Local { var, .. } = &**target {
1931 grow_to(&mut a.assigns, *var);
1932 let idx = *var as usize;
1933 a.assigns[idx] += 1;
1934 if a.assigns[idx] == 1 {
1935 if idx >= a.values.len() {
1936 a.values.resize(idx + 1, None);
1937 }
1938 a.values[idx] = Some((**value).clone());
1939 } else if idx < a.values.len() {
1940 a.values[idx] = None;
1941 }
1942 }
1943 let mut v: Vec<&Expr> = vec![value];
1944 if !matches!(&**target, Expr::Local { .. }) {
1945 v.push(target);
1946 }
1947 v
1948 }
1949 Stmt::ExprStmt(e @ (Expr::PreIncDec { .. } | Expr::PostIncDec { .. })) => {
1950 let inner = match e {
1955 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => e,
1956 _ => unreachable!(),
1957 };
1958 if let Expr::Local { var, .. } = &**inner {
1959 grow_to(&mut a.assigns, *var);
1960 let idx = *var as usize;
1961 a.assigns[idx] += 1;
1962 if idx < a.values.len() {
1963 a.values[idx] = None;
1964 }
1965 return;
1966 }
1967 vec![e]
1968 }
1969 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
1970 vec![e]
1971 }
1972 Stmt::Return(Some(e)) => vec![e],
1973 Stmt::LocalDef {
1974 var, init: Some(e), ..
1975 } => {
1976 grow_to(&mut a.assigns, *var);
1977 let idx = *var as usize;
1978 a.assigns[idx] += 1;
1979 if a.assigns[idx] == 1 {
1980 if idx >= a.values.len() {
1981 a.values.resize(idx + 1, None);
1982 }
1983 a.values[idx] = Some(e.clone());
1984 } else if idx < a.values.len() {
1985 a.values[idx] = None;
1986 }
1987 vec![e]
1988 }
1989 Stmt::If { cond, .. } => vec![cond],
1990 Stmt::While { cond, .. } => vec![cond],
1991 Stmt::DoWhile { cond, .. } => vec![cond],
1992 _ => Vec::new(),
1993 };
1994 for e in exprs {
1995 visit_exprs(e, &mut |x| {
1996 if let Expr::Local { var, .. } = x {
1997 grow_to(&mut a.reads, *var);
1998 a.reads[*var as usize] += 1;
1999 }
2000 });
2001 }
2002 });
2003 let n = a.assigns.len().max(a.reads.len()).max(a.values.len());
2005 a.assigns.resize(n, 0);
2006 a.reads.resize(n, 0);
2007 a.values.resize(n, None);
2008}
2009
2010fn grow_to(v: &mut Vec<usize>, var: u32) {
2011 if var as usize >= v.len() {
2012 v.resize(var as usize + 1, 0);
2013 }
2014}
2015
2016fn count_assignments(s: &Stmt, counts: &mut Vec<usize>) {
2017 walk_all(s, &mut |st| match st {
2018 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
2019 if let Expr::Local { var, .. } = &**target {
2020 grow_to(counts, *var);
2021 counts[*var as usize] += 1;
2022 }
2023 }
2024 Stmt::LocalDef {
2025 var, init: Some(_), ..
2026 } => {
2027 grow_to(counts, *var);
2028 counts[*var as usize] += 1;
2029 }
2030 _ => {}
2031 });
2032}
2033
2034fn record_assignments(s: &Stmt, counts: &[usize], out: &mut Vec<Option<Expr>>) {
2035 walk_all(s, &mut |st| match st {
2036 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
2037 if let Expr::Local { var, .. } = &**target {
2038 if counts.get(*var as usize).copied().unwrap_or(0) == 1 {
2039 if *var as usize >= out.len() {
2040 out.resize(*var as usize + 1, None);
2041 }
2042 out[*var as usize] = Some((**value).clone());
2043 }
2044 }
2045 }
2046 Stmt::LocalDef {
2047 var, init: Some(e), ..
2048 } if counts.get(*var as usize).copied().unwrap_or(0) == 1 => {
2049 if *var as usize >= out.len() {
2050 out.resize(*var as usize + 1, None);
2051 }
2052 out[*var as usize] = Some(e.clone());
2053 }
2054 _ => {}
2055 });
2056}
2057
2058fn fold_concat_in_expr(e: &mut Expr, values: &[Option<Expr>], appended: &HashSet<u32>) {
2059 deep_rewrite(e, &mut |x| {
2060 if let Expr::Method {
2061 cls,
2062 name,
2063 args,
2064 owner,
2065 ..
2066 } = x
2067 {
2068 if name.as_ref() == "toString" && args.is_empty() && is_string_builder(cls) {
2069 if let Some(o) = owner {
2070 let mut has_stmt_appends = false;
2073 visit_exprs(o, &mut |y| {
2074 if let Expr::Local { var, .. } = y {
2075 if appended.contains(var) {
2076 has_stmt_appends = true;
2077 }
2078 }
2079 });
2080 if has_stmt_appends {
2081 return;
2082 }
2083 let resolved = resolve_local(o, values);
2084 if let Some(parts) = collect_sb_parts(&resolved, values, 0) {
2085 if !parts.is_empty() {
2094 *x = Expr::StringConcat(parts);
2095 }
2096 }
2097 }
2098 }
2099 }
2100 });
2101}
2102
2103fn is_string_builder(cls: &str) -> bool {
2104 cls == "java/lang/StringBuilder" || cls == "java/lang/StringBuffer"
2105}
2106
2107fn resolve_local(e: &Expr, values: &[Option<Expr>]) -> Expr {
2108 match e {
2109 Expr::Local { var, ty } => values
2110 .get(*var as usize)
2111 .and_then(|o| o.clone())
2112 .unwrap_or_else(|| Expr::Local {
2113 var: *var,
2114 ty: ty.clone(),
2115 }),
2116 other => other.clone(),
2117 }
2118}
2119
2120fn collect_sb_parts(e: &Expr, values: &[Option<Expr>], depth: u32) -> Option<Vec<ConcatPart>> {
2122 if depth > 16 {
2123 return None;
2124 }
2125 match e {
2126 Expr::Method {
2127 cls,
2128 name,
2129 args,
2130 owner,
2131 ..
2132 } if name.as_ref() == "append" && is_string_builder(cls) && args.len() == 1 => {
2133 let mut parts =
2134 collect_sb_parts(&resolve_local(owner.as_deref()?, values), values, depth + 1)?;
2135 parts.push(ConcatPart::Str(args[0].clone()));
2136 Some(parts)
2137 }
2138 Expr::New {
2139 cls,
2140 args,
2141 raw: false,
2142 ..
2143 } if is_string_builder(cls) => {
2144 let mut parts = Vec::new();
2145 for a in args {
2146 if let Expr::Const(ConstVal::Str(sv)) = a {
2147 parts.push(ConcatPart::Const(sv.to_string()));
2148 } else {
2149 parts.push(ConcatPart::Str(a.clone()));
2150 }
2151 }
2152 Some(parts)
2153 }
2154 _ => None,
2155 }
2156}
2157
2158fn drop_unused_assigns(s: &mut Stmt, reads: &HashSet<u32>) -> usize {
2159 let mut dropped = 0usize;
2160 walk_mut_deep(s, &mut |st| {
2161 if let Stmt::Block(v) = st {
2162 let before = v.len();
2163 v.retain(|x| match x {
2164 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
2167 Expr::Local { var, .. } => reads.contains(var) || !is_sbish(value),
2168 _ => true,
2169 },
2170 Stmt::LocalDef { var, init, .. } => {
2171 reads.contains(var) || !init.as_ref().map(is_sbish).unwrap_or(false)
2172 }
2173 _ => true,
2174 });
2175 dropped += before - v.len();
2176 }
2177 });
2178 dropped
2179}
2180
2181fn is_sbish(e: &Expr) -> bool {
2182 match e {
2183 Expr::New {
2184 cls, raw: false, ..
2185 } => is_string_builder(cls),
2186 Expr::Method { name, cls, .. } => name.as_ref() == "append" && is_string_builder(cls),
2187 _ => false,
2188 }
2189}
2190
2191fn walk_mut_deep<F: FnMut(&mut Stmt)>(s: &mut Stmt, f: &mut F) {
2192 f(s);
2193 walk_mut(s, &mut |x| walk_mut_deep(x, f));
2194}
2195
2196pub fn drop_dead_raw_news(body: &mut Stmt) {
2208 fn is_orphan(s: &Stmt) -> bool {
2209 matches!(s, Stmt::ExprStmt(Expr::New { raw: true, .. }))
2210 }
2211 walk_mut_deep(body, &mut |st| match st {
2212 Stmt::Block(v) => v.retain(|x| !is_orphan(x)),
2213 Stmt::Switch { cases, .. } => {
2214 for c in cases.iter_mut() {
2215 c.body.retain(|x| !is_orphan(x));
2216 }
2217 }
2218 Stmt::For { init, .. } => init.retain(|x| !is_orphan(x)),
2219 Stmt::TryWithResources { resources, .. } => resources.retain(|x| !is_orphan(x)),
2220 _ => {}
2221 });
2222}
2223
2224pub fn fold_synchronized(s: &mut Stmt) {
2228 fold_sync_walk(s);
2229}
2230
2231fn fold_sync_walk(s: &mut Stmt) {
2232 walk_mut_deep(s, &mut |st| {
2233 let Stmt::Block(v) = st else { return };
2234 let mut i = 0;
2235 while i < v.len() {
2236 if i + 1 < v.len() {
2237 if let Some(sync) = try_sync_at(v, i) {
2238 let _ = v.drain(i..i + 2);
2239 v.insert(i, sync);
2240 continue;
2241 }
2242 }
2243 i += 1;
2244 }
2245 });
2246}
2247
2248fn try_sync_at(v: &[Stmt], i: usize) -> Option<Stmt> {
2249 let Stmt::MonitorEnter(lock) = &v[i] else {
2250 return None;
2251 };
2252 let Stmt::Try {
2253 body,
2254 catches,
2255 finally,
2256 } = &v[i + 1]
2257 else {
2258 return None;
2259 };
2260 if finally.is_some() || catches.len() != 1 {
2261 return None;
2262 }
2263 let c = &catches[0];
2264 if !c.exc.is_empty() {
2265 return None;
2266 }
2267 let Stmt::Block(cv) = c.body.as_ref() else {
2269 return None;
2270 };
2271 let exit_matches = cv.len() >= 2
2272 && matches!(&cv[0], Stmt::MonitorExit(e) if expr_local_var(e) == expr_local_var(lock));
2273 let throws = cv.len() >= 2 && matches!(&cv[1], Stmt::Throw(_));
2274 if !(exit_matches && throws) {
2275 return None;
2276 }
2277 Some(Stmt::Synchronized {
2278 lock: lock.clone(),
2279 body: body.clone(),
2280 })
2281}
2282
2283fn expr_local_var(e: &Expr) -> Option<u32> {
2284 if let Expr::Local { var, .. } = e {
2285 Some(*var)
2286 } else {
2287 None
2288 }
2289}
2290
2291pub fn forward_single_use(s: &mut Stmt, _vt: &VarTable) {
2296 let mut analysis = VarAnalysis {
2300 assigns: Vec::new(),
2301 reads: Vec::new(),
2302 values: Vec::new(),
2303 };
2304 analyze_vars(s, &mut analysis);
2305 let mut assigns = analysis.assigns;
2306 let mut reads = analysis.reads;
2307 let n_vars = assigns.len().max(_vt.vars.len()).max(1);
2310 assigns.resize(n_vars, 0);
2311 reads.resize(n_vars, 0);
2312 let mut values = analysis.values;
2313 values.resize(n_vars, None);
2314
2315 let mut cand = vec![false; n_vars];
2320 let mut edges: Vec<Vec<u32>> = vec![Vec::new(); n_vars];
2321 for (v, cand_v) in cand.iter_mut().enumerate() {
2322 if assigns.get(v).copied().unwrap_or(0) != 1 || reads.get(v).copied().unwrap_or(0) != 1 {
2323 continue;
2324 }
2325 if let Some(val) = values.get(v).and_then(|o| o.as_ref()) {
2326 let mut refs = HashSet::default();
2327 collect_vars(val, &mut refs);
2328 if refs.iter().any(|r| assigns[*r as usize] > 1) {
2329 continue;
2330 }
2331 edges[v] = refs.into_iter().collect();
2335 }
2336 *cand_v = true;
2337 }
2338
2339 let mut color = vec![0u8; n_vars]; let mut bad = vec![false; n_vars];
2357 for root in 0..n_vars {
2358 if color[root] != 0 || edges[root].is_empty() {
2359 continue;
2360 }
2361 color[root] = 1;
2362 let mut stack: Vec<(usize, usize)> = vec![(root, 0)];
2363 while let Some(top) = stack.last_mut() {
2364 let v = top.0;
2365 let i = top.1;
2366 top.1 += 1;
2367 if i < edges[v].len() {
2368 let r = edges[v][i] as usize;
2369 if color[r] == 1 {
2370 for &(pn, _) in &stack {
2371 bad[pn] = true;
2372 }
2373 } else if color[r] == 0 {
2374 color[r] = 1;
2375 stack.push((r, 0));
2376 } else if bad[r] {
2377 for &(pn, _) in &stack {
2378 bad[pn] = true;
2379 }
2380 }
2381 } else {
2382 color[v] = 2;
2383 stack.pop();
2384 }
2385 }
2386 }
2387 let single: Vec<bool> = (0..n_vars).map(|v| cand[v] && !bad[v]).collect();
2388 if !single.iter().any(|&b| b) {
2389 return;
2390 }
2391 let pure: Vec<bool> = (0..n_vars)
2392 .map(|v| {
2393 single[v]
2394 && values
2395 .get(v)
2396 .and_then(|o| o.as_ref())
2397 .map(|e| !has_side_effects(e))
2398 .unwrap_or(false)
2399 })
2400 .collect();
2401 let impure: Vec<bool> = (0..n_vars).map(|v| single[v] && !pure[v]).collect();
2402
2403 if pure.iter().any(|&b| b) {
2405 let vals: Vec<Option<Expr>> = (0..n_vars)
2406 .map(|v| {
2407 if pure[v] {
2408 values.get(v).cloned().flatten()
2409 } else {
2410 None
2411 }
2412 })
2413 .collect();
2414 rewrite_exprs(s, &mut |e| {
2415 deep_rewrite_reads(e, &mut |x| {
2416 if let Expr::Local { var, .. } = x {
2417 if let Some(Some(v)) = vals.get(*var as usize) {
2418 *x = v.clone();
2419 }
2420 }
2421 });
2422 });
2423 drop_defs(s, &pure);
2424 }
2425
2426 if impure.iter().any(|&b| b) {
2429 forward_adjacent_impure(s, &values, &impure);
2430 }
2431}
2432
2433fn count_reads(s: &Stmt, out: &mut Vec<usize>) {
2435 walk_all(s, &mut |st| {
2436 let exprs: Vec<&Expr> = match st {
2437 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
2440 let mut v: Vec<&Expr> = vec![value];
2441 if !matches!(&**target, Expr::Local { .. }) {
2442 v.push(target);
2443 }
2444 v
2445 }
2446 Stmt::ExprStmt(e @ (Expr::PreIncDec { .. } | Expr::PostIncDec { .. })) => {
2449 let inner = match e {
2450 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => e,
2451 _ => unreachable!(),
2452 };
2453 if matches!(**inner, Expr::Local { .. }) {
2454 Vec::new()
2455 } else {
2456 vec![e]
2457 }
2458 }
2459 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
2460 vec![e]
2461 }
2462 Stmt::Return(Some(e)) => vec![e],
2463 Stmt::LocalDef { init: Some(e), .. } => vec![e],
2464 Stmt::If { cond, .. } => vec![cond],
2465 Stmt::While { cond, .. } => vec![cond],
2466 Stmt::DoWhile { cond, .. } => vec![cond],
2467 Stmt::Switch { selector, .. } => vec![selector],
2473 Stmt::ForEach { iterable, .. } => vec![iterable],
2474 Stmt::Synchronized { lock, .. } => vec![lock],
2475 Stmt::For { cond, update, .. } => {
2476 let mut v: Vec<&Expr> = Vec::with_capacity(update.len() + 1);
2477 if let Some(c) = cond {
2478 v.push(c);
2479 }
2480 v.extend(update.iter());
2481 v
2482 }
2483 Stmt::Assert { cond, msg } => {
2484 let mut v: Vec<&Expr> = vec![cond];
2485 if let Some(m) = msg {
2486 v.push(m);
2487 }
2488 v
2489 }
2490 _ => Vec::new(),
2491 };
2492 for e in exprs {
2493 visit_exprs(e, &mut |x| {
2494 if let Expr::Local { var, .. } = x {
2495 grow_to(out, *var);
2496 out[*var as usize] += 1;
2497 }
2498 });
2499 }
2500 });
2501}
2502
2503fn drop_defs(s: &mut Stmt, vars: &[bool]) {
2504 walk_mut_deep(s, &mut |st| {
2505 if let Stmt::Block(v) = st {
2506 v.retain(|x| match x {
2507 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
2508 Expr::Local { var, .. } => !vars.get(*var as usize).copied().unwrap_or(false),
2509 _ => true,
2510 },
2511 Stmt::LocalDef { var, .. } => !vars.get(*var as usize).copied().unwrap_or(false),
2512 _ => true,
2513 });
2514 }
2515 });
2516}
2517
2518fn forward_adjacent_impure(s: &mut Stmt, values: &[Option<Expr>], impure: &[bool]) {
2520 if let Stmt::Block(v) = s {
2521 let mut i = 0;
2522 while i + 1 < v.len() {
2523 let def_var = match &v[i] {
2524 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
2525 Expr::Local { var, .. } => Some(*var),
2526 _ => None,
2527 },
2528 Stmt::LocalDef { var, .. } => Some(*var),
2529 _ => None,
2530 };
2531 if let Some(var) = def_var {
2532 if impure.get(var as usize).copied().unwrap_or(false) {
2533 let mut reads_here = vec![0usize; values.len().max(1)];
2535 count_reads(&v[i + 1], &mut reads_here);
2536 if reads_here.get(var as usize).copied().unwrap_or(0) == 1 {
2537 let val = match &v[i] {
2541 Stmt::ExprStmt(Expr::Assign { value, .. }) => (**value).clone(),
2542 Stmt::LocalDef { init: Some(e), .. } => (*e).clone(),
2543 _ => {
2544 i += 1;
2545 continue;
2546 }
2547 };
2548 rewrite_exprs(&mut v[i + 1], &mut |e| {
2549 deep_rewrite_reads(e, &mut |x| {
2550 if let Expr::Local { var: v2, .. } = x {
2551 if *v2 == var {
2552 *x = val.clone();
2553 }
2554 }
2555 });
2556 });
2557 v.remove(i);
2558 i = i.saturating_sub(1);
2561 continue;
2562 }
2563 }
2564 }
2565 i += 1;
2566 }
2567 }
2568 walk_mut(s, &mut |x| forward_adjacent_impure(x, values, impure));
2569}
2570
2571pub fn infer_types(vt: &mut VarTable, body: &mut Stmt, ret: &JavaType, env: &MethodEnv) {
2577 let n = vt.vars.len();
2578 let mut evidence: Vec<Vec<(JavaType, bool)>> = vec![Vec::new(); n];
2586 let ev = |evidence: &mut Vec<Vec<(JavaType, bool)>>, var: u32, t: JavaType, strong: bool| {
2587 if (var as usize) < evidence.len() {
2588 evidence[var as usize].push((t, strong));
2589 }
2590 };
2591
2592 walk_all(body, &mut |st| match st {
2593 Stmt::LocalDef { var, init, .. } => {
2594 if let Some(e) = init {
2595 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
2596 let strong = match e {
2604 Expr::Const(_) => false,
2605 Expr::Local { var: src, .. } => {
2606 let src_ty = vt.vars.get(*src as usize).map(|v| v.ty.erased());
2607 let tgt_ty = vt.vars.get(*var as usize).map(|v| v.ty.erased());
2608 match (src_ty, tgt_ty) {
2609 (Some(s), Some(g)) => {
2610 (s.is_numeric() && g.is_numeric())
2611 || (s.is_reference() && g.is_reference())
2612 }
2613 _ => false,
2614 }
2615 }
2616 _ => true,
2617 };
2618 ev(&mut evidence, *var, e.type_ref().erased(), strong);
2619 }
2620 }
2621 Stmt::ExprStmt(e) => {
2622 if let Expr::Assign { target, value, .. } = e {
2629 if let Expr::Field { owner: Some(o), cls, is_static: false, .. } = &**target {
2636 if cls.is_empty() {
2637 } else if let Expr::Local { var, .. } = &**o {
2639 ev(
2640 &mut evidence,
2641 *var,
2642 JavaType::Object(cls.clone()),
2643 true,
2644 );
2645 }
2646 }
2647 if let Expr::Local { var, .. } = &**target {
2648 let strong = match &**value {
2649 Expr::Const(_) => false,
2650 Expr::Local { var: src, .. } => {
2651 let src_ty = vt.vars.get(*src as usize).map(|v| v.ty.erased());
2658 let tgt_ty = vt.vars.get(*var as usize).map(|v| v.ty.erased());
2659 match (src_ty, tgt_ty) {
2660 (Some(s), Some(g)) => {
2661 (s.is_numeric() && g.is_numeric())
2662 || (s.is_reference() && g.is_reference())
2663 }
2664 _ => false,
2665 }
2666 }
2667 _ => true,
2668 };
2669 ev(&mut evidence, *var, value.type_ref().erased(), strong);
2670 }
2671 }
2672 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
2673 }
2674 Stmt::Throw(e) => {
2675 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
2676 if let Expr::Local { var, .. } = e {
2677 ev(
2678 &mut evidence,
2679 *var,
2680 JavaType::Object("java/lang/Throwable".into()),
2681 true,
2682 );
2683 }
2684 }
2685 Stmt::Return(Some(e)) => {
2686 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
2687 if let Expr::Local { var, .. } = e {
2688 ev(&mut evidence, *var, ret.clone(), true);
2689 }
2690 }
2691 Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
2692 if let Expr::Local { var, .. } = e {
2693 ev(
2694 &mut evidence,
2695 *var,
2696 JavaType::Object("java/lang/Object".into()),
2697 true,
2698 );
2699 }
2700 }
2701 Stmt::ForEach {
2702 var,
2703 iterable,
2704 is_array,
2705 ..
2706 } => {
2707 if let Expr::Local { var: v0, .. } = iterable {
2708 if *is_array {
2709 ev(&mut evidence, *v0, JavaType::Array(Box::new(JavaType::Int)), true);
2710 } else {
2711 ev(
2712 &mut evidence,
2713 *v0,
2714 JavaType::Object("java/lang/Object".into()),
2715 true,
2716 );
2717 }
2718 }
2719 let _ = var;
2720 }
2721 _ => {}
2722 });
2723 let _ = env;
2724
2725 let mut derefed: Vec<bool> = vec![false; vt.vars.len()];
2735 mark_derefs(body, &mut derefed);
2736 let mut arr_derefed: Vec<bool> = vec![false; vt.vars.len()];
2741 visit_all_exprs(body, &mut |x| {
2742 if let Expr::ArrayIndex { array, .. } = x {
2743 if let Expr::Local { var, .. } = &**array {
2744 if (*var as usize) < arr_derefed.len() {
2745 arr_derefed[*var as usize] = true;
2746 }
2747 }
2748 }
2749 if let Expr::Bin { op, l, r, .. } = x {
2757 if matches!(op, BinOp::Eq | BinOp::Ne)
2758 && (matches!(&**r, Expr::Const(ConstVal::Null))
2759 || matches!(&**l, Expr::Const(ConstVal::Null)))
2760 {
2761 for side in [l, r] {
2762 if let Expr::Local { var, .. } = &**side {
2763 ev(
2764 &mut evidence,
2765 *var,
2766 JavaType::Object("java/lang/Object".into()),
2767 true,
2768 );
2769 }
2770 }
2771 }
2772 }
2773 });
2774 const FINAL_JDK: &[&str] = &[
2775 "java/lang/Boolean",
2776 "java/lang/String",
2777 "java/lang/Integer",
2778 "java/lang/Long",
2779 "java/lang/Short",
2780 "java/lang/Byte",
2781 "java/lang/Character",
2782 "java/lang/Float",
2783 "java/lang/Double",
2784 ];
2785 for (i, evs) in evidence.iter().enumerate() {
2786 let info = &vt.vars[i];
2787 if info.is_param {
2788 continue;
2789 }
2790 let strong: Vec<JavaType> = evs
2791 .iter()
2792 .filter(|(_, s)| *s)
2793 .map(|(t, _)| t.clone())
2794 .collect();
2795 let all: Vec<JavaType> = evs.iter().map(|(t, _)| t.clone()).collect();
2796 if info.ty.erased().is_reference() {
2797 if strong.is_empty() {
2803 let refs: std::collections::HashSet<&str> = all
2804 .iter()
2805 .filter_map(|t| match t {
2806 JavaType::Object(n) if n.as_ref() != "java/lang/Object" => {
2807 Some(n.as_ref())
2808 }
2809 _ => None,
2810 })
2811 .collect();
2812 let mixed = all.iter().any(|t| t.is_numeric());
2813 if refs.len() >= 2 || (mixed && !refs.is_empty()) || (mixed && all.iter().any(|t| matches!(t, JavaType::Array(_)))) {
2814 vt.vars[i].ty =
2815 TypeRef::J(JavaType::Object("java/lang/Object".into()));
2816 continue;
2817 }
2818 }
2819 if arr_derefed[i] {
2822 let arr = strong
2823 .iter()
2824 .chain(all.iter())
2825 .find(|t| matches!(t, JavaType::Array(_)))
2826 .cloned();
2827 if let Some(t) = arr {
2828 vt.vars[i].ty = TypeRef::J(t);
2829 continue;
2830 }
2831 }
2832 let deref_pick = || {
2836 if !derefed[i] {
2837 return None;
2838 }
2839 strong
2840 .iter()
2841 .find(|t| {
2842 matches!(t, JavaType::Object(n)
2843 if !n.is_empty()
2844 && n.as_ref() != "java/lang/Object"
2845 && !FINAL_JDK.contains(&n.as_ref()))
2846 })
2847 .cloned()
2848 };
2849 if let Some(t) = deref_pick()
2850 .or_else(|| pick_object(&strong))
2851 .or_else(|| pick_object(&all))
2852 {
2853 vt.vars[i].ty = TypeRef::J(t);
2854 }
2855 } else if let Some(t) = pick_object(&strong) {
2856 vt.vars[i].ty = TypeRef::J(t);
2860 } else if strong.contains(&JavaType::Boolean) {
2861 vt.vars[i].ty = TypeRef::J(JavaType::Boolean);
2865 } else if !all.is_empty() && all.iter().all(|t| t.is_numeric()) {
2866 if let Some(t) = pick_numeric(&all) {
2867 vt.vars[i].ty = TypeRef::J(t);
2868 }
2869 } else {
2870 let refs = all.iter().filter(|t| t.is_reference()).count();
2874 let nums = all.iter().filter(|t| t.is_numeric()).count();
2875 if refs > 0 && nums > 0 {
2876 vt.vars[i].ty =
2877 TypeRef::J(JavaType::Object("java/lang/Object".into()));
2878 } else if let Some(t) = pick_object(&strong).or_else(|| pick_object(&all)) {
2879 vt.vars[i].ty = TypeRef::J(t);
2880 }
2881 }
2882 }
2883
2884 let types: Vec<&TypeRef> = vt.vars.iter().map(|v| &v.ty).collect();
2889 rewrite_exprs(body, &mut |e| {
2890 deep_rewrite(e, &mut |x| {
2891 if let Expr::Local { var, ty } = x {
2892 if let Some(want) = types.get(*var as usize) {
2893 if ty != *want {
2894 *ty = (*want).clone();
2895 }
2896 }
2897 }
2898 });
2899 });
2900
2901 coerce_num_consts(body, &types);
2904}
2905
2906fn coerce_num_consts(body: &mut Stmt, types: &[&TypeRef]) {
2907 walk_mut_deep(body, &mut |st| {
2908 let (var, val): (u32, &mut Expr) = match st {
2909 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &mut **target {
2910 Expr::Local { var, .. } => (*var, value),
2911 _ => return,
2912 },
2913 Stmt::LocalDef {
2914 var, init: Some(e), ..
2915 } => (*var, e),
2916 _ => return,
2917 };
2918 let want = match types.get(var as usize) {
2919 Some(TypeRef::J(t)) => t.clone(),
2920 _ => return,
2921 };
2922 let rewritten = match &*val {
2923 Expr::Const(ConstVal::Long(l)) => match &want {
2924 JavaType::Double => Some(Expr::Const(ConstVal::Double(*l as f64))),
2925 JavaType::Float => Some(Expr::Const(ConstVal::Float(*l as f32))),
2926 _ => None,
2927 },
2928 Expr::Const(ConstVal::Int(i)) => match &want {
2929 JavaType::Double => Some(Expr::Const(ConstVal::Double(*i as f64))),
2930 JavaType::Float => Some(Expr::Const(ConstVal::Float(*i as f32))),
2931 _ => None,
2932 },
2933 _ => None,
2934 };
2935 if let Some(e) = rewritten {
2936 *val = e;
2937 }
2938 });
2939}
2940
2941fn pick_object(evs: &[JavaType]) -> Option<JavaType> {
2942 evs.iter()
2943 .find(|t| {
2944 t.is_reference()
2945 && !matches!(t, JavaType::Object(n) if n.is_empty() || n.as_ref() == "java/lang/Object")
2946 })
2947 .cloned()
2948}
2949
2950fn pick_numeric(evs: &[JavaType]) -> Option<JavaType> {
2951 if evs.is_empty() {
2952 return None;
2953 }
2954 let mut t = evs[0].clone();
2955 for x in &evs[1..] {
2956 t = join_numeric(&t, x);
2957 }
2958 Some(t)
2959}
2960
2961fn join_numeric(a: &JavaType, b: &JavaType) -> JavaType {
2962 if a == b {
2963 return a.clone();
2964 }
2965 match (a, b) {
2966 (JavaType::Double, _) | (_, JavaType::Double) => JavaType::Double,
2967 (JavaType::Float, _) | (_, JavaType::Float) => JavaType::Float,
2968 (JavaType::Long, _) | (_, JavaType::Long) => JavaType::Long,
2969 (JavaType::Int, x) if x.is_integral() => JavaType::Int,
2970 (x, JavaType::Int) if x.is_integral() => JavaType::Int,
2971 (x, y) if x.is_integral() && y.is_integral() => JavaType::Int,
2972 _ => JavaType::Int,
2973 }
2974}
2975
2976fn expr_evidence<F: FnMut(u32, JavaType)>(e: &Expr, f: &mut F) {
2978 visit_exprs(e, &mut |x| match x {
2979 Expr::Method {
2980 owner,
2981 args,
2982 desc,
2983 is_static,
2984 cls,
2985 ..
2986 } => {
2987 if !*is_static {
2988 if let Some(o) = owner {
2989 if let Expr::Local { var, .. } = &**o {
2990 f(*var, JavaType::Object(cls.clone()));
2991 }
2992 }
2993 }
2994 for (i, a) in args.iter().enumerate() {
2995 if let Expr::Local { var, .. } = a {
2996 if let Some(t) = desc.args.get(i) {
2997 f(*var, t.clone());
2998 }
2999 }
3000 }
3001 }
3002 Expr::Field {
3003 owner,
3004 cls,
3005 ty,
3006 is_static,
3007 ..
3008 } => {
3009 if !*is_static && !cls.is_empty() {
3010 if let Some(o) = owner {
3011 if let Expr::Local { var, .. } = &**o {
3012 f(*var, JavaType::Object(cls.clone()));
3022 }
3023 }
3024 }
3025 let _ = ty;
3026 }
3027 Expr::Bin { op, l, r, .. }
3032 if matches!(op, BinOp::Eq | BinOp::Ne)
3033 && (matches!(&**r, Expr::Const(ConstVal::Null))
3034 || matches!(&**l, Expr::Const(ConstVal::Null))) =>
3035 {
3036 for side in [l, r] {
3037 if let Expr::Local { var, .. } = &**side {
3038 f(*var, JavaType::Object("java/lang/Object".into()));
3039 }
3040 }
3041 }
3042 Expr::ArrayIndex { array, .. } => {
3043 if let Expr::Local { var, ty, .. } = &**array {
3044 let t = ty.erased();
3049 if matches!(t, JavaType::Array(_)) {
3050 f(*var, t);
3051 } else {
3052 f(*var, JavaType::Array(Box::new(JavaType::Int)));
3053 }
3054 }
3055 }
3056 Expr::Assign { .. } => {
3057 }
3060 Expr::Cast { e: inner, .. } => {
3061 let _ = inner;
3067 }
3068 Expr::InstanceOf { e: inner, .. } => {
3069 let _ = inner;
3070 }
3071 _ => {}
3072 });
3073}
3074
3075pub fn mark_field_owner_concrete(vt: &mut VarTable, body: &mut Stmt) {
3087 let mut owners: HashSet<u32> = HashSet::default();
3088 rewrite_exprs(body, &mut |e| {
3089 visit_exprs(e, &mut |x| {
3090 if let Expr::Field {
3091 owner: Some(o),
3092 is_static: false,
3093 ..
3094 } = x
3095 {
3096 if let Expr::Local { var, .. } = &**o {
3097 owners.insert(*var);
3098 }
3099 }
3100 });
3101 });
3102 for var in owners {
3103 let i = var as usize;
3104 if i >= vt.vars.len() {
3105 continue;
3106 }
3107 if let JavaType::Object(cls) = vt.vars[i].ty.erased() {
3108 if let Some(last) = cls.rsplit('$').next() {
3109 if !last.is_empty() && last.chars().all(|c| c.is_ascii_digit()) {
3110 vt.force_concrete_vars.insert(var);
3111 }
3112 }
3113 }
3114 }
3115 let concrete: Vec<u32> = vt
3124 .vars
3125 .iter()
3126 .filter(|v| {
3127 if let JavaType::Object(cls) = v.ty.erased() {
3128 cls.rsplit('$')
3129 .next()
3130 .is_some_and(|l| !l.is_empty() && l.chars().all(|c| c.is_ascii_digit()))
3131 } else {
3132 false
3133 }
3134 })
3135 .map(|v| v.id)
3136 .collect();
3137 for var in concrete {
3138 vt.force_concrete_vars.insert(var);
3139 }
3140}
3141
3142pub fn insert_object_narrowing_casts(
3154 vt: &VarTable,
3155 body: &mut Stmt,
3156 pool: &DexPool,
3157 ret: &JavaType,
3158) {
3159 let value_ty = |e: &Expr| -> JavaType {
3162 match e {
3163 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
3164 other => other.type_ref().erased(),
3165 }
3166 };
3167 let is_top_object = |e: &Expr| -> bool {
3168 value_ty(e) == JavaType::Object("java/lang/Object".into())
3169 };
3170 let specific_ref = |ty: &TypeRef| -> Option<TypeRef> {
3174 match ty.erased() {
3175 JavaType::Object(c) if c.as_ref() != "java/lang/Object" => Some(ty.clone()),
3176 JavaType::Array(_) => Some(ty.clone()),
3177 _ => None,
3178 }
3179 };
3180 let castable = |e: &Expr| -> bool {
3181 is_top_object(e)
3182 && !matches!(e, Expr::Const(_) | Expr::Cast { .. } | Expr::InstanceOf { .. })
3183 };
3184 let needs_downcast = |e: &Expr, tt: &JavaType| -> bool {
3194 if matches!(e, Expr::Const(_) | Expr::Cast { .. } | Expr::InstanceOf { .. }) {
3195 return false;
3196 }
3197 let v = value_ty(e);
3198 let (JavaType::Object(vn), JavaType::Object(tn)) = (&v, tt) else {
3199 return false;
3200 };
3201 if vn.as_ref() == "java/lang/Object" || tn.as_ref() == "java/lang/Object" {
3202 return false;
3203 }
3204 vn != tn && !pool.is_subtype(vn, tn)
3205 };
3206 walk_mut_deep(body, &mut |st| match st {
3207 Stmt::ExprStmt(Expr::Assign {
3208 target,
3209 value,
3210 op: AssignOp::Plain,
3211 ..
3212 }) => {
3213 let tgt = match &**target {
3214 Expr::Local { var, .. } => vt.var(*var).ty.clone(),
3215 Expr::Field { ty, .. } => ty.clone(),
3216 Expr::ArrayIndex { array, .. } => {
3223 let aty = match &**array {
3224 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
3225 other => other.type_ref().erased(),
3226 };
3227 match aty {
3228 JavaType::Array(inner) => TypeRef::J(*inner),
3229 _ => return,
3230 }
3231 }
3232 _ => return,
3233 };
3234 if let Some(t) = specific_ref(&tgt) {
3235 if castable(value) || needs_downcast(value, &tgt.erased()) {
3236 let v = std::mem::replace(value, Box::new(Expr::This));
3237 **value = Expr::Cast { ty: t, e: v };
3238 }
3239 }
3240 }
3241 Stmt::LocalDef { var, init: Some(value), .. } => {
3242 if let Some(t) = specific_ref(&vt.var(*var).ty) {
3243 let tt = vt.var(*var).ty.erased();
3244 if castable(value) || needs_downcast(value, &tt) {
3245 let v = std::mem::replace(value, Expr::This);
3246 *value = Expr::Cast {
3247 ty: t,
3248 e: Box::new(v),
3249 };
3250 }
3251 }
3252 }
3253 Stmt::Return(Some(value)) => {
3254 if let Some(t) = specific_ref(&TypeRef::J(ret.clone())) {
3255 if castable(value) || needs_downcast(value, ret) {
3256 let v = std::mem::replace(value, Expr::This);
3257 *value = Expr::Cast { ty: t, e: Box::new(v) };
3258 }
3259 }
3260 }
3261 _ => {}
3262 });
3263 walk_stmt_exprs(body, &mut |e| {
3270 deep_rewrite(e, &mut |x| {
3271 let (args, arg_tys): (&mut Vec<Expr>, &[JavaType]) = match x {
3272 Expr::Method { desc, args, is_dynamic: false, .. } => {
3273 (args, desc.args.as_slice())
3274 }
3275 Expr::New { arg_tys, args, .. } => (args, arg_tys.as_slice()),
3279 _ => return,
3280 };
3281 for (i, a) in args.iter_mut().enumerate() {
3282 let Some(formal) = arg_tys.get(i) else {
3283 continue;
3284 };
3285 let Some(t) = specific_ref(&TypeRef::J(formal.clone())) else {
3286 continue;
3287 };
3288 if !(castable(a) || needs_downcast(a, formal)) {
3289 continue;
3290 }
3291 let v = std::mem::replace(a, Expr::Const(ConstVal::Null));
3292 *a = Expr::Cast { ty: t, e: Box::new(v) };
3293 }
3294 });
3295 });
3296}
3297
3298pub fn fix_primitive_assign_casts(vt: &VarTable, body: &mut Stmt, ret_ty: &JavaType) {
3307 fn numlike(t: &JavaType) -> bool {
3308 matches!(
3309 t,
3310 JavaType::Byte
3311 | JavaType::Short
3312 | JavaType::Int
3313 | JavaType::Char
3314 | JavaType::Long
3315 | JavaType::Float
3316 | JavaType::Double
3317 )
3318 }
3319 fn val_ty(e: &Expr, vt: &VarTable) -> JavaType {
3320 match e {
3321 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
3322 vt.var(*var).ty.erased()
3323 }
3324 other => other.type_ref().erased(),
3325 }
3326 }
3327 fn wrap(ty: &JavaType, value: &mut Expr) {
3328 if matches!(value, Expr::Cast { .. }) {
3329 return;
3330 }
3331 if val_ty_static(value) == *ty {
3332 return;
3333 }
3334 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
3335 *value = Expr::Cast {
3336 ty: TypeRef::J(ty.clone()),
3337 e: Box::new(taken),
3338 };
3339 }
3340 fn val_ty_static(e: &Expr) -> JavaType {
3341 e.type_ref().erased()
3342 }
3343 fn coerce(tt: &JavaType, value: &mut Expr, vt: &VarTable) {
3351 match (&*value, tt) {
3356 (Expr::Const(ConstVal::Int(b)), JavaType::Float) => {
3357 *value = Expr::Const(ConstVal::Float(f32::from_bits(*b as u32)));
3358 return;
3359 }
3360 (Expr::Const(ConstVal::Long(b)), JavaType::Double) => {
3361 *value = Expr::Const(ConstVal::Double(f64::from_bits(*b as u64)));
3362 return;
3363 }
3364 (Expr::Const(ConstVal::Float(f)), JavaType::Int) => {
3365 *value = Expr::Const(ConstVal::Int(f.to_bits() as i32));
3366 return;
3367 }
3368 (Expr::Const(ConstVal::Double(d)), JavaType::Long) => {
3369 *value = Expr::Const(ConstVal::Long(d.to_bits() as i64));
3370 return;
3371 }
3372 _ => {}
3373 }
3374 let vt_val = val_ty(value, vt);
3375 let tt_bool = matches!(tt, JavaType::Boolean);
3376 let val_bool = matches!(vt_val, JavaType::Boolean);
3377 if tt_bool && !val_bool && numlike(&vt_val) {
3378 if matches!(
3379 value,
3380 Expr::Const(_)
3381 | Expr::Bin { op: BinOp::Eq | BinOp::Ne, .. }
3382 | Expr::Un { op: UnOp::Not, .. }
3383 ) {
3384 return;
3389 }
3390 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
3391 *value = Expr::Bin {
3392 op: BinOp::Ne,
3393 l: Box::new(taken),
3394 r: Box::new(Expr::Const(ConstVal::Int(0))),
3395 ty: Some(TypeRef::J(JavaType::Boolean)),
3396 };
3397 } else if !tt_bool && val_bool && numlike(tt) {
3398 if matches!(value, Expr::Cast { .. }) {
3399 return;
3400 }
3401 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
3402 *value = Expr::Cast {
3403 ty: TypeRef::J(tt.clone()),
3404 e: Box::new(Expr::Cond {
3405 c: Box::new(taken),
3406 t: Box::new(Expr::Const(ConstVal::Int(1))),
3407 f: Box::new(Expr::Const(ConstVal::Int(0))),
3408 }),
3409 };
3410 } else if numlike(tt) && numlike(&vt_val) {
3411 wrap(tt, value);
3412 }
3413 }
3414 walk_mut_deep(body, &mut |st| match st {
3415 Stmt::ExprStmt(Expr::Assign {
3416 target,
3417 op: AssignOp::Plain,
3418 value,
3419 }) => {
3420 let tt = match &**target {
3421 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
3422 vt.var(*var).ty.erased()
3423 }
3424 Expr::Field { ty, .. } => ty.erased(),
3425 Expr::ArrayIndex { array, .. } => match array.type_ref().erased() {
3429 JavaType::Array(inner) => *inner,
3430 _ => return,
3431 },
3432 _ => return,
3433 };
3434 coerce(&tt, value, vt);
3435 }
3436 Stmt::LocalDef {
3437 var,
3438 init: Some(value),
3439 ..
3440 } => {
3441 if (*var as usize) >= vt.vars.len() {
3442 return;
3443 }
3444 let tt = vt.var(*var).ty.erased();
3445 coerce(&tt, value, vt);
3446 }
3447 Stmt::Return(Some(e)) => {
3448 if !numlike(ret_ty) || !numlike(&val_ty(e, vt)) {
3449 return;
3450 }
3451 wrap(ret_ty, e);
3452 }
3453 _ => {}
3454 });
3455}
3456
3457pub fn fix_field_owner_downcasts(body: &mut Stmt, vt: &VarTable, pool: &DexPool) {
3476 fn owner_internal(o: &Expr, vt: &VarTable) -> Option<std::sync::Arc<str>> {
3477 let t = match o {
3478 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
3479 other => other.type_ref().erased(),
3480 };
3481 match t {
3482 JavaType::Object(n) => Some(n),
3483 _ => None,
3484 }
3485 }
3486 fn declares_up(pool: &DexPool, class: &str, name: &str) -> bool {
3487 let mut cur = Some(class.to_string());
3488 let mut hops = 0;
3489 while let Some(c) = cur {
3490 hops += 1;
3491 if hops > 64 {
3492 return false;
3493 }
3494 let Some(pc) = pool.get(&c) else { return false };
3495 if pc
3496 .instance_fields
3497 .iter()
3498 .chain(pc.static_fields.iter())
3499 .any(|f| f.name.as_str() == name)
3500 {
3501 return true;
3502 }
3503 cur = pc.super_name.clone();
3504 }
3505 false
3506 }
3507 walk_stmt_exprs(body, &mut |e| {
3508 deep_rewrite(e, &mut |x| {
3509 if let Expr::Field {
3510 owner: Some(o),
3511 cls,
3512 name,
3513 is_static: false,
3514 ..
3515 } = x
3516 {
3517 let Some(ot) = owner_internal(o, vt) else {
3518 return;
3519 };
3520 if ot.as_ref() == cls.as_ref() || !pool.is_subtype(cls, &ot) {
3521 return;
3522 }
3523 if declares_up(pool, &ot, name) || !declares_up(pool, cls, name) {
3524 return;
3525 }
3526 let ty = TypeRef::J(JavaType::Object(cls.clone()));
3527 if let Expr::Cast { ty: ct, .. } = &mut **o {
3528 *ct = ty;
3529 } else {
3530 let taken = std::mem::replace(&mut **o, Expr::This);
3531 **o = Expr::Cast { ty, e: Box::new(taken) };
3532 }
3533 }
3534 let (tgt_ty, value) = match x {
3536 Expr::Assign { target, op: AssignOp::Plain, value } => {
3537 let t = match &**target {
3538 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
3539 Expr::Field { ty, .. } => ty.erased(),
3540 _ => return,
3541 };
3542 (t, value)
3543 }
3544 _ => return,
3545 };
3546 let JavaType::Object(sn) = tgt_ty else { return };
3547 if let Expr::Cast { ty, .. } = &mut **value {
3548 if let TypeRef::J(JavaType::Object(tn)) = ty {
3549 if tn.as_ref() != sn.as_ref() && pool.is_subtype(&sn, tn) {
3550 *ty = TypeRef::J(JavaType::Object(sn.clone()));
3551 }
3552 }
3553 }
3554 });
3555 });
3556 walk_mut_deep(body, &mut |st| {
3558 if let Stmt::LocalDef { var, init: Some(value), .. } = st {
3559 let tt = if (*var as usize) < vt.vars.len() {
3560 vt.vars[*var as usize].ty.erased()
3561 } else {
3562 return;
3563 };
3564 let JavaType::Object(sn) = tt else { return };
3565 if let Expr::Cast { ty, .. } = value {
3566 if let TypeRef::J(JavaType::Object(tn)) = ty {
3567 if tn.as_ref() != sn.as_ref() && pool.is_subtype(&sn, tn) {
3568 *ty = TypeRef::J(JavaType::Object(sn.clone()));
3569 }
3570 }
3571 }
3572 }
3573 });
3574}
3575
3576pub fn fix_incomparable_equality(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
3583 fn side_ty(e: &Expr, vt: &VarTable) -> JavaType {
3584 match e {
3585 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
3586 vt.var(*var).ty.erased()
3587 }
3588 other => other.type_ref().erased(),
3589 }
3590 }
3591 let incomparable = |a: &JavaType, b: &JavaType| -> bool {
3592 use JavaType::{Array, Object};
3593 match (a, b) {
3594 (Object(x), Object(y)) => {
3595 if x == y || x.as_ref() == "java/lang/Object" || y.as_ref() == "java/lang/Object"
3596 {
3597 return false;
3598 }
3599 let xi = pool.get(x).map(|c| c.is_interface()).unwrap_or(true);
3600 let yi = pool.get(y).map(|c| c.is_interface()).unwrap_or(true);
3601 if xi || yi {
3605 return false;
3606 }
3607 !pool.is_subtype(x, y) && !pool.is_subtype(y, x)
3608 }
3609 (Array(_), Object(y)) | (Object(y), Array(_)) => {
3610 y.as_ref() != "java/lang/Object"
3611 && y.as_ref() != "java/lang/Cloneable"
3612 && y.as_ref() != "java/io/Serializable"
3613 && !pool.get(y).map(|c| c.is_interface()).unwrap_or(false)
3614 }
3615 _ => false,
3616 }
3617 };
3618 let is_ref = |t: &JavaType| matches!(t, JavaType::Object(_) | JavaType::Array(_));
3619 walk_stmt_exprs(body, &mut |e| {
3620 deep_rewrite(e, &mut |x| {
3621 if let Expr::Bin {
3622 op: BinOp::Eq | BinOp::Ne,
3623 l,
3624 r,
3625 ..
3626 } = x
3627 {
3628 let lt = side_ty(l, vt);
3629 let rt = side_ty(r, vt);
3630 if is_ref(<) && matches!(&**r, Expr::Const(ConstVal::Int(0))) {
3636 **r = Expr::Const(ConstVal::Null);
3637 return;
3638 }
3639 if is_ref(&rt) && matches!(&**l, Expr::Const(ConstVal::Int(0))) {
3640 **l = Expr::Const(ConstVal::Null);
3641 return;
3642 }
3643 if incomparable(<, &rt) {
3644 let taken = std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
3645 **l = Expr::Cast {
3646 ty: TypeRef::J(JavaType::Object("java/lang/Object".into())),
3647 e: taken,
3648 };
3649 }
3650 }
3651 });
3652 });
3653}
3654
3655pub fn fix_primitive_arg_bridges(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
3669 fn val_ty(e: &Expr, vt: &VarTable) -> JavaType {
3670 match e {
3671 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
3672 other => other.type_ref().erased(),
3673 }
3674 }
3675 #[allow(clippy::type_complexity)]
3676 let is_bool = |t: &JavaType| matches!(t, JavaType::Boolean);
3677 let is_num = |t: &JavaType| {
3678 matches!(
3679 t,
3680 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
3681 | JavaType::Long | JavaType::Float | JavaType::Double
3682 )
3683 };
3684 fn bridge(actual: &JavaType, formal: &JavaType, arg: &Expr) -> Option<Expr> {
3689 match (arg, formal) {
3699 (Expr::Const(ConstVal::Int(b)), JavaType::Float) => {
3700 return Some(Expr::Const(ConstVal::Float(f32::from_bits(*b as u32))));
3701 }
3702 (Expr::Const(ConstVal::Long(b)), JavaType::Double) => {
3703 return Some(Expr::Const(ConstVal::Double(f64::from_bits(*b as u64))));
3704 }
3705 (Expr::Const(ConstVal::Float(f)), JavaType::Int) => {
3706 return Some(Expr::Const(ConstVal::Int(f.to_bits() as i32)));
3707 }
3708 (Expr::Const(ConstVal::Double(d)), JavaType::Long) => {
3709 return Some(Expr::Const(ConstVal::Long(d.to_bits() as i64)));
3710 }
3711 _ => {}
3712 }
3713 if matches!(actual, JavaType::Boolean)
3714 && matches!(
3715 formal,
3716 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
3717 | JavaType::Long | JavaType::Float | JavaType::Double
3718 )
3719 {
3720 Some(Expr::Cast {
3721 ty: TypeRef::J(formal.clone()),
3722 e: Box::new(Expr::Cond {
3723 c: Box::new(arg.clone()),
3724 t: Box::new(Expr::Const(ConstVal::Int(1))),
3725 f: Box::new(Expr::Const(ConstVal::Int(0))),
3726 }),
3727 })
3728 } else if matches!(formal, JavaType::Boolean)
3729 && matches!(
3730 actual,
3731 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
3732 | JavaType::Long | JavaType::Float | JavaType::Double
3733 )
3734 {
3735 Some(Expr::Bin {
3736 op: BinOp::Ne,
3737 l: Box::new(arg.clone()),
3738 r: Box::new(Expr::Const(ConstVal::Int(0))),
3739 ty: Some(TypeRef::J(JavaType::Boolean)),
3740 })
3741 } else if matches!(
3742 actual,
3743 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
3744 | JavaType::Long | JavaType::Float | JavaType::Double
3745 ) && matches!(
3746 formal,
3747 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
3748 | JavaType::Long | JavaType::Float | JavaType::Double
3749 ) && !implicit_widening(actual, formal)
3750 {
3751 Some(Expr::Cast {
3760 ty: TypeRef::J(formal.clone()),
3761 e: Box::new(arg.clone()),
3762 })
3763 } else {
3764 None
3765 }
3766 }
3767 fn fw_resolvable(n: &str) -> bool {
3783 crate::fwdb::exists(n)
3784 }
3785 fn null_arg_cast(a: &mut Expr, f: &JavaType, pool: &DexPool) -> bool {
3786 if !matches!(&*a, Expr::Const(ConstVal::Null)) {
3787 return false;
3788 }
3789 if let JavaType::Object(n) = f {
3790 let resolvable = pool.get(n).is_some() || fw_resolvable(n);
3796 if n.as_ref() != "java/lang/Object" && resolvable {
3797 *a = Expr::Cast {
3798 ty: TypeRef::J(f.clone()),
3799 e: Box::new(Expr::Const(ConstVal::Null)),
3800 };
3801 return true;
3802 }
3803 } else if let JavaType::Array(_) = f {
3804 let mut e: &JavaType = f;
3811 while let JavaType::Array(inner) = e {
3812 e = &**inner;
3813 }
3814 let resolvable = match e {
3815 JavaType::Object(n) => pool.get(n).is_some() || fw_resolvable(n),
3816 _ => true, };
3818 if resolvable {
3819 *a = Expr::Cast {
3820 ty: TypeRef::J(f.clone()),
3821 e: Box::new(Expr::Const(ConstVal::Null)),
3822 };
3823 return true;
3824 }
3825 }
3826 false
3829 }
3830 fn implicit_widening(a: &JavaType, f: &JavaType) -> bool {
3833 use JavaType::*;
3834 match (a, f) {
3835 (Byte, Short) | (Byte, Int) | (Byte, Long) | (Byte, Float) | (Byte, Double) => true,
3836 (Short, Int) | (Short, Long) | (Short, Float) | (Short, Double) => true,
3837 (Char, Int) | (Char, Long) | (Char, Float) | (Char, Double) => true,
3838 (Int, Long) | (Int, Float) | (Int, Double) => true,
3839 (Long, Float) | (Long, Double) => true,
3840 (Float, Double) => true,
3841 (x, y) => x == y,
3842 }
3843 }
3844 let overload_competes = |cls: &str, name: &str, args: &[JavaType]| -> bool {
3853 if let Some(c) = pool.get(cls) {
3854 return c.all_methods().any(|m| {
3855 &*m.name == name
3856 && m.access & (crate::access::ACC_BRIDGE | crate::access::ACC_SYNTHETIC) == 0
3862 && m.parsed_desc()
3863 .is_some_and(|d| d.args.len() == args.len() && d.args != args)
3864 });
3865 }
3866 let _ = args;
3875 false
3876 };
3877 let ctor_formals = |cls: &str, n: usize| -> Option<Vec<JavaType>> {
3880 let c = pool.get(cls)?;
3881 let mut out: Option<Vec<JavaType>> = None;
3882 for m in c.all_methods() {
3883 if &*m.name != "<init>" {
3884 continue;
3885 }
3886 let Some(d) = m.parsed_desc() else { continue };
3887 if d.args.len() != n {
3888 continue;
3889 }
3890 match &out {
3891 None => out = Some(d.args.clone()),
3892 Some(prev) if *prev != d.args => return None,
3893 Some(_) => {}
3894 }
3895 }
3896 out
3897 };
3898 walk_stmt_exprs(body, &mut |e| {
3899 deep_rewrite(e, &mut |x| match x {
3900 Expr::Method {
3901 cls,
3902 name,
3903 desc,
3904 args,
3905 is_static,
3906 ..
3907 } => {
3908 if args.len() != desc.args.len() {
3909 return;
3910 }
3911 let _ = is_static;
3912 let mut competes: Option<bool> = None;
3913 for (a, f) in args.iter_mut().zip(desc.args.iter()) {
3914 if null_arg_cast(a, f, pool) {
3915 continue;
3916 }
3917 if matches!(a, Expr::Cast { .. }) {
3918 continue;
3919 }
3920 let subtype_arg = match (&val_ty(a, vt), f) {
3931 (JavaType::Object(an), JavaType::Object(fn_)) => {
3932 (an != fn_ && pool.is_subtype(an, fn_))
3937 || (fn_.as_ref() == "java/lang/Object"
3938 && an.as_ref() != "java/lang/Object")
3939 }
3940 (JavaType::Array(_), JavaType::Object(fn_))
3941 if fn_.as_ref() == "java/lang/Object" =>
3942 {
3943 true
3944 }
3945 _ => false,
3946 };
3947 let null_obj =
3961 matches!(a, Expr::Const(ConstVal::Null)) && matches!(f, JavaType::Object(_));
3962 let framework_owner = pool.get(cls.as_ref()).is_none();
3963 let fw_null_ambiguous = framework_owner
3964 && matches!(
3965 (cls.as_ref(), name.as_ref()),
3966 ("java/lang/StringBuilder", "append")
3967 | ("java/lang/StringBuffer", "append")
3968 | ("java/io/PrintStream", "println")
3969 | ("java/io/PrintStream", "print")
3970 | ("java/io/PrintWriter", "println")
3971 | ("java/io/PrintWriter", "print")
3972 | ("java/lang/String", "valueOf")
3973 );
3974 let fw_subtype_pinnable = framework_owner
3985 && subtype_arg
3986 && !matches!(f, JavaType::Object(fn_) if fn_.as_ref() == "java/lang/Object")
3987 && matches!(
3988 (cls.as_ref(), name.as_ref()),
3989 ("android/content/Intent", "putExtra")
3990 | ("java/util/concurrent/ScheduledExecutorService", "schedule")
3991 | ("java/util/concurrent/ExecutorService", "submit")
3992 | ("java/util/concurrent/ExecutorService", "invokeAll")
3993 );
3994 if fw_subtype_pinnable
3995 || (subtype_arg
3996 && *competes.get_or_insert_with(|| {
3997 overload_competes(cls, name, &desc.args)
3998 }))
3999 || (null_obj
4000 && (fw_null_ambiguous
4001 || *competes.get_or_insert_with(|| {
4002 overload_competes(cls, name, &desc.args)
4003 })))
4004 {
4005 let old =
4006 std::mem::replace(a, Expr::Const(ConstVal::Null));
4007 *a = Expr::Cast {
4008 ty: TypeRef::J(f.clone()),
4009 e: Box::new(old),
4010 };
4011 continue;
4012 }
4013 if !is_bool(&val_ty(a, vt)) && !is_num(&val_ty(a, vt)) {
4014 continue;
4015 }
4016 if !is_bool(f) && !is_num(f) {
4017 continue;
4018 }
4019 if let Some(b) = bridge(&val_ty(a, vt), f, a) {
4020 *a = b;
4021 }
4022 }
4023 }
4024 Expr::New { cls, args, arg_tys, .. } => {
4025 let formals_owned = if arg_tys.len() == args.len() && !arg_tys.is_empty() {
4026 Some(arg_tys.clone())
4027 } else {
4028 ctor_formals(cls, args.len())
4029 };
4030 if let Some(formals) = formals_owned {
4031 for (a, f) in args.iter_mut().zip(formals.iter()) {
4032 if null_arg_cast(a, f, pool) {
4033 continue;
4034 }
4035 if matches!(a, Expr::Cast { .. }) {
4036 continue;
4037 }
4038 if !is_bool(f) && !is_num(f) {
4039 continue;
4040 }
4041 if let Some(b) = bridge(&val_ty(a, vt), f, a) {
4042 *a = b;
4043 }
4044 }
4045 }
4046 }
4047 _ => {}
4048 });
4049 });
4050}
4051
4052pub fn deshadow_locals(vt: &mut VarTable, pool: &crate::DexPool, class_name: &str, body: &Stmt) {
4064 let segs = pool.root_pkg_segs();
4065 let obscured = crate::classdec::obscured_simples_snapshot();
4069 let self_pkg = class_name.rfind('/').map(|i| &class_name[..i]).unwrap_or("");
4076 let mut same_pkg_simples: jdc_core::FxHashSet<String> = jdc_core::FxHashSet::default();
4077 visit_all_exprs(body, &mut |x| {
4078 let cls = match x {
4079 Expr::Field { cls, is_static: true, .. } => Some(cls.as_ref()),
4080 Expr::Method { cls, is_static: true, .. } => Some(cls.as_ref()),
4081 _ => None,
4082 };
4083 if let Some(c) = cls {
4084 let pkg = c.rfind('/').map(|i| &c[..i]).unwrap_or("");
4085 if pkg == self_pkg {
4086 let simple = &c[c.rfind('/').map(|i| i + 1).unwrap_or(0)..];
4087 let head = simple.split('$').next().unwrap_or(simple);
4088 if !head.is_empty() {
4089 same_pkg_simples.insert(head.to_string());
4090 }
4091 }
4092 }
4093 });
4094 let hit = |name: &str| {
4095 segs.contains(name) || obscured.contains(name) || same_pkg_simples.contains(name)
4096 };
4097 if (segs.is_empty() && obscured.is_empty() && same_pkg_simples.is_empty())
4098 || !vt.vars.iter().any(|v| hit(&v.name))
4099 {
4100 return;
4101 }
4102 let mut taken: jdc_core::FxHashSet<String> =
4103 vt.vars.iter().map(|v| v.name.clone()).collect();
4104 for v in vt.vars.iter_mut() {
4105 if !hit(&v.name) {
4106 continue;
4107 }
4108 let mut k = 0u32;
4109 let cand = loop {
4110 k += 1;
4111 let c = if k == 1 {
4112 format!("{}x", v.name)
4113 } else {
4114 format!("{}x{}", v.name, k)
4115 };
4116 if !taken.contains(&c) {
4117 break c;
4118 }
4119 };
4120 taken.insert(cand.clone());
4121 v.name = cand;
4122 }
4123}
4124
4125pub fn fix_ctor_this_aliases(body: &mut Stmt, vt: &VarTable) {
4151 let Some(this_var) = vt
4152 .vars
4153 .iter()
4154 .find(|v| v.is_param && v.name == "this")
4155 .map(|v| v.id)
4156 else {
4157 return;
4158 };
4159 let mut analysis = VarAnalysis {
4160 assigns: Vec::new(),
4161 reads: Vec::new(),
4162 values: Vec::new(),
4163 };
4164 analyze_vars(body, &mut analysis);
4165 let n = vt.vars.len().max(analysis.assigns.len());
4166 let mut alias = vec![false; n];
4167 for _ in 0..4 {
4168 let mut changed = false;
4169 for v in 0..n as u32 {
4170 if alias[v as usize] {
4171 continue;
4172 }
4173 if analysis.assigns.get(v as usize).copied().unwrap_or(0) != 1 {
4174 continue;
4175 }
4176 let Some(Expr::Local { var: src, .. }) = analysis.values.get(v as usize).and_then(|o| o.as_ref()) else {
4177 continue;
4178 };
4179 if *src == this_var
4180 || (*src < n as u32 && alias[*src as usize])
4181 {
4182 alias[v as usize] = true;
4183 changed = true;
4184 }
4185 }
4186 if !changed {
4187 break;
4188 }
4189 }
4190 if !alias.iter().any(|&b| b) {
4191 return;
4192 }
4193 walk_stmt_exprs(body, &mut |e| {
4194 deep_rewrite_reads(e, &mut |x| {
4195 if let Expr::Local { var, .. } = x {
4196 if (*var as usize) < n && alias[*var as usize] {
4197 *x = Expr::This;
4198 }
4199 }
4200 });
4201 });
4202}
4203
4204pub fn fix_this0_owners(body: &mut Stmt, class_name: &str) {
4205 walk_stmt_exprs(body, &mut |e| {
4206 deep_rewrite(e, &mut |x| {
4207 if let Expr::Field { owner: Some(_), cls, name, .. } = x {
4208 if name.as_ref() == "this$0" && cls.as_ref() == class_name {
4209 if let Expr::Field { owner, .. } = x {
4210 *owner = None;
4211 }
4212 }
4213 }
4214 });
4215 });
4216}
4217
4218pub fn rescue_primitive_receivers(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
4219 fn prim(t: &JavaType) -> bool {
4220 match t {
4221 JavaType::Int
4222 | JavaType::Long
4223 | JavaType::Short
4224 | JavaType::Byte
4225 | JavaType::Char
4226 | JavaType::Float
4227 | JavaType::Double
4228 | JavaType::Boolean => true,
4229 JavaType::Object(n) => matches!(
4233 n.as_ref(),
4234 "java/lang/Boolean"
4235 | "java/lang/String"
4236 | "java/lang/Integer"
4237 | "java/lang/Long"
4238 | "java/lang/Short"
4239 | "java/lang/Byte"
4240 | "java/lang/Character"
4241 | "java/lang/Float"
4242 | "java/lang/Double"
4243 ),
4244 _ => false,
4245 }
4246 }
4247 walk_stmt_exprs(body, &mut |e| {
4248 deep_rewrite(e, &mut |x| {
4249 let (recv_var, recv_ty) = {
4251 let o: &Expr = match &*x {
4252 Expr::Method { owner: Some(o), .. } => o,
4253 Expr::Field { owner: Some(o), is_static: false, .. } => o,
4254 _ => return,
4255 };
4256 let Expr::Local { var, ty } = o else {
4257 return;
4258 };
4259 let vt_ty = if (*var as usize) < vt.vars.len() {
4260 vt.var(*var).ty.erased()
4261 } else {
4262 ty.erased()
4263 };
4264 if !prim(&vt_ty) {
4265 return;
4266 }
4267 (*var, vt_ty)
4268 };
4269 let cls: std::sync::Arc<str> = match &*x {
4270 Expr::Method { cls, .. } | Expr::Field { cls, .. } => cls.clone(),
4271 _ => return,
4272 };
4273 if let JavaType::Object(n) = &recv_ty {
4282 if n.as_ref() == cls.as_ref() || pool.is_subtype(n.as_ref(), cls.as_ref()) {
4283 return;
4284 }
4285 }
4286 let mut cands: Vec<u32> = Vec::new();
4288 for v in &vt.vars {
4289 if let JavaType::Object(n) = v.ty.erased() {
4290 if n.as_ref() == cls.as_ref()
4291 || pool.is_subtype(n.as_ref(), cls.as_ref())
4292 {
4293 cands.push(v.id);
4294 }
4295 }
4296 }
4297 let id = if cands.len() == 1 {
4298 cands[0]
4299 } else if cands.len() > 1 {
4300 let slot = if (recv_var as usize) < vt.vars.len() {
4305 Some(vt.var(recv_var).slot)
4306 } else {
4307 None
4308 };
4309 let mut hits: Vec<u32> = match slot {
4310 Some(sl) => cands
4311 .iter()
4312 .copied()
4313 .filter(|&c| {
4314 c != recv_var
4315 && (c as usize) < vt.vars.len()
4316 && vt.var(c).slot == sl
4317 })
4318 .collect(),
4319 None => Vec::new(),
4320 };
4321 if hits.is_empty() {
4322 hits = cands
4325 .iter()
4326 .copied()
4327 .filter(|&c| c != recv_var)
4328 .collect();
4329 }
4330 hits.sort_unstable();
4331 hits.dedup();
4332 if hits.is_empty() {
4333 return;
4334 }
4335 *hits.last().unwrap()
4336 } else {
4337 if cands.is_empty() {
4344 return;
4345 }
4346 let slot = if (recv_var as usize) < vt.vars.len() {
4347 vt.var(recv_var).slot
4348 } else {
4349 return;
4350 };
4351 let mut hits: Vec<u32> = cands
4352 .into_iter()
4353 .filter(|&c| {
4354 c != recv_var
4355 && (c as usize) < vt.vars.len()
4356 && vt.var(c).slot == slot
4357 })
4358 .collect();
4359 hits.sort_unstable();
4360 hits.dedup();
4361 if hits.len() != 1 {
4362 return;
4363 }
4364 hits[0]
4365 };
4366 let o: &mut Box<Expr> = match x {
4368 Expr::Method { owner: Some(o), .. } => o,
4369 Expr::Field { owner: Some(o), is_static: false, .. } => o,
4370 _ => return,
4371 };
4372 if let Expr::Local { var, ty } = &mut **o {
4373 if *var == recv_var {
4374 *var = id;
4375 *ty = vt.vars[id as usize].ty.clone();
4376 }
4377 }
4378 });
4379 });
4380
4381}
4382
4383pub fn rescue_arg_return_swaps(
4389 body: &mut Stmt,
4390 vt: &VarTable,
4391 pool: &crate::DexPool,
4392 ret_ty: &JavaType,
4393) {
4394 let mut top_ids: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
4402 for v in &vt.vars {
4403 if v.is_param {
4404 top_ids.insert(v.id);
4405 }
4406 }
4407 if let Stmt::Block(stmts) = body {
4408 for st in stmts.iter() {
4409 if let Stmt::LocalDef { var, .. } = st {
4410 top_ids.insert(*var);
4411 }
4412 }
4413 }
4414 let ret_wants_ref = match ret_ty {
4415 JavaType::Object(n) => n.as_ref() != "java/lang/Object",
4416 JavaType::Array(_) => true,
4417 _ => false,
4418 };
4419 let try_swap = |a: &mut Expr, want: &JavaType| {
4420 let Expr::Local { var, ty } = &mut *a else {
4421 return;
4422 };
4423 if (*var as usize) >= vt.vars.len() {
4424 return;
4425 }
4426 let avt = vt.var(*var).ty.erased();
4427 let avt_ref = matches!(avt, JavaType::Object(_) | JavaType::Array(_));
4428 let want_arr = matches!(want, JavaType::Array(_));
4429 if avt_ref && !(avt == JavaType::Object("java/lang/Object".into())) {
4432 return;
4433 }
4434 if avt_ref && !matches!(want, JavaType::Object(_)) {
4435 return;
4436 }
4437 let slot = vt.var(*var).slot;
4438 let mut hits: Vec<u32> = Vec::new();
4439 for v in &vt.vars {
4440 if v.slot != slot || !top_ids.contains(&v.id) || v.id == *var {
4441 continue;
4442 }
4443 let t = v.ty.erased();
4444 let ok = match (&t, want) {
4445 (JavaType::Object(n), JavaType::Object(w)) => {
4446 n.as_ref() == w.as_ref()
4447 || pool.is_subtype(n.as_ref(), w.as_ref())
4448 }
4449 (JavaType::Array(_), JavaType::Array(_)) => want_arr,
4450 _ => false,
4451 };
4452 if ok {
4453 hits.push(v.id);
4454 }
4455 }
4456 hits.sort_unstable();
4457 hits.dedup();
4458 if hits.is_empty() {
4459 return;
4460 }
4461 let id = *hits.last().unwrap();
4467 *var = id;
4468 *ty = vt.vars[id as usize].ty.clone();
4469 };
4470 walk_stmt_exprs(body, &mut |e| {
4471 deep_rewrite(e, &mut |x| {
4472 match x {
4473 Expr::Method { desc, args, is_dynamic: false, .. } => {
4474 for (i, a) in args.iter_mut().enumerate() {
4475 if let Some(f) = desc.args.get(i) {
4476 if matches!(f, JavaType::Object(n) if n.as_ref() != "java/lang/Object")
4477 || matches!(f, JavaType::Array(_))
4478 {
4479 try_swap(a, f);
4480 }
4481 }
4482 }
4483 }
4484 Expr::New { arg_tys, args, .. } => {
4485 for (i, a) in args.iter_mut().enumerate() {
4486 if let Some(f) = arg_tys.get(i) {
4487 if matches!(f, JavaType::Object(n) if n.as_ref() != "java/lang/Object")
4488 || matches!(f, JavaType::Array(_))
4489 {
4490 try_swap(a, f);
4491 }
4492 }
4493 }
4494 }
4495 _ => {}
4496 }
4497 });
4498 });
4499 if ret_wants_ref {
4500 walk_mut_deep(body, &mut |st| {
4501 if let Stmt::Return(Some(e)) = st {
4502 try_swap(e, ret_ty);
4503 }
4504 });
4505 }
4506}
4507
4508pub fn fix_ref_null_assigns(vt: &VarTable, body: &mut Stmt) {
4523 let is_ref_var = |var: u32| -> bool {
4524 (var as usize) < vt.vars.len()
4525 && matches!(
4526 vt.vars[var as usize].ty.erased(),
4527 JavaType::Object(_) | JavaType::Array(_)
4528 )
4529 };
4530 walk_mut_deep(body, &mut |st| match st {
4531 Stmt::ExprStmt(Expr::Assign {
4532 target,
4533 value,
4534 op: AssignOp::Plain,
4535 ..
4536 }) => {
4537 if let Expr::Local { var, .. } = &**target {
4538 if is_ref_var(*var) && matches!(&**value, Expr::Const(ConstVal::Int(0))) {
4539 **value = Expr::Const(ConstVal::Null);
4540 }
4541 }
4542 }
4543 Stmt::LocalDef {
4544 var,
4545 init: Some(e),
4546 ..
4547 } => {
4548 if is_ref_var(*var) && matches!(e, Expr::Const(ConstVal::Int(0))) {
4549 *e = Expr::Const(ConstVal::Null);
4550 }
4551 }
4552 _ => {}
4553 });
4554}
4555
4556pub fn fix_ref_array_null_consts(body: &mut Stmt) {
4557 walk_stmt_exprs(body, &mut |e| {
4558 deep_rewrite(e, &mut |x| {
4559 if let Expr::NewArray { elem, init: Some(list), .. } = x {
4560 if matches!(elem.erased(), JavaType::Object(_) | JavaType::Array(_)) {
4561 for slot in list.iter_mut() {
4562 if matches!(&*slot, Expr::Const(ConstVal::Int(0))) {
4563 *slot = Expr::Const(ConstVal::Null);
4564 }
4565 }
4566 }
4567 }
4568 });
4569 });
4570}
4571
4572pub fn fix_null_sentinels(body: &mut Stmt, vt: &VarTable, ret: &JavaType) {
4611 #[derive(Clone, Copy, PartialEq)]
4612 enum WK {
4613 Zero,
4614 Copy(u32),
4615 Other,
4616 }
4617 fn is_ref_jt(t: &JavaType) -> bool {
4618 matches!(t, JavaType::Object(_) | JavaType::Array(_))
4619 }
4620 fn cls_write(e: &Expr) -> WK {
4621 match e {
4622 Expr::Const(ConstVal::Int(0)) => WK::Zero,
4623 Expr::Local { var, .. } => WK::Copy(*var),
4624 _ => WK::Other,
4625 }
4626 }
4627 fn expr_writes(e: &Expr, writes: &mut std::collections::HashMap<u32, Vec<WK>>) {
4628 visit_exprs(e, &mut |x| match x {
4629 Expr::Assign { target, op, value } => {
4630 if let Expr::Local { var, .. } = &**target {
4631 let k = if matches!(op, AssignOp::Plain) {
4632 cls_write(value)
4633 } else {
4634 WK::Other
4635 };
4636 writes.entry(*var).or_default().push(k);
4637 }
4638 }
4639 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
4640 if let Expr::Local { var, .. } = &**inner {
4641 writes.entry(*var).or_default().push(WK::Other);
4642 }
4643 }
4644 _ => {}
4645 });
4646 }
4647 fn collect_writes(
4648 s: &Stmt,
4649 writes: &mut std::collections::HashMap<u32, Vec<WK>>,
4650 has_zero: &mut bool,
4651 ) {
4652 fn note(e: &Expr, has_zero: &mut bool, writes: &mut std::collections::HashMap<u32, Vec<WK>>) {
4653 visit_exprs(e, &mut |x| {
4654 if let Expr::Const(ConstVal::Int(0)) = x {
4655 *has_zero = true;
4656 }
4657 });
4658 expr_writes(e, writes);
4659 }
4660 match s {
4661 Stmt::Block(v) => {
4662 for x in v {
4663 collect_writes(x, writes, has_zero);
4664 }
4665 }
4666 Stmt::LocalDef { var, init, .. } => {
4667 if let Some(e) = init {
4668 if matches!(e, Expr::Const(ConstVal::Int(0))) {
4669 *has_zero = true;
4670 }
4671 writes.entry(*var).or_default().push(cls_write(e));
4672 }
4673 }
4674 Stmt::ExprStmt(e) => note(e, has_zero, writes),
4675 Stmt::Return(e) => {
4676 if let Some(x) = e {
4677 note(x, has_zero, writes);
4678 }
4679 }
4680 Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
4681 note(e, has_zero, writes);
4682 }
4683 Stmt::TernaryValue { e } => note(e, has_zero, writes),
4684 Stmt::If { cond, then_stmt, else_stmt } => {
4685 note(cond, has_zero, writes);
4686 collect_writes(then_stmt, writes, has_zero);
4687 if let Some(b) = else_stmt {
4688 collect_writes(b, writes, has_zero);
4689 }
4690 }
4691 Stmt::While { cond, body } | Stmt::DoWhile { body, cond } => {
4692 note(cond, has_zero, writes);
4693 collect_writes(body, writes, has_zero);
4694 }
4695 Stmt::For { init, cond, update, body } => {
4696 for x in init {
4697 collect_writes(x, writes, has_zero);
4698 }
4699 if let Some(c) = cond {
4700 note(c, has_zero, writes);
4701 }
4702 for u in update {
4703 note(u, has_zero, writes);
4704 }
4705 collect_writes(body, writes, has_zero);
4706 }
4707 Stmt::ForEach { var, iterable, body, .. } => {
4708 writes.entry(*var).or_default().push(WK::Other);
4709 note(iterable, has_zero, writes);
4710 collect_writes(body, writes, has_zero);
4711 }
4712 Stmt::Switch { selector, cases, default, .. } => {
4713 note(selector, has_zero, writes);
4714 for c in cases {
4715 if let Some(g) = &c.guard {
4716 note(g, has_zero, writes);
4717 }
4718 for x in &c.body {
4719 collect_writes(x, writes, has_zero);
4720 }
4721 }
4722 if let Some(d) = default {
4723 collect_writes(d, writes, has_zero);
4724 }
4725 }
4726 Stmt::Try { body, catches, finally } => {
4727 collect_writes(body, writes, has_zero);
4728 for c in catches {
4729 writes.entry(c.var).or_default().push(WK::Other);
4730 collect_writes(&c.body, writes, has_zero);
4731 }
4732 if let Some(f) = finally {
4733 collect_writes(f, writes, has_zero);
4734 }
4735 }
4736 Stmt::TryWithResources { resources, body, catches, finally, .. } => {
4737 for r in resources {
4738 collect_writes(r, writes, has_zero);
4739 }
4740 collect_writes(body, writes, has_zero);
4741 for c in catches {
4742 writes.entry(c.var).or_default().push(WK::Other);
4743 collect_writes(&c.body, writes, has_zero);
4744 }
4745 if let Some(f) = finally {
4746 collect_writes(f, writes, has_zero);
4747 }
4748 }
4749 Stmt::Assert { cond, msg } => {
4750 note(cond, has_zero, writes);
4751 if let Some(m) = msg {
4752 note(m, has_zero, writes);
4753 }
4754 }
4755 Stmt::Synchronized { lock, body } => {
4756 note(lock, has_zero, writes);
4757 collect_writes(body, writes, has_zero);
4758 }
4759 Stmt::Labeled { body, .. } => collect_writes(body, writes, has_zero),
4760 _ => {}
4761 }
4762 }
4763
4764 let mut writes: std::collections::HashMap<u32, Vec<WK>> =
4765 std::collections::HashMap::new();
4766 let mut has_zero = false;
4767 collect_writes(body, &mut writes, &mut has_zero);
4768
4769 let mut eligible: HashSet<u32> = HashSet::default();
4771 let int_typed = |v: u32| {
4772 (v as usize) < vt.vars.len() && vt.vars[v as usize].ty.erased() == JavaType::Int
4773 };
4774 loop {
4775 let mut changed = false;
4776 for (&v, ws) in writes.iter() {
4777 if eligible.contains(&v) || ws.is_empty() || !int_typed(v) {
4778 continue;
4779 }
4780 if ws.iter().all(|k| match k {
4781 WK::Zero => true,
4782 WK::Copy(u) => *u != v && eligible.contains(u),
4783 WK::Other => false,
4784 }) {
4785 eligible.insert(v);
4786 changed = true;
4787 }
4788 }
4789 if !changed {
4790 break;
4791 }
4792 }
4793 if eligible.is_empty() && !has_zero {
4794 return; }
4796
4797 #[derive(Clone, Copy, PartialEq)]
4798 enum Mode {
4799 Scan,
4800 Rewrite,
4801 }
4802 struct St<'a> {
4803 mode: Mode,
4804 vt: &'a VarTable,
4805 eligible: &'a HashSet<u32>,
4806 ref_read: HashSet<u32>,
4807 dead: HashSet<u32>,
4808 sentinels: HashSet<u32>,
4809 n_lit: usize,
4810 n_var: usize,
4811 }
4812 fn arg_ctx(t: &JavaType) -> Option<JavaType> {
4816 match t {
4817 JavaType::Object(n) if n.as_ref() == "java/lang/Object" => None,
4818 t if is_ref_jt(t) => Some(t.clone()),
4819 _ => None,
4820 }
4821 }
4822 fn walk_expr(e: &mut Expr, ctx: Option<JavaType>, st: &mut St) {
4823 walk_expr_c(e, ctx, false, st);
4824 }
4825 fn walk_expr_c(e: &mut Expr, ctx: Option<JavaType>, cast_arg: bool, st: &mut St) {
4831 let slot_ref = ctx.as_ref().is_some_and(is_ref_jt);
4832 fn mk_null(ctx: &Option<JavaType>, cast_arg: bool) -> Expr {
4833 match (cast_arg, ctx) {
4834 (true, Some(t)) => Expr::Cast {
4835 ty: TypeRef::J(t.clone()),
4836 e: Box::new(Expr::Const(ConstVal::Null)),
4837 },
4838 _ => Expr::Const(ConstVal::Null),
4839 }
4840 }
4841 match e {
4842 Expr::Const(ConstVal::Int(0)) if slot_ref => {
4843 if st.mode == Mode::Rewrite {
4844 *e = mk_null(&ctx, cast_arg);
4845 st.n_lit += 1;
4846 }
4847 return;
4848 }
4849 Expr::Local { var, .. } => {
4850 if st.eligible.contains(var) {
4851 if slot_ref {
4852 st.ref_read.insert(*var);
4853 } else {
4854 st.dead.insert(*var);
4855 }
4856 }
4857 if st.mode == Mode::Rewrite && slot_ref && st.sentinels.contains(var) {
4858 *e = mk_null(&ctx, cast_arg);
4859 st.n_var += 1;
4860 }
4861 return;
4862 }
4863 _ => {}
4864 }
4865 match e {
4866 Expr::Method { owner, desc, args, .. } => {
4867 if let Some(o) = owner {
4868 walk_expr(o, None, st);
4869 }
4870 for (i, a) in args.iter_mut().enumerate() {
4871 let c = desc.args.get(i).and_then(arg_ctx);
4872 walk_expr_c(a, c, true, st);
4873 }
4874 }
4875 Expr::Invokedynamic { desc, args, .. } => {
4876 for (i, a) in args.iter_mut().enumerate() {
4877 let c = desc.args.get(i).and_then(arg_ctx);
4878 walk_expr_c(a, c, true, st);
4879 }
4880 }
4881 Expr::New { args, arg_tys, .. } => {
4882 for (i, a) in args.iter_mut().enumerate() {
4883 let c = arg_tys.get(i).and_then(arg_ctx);
4884 walk_expr_c(a, c, true, st);
4885 }
4886 }
4887 Expr::AnonNew { args, .. } => {
4888 for a in args.iter_mut() {
4889 walk_expr(a, None, st);
4890 }
4891 }
4892 Expr::Assign { target, value, .. } => {
4893 if let Expr::Local { var: u, .. } = &**target {
4897 if st.eligible.contains(u) {
4898 return;
4899 }
4900 }
4901 let vctx = match &**target {
4902 Expr::Field { ty, .. } => {
4903 let t = ty.erased();
4904 is_ref_jt(&t).then_some(t)
4905 }
4906 Expr::ArrayIndex { array, .. } => match array.type_ref().erased() {
4907 JavaType::Array(inner) if is_ref_jt(&inner) => Some(*inner),
4908 _ => None,
4909 },
4910 _ => None,
4911 };
4912 walk_expr(target, None, st);
4913 walk_expr(value, vctx, st);
4914 }
4915 Expr::Cast { ty, e: inner } => {
4916 let t = ty.erased();
4917 let c = is_ref_jt(&t).then_some(t);
4918 walk_expr(inner, c, st);
4919 }
4920 Expr::InstanceOf { e: inner, .. } => {
4921 walk_expr(inner, Some(JavaType::Object("java/lang/Object".into())), st);
4922 }
4923 Expr::Cond { c, t, f } => {
4924 walk_expr(c, None, st);
4925 let tc = ctx.clone();
4926 walk_expr(t, tc, st);
4927 walk_expr(f, ctx, st);
4928 }
4929 Expr::NewArray { elem, dims, init, .. } => {
4930 for d in dims.iter_mut() {
4931 walk_expr(d, None, st);
4932 }
4933 if let Some(list) = init {
4934 let et = elem.erased();
4935 let c = is_ref_jt(&et).then_some(et);
4936 for slot in list.iter_mut() {
4937 walk_expr(slot, c.clone(), st);
4938 }
4939 }
4940 }
4941 Expr::NewMultiArray { dims, .. } => {
4942 for d in dims.iter_mut() {
4943 walk_expr(d, None, st);
4944 }
4945 }
4946 Expr::ArrayIndex { array, index } => {
4947 walk_expr(array, None, st);
4948 walk_expr(index, None, st);
4949 }
4950 Expr::Field { owner, .. } => {
4951 if let Some(o) = owner {
4952 walk_expr(o, None, st);
4953 }
4954 }
4955 Expr::Bin { l, r, .. } => {
4956 walk_expr(l, None, st);
4957 walk_expr(r, None, st);
4958 }
4959 Expr::Un { e: inner, .. } => walk_expr(inner, None, st),
4960 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
4961 walk_expr(inner, None, st);
4962 }
4963 Expr::StringConcat(parts) => {
4964 for p in parts.iter_mut() {
4965 if let ConcatPart::Str(x) = p {
4966 walk_expr(x, None, st);
4967 }
4968 }
4969 }
4970 Expr::Lambda(l) => {
4971 for c in l.captures.iter_mut() {
4972 walk_expr(c, None, st);
4973 }
4974 }
4975 _ => {}
4976 }
4977 }
4978 fn walk_stmt(s: &mut Stmt, st: &mut St, ret: &JavaType) {
4979 match s {
4980 Stmt::Block(v) => {
4981 for x in v.iter_mut() {
4982 walk_stmt(x, st, ret);
4983 }
4984 }
4985 Stmt::ExprStmt(e) => walk_expr(e, None, st),
4986 Stmt::LocalDef { var, init, .. } => {
4987 if let Some(e) = init {
4988 let ctx = if (*var as usize) < st.vt.vars.len() {
4989 let t = st.vt.vars[*var as usize].ty.erased();
4990 is_ref_jt(&t).then_some(t)
4991 } else {
4992 None
4993 };
4994 walk_expr(e, ctx, st);
4995 }
4996 }
4997 Stmt::Return(e) => {
4998 if let Some(x) = e {
4999 let c = is_ref_jt(ret).then(|| ret.clone());
5000 walk_expr(x, c, st);
5001 }
5002 }
5003 Stmt::Throw(e) => {
5004 walk_expr(e, Some(JavaType::Object("java/lang/Throwable".into())), st);
5005 }
5006 Stmt::If { cond, then_stmt, else_stmt } => {
5007 walk_expr(cond, None, st);
5008 walk_stmt(then_stmt, st, ret);
5009 if let Some(b) = else_stmt {
5010 walk_stmt(b, st, ret);
5011 }
5012 }
5013 Stmt::While { cond, body } => {
5014 walk_expr(cond, None, st);
5015 walk_stmt(body, st, ret);
5016 }
5017 Stmt::DoWhile { body, cond } => {
5018 walk_stmt(body, st, ret);
5019 walk_expr(cond, None, st);
5020 }
5021 Stmt::For { init, cond, update, body } => {
5022 for x in init.iter_mut() {
5023 walk_stmt(x, st, ret);
5024 }
5025 if let Some(c) = cond {
5026 walk_expr(c, None, st);
5027 }
5028 for u in update.iter_mut() {
5029 walk_expr(u, None, st);
5030 }
5031 walk_stmt(body, st, ret);
5032 }
5033 Stmt::ForEach { iterable, body, .. } => {
5034 walk_expr(iterable, None, st);
5035 walk_stmt(body, st, ret);
5036 }
5037 Stmt::Switch { selector, cases, default, .. } => {
5038 walk_expr(selector, None, st);
5039 for c in cases.iter_mut() {
5040 if let Some(g) = &mut c.guard {
5041 walk_expr(g, None, st);
5042 }
5043 for x in c.body.iter_mut() {
5044 walk_stmt(x, st, ret);
5045 }
5046 }
5047 if let Some(d) = default {
5048 walk_stmt(d, st, ret);
5049 }
5050 }
5051 Stmt::Try { body, catches, finally } => {
5052 walk_stmt(body, st, ret);
5053 for c in catches.iter_mut() {
5054 walk_stmt(&mut c.body, st, ret);
5055 }
5056 if let Some(f) = finally {
5057 walk_stmt(f, st, ret);
5058 }
5059 }
5060 Stmt::TryWithResources { resources, body, catches, finally, .. } => {
5061 for r in resources.iter_mut() {
5062 walk_stmt(r, st, ret);
5063 }
5064 walk_stmt(body, st, ret);
5065 for c in catches.iter_mut() {
5066 walk_stmt(&mut c.body, st, ret);
5067 }
5068 if let Some(f) = finally {
5069 walk_stmt(f, st, ret);
5070 }
5071 }
5072 Stmt::Assert { cond, msg } => {
5073 walk_expr(cond, None, st);
5074 if let Some(m) = msg {
5075 walk_expr(m, None, st);
5076 }
5077 }
5078 Stmt::Synchronized { lock, body } => {
5079 walk_expr(lock, Some(JavaType::Object("java/lang/Object".into())), st);
5080 walk_stmt(body, st, ret);
5081 }
5082 Stmt::TernaryValue { e } => walk_expr(e, None, st),
5083 Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
5084 walk_expr(e, Some(JavaType::Object("java/lang/Object".into())), st);
5085 }
5086 Stmt::Labeled { body, .. } => walk_stmt(body, st, ret),
5087 _ => {}
5088 }
5089 }
5090
5091 let mut st = St {
5092 mode: Mode::Scan,
5093 vt,
5094 eligible: &eligible,
5095 ref_read: HashSet::default(),
5096 dead: HashSet::default(),
5097 sentinels: HashSet::default(),
5098 n_lit: 0,
5099 n_var: 0,
5100 };
5101 if !eligible.is_empty() {
5102 walk_stmt(body, &mut st, ret);
5103 st.sentinels = eligible
5104 .iter()
5105 .copied()
5106 .filter(|v| st.ref_read.contains(v) && !st.dead.contains(v))
5107 .collect();
5108 }
5109 if st.sentinels.is_empty() && !has_zero {
5110 return;
5111 }
5112 st.mode = Mode::Rewrite;
5113 walk_stmt(body, &mut st, ret);
5114 if std::env::var_os("DDC_SENT").is_some() {
5115 eprintln!(
5116 "[sent] elig={} confirmed={} lit={} var={}",
5117 eligible.len(),
5118 st.sentinels.len(),
5119 st.n_lit,
5120 st.n_var
5121 );
5122 }
5123}
5124
5125pub fn fix_int_operand_bridges(body: &mut Stmt, vt: &VarTable) {
5133 fn side_bool(e: &Expr, vt: &VarTable) -> bool {
5134 matches!(
5135 match e {
5136 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
5137 other => other.type_ref().erased(),
5138 },
5139 JavaType::Boolean
5140 )
5141 }
5142 fn side_int(e: &Expr, vt: &VarTable) -> bool {
5143 matches!(
5144 match e {
5145 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
5146 other => other.type_ref().erased(),
5147 },
5148 JavaType::Int | JavaType::Short | JavaType::Byte | JavaType::Char | JavaType::Long
5149 )
5150 }
5151 fn wrap(e: Box<Expr>) -> Box<Expr> {
5152 Box::new(Expr::Cond {
5153 c: e,
5154 t: Box::new(Expr::Const(ConstVal::Int(1))),
5155 f: Box::new(Expr::Const(ConstVal::Int(0))),
5156 })
5157 }
5158 walk_stmt_exprs(body, &mut |e| {
5159 deep_rewrite(e, &mut |x| {
5160 if let Expr::Assign { target, value, op: AssignOp::Plain } = x {
5167 let tgt_ty: Option<JavaType> = match &**target {
5168 Expr::Field { ty, .. } => Some(ty.erased()),
5169 Expr::ArrayIndex { array, .. } => {
5170 match array.type_ref().erased() {
5171 JavaType::Array(el) => Some(el.as_ref().clone()),
5172 _ => None,
5173 }
5174 }
5175 Expr::Local { var, .. } => {
5180 if (*var as usize) < vt.vars.len() {
5181 Some(vt.var(*var).ty.erased())
5182 } else {
5183 None
5184 }
5185 }
5186 _ => None,
5187 };
5188 if let Some(t) = tgt_ty {
5189 let tgt_int = matches!(
5190 t,
5191 JavaType::Int
5192 | JavaType::Short
5193 | JavaType::Byte
5194 | JavaType::Char
5195 | JavaType::Long
5196 );
5197 if tgt_int && side_bool(value, vt) && !side_int(value, vt) {
5198 let taken = std::mem::replace(
5199 value,
5200 Box::new(Expr::Const(ConstVal::Null)),
5201 );
5202 let mut wrapped = wrap(taken);
5203 if !matches!(t, JavaType::Int | JavaType::Long) {
5204 wrapped = Box::new(Expr::Cast {
5205 ty: TypeRef::J(t.clone()),
5206 e: wrapped,
5207 });
5208 }
5209 *value = wrapped;
5210 } else if matches!(t, JavaType::Boolean)
5211 && side_int(value, vt)
5212 && !side_bool(value, vt)
5213 {
5214 let taken = std::mem::replace(
5215 value,
5216 Box::new(Expr::Const(ConstVal::Null)),
5217 );
5218 **value = Expr::Bin {
5219 op: BinOp::Ne,
5220 l: taken,
5221 r: Box::new(Expr::Const(ConstVal::Int(0))),
5222 ty: Some(TypeRef::J(JavaType::Boolean)),
5223 };
5224 }
5225 }
5226 }
5227 if let Expr::Cast { ty, e } = x {
5233 let t = ty.erased();
5234 let t_num = matches!(
5235 t,
5236 JavaType::Int
5237 | JavaType::Short
5238 | JavaType::Byte
5239 | JavaType::Char
5240 | JavaType::Long
5241 | JavaType::Float
5242 | JavaType::Double
5243 );
5244 if t_num && side_bool(e, vt) && !side_int(e, vt) {
5245 let taken = std::mem::replace(e, Box::new(Expr::Const(ConstVal::Null)));
5246 **e = Expr::Cond {
5247 c: taken,
5248 t: Box::new(Expr::Const(ConstVal::Int(1))),
5249 f: Box::new(Expr::Const(ConstVal::Int(0))),
5250 };
5251 } else if matches!(t, JavaType::Boolean)
5252 && side_int(e, vt)
5253 && !side_bool(e, vt)
5254 {
5255 let taken = std::mem::replace(e, Box::new(Expr::Const(ConstVal::Null)));
5256 *x = Expr::Bin {
5257 op: BinOp::Ne,
5258 l: taken,
5259 r: Box::new(Expr::Const(ConstVal::Int(0))),
5260 ty: Some(TypeRef::J(JavaType::Boolean)),
5261 };
5262 }
5263 }
5264 if let Expr::Bin { op, l, r, .. } = x {
5270 if matches!(op, BinOp::Eq | BinOp::Ne) {
5271 if matches!(&**r, Expr::Const(ConstVal::Null))
5272 && side_int(l, vt)
5273 && !side_bool(l, vt)
5274 {
5275 **r = Expr::Const(ConstVal::Int(0));
5276 } else if matches!(&**l, Expr::Const(ConstVal::Null))
5277 && side_int(r, vt)
5278 && !side_bool(r, vt)
5279 {
5280 **l = Expr::Const(ConstVal::Int(0));
5281 }
5282 }
5283 }
5284 if let Expr::ArrayIndex { index, .. } = x {
5288 if side_bool(index, vt) && !side_int(index, vt) {
5289 let taken =
5290 std::mem::replace(index, Box::new(Expr::Const(ConstVal::Null)));
5291 *index = wrap(taken);
5292 }
5293 }
5294 if let Expr::NewArray { dims, .. } = x {
5300 for d in dims.iter_mut() {
5301 if side_bool(d, vt) && !side_int(d, vt) {
5302 let taken =
5303 std::mem::replace(d, Expr::Const(ConstVal::Null));
5304 *d = *wrap(Box::new(taken));
5305 }
5306 }
5307 }
5308 if let Expr::Bin { op, l, r, .. } = x {
5309 let int_kind = matches!(
5310 op,
5311 BinOp::Or
5312 | BinOp::And
5313 | BinOp::Xor
5314 | BinOp::Add
5315 | BinOp::Sub
5316 | BinOp::Mul
5317 | BinOp::Div
5318 | BinOp::Rem
5319 | BinOp::Shl
5320 | BinOp::Shr
5321 | BinOp::Ushr
5322 | BinOp::Eq
5323 | BinOp::Ne
5324 | BinOp::Lt
5325 | BinOp::Ge
5326 | BinOp::Gt
5327 | BinOp::Le
5328 );
5329 if !int_kind {
5330 return;
5331 }
5332 if side_bool(l, vt) && side_int(r, vt) && !side_bool(r, vt) {
5335 let taken = std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
5336 *l = wrap(taken);
5337 } else if side_bool(r, vt) && side_int(l, vt) && !side_bool(l, vt) {
5338 let taken = std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
5339 *r = wrap(taken);
5340 }
5341 }
5342 });
5343 });
5344}
5345
5346pub fn fold_value_diamonds(body: &mut Stmt, vt: &VarTable) {
5384 let Stmt::Block(stmts) = body else { return };
5385 fold_diamonds_in(stmts, vt);
5386}
5387
5388fn diamond_types_ok(a: &Expr, b: &Expr) -> bool {
5389 fn num(t: &JavaType) -> bool {
5390 matches!(
5391 t,
5392 JavaType::Byte
5393 | JavaType::Short
5394 | JavaType::Int
5395 | JavaType::Char
5396 | JavaType::Long
5397 | JavaType::Float
5398 | JavaType::Double
5399 )
5400 }
5401 if let Some(other) = if matches!(a, Expr::Const(ConstVal::Null)) {
5409 Some(b)
5410 } else if matches!(b, Expr::Const(ConstVal::Null)) {
5411 Some(a)
5412 } else {
5413 None
5414 } {
5415 return !num(&other.type_ref().erased());
5416 }
5417 let ta = a.type_ref().erased();
5418 let tb = b.type_ref().erased();
5419 ta == tb || (num(&ta) && num(&tb))
5420}
5421
5422fn single_local_assign(st: &Stmt) -> Option<(u32, Expr, Expr)> {
5430 let mut inner = st;
5431 while let Stmt::Block(v) = inner {
5432 if v.len() != 1 {
5433 return None;
5434 }
5435 inner = &v[0];
5436 }
5437 match inner {
5438 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain }) => {
5439 match &**target {
5440 Expr::Local { var, .. } => Some((*var, *target.clone(), *value.clone())),
5441 _ => None,
5442 }
5443 }
5444 _ => None,
5445 }
5446}
5447
5448fn arm_assign_inlined(st: &Stmt) -> Option<(u32, Expr, Expr)> {
5457 if let Some(r) = single_local_assign(st) {
5458 return Some(r);
5459 }
5460 let mut inner = st;
5461 while let Stmt::Block(v) = inner {
5462 if v.len() == 1 {
5463 inner = &v[0];
5464 } else {
5465 break;
5466 }
5467 }
5468 let Stmt::Block(v) = inner else { return None };
5469 if v.len() != 2 {
5470 return None;
5471 }
5472 let Stmt::LocalDef {
5473 var: x,
5474 init: Some(init),
5475 ..
5476 } = &v[0]
5477 else {
5478 return None;
5479 };
5480 let (var, tgt, val) = single_local_assign(&v[1])?;
5481 let mut count = 0usize;
5482 let mut c = val.clone();
5483 deep_rewrite(&mut c, &mut |e| {
5484 if let Expr::Local { var: lv, .. } = e {
5485 if lv == x {
5486 count += 1;
5487 }
5488 }
5489 });
5490 if count != 1 {
5491 return None;
5492 }
5493 let mut out = val.clone();
5494 deep_rewrite(&mut out, &mut |e| {
5495 if let Expr::Local { var: lv, .. } = e {
5496 if lv == x {
5497 *e = init.clone();
5498 }
5499 }
5500 });
5501 Some((var, tgt, out))
5502}
5503
5504fn mk_ternary_assign(tgt: Expr, c: Expr, a: Expr, b: Expr) -> Stmt {
5505 Stmt::ExprStmt(Expr::Assign {
5506 target: Box::new(tgt),
5507 op: AssignOp::Plain,
5508 value: Box::new(Expr::Cond {
5509 c: Box::new(c),
5510 t: Box::new(a),
5511 f: Box::new(b),
5512 }),
5513 })
5514}
5515
5516fn same_render_var(v1: u32, v2: u32, vt: &VarTable) -> bool {
5521 let (a, b) = (vt.vars.get(v1 as usize), vt.vars.get(v2 as usize));
5522 match (a, b) {
5523 (Some(x), Some(y)) => x.name == y.name && x.ty.erased() == y.ty.erased(),
5524 _ => false,
5525 }
5526}
5527
5528fn canon_stmts_eq(a: &[Stmt], b: &[Stmt], vt: &VarTable) -> bool {
5535 if a.len() != b.len() {
5536 return false;
5537 }
5538 let mut map: std::collections::HashMap<String, u32> = std::collections::HashMap::default();
5539 let canon = |s: &Stmt, map: &mut std::collections::HashMap<String, u32>| -> Stmt {
5540 let mut c = s.clone();
5541 walk_stmt_exprs(&mut c, &mut |e| {
5542 deep_rewrite(e, &mut |x| {
5543 if let Expr::Local { var, .. } = x {
5544 let key = match vt.vars.get(*var as usize) {
5545 Some(vi) => format!("{}#{:?}", vi.name, vi.ty.erased()),
5546 None => format!("v{}", var),
5547 };
5548 let id = *map.entry(key).or_insert(*var);
5549 *x = Expr::Local {
5550 var: id,
5551 ty: vt.var(id).ty.clone(),
5552 };
5553 }
5554 });
5555 });
5556 c
5557 };
5558 a.iter()
5559 .zip(b.iter())
5560 .all(|(x, y)| canon(x, &mut map) == canon(y, &mut map))
5561}
5562
5563fn fold_diamonds_in(stmts: &mut Vec<Stmt>, vt: &VarTable) {
5564 let mut i = 0usize;
5565 while i < stmts.len() {
5566 match &mut stmts[i] {
5567 Stmt::Block(v) => fold_diamonds_in(v, vt),
5568 Stmt::If { then_stmt, else_stmt, .. } => {
5569 if let Stmt::Block(v) = &mut **then_stmt {
5570 fold_diamonds_in(v, vt);
5571 }
5572 if let Some(e) = else_stmt {
5573 if let Stmt::Block(v) = &mut **e {
5574 fold_diamonds_in(v, vt);
5575 }
5576 }
5577 }
5578 _ => {}
5579 }
5580 let shape1 = if let Stmt::If { cond, then_stmt, else_stmt: Some(el), .. } = &stmts[i] {
5582 match (arm_assign_inlined(then_stmt), arm_assign_inlined(el)) {
5583 (Some((v1, tgt, a)), Some((v2, _, b)))
5584 if v1 == v2 && diamond_types_ok(&a, &b) =>
5585 {
5586 Some(mk_ternary_assign(tgt, cond.clone(), a, b))
5587 }
5588 _ => None,
5589 }
5590 } else {
5591 None
5592 };
5593 if let Some(f) = shape1 {
5594 stmts[i] = f;
5595 i += 1;
5596 continue;
5597 }
5598 let shape3 = if let Stmt::If {
5611 cond,
5612 then_stmt,
5613 else_stmt: Some(el),
5614 ..
5615 } = &stmts[i]
5616 {
5617 let arm = |st: &Stmt| -> Option<((u32, Expr, Expr), Vec<Stmt>)> {
5618 let Stmt::Block(v) = st else { return None };
5619 if v.len() < 2 {
5620 return None;
5621 }
5622 let first = single_local_assign(&v[0])?;
5623 Some((first, v[1..].to_vec()))
5624 };
5625 match (arm(then_stmt), arm(el)) {
5626 (Some(((v1, tgt1, a), mut rest_t)), Some(((v2, _t2, b), rest_e)))
5627 if !rest_t.is_empty()
5628 && is_bare_ctor_call(first_leaf_stmt(&rest_t[0]))
5629 && stmts_count_writes(&rest_t, v1) == 0
5630 && stmts_count_writes(&rest_t, v2) == 0
5631 && diamond_types_ok(&a, &b) =>
5632 {
5633 if v1 == v2 || same_render_var(v1, v2, vt) {
5634 if canon_stmts_eq(&rest_t, &rest_e, vt) {
5636 Some((mk_ternary_assign(tgt1, cond.clone(), a, b), rest_t))
5637 } else {
5638 None
5639 }
5640 } else if stmts_read_var(&rest_t, v1) == 1
5641 && stmts_read_var(&rest_e, v2) == 1
5642 && stmts_read_var(&rest_t, v2) == 0
5643 && stmts_read_var(&rest_e, v1) == 0
5644 {
5645 let mut unified = rest_e.clone();
5653 for st in unified.iter_mut() {
5654 let mut c2 = st.clone();
5655 walk_stmt_exprs(&mut c2, &mut |e| {
5656 deep_rewrite(e, &mut |x| {
5657 if let Expr::Local { var, .. } = x {
5658 if *var == v2 {
5659 *x = Expr::Local {
5660 var: v1,
5661 ty: vt.var(v1).ty.clone(),
5662 };
5663 }
5664 }
5665 });
5666 });
5667 *st = c2;
5668 }
5669 if unified != rest_t {
5670 None
5671 } else {
5672 let tern = Expr::Cond {
5673 c: Box::new(cond.clone()),
5674 t: Box::new(a.clone()),
5675 f: Box::new(b.clone()),
5676 };
5677 for st in rest_t.iter_mut() {
5678 let mut c2 = st.clone();
5679 walk_stmt_exprs(&mut c2, &mut |e| {
5680 deep_rewrite(e, &mut |x| {
5681 if let Expr::Local { var, .. } = x {
5682 if *var == v1 {
5683 *x = tern.clone();
5684 }
5685 }
5686 });
5687 });
5688 *st = c2;
5689 }
5690 let first = rest_t.remove(0);
5694 Some((first, rest_t))
5695 }
5696 } else {
5697 None
5698 }
5699 }
5700 _ => None,
5701 }
5702 } else {
5703 None
5704 };
5705 if let Some((folded, rest)) = shape3 {
5706 stmts[i] = folded;
5707 stmts.splice(i + 1..i + 1, rest);
5708 i += 1;
5709 continue;
5710 }
5711 if i + 1 < stmts.len() {
5721 let shape2 = {
5741 let s_i = single_local_assign(&stmts[i]);
5742 if let Stmt::If {
5743 cond,
5744 then_stmt,
5745 else_stmt,
5746 ..
5747 } = &stmts[i + 1]
5748 {
5749 let then_a = single_local_assign(then_stmt);
5750 let else_a = else_stmt.as_deref().and_then(single_local_assign);
5751 let then_empty =
5752 matches!(&**then_stmt, Stmt::Block(v) if v.is_empty());
5753 let else_empty = else_stmt
5754 .as_deref()
5755 .map(|e| matches!(e, Stmt::Block(v) if v.is_empty()))
5756 .unwrap_or(true);
5757 let try_fold = |over: Option<(u32, Expr, Expr)>,
5758 keep_over: bool|
5759 -> Option<Stmt> {
5760 let (v2, tgt2, a) = over?;
5761 let (v1, _t1, b) = s_i.as_ref()?;
5762 if !(*v1 == v2 || same_render_var(*v1, v2, vt)) {
5763 return None;
5764 }
5765 let keep = if *v1 == v2 {
5766 b.clone()
5767 } else {
5768 Expr::Local {
5769 var: *v1,
5770 ty: vt.var(*v1).ty.clone(),
5771 }
5772 };
5773 if !diamond_types_ok(&a, &keep) {
5774 return None;
5775 }
5776 if keep_over {
5777 Some(mk_ternary_assign(tgt2, cond.clone(), keep, a))
5778 } else {
5779 Some(mk_ternary_assign(tgt2, cond.clone(), a, keep))
5780 }
5781 };
5782 if then_a.is_some() && else_empty {
5783 try_fold(then_a, false)
5784 } else if else_a.is_some() && then_empty {
5785 try_fold(else_a, true)
5786 } else {
5787 None
5788 }
5789 } else {
5790 None
5791 }
5792 };
5793 let mut lead_fold: Option<(Stmt, Stmt)> = None;
5802 if shape2.is_none() && i + 1 < stmts.len() {
5803 let s_i2 = single_local_assign(&stmts[i]);
5804 let leading = |st: &Stmt| -> Option<((u32, Expr, Expr), Vec<Stmt>)> {
5805 let Stmt::Block(v) = st else { return None };
5806 if v.len() < 2 {
5807 return None;
5808 }
5809 let first = single_local_assign(&v[0])?;
5810 Some((first, v[1..].to_vec()))
5811 };
5812 if let (Some((v1, _t1, b)), Stmt::If { cond, then_stmt, else_stmt, .. }) =
5813 (&s_i2, &stmts[i + 1])
5814 {
5815 let else_absent = else_stmt.is_none()
5816 || else_stmt
5817 .as_deref()
5818 .is_some_and(|e| matches!(e, Stmt::Block(v) if v.is_empty()));
5819 let then_absent = matches!(&**then_stmt, Stmt::Block(v) if v.is_empty());
5820 let try_lead = |over: Option<((u32, Expr, Expr), Vec<Stmt>)>,
5821 keep_over: bool|
5822 -> Option<(Stmt, Stmt)> {
5823 let ((v2, tgt2, a), rest) = over?;
5824 if !(*v1 == v2 || same_render_var(*v1, v2, vt)) {
5825 return None;
5826 }
5827 if stmts_count_writes(&rest, *v1) != 0
5828 || stmts_count_writes(&rest, v2) != 0
5829 {
5830 return None;
5831 }
5832 let keep = if *v1 == v2 {
5833 b.clone()
5834 } else {
5835 Expr::Local {
5836 var: *v1,
5837 ty: vt.var(*v1).ty.clone(),
5838 }
5839 };
5840 if !diamond_types_ok(&a, &keep) {
5841 return None;
5842 }
5843 let t = if keep_over {
5844 mk_ternary_assign(tgt2, cond.clone(), keep, a)
5845 } else {
5846 mk_ternary_assign(tgt2, cond.clone(), a, keep)
5847 };
5848 let mut new_if = stmts[i + 1].clone();
5849 if let Stmt::If { then_stmt, else_stmt, .. } = &mut new_if {
5850 if keep_over {
5851 *else_stmt = Some(Box::new(Stmt::Block(rest)));
5852 } else {
5853 **then_stmt = Stmt::Block(rest);
5854 }
5855 }
5856 Some((t, new_if))
5857 };
5858 if else_absent {
5859 lead_fold = try_lead(leading(then_stmt), false);
5860 } else if then_absent {
5861 if let Some(e) = else_stmt {
5862 lead_fold = try_lead(leading(e), true);
5863 }
5864 }
5865 }
5866 }
5867 if let Some((folded, new_if)) = lead_fold {
5868 stmts[i] = folded;
5869 stmts[i + 1] = new_if;
5870 continue;
5871 }
5872 if let Some(f) = shape2 {
5873 stmts[i] = f;
5874 stmts.remove(i + 1);
5875 continue; }
5877 }
5878 i += 1;
5879 }
5880}
5881
5882pub fn fold_bool_value_diamonds(body: &mut Stmt, vt: &VarTable) {
5883 fn is_bool_var(v: u32, vt: &VarTable) -> bool {
5884 (v as usize) < vt.vars.len()
5885 && matches!(vt.vars[v as usize].ty.erased(), JavaType::Boolean)
5886 }
5887 fn bool_const(e: &Expr) -> Option<bool> {
5888 fn cint(e: &Expr) -> Option<i32> {
5889 match e {
5890 Expr::Const(ConstVal::Int(n)) => Some(*n),
5891 _ => None,
5892 }
5893 }
5894 match e {
5895 Expr::Const(ConstVal::Int(0)) => Some(false),
5896 Expr::Const(ConstVal::Int(1)) => Some(true),
5897 Expr::Bin { op: op @ (BinOp::Eq | BinOp::Ne), l, r, .. } => {
5901 let (a, b) = (cint(l)?, cint(r)?);
5902 let eq = a == b;
5903 Some(if matches!(op, BinOp::Eq) { eq } else { !eq })
5904 }
5905 _ => None,
5906 }
5907 }
5908 fn reads_var(e: &Expr, v: u32) -> bool {
5909 let mut hit = false;
5910 visit_exprs(e, &mut |x| {
5911 if let Expr::Local { var, .. } = x {
5912 if *var == v {
5913 hit = true;
5914 }
5915 }
5916 });
5917 hit
5918 }
5919 fn bool_valued(e: &Expr, vt: &VarTable) -> bool {
5923 fn side(e: &Expr, vt: &VarTable) -> bool {
5924 bool_const(e).is_some() || bool_valued(e, vt)
5925 }
5926 match e {
5927 Expr::Local { var, .. } => is_bool_var(*var, vt),
5928 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
5929 Expr::InstanceOf { .. } => true,
5930 Expr::Un { op: UnOp::Not, e: x, .. } => bool_valued(x, vt) || bool_const(x).is_some(),
5931 Expr::Bin { op, l, r, .. } => match op {
5932 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le => true,
5933 BinOp::LogAnd | BinOp::LogOr => {
5936 side(l, vt) && side(r, vt)
5937 }
5938 _ => false,
5939 },
5940 Expr::Cond { t, f, .. } => side(t, vt) && side(f, vt),
5941 _ => false,
5942 }
5943 }
5944 fn lone_assign(
5953 b: &Stmt,
5954 v: u32,
5955 vt: &VarTable,
5956 an: &VarAnalysis,
5957 consumed: &mut HashSet<u32>,
5958 ) -> Option<Expr> {
5959 let l = flat_list(b);
5960 if l.len() == 2 {
5961 let (u, e): (u32, &Expr) = match l[0] {
5962 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e),
5963 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
5964 match &**target {
5965 Expr::Local { var, .. } => (*var, &**value),
5966 _ => return None,
5967 }
5968 }
5969 _ => return None,
5970 };
5971 if u == v || consumed.contains(&u) {
5972 return None;
5973 }
5974 if an.assigns.get(u as usize).copied().unwrap_or(0) != 1
5981 || an.reads.get(u as usize).copied().unwrap_or(0) != 1
5982 {
5983 return None;
5984 }
5985 let (tv, rv) = match l[1] {
5986 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
5987 match (&**target, &**value) {
5988 (Expr::Local { var: t, .. }, Expr::Local { var: r, .. }) => (*t, *r),
5989 _ => return None,
5990 }
5991 }
5992 Stmt::LocalDef { var, init: Some(Expr::Local { var: r, .. }), .. } => (*var, *r),
5993 _ => return None,
5994 };
5995 if tv != v || rv != u {
5996 return None;
5997 }
5998 if reads_var(e, v) || !(bool_valued(e, vt) || bool_const(e).is_some()) {
5999 return None;
6000 }
6001 consumed.insert(u);
6002 return Some(e.clone());
6003 }
6004 if l.len() != 1 {
6005 return None;
6006 }
6007 let (target, value) = match l[0] {
6008 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => (target, value),
6009 Stmt::LocalDef { var, init: Some(value), .. } if *var == v => {
6010 if reads_var(value, v) || !(bool_valued(value, vt) || bool_const(value).is_some()) {
6011 return None;
6012 }
6013 return Some(value.clone());
6014 }
6015 _ => return None,
6016 };
6017 let Expr::Local { var: tv, .. } = &**target else { return None };
6018 if *tv != v || reads_var(value, v) {
6019 return None;
6020 }
6021 if !(bool_valued(value, vt) || bool_const(value).is_some()) {
6022 return None;
6023 }
6024 Some((**value).clone())
6025 }
6026 fn not(e: Expr) -> Expr {
6027 Expr::Un { op: UnOp::Not, e: Box::new(e) }
6028 }
6029 fn simplify_bool(e: Expr) -> Expr {
6034 match e {
6035 Expr::Un { op: UnOp::Not, e: inner, .. } => match *inner {
6036 Expr::Bin { op: BinOp::Ne, l, r, ty } => {
6041 simplify_bool(Expr::Bin { op: BinOp::Eq, l, r, ty })
6042 }
6043 Expr::Bin { op: BinOp::Eq, l, r, ty } => {
6044 simplify_bool(Expr::Bin { op: BinOp::Ne, l, r, ty })
6045 }
6046 other => Expr::Un { op: UnOp::Not, e: Box::new(simplify_bool(other)) },
6047 },
6048 Expr::Bin { op: op @ (BinOp::Eq | BinOp::Ne), l, r, ty } => {
6049 fn cond01(e: &Expr) -> Option<Expr> {
6051 if let Expr::Cond { c, t, f } = e {
6052 if matches!(&**t, Expr::Const(ConstVal::Int(1)))
6053 && matches!(&**f, Expr::Const(ConstVal::Int(0)))
6054 {
6055 return Some((**c).clone());
6056 }
6057 }
6058 None
6059 }
6060 let (lc, rc) = (cond01(&l), cond01(&r));
6061 match (lc, rc) {
6062 (Some(c), None) => match (&*r, op) {
6063 (Expr::Const(ConstVal::Int(1)), BinOp::Eq) => c,
6064 (Expr::Const(ConstVal::Int(1)), BinOp::Ne) => not(c),
6065 (Expr::Const(ConstVal::Int(0)), BinOp::Eq) => not(c),
6066 (Expr::Const(ConstVal::Int(0)), BinOp::Ne) => c,
6067 _ => Expr::Bin { op, l: Box::new(c), r, ty },
6068 },
6069 (None, Some(c)) => match (&*l, op) {
6070 (Expr::Const(ConstVal::Int(1)), BinOp::Eq) => c,
6071 (Expr::Const(ConstVal::Int(1)), BinOp::Ne) => not(c),
6072 (Expr::Const(ConstVal::Int(0)), BinOp::Eq) => not(c),
6073 (Expr::Const(ConstVal::Int(0)), BinOp::Ne) => c,
6074 _ => Expr::Bin { op, l, r: Box::new(c), ty },
6075 },
6076 _ => Expr::Bin { op, l, r, ty },
6077 }
6078 }
6079 other => other,
6080 }
6081 }
6082 fn bin(op: BinOp, l: Expr, r: Expr) -> Expr {
6083 Expr::Bin { op, l: Box::new(l), r: Box::new(r), ty: None }
6084 }
6085
6086 for _round in 0..4 {
6087 let mut changed = false;
6088 let mut analysis = VarAnalysis {
6089 assigns: Vec::new(),
6090 reads: Vec::new(),
6091 values: Vec::new(),
6092 };
6093 analyze_vars(body, &mut analysis);
6094 let mut consumed: HashSet<u32> = HashSet::default();
6095 walk_mut_deep(body, &mut |st| {
6096 let Stmt::Block(list) = st else { return };
6097 let mut i = 0usize;
6098 while i + 1 < list.len() {
6099 let folded = try_at(list, i, vt, &is_bool_var, &bool_const, &reads_var, &lone_assign, &analysis, &mut consumed);
6100 match folded {
6101 Some(expr) => {
6102 let is_def = matches!(&list[i], Stmt::LocalDef { .. });
6103 let var = match &list[i] {
6104 Stmt::LocalDef { var, .. } => *var,
6105 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
6106 match &**target { Expr::Local { var, .. } => *var, _ => unreachable!() }
6107 }
6108 _ => unreachable!(),
6109 };
6110 let (is_final, force_type) = match &list[i] {
6111 Stmt::LocalDef { is_final, force_type, .. } => (*is_final, *force_type),
6112 _ => (false, false),
6113 };
6114 list[i] = if is_def {
6115 Stmt::LocalDef { var, init: Some(expr), is_final, force_type }
6116 } else {
6117 Stmt::ExprStmt(Expr::Assign {
6118 target: Box::new(Expr::Local {
6119 var,
6120 ty: TypeRef::J(JavaType::Boolean),
6121 }),
6122 op: AssignOp::Plain,
6123 value: Box::new(expr),
6124 })
6125 };
6126 list.remove(i + 1);
6127 changed = true;
6128 }
6131 None => i += 1,
6132 }
6133 }
6134 });
6135 if !changed {
6136 break;
6137 }
6138 }
6139
6140 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
6141 fn try_at(
6142 list: &[Stmt],
6143 i: usize,
6144 vt: &VarTable,
6145 is_bool_var: &dyn Fn(u32, &VarTable) -> bool,
6146 bool_const: &dyn Fn(&Expr) -> Option<bool>,
6147 reads_var: &dyn Fn(&Expr, u32) -> bool,
6148 lone_assign: &dyn Fn(&Stmt, u32, &VarTable, &VarAnalysis, &mut HashSet<u32>) -> Option<Expr>,
6149 an: &VarAnalysis,
6150 consumed: &mut HashSet<u32>,
6151 ) -> Option<Expr> {
6152 let (v, base) = match &list[i] {
6154 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
6155 match &**target {
6156 Expr::Local { var, .. } => {
6157 let b = bool_const(value)?;
6160 if !is_bool_var(*var, vt) { return None; }
6161 (*var, b)
6162 }
6163 _ => return None,
6164 }
6165 }
6166 Stmt::LocalDef { var, init: Some(e), .. } => {
6167 let b = bool_const(e)?;
6168 if !is_bool_var(*var, vt) { return None; }
6169 (*var, b)
6170 }
6171 _ => return None,
6172 };
6173 let Stmt::If { cond, then_stmt, else_stmt } = &list[i + 1] else {
6174 return None;
6175 };
6176 if reads_var(cond, v) {
6177 return None;
6178 }
6179 let mut touches_v = false;
6180 visit_exprs(cond, &mut |x| {
6181 if let Expr::Assign { target, .. } = x {
6182 if let Expr::Local { var, .. } = &**target {
6183 if *var == v { touches_v = true; }
6184 }
6185 }
6186 });
6187 if touches_v {
6188 return None;
6189 }
6190 let (cond_n, then_arm, else_arm): (Expr, &Stmt, Option<&Stmt>) =
6193 if flat_list(then_stmt).is_empty() && else_stmt.is_some() {
6194 (not(cond.clone()), else_stmt.as_ref().unwrap(), None)
6195 } else {
6196 (cond.clone(), then_stmt.as_ref(), else_stmt.as_deref())
6197 };
6198 let e1 = lone_assign(then_arm, v, vt, an, consumed)?;
6199 let e2 = match else_arm {
6200 None => None,
6201 Some(b) => Some(lone_assign(b, v, vt, an, consumed)?),
6202 };
6203 let e1c = bool_const(&e1);
6204 let c = simplify_bool(cond_n);
6205 Some(match e2 {
6206 None => {
6207 if !base {
6208 match e1c {
6209 Some(true) => c,
6210 Some(false) => return None, None => bin(BinOp::LogAnd, c, e1),
6212 }
6213 } else {
6214 match e1c {
6215 Some(false) => not(c),
6216 Some(true) => return None,
6217 None => bin(BinOp::LogOr, not(c), e1),
6218 }
6219 }
6220 }
6221 Some(e2) => {
6222 if e1 == e2 {
6224 return None; }
6226 Expr::Cond { c: Box::new(c), t: Box::new(e1), f: Box::new(e2) }
6227 }
6228 })
6229 }
6230}
6231
6232pub fn idiom_compounds(body: &mut Stmt, vt: &VarTable) {
6233 walk_mut_deep(body, &mut |st| {
6234 let Stmt::ExprStmt(e) = st else { return };
6235 let Expr::Assign { target, op: AssignOp::Plain, value } = &mut *e else {
6236 return;
6237 };
6238 enum Tgt {
6241 Loc(u32),
6242 Fld,
6243 }
6244 let tgt = match &**target {
6245 Expr::Local { var, .. } => Tgt::Loc(*var),
6246 Expr::Field { owner, .. }
6247 if owner.is_none()
6248 || matches!(&**owner.as_ref().unwrap(), Expr::This) =>
6249 {
6250 Tgt::Fld
6251 }
6252 _ => return,
6253 };
6254 let Expr::Bin { op, l, r, .. } = &mut **value else {
6255 return;
6256 };
6257 match (&tgt, &**l) {
6259 (Tgt::Loc(v), Expr::Local { var, .. }) if *var == *v => {}
6260 (Tgt::Fld, Expr::Field { cls: c2, name: n2, .. }) => {
6261 let Expr::Field { cls: c1, name: n1, .. } = &**target else {
6262 return;
6263 };
6264 if c1 != c2 || n1 != n2 {
6265 return;
6266 }
6267 }
6268 _ => return,
6269 }
6270 let one = match &**r {
6272 Expr::Const(ConstVal::Int(1)) => Some(1i64),
6273 Expr::Const(ConstVal::Int(-1)) => Some(-1),
6274 Expr::Const(ConstVal::Long(1)) => Some(1),
6275 Expr::Const(ConstVal::Long(-1)) => Some(-1),
6276 _ => None,
6277 };
6278 if *op == BinOp::Add || *op == BinOp::Sub {
6279 if let Some(mut d) = one {
6280 if *op == BinOp::Sub {
6281 d = -d;
6282 }
6283 let tgt_ty = match &tgt {
6284 Tgt::Loc(v) => vt.var(*v).ty.erased(),
6285 Tgt::Fld => {
6286 let Expr::Field { ty, .. } = &**target else {
6287 return;
6288 };
6289 ty.erased()
6290 }
6291 };
6292 let numeric = matches!(
6293 tgt_ty,
6294 JavaType::Int
6295 | JavaType::Long
6296 | JavaType::Short
6297 | JavaType::Byte
6298 | JavaType::Char
6299 | JavaType::Float
6300 | JavaType::Double
6301 );
6302 if numeric {
6303 let wide = matches!(tgt_ty, JavaType::Long);
6304 let inner = match &tgt {
6305 Tgt::Loc(v) => Expr::Local {
6306 var: *v,
6307 ty: vt.var(*v).ty.clone(),
6308 },
6309 Tgt::Fld => (**target).clone(),
6310 };
6311 *e = Expr::PostIncDec {
6312 e: Box::new(inner),
6313 delta: d,
6314 wide,
6315 };
6316 return;
6317 }
6318 }
6319 }
6320 let cop = match op {
6321 BinOp::Add => AssignOp::Add,
6322 BinOp::Sub => AssignOp::Sub,
6323 BinOp::Mul => AssignOp::Mul,
6324 BinOp::Div => AssignOp::Div,
6325 BinOp::Rem => AssignOp::Rem,
6326 BinOp::Shl => AssignOp::Shl,
6327 BinOp::Shr => AssignOp::Shr,
6328 BinOp::Ushr => AssignOp::Ushr,
6329 BinOp::And => AssignOp::And,
6330 BinOp::Or => AssignOp::Or,
6331 BinOp::Xor => AssignOp::Xor,
6332 _ => return,
6333 };
6334 let rhs = std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
6335 let new_target = match &tgt {
6336 Tgt::Loc(v) => Box::new(Expr::Local {
6337 var: *v,
6338 ty: vt.var(*v).ty.clone(),
6339 }),
6340 Tgt::Fld => target.clone(),
6341 };
6342 *e = Expr::Assign {
6343 target: new_target,
6344 op: cop,
6345 value: rhs,
6346 };
6347 });
6348}
6349
6350pub fn fix_bool_xor(body: &mut Stmt, vt: &VarTable, ret_bool: bool) {
6351 fn is_one(e: &Expr) -> bool {
6352 matches!(e, Expr::Const(ConstVal::Int(1)))
6353 }
6354 fn bty(e: &Expr, vt: &VarTable) -> bool {
6355 matches!(
6356 match e {
6357 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
6358 other => other.type_ref().erased(),
6359 },
6360 JavaType::Boolean
6361 )
6362 }
6363 fn inv_of_xor(x: &Expr, vt: &VarTable) -> Option<Expr> {
6368 if let Expr::Bin { op: BinOp::Xor, l, r, .. } = x {
6369 let (int_side, one_side) = if is_one(r) {
6370 (l, r)
6371 } else if is_one(l) {
6372 (r, l)
6373 } else {
6374 return None;
6375 };
6376 let _ = one_side;
6377 if !bty(int_side, vt) {
6378 return Some(Expr::Bin {
6379 op: BinOp::Eq,
6380 l: int_side.clone(),
6381 r: Box::new(Expr::Const(ConstVal::Int(0))),
6382 ty: Some(TypeRef::J(JavaType::Boolean)),
6383 });
6384 }
6385 }
6386 None
6387 }
6388 let tgt_bool = |target: &Expr, vt: &VarTable| -> bool {
6389 match target {
6390 Expr::Local { var, .. } => matches!(vt.var(*var).ty.erased(), JavaType::Boolean),
6391 Expr::Field { ty, .. } => matches!(ty.erased(), JavaType::Boolean),
6392 _ => false,
6393 }
6394 };
6395 walk_mut_deep(body, &mut |st| match st {
6396 Stmt::Return(Some(e)) if ret_bool => {
6397 if let Some(r) = inv_of_xor(e, vt) {
6398 *e = r;
6399 }
6400 }
6401 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain, .. }) => {
6402 if tgt_bool(target, vt) {
6403 if let Some(r) = inv_of_xor(value, vt) {
6404 **value = r;
6405 }
6406 }
6407 }
6408 Stmt::LocalDef { var, init: Some(value), .. }
6409 if matches!(vt.var(*var).ty.erased(), JavaType::Boolean) =>
6410 {
6411 if let Some(r) = inv_of_xor(value, vt) {
6412 *value = r;
6413 }
6414 }
6415 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
6416 if let Some(r) = inv_of_xor(cond, vt) {
6417 *cond = r;
6418 }
6419 }
6420 _ => {}
6421 });
6422 fn ity(e: &Expr, vt: &VarTable) -> bool {
6423 matches!(
6424 match e {
6425 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
6426 other => other.type_ref().erased(),
6427 },
6428 JavaType::Int | JavaType::Short | JavaType::Byte | JavaType::Char
6429 )
6430 }
6431 fn mixed_rewrite(e: &mut Expr, vt: &VarTable) {
6440 deep_rewrite(e, &mut |x| {
6441 if let Expr::Bin { op, l, r, ty } = x {
6442 match op {
6443 BinOp::Xor => {
6444 if is_one(r) && bty(l, vt) {
6445 let taken =
6446 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
6447 *x = Expr::Un { op: UnOp::Not, e: taken };
6448 } else if is_one(l) && bty(r, vt) {
6449 let taken =
6450 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
6451 *x = Expr::Un { op: UnOp::Not, e: taken };
6452 }
6453 }
6454 BinOp::Or | BinOp::And => {
6455 if bty(l, vt) && ity(r, vt) {
6456 let taken =
6457 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
6458 **r = Expr::Bin {
6459 op: BinOp::Ne,
6460 l: taken,
6461 r: Box::new(Expr::Const(ConstVal::Int(0))),
6462 ty: Some(TypeRef::J(JavaType::Boolean)),
6463 };
6464 *ty = Some(TypeRef::J(JavaType::Boolean));
6465 } else if ity(l, vt) && bty(r, vt) {
6466 let taken =
6467 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
6468 **l = Expr::Bin {
6469 op: BinOp::Ne,
6470 l: taken,
6471 r: Box::new(Expr::Const(ConstVal::Int(0))),
6472 ty: Some(TypeRef::J(JavaType::Boolean)),
6473 };
6474 *ty = Some(TypeRef::J(JavaType::Boolean));
6475 }
6476 }
6477 _ => {}
6478 }
6479 }
6480 });
6481 }
6482 walk_mut_deep(body, &mut |st| match st {
6485 Stmt::Return(Some(e)) if ret_bool => mixed_rewrite(e, vt),
6486 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain, .. }) => {
6487 if tgt_bool(target, vt) {
6488 mixed_rewrite(value, vt);
6489 }
6490 }
6491 Stmt::LocalDef { var, init: Some(e), .. }
6492 if matches!(vt.var(*var).ty.erased(), JavaType::Boolean) =>
6493 {
6494 mixed_rewrite(e, vt);
6495 }
6496 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
6497 mixed_rewrite(cond, vt);
6498 }
6499 _ => {}
6500 });
6501 walk_stmt_exprs(body, &mut |e| {
6504 deep_rewrite(e, &mut |x| {
6505 if let Expr::Cond { c, .. } = x {
6506 mixed_rewrite(c, vt);
6507 }
6508 });
6509 });
6510}
6511
6512pub fn booleanize(vt: &mut VarTable, body: &mut Stmt, ret_bool: bool) -> usize {
6518 let n0 = vt.vars.len();
6525 let mut derefed = vec![false; n0];
6526 mark_derefs(body, &mut derefed);
6527 let mut total = 0usize;
6528 for _ in 0..4 {
6529 let n = booleanize_round(vt, body, ret_bool, &derefed);
6530 if n == 0 {
6531 break;
6532 }
6533 total += n;
6534 }
6535 total
6536}
6537
6538fn mark_deref_expr(e: &Expr, derefed: &mut [bool]) {
6542 match e {
6543 Expr::Field { owner: Some(o), is_static: false, .. }
6544 | Expr::Method { owner: Some(o), .. } => {
6545 if let Expr::Local { var, .. } = &**o {
6546 if (*var as usize) < derefed.len() {
6547 derefed[*var as usize] = true;
6548 }
6549 }
6550 }
6551 Expr::ArrayIndex { array, .. } => {
6552 if let Expr::Local { var, .. } = &**array {
6553 if (*var as usize) < derefed.len() {
6554 derefed[*var as usize] = true;
6555 }
6556 }
6557 }
6558 _ => {}
6559 }
6560 for_each_child(e, &mut |c| mark_deref_expr(c, derefed));
6561}
6562
6563fn mark_derefs(body: &Stmt, derefed: &mut [bool]) {
6564 walk_all(body, &mut |st| {
6565 match st {
6566 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
6567 mark_deref_expr(e, derefed);
6568 }
6569 Stmt::Return(Some(e)) => mark_deref_expr(e, derefed),
6570 Stmt::LocalDef { init: Some(e), .. } => mark_deref_expr(e, derefed),
6571 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
6572 mark_deref_expr(cond, derefed);
6573 }
6574 Stmt::For { init, .. } => {
6575 for x in init {
6576 mark_derefs(x, derefed);
6577 }
6578 }
6579 Stmt::Switch { selector, .. } => mark_deref_expr(selector, derefed),
6580 Stmt::ForEach { iterable, .. } => mark_deref_expr(iterable, derefed),
6581 _ => {}
6582 }
6583 });
6584}
6585
6586fn booleanize_round(
6587 vt: &mut VarTable,
6588 body: &mut Stmt,
6589 ret_bool: bool,
6590 derefed: &[bool],
6591) -> usize {
6592 let n = vt.vars.len();
6593 let mut in_cond = vec![false; n];
6594
6595 walk_all(body, &mut |st| {
6598 let cond: Option<&Expr> = match st {
6599 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
6600 Some(cond)
6601 }
6602 _ => None,
6603 };
6604 if let Some(c) = cond {
6605 if let Expr::Bin {
6606 op: BinOp::Eq | BinOp::Ne,
6607 l,
6608 r,
6609 ..
6610 } = c
6611 {
6612 for (x, other) in [(l, r), (r, l)] {
6613 if let (Expr::Local { var, .. }, Expr::Const(ConstVal::Int(0))) =
6614 (&**x, &**other)
6615 {
6616 if (*var as usize) < n {
6617 in_cond[*var as usize] = true;
6618 }
6619 }
6620 }
6621 }
6622 }
6623 if ret_bool {
6628 if let Stmt::Return(Some(Expr::Local { var, .. })) = st {
6629 if (*var as usize) < n {
6630 in_cond[*var as usize] = true;
6631 }
6632 }
6633 }
6634 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = st {
6641 if let Expr::Field { ty, .. } = &**target {
6642 if ty.erased() == JavaType::Boolean {
6643 if let Expr::Local { var, .. } = &**value {
6644 if (*var as usize) < n {
6645 in_cond[*var as usize] = true;
6646 }
6647 }
6648 }
6649 }
6650 }
6651 let val: Option<&Expr> = match st {
6652 Stmt::ExprStmt(Expr::Assign { value, .. }) => Some(value),
6653 Stmt::ExprStmt(e) => Some(e),
6656 Stmt::LocalDef { init: Some(e), .. } => Some(e),
6657 Stmt::Return(Some(e)) => Some(e),
6658 _ => None,
6659 };
6660 if let Some(v) = val {
6661 visit_exprs(v, &mut |x| {
6662 if let Expr::Method { desc, args, .. } = x {
6668 for (i, a) in args.iter().enumerate() {
6669 if desc.args.get(i) == Some(&JavaType::Boolean) {
6670 if let Expr::Local { var, .. } = a {
6671 if (*var as usize) < n {
6672 in_cond[*var as usize] = true;
6673 }
6674 }
6675 }
6676 }
6677 }
6678 if let Expr::Bin { op, l, r, .. } = x {
6679 if matches!(
6680 op,
6681 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
6682 ) {
6683 for side in [l, r] {
6684 if let Expr::Local { var, .. } = &**side {
6685 if (*var as usize) < n {
6686 in_cond[*var as usize] = true;
6687 }
6688 }
6689 }
6690 }
6691 if matches!(op, BinOp::Or | BinOp::And) {
6697 let l_bool = l.type_ref().erased() == JavaType::Boolean;
6698 let r_bool = r.type_ref().erased() == JavaType::Boolean;
6699 if l_bool != r_bool {
6700 let side = if l_bool { r } else { l };
6701 if let Expr::Local { var, .. } = &**side {
6702 if (*var as usize) < n {
6703 in_cond[*var as usize] = true;
6704 }
6705 }
6706 }
6707 }
6708 }
6709 });
6710 }
6711 });
6712
6713 let mut assigned_any = vec![false; n];
6720 let mut all_bool = vec![true; n];
6721 let mut edges: Vec<(usize, usize)> = Vec::new();
6722 walk_all(body, &mut |st| {
6723 let (var, value) = match st {
6724 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
6725 Expr::Local { var, .. } => (var, &**value),
6726 _ => return,
6727 },
6728 Stmt::LocalDef {
6729 var, init: Some(e), ..
6730 } => (var, e),
6731 _ => return,
6732 };
6733 let i = *var as usize;
6734 if i < n {
6735 assigned_any[i] = true;
6736 if !is_boolean_valued(value, vt) {
6737 all_bool[i] = false;
6738 }
6739 if let Expr::Local { var: src, .. } = value {
6745 if (*src as usize) < n {
6746 edges.push((i, *src as usize));
6747 }
6748 }
6749 }
6750 });
6751 loop {
6756 let mut changed = false;
6757 for &(tgt, src) in &edges {
6758 if in_cond[tgt] && !in_cond[src] && all_bool[src] {
6759 in_cond[src] = true;
6760 changed = true;
6761 }
6762 if in_cond[src] && !in_cond[tgt] && all_bool[tgt] {
6768 in_cond[tgt] = true;
6769 changed = true;
6770 }
6771 }
6772 if !changed {
6773 break;
6774 }
6775 }
6776 let reads = count_locals_stmts(std::slice::from_ref(body));
6777 let mut boolean_vars: HashSet<u32> = HashSet::default();
6778 for i in 0..n {
6779 if vt.vars[i].is_param {
6780 continue;
6781 }
6782 if derefed[i] {
6783 continue;
6784 }
6785 if !in_cond[i] && !(reads.get(&(i as u32)).copied().unwrap_or(0) == 0) {
6786 continue;
6787 }
6788 if assigned_any[i] && all_bool[i] {
6789 boolean_vars.insert(i as u32);
6790 }
6791 }
6792 if boolean_vars.is_empty() {
6793 return 0;
6794 }
6795 for v in &boolean_vars {
6796 vt.vars[*v as usize].ty = TypeRef::J(JavaType::Boolean);
6797 }
6798 let types: Vec<&TypeRef> = vt.vars.iter().map(|v| &v.ty).collect();
6799 rewrite_exprs(body, &mut |e| {
6800 deep_rewrite(e, &mut |x| {
6801 if let Expr::Local { var, ty } = x {
6802 if let Some(want) = types.get(*var as usize) {
6803 if ty != *want {
6804 *ty = (*want).clone();
6805 }
6806 }
6807 }
6808 });
6809 });
6810
6811 fold_bool_conditions(body, &boolean_vars);
6813 boolean_vars.len()
6814}
6815
6816fn is_boolean_valued(e: &Expr, vt: &VarTable) -> bool {
6817 match e {
6818 Expr::Const(ConstVal::Int(0)) | Expr::Const(ConstVal::Int(1)) => true,
6819 Expr::Bin { op: BinOp::Or | BinOp::And, l, r, .. } => {
6826 is_boolean_valued(l, vt) || is_boolean_valued(r, vt)
6827 }
6828 Expr::Bin {
6836 op: BinOp::Xor,
6837 l,
6838 r,
6839 ..
6840 } => is_boolean_valued(l, vt) && is_boolean_valued(r, vt),
6841 Expr::Bin { op, .. } => matches!(
6842 op,
6843 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
6844 ),
6845 Expr::InstanceOf { .. } => true,
6846 Expr::Local { var, ty } => {
6852 let t = if (*var as usize) < vt.vars.len() {
6853 vt.var(*var).ty.erased()
6854 } else {
6855 ty.erased()
6856 };
6857 t == JavaType::Boolean
6858 }
6859 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
6860 Expr::Cond { t, f, .. } => is_boolean_valued(t, vt) && is_boolean_valued(f, vt),
6861 Expr::Un { op: UnOp::Not, .. } => true,
6862 Expr::Field { ty, .. } => ty.erased() == JavaType::Boolean,
6866 _ => false,
6867 }
6868}
6869
6870fn fold_bool_conditions(s: &mut Stmt, bv: &HashSet<u32>) {
6871 let fold = |e: &mut Expr| {
6872 deep_rewrite(e, &mut |x| {
6873 if let Expr::Bin { op, l, r, .. } = x {
6874 let (var_side, const_side) = match (&**l, &**r) {
6875 (Expr::Local { .. }, Expr::Const(ConstVal::Int(_))) => (l, 0),
6876 (Expr::Const(ConstVal::Int(_)), Expr::Local { .. }) => (r, 1),
6877 _ => return,
6878 };
6879 let var = match &**var_side {
6880 Expr::Local { var, .. } => *var,
6881 _ => return,
6882 };
6883 if !bv.contains(&var) {
6884 return;
6885 }
6886 let _ = const_side;
6887 match op {
6888 BinOp::Ne => {
6889 let inner = var_side.clone();
6890 *x = *inner;
6891 }
6892 BinOp::Eq => {
6893 let inner = var_side.clone();
6894 *x = Expr::Un {
6895 op: UnOp::Not,
6896 e: Box::new(*inner),
6897 };
6898 }
6899 _ => {}
6900 }
6901 }
6902 });
6903 };
6904 match s {
6905 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => fold(cond),
6906 _ => {}
6907 }
6908 walk_mut_deep(s, &mut |st| {
6909 let cond = match st {
6910 Stmt::If { cond, .. } => Some(cond),
6911 Stmt::While { cond, .. } => Some(cond),
6912 Stmt::DoWhile { cond, .. } => Some(cond),
6913 _ => None,
6914 };
6915 if let Some(c) = cond {
6916 fold(c);
6917 }
6918 });
6919}
6920
6921pub fn flip_const_compares(s: &mut Stmt) {
6923 rewrite_exprs(s, &mut |e| {
6924 deep_rewrite(e, &mut |x| {
6925 if let Expr::Bin { op, l, r, .. } = x {
6926 if matches!(
6927 op,
6928 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
6929 ) {
6930 let l_const = matches!(&**l, Expr::Const(_));
6931 let r_var = matches!(&**r, Expr::Local { .. });
6932 if l_const && r_var {
6933 std::mem::swap(l, r);
6934 }
6935 }
6936 }
6937 });
6938 });
6939}
6940
6941pub fn null_compares(vt: &VarTable, body: &mut Stmt) {
6943 let obj_vars: HashSet<u32> = vt
6944 .vars
6945 .iter()
6946 .filter(|v| v.ty.erased().is_reference())
6947 .map(|v| v.id)
6948 .collect();
6949 rewrite_exprs(body, &mut |e| {
6950 deep_rewrite(e, &mut |x| {
6951 null_side_rewrite(x, &|v| obj_vars.contains(&v));
6952 });
6953 });
6954}
6955
6956pub fn drop_dead_locals(body: &mut Stmt) {
6965 loop {
6966 let mut reads: Vec<usize> = Vec::new();
6967 count_reads(body, &mut reads);
6968 let mut dropped = 0usize;
6969 walk_mut_deep(body, &mut |st| match st {
6970 Stmt::Block(items) => prune_dead_items(items, &reads, &mut dropped),
6971 Stmt::Switch { cases, .. } => {
6981 for c in cases {
6982 prune_dead_items(&mut c.body, &reads, &mut dropped);
6983 }
6984 }
6985 Stmt::For { init, .. } => prune_dead_items(init, &reads, &mut dropped),
6986 _ => {}
6987 });
6988 if dropped == 0 {
6989 break;
6990 }
6991 }
6992}
6993
6994fn prune_dead_items(items: &mut Vec<Stmt>, reads: &[usize], dropped: &mut usize) {
6999 let dead = |v: u32| reads.get(v as usize).copied().unwrap_or(0) == 0;
7000 items.retain_mut(|x| match x {
7001 Stmt::ExprStmt(Expr::Assign { target, value, op, .. }) => {
7002 let dead_target = matches!(&**target, Expr::Local { var, .. } if dead(*var));
7003 if dead_target && matches!(*op, AssignOp::Plain) {
7004 *dropped += 1;
7005 if has_side_effects(value) {
7006 let e = std::mem::replace(value, Box::new(Expr::This));
7007 *x = Stmt::ExprStmt(*e);
7008 true
7009 } else {
7010 false
7011 }
7012 } else {
7013 true
7014 }
7015 }
7016 Stmt::LocalDef { var, init, .. } if dead(*var) => {
7017 *dropped += 1;
7018 if let Some(e) = init.take() {
7019 if has_side_effects(&e) {
7020 *x = Stmt::ExprStmt(e);
7021 return true;
7022 }
7023 }
7024 false
7025 }
7026 _ => true,
7027 });
7028}
7029
7030
7031fn stmt_shallow_vars<F: FnMut(u32)>(st: &Stmt, out: &mut F) {
7036 fn ex<F: FnMut(u32)>(e: &Expr, out: &mut F) {
7037 visit_exprs(e, &mut |x| {
7038 if let Expr::Local { var, .. } = x {
7039 out(*var);
7040 }
7041 });
7042 }
7043 match st {
7044 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
7045 ex(e, out)
7046 }
7047 Stmt::TernaryValue { e } => ex(e, out),
7048 Stmt::Return(Some(e)) => ex(e, out),
7049 Stmt::LocalDef { var, init, .. } => {
7050 out(*var);
7051 if let Some(e) = init {
7052 ex(e, out);
7053 }
7054 }
7055 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
7056 ex(cond, out)
7057 }
7058 Stmt::For { cond, update, .. } => {
7059 if let Some(c) = cond {
7060 ex(c, out);
7061 }
7062 for u in update {
7063 ex(u, out);
7064 }
7065 }
7066 Stmt::ForEach { iterable, .. } => ex(iterable, out),
7067 Stmt::Switch { selector, cases, .. } => {
7068 ex(selector, out);
7069 for cg in cases {
7070 if let Some(g) = &cg.guard {
7071 ex(g, out);
7072 }
7073 }
7074 }
7075 Stmt::Assert { cond, msg } => {
7076 ex(cond, out);
7077 if let Some(m) = msg {
7078 ex(m, out);
7079 }
7080 }
7081 Stmt::Synchronized { lock, .. } => ex(lock, out),
7082 _ => {}
7083 }
7084}
7085
7086fn scope_pass_a(st: &Stmt, cur: u32, next: &mut u32, def_block: &mut [u32], sizes: &mut Vec<u32>) {
7098 if let Stmt::LocalDef { var, .. } = st {
7099 let i = *var as usize;
7100 if i < def_block.len() && def_block[i] == u32::MAX {
7101 def_block[i] = cur;
7102 }
7103 }
7104 fn scoped(c: &Stmt, next: &mut u32, def_block: &mut [u32], sizes: &mut Vec<u32>) {
7105 let id = *next;
7106 *next += 1;
7107 sizes.push(0);
7108 scope_pass_a(c, id, next, def_block, sizes);
7109 sizes[id as usize] = *next - id;
7110 }
7111 match st {
7112 Stmt::Block(v) => {
7113 for x in v {
7114 scope_pass_a(x, cur, next, def_block, sizes);
7115 }
7116 }
7117 Stmt::If {
7118 then_stmt,
7119 else_stmt,
7120 ..
7121 } => {
7122 scoped(then_stmt, next, def_block, sizes);
7123 if let Some(e) = else_stmt {
7124 scoped(e, next, def_block, sizes);
7125 }
7126 }
7127 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => {
7128 scoped(body, next, def_block, sizes)
7129 }
7130 Stmt::For { init, body, .. } => {
7131 let id = *next;
7134 *next += 1;
7135 sizes.push(0);
7136 for x in init {
7137 scope_pass_a(x, id, next, def_block, sizes);
7138 }
7139 scoped(body, next, def_block, sizes);
7140 sizes[id as usize] = *next - id;
7141 }
7142 Stmt::ForEach { body, .. } => scoped(body, next, def_block, sizes),
7143 Stmt::Switch { cases, default, .. } => {
7144 for c in cases {
7145 let id = *next;
7150 *next += 1;
7151 sizes.push(0);
7152 for x in &c.body {
7153 if matches!(x, Stmt::Block(_)) {
7154 scoped(x, next, def_block, sizes);
7155 } else {
7156 scope_pass_a(x, id, next, def_block, sizes);
7157 }
7158 }
7159 sizes[id as usize] = *next - id;
7160 }
7161 if let Some(d) = default {
7162 scoped(d, next, def_block, sizes);
7163 }
7164 }
7165 Stmt::Try {
7166 body,
7167 catches,
7168 finally,
7169 } => {
7170 scoped(body, next, def_block, sizes);
7171 for c in catches {
7172 scoped(&c.body, next, def_block, sizes);
7173 }
7174 if let Some(f) = finally {
7175 scoped(f, next, def_block, sizes);
7176 }
7177 }
7178 Stmt::TryWithResources {
7179 resources,
7180 body,
7181 catches,
7182 finally,
7183 } => {
7184 let id = *next;
7186 *next += 1;
7187 sizes.push(0);
7188 for r in resources {
7189 scope_pass_a(r, id, next, def_block, sizes);
7190 }
7191 scoped(body, next, def_block, sizes);
7192 for c in catches {
7193 scoped(&c.body, next, def_block, sizes);
7194 }
7195 if let Some(f) = finally {
7196 scoped(f, next, def_block, sizes);
7197 }
7198 sizes[id as usize] = *next - id;
7199 }
7200 Stmt::Synchronized { body, .. } => scoped(body, next, def_block, sizes),
7201 Stmt::Labeled { body, .. } => scope_pass_a(body, cur, next, def_block, sizes),
7202 _ => {}
7203 }
7204}
7205
7206fn scope_pass_b(
7211 st: &Stmt,
7212 cur: u32,
7213 next: &mut u32,
7214 def_block: &[u32],
7215 sizes: &[u32],
7216 within: &mut [u32],
7217 total: &mut [u32],
7218) {
7219 fn cnt(
7220 v: u32,
7221 at: u32,
7222 def_block: &[u32],
7223 sizes: &[u32],
7224 within: &mut [u32],
7225 total: &mut [u32],
7226 ) {
7227 let i = v as usize;
7228 if i < total.len() {
7229 total[i] += 1;
7230 let d = def_block[i];
7231 if d != u32::MAX && d <= at && at < d + sizes[d as usize] {
7232 within[i] += 1;
7233 }
7234 }
7235 }
7236 fn scoped(
7237 c: &Stmt,
7238 next: &mut u32,
7239 def_block: &[u32],
7240 sizes: &[u32],
7241 within: &mut [u32],
7242 total: &mut [u32],
7243 ) {
7244 let id = *next;
7245 *next += 1;
7246 scope_pass_b(c, id, next, def_block, sizes, within, total);
7247 }
7248 fn ex_at(
7249 e: &Expr,
7250 at: u32,
7251 def_block: &[u32],
7252 sizes: &[u32],
7253 within: &mut [u32],
7254 total: &mut [u32],
7255 ) {
7256 visit_exprs(e, &mut |x| {
7257 if let Expr::Local { var, .. } = x {
7258 cnt(*var, at, def_block, sizes, within, total);
7259 }
7260 });
7261 }
7262 match st {
7263 Stmt::For {
7264 init,
7265 cond,
7266 update,
7267 body,
7268 } => {
7269 let id = *next;
7270 *next += 1;
7271 for x in init {
7272 scope_pass_b(x, id, next, def_block, sizes, within, total);
7273 }
7274 if let Some(c) = cond {
7275 ex_at(c, id, def_block, sizes, within, total);
7276 }
7277 for u in update {
7278 ex_at(u, id, def_block, sizes, within, total);
7279 }
7280 scoped(body, next, def_block, sizes, within, total);
7281 }
7282 Stmt::TryWithResources {
7283 resources,
7284 body,
7285 catches,
7286 finally,
7287 } => {
7288 let id = *next;
7289 *next += 1;
7290 for r in resources {
7291 scope_pass_b(r, id, next, def_block, sizes, within, total);
7292 }
7293 scoped(body, next, def_block, sizes, within, total);
7294 for c in catches {
7295 scoped(&c.body, next, def_block, sizes, within, total);
7296 }
7297 if let Some(f) = finally {
7298 scoped(f, next, def_block, sizes, within, total);
7299 }
7300 }
7301 _ => {
7302 stmt_shallow_vars(st, &mut |v| cnt(v, cur, def_block, sizes, within, total));
7303 match st {
7304 Stmt::Block(v) => {
7305 for x in v {
7306 scope_pass_b(x, cur, next, def_block, sizes, within, total);
7307 }
7308 }
7309 Stmt::If {
7310 then_stmt,
7311 else_stmt,
7312 ..
7313 } => {
7314 scoped(then_stmt, next, def_block, sizes, within, total);
7315 if let Some(e) = else_stmt {
7316 scoped(e, next, def_block, sizes, within, total);
7317 }
7318 }
7319 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => {
7320 scoped(body, next, def_block, sizes, within, total)
7321 }
7322 Stmt::ForEach { body, .. } => {
7323 scoped(body, next, def_block, sizes, within, total)
7324 }
7325 Stmt::Switch { cases, default, .. } => {
7326 for c in cases {
7327 let id = *next;
7328 *next += 1;
7329 for x in &c.body {
7330 if matches!(x, Stmt::Block(_)) {
7331 scoped(x, next, def_block, sizes, within, total);
7332 } else {
7333 scope_pass_b(x, id, next, def_block, sizes, within, total);
7334 }
7335 }
7336 }
7337 if let Some(d) = default {
7338 scoped(d, next, def_block, sizes, within, total);
7339 }
7340 }
7341 Stmt::Try {
7342 body,
7343 catches,
7344 finally,
7345 } => {
7346 scoped(body, next, def_block, sizes, within, total);
7347 for c in catches {
7348 scoped(&c.body, next, def_block, sizes, within, total);
7349 }
7350 if let Some(f) = finally {
7351 scoped(f, next, def_block, sizes, within, total);
7352 }
7353 }
7354 Stmt::Synchronized { body, .. } => {
7355 scoped(body, next, def_block, sizes, within, total)
7356 }
7357 Stmt::Labeled { body, .. } => {
7358 scope_pass_b(body, cur, next, def_block, sizes, within, total)
7359 }
7360 _ => {}
7361 }
7362 }
7363 }
7364}
7365
7366pub fn strip_enum_const_inits(s: &mut Stmt, class: &crate::PoolClass) {
7375 let const_fields: jdc_core::FxHashSet<&str> = class
7376 .static_fields
7377 .iter()
7378 .filter(|f| f.access & crate::access::ACC_ENUM != 0)
7379 .map(|f| f.name.as_str())
7380 .collect();
7381 if const_fields.is_empty() {
7382 return;
7383 }
7384 if let Stmt::Block(stmts) = s {
7385 stmts.retain(|st| {
7386 !matches!(
7387 st,
7388 Stmt::ExprStmt(Expr::Assign { target, value, .. })
7389 if matches!(&**target,
7390 Expr::Field { cls, name, is_static: true, .. }
7391 if cls.as_ref() == class.name
7392 && const_fields.contains(&**name))
7393 && matches!(&**value, Expr::New { cls: ncls, .. } if ncls.as_ref() == class.name)
7394 )
7395 });
7396 }
7397}
7398
7399fn contains_this_access(e: &Expr) -> bool {
7408 let mut hit = false;
7409 visit_exprs(e, &mut |x| {
7410 if hit {
7411 return;
7412 }
7413 match x {
7414 Expr::This => hit = true,
7415 Expr::Field { owner, is_static: false, .. } => {
7416 if owner.is_none() || matches!(&**owner.as_ref().unwrap(), Expr::This) {
7417 hit = true;
7418 }
7419 }
7420 Expr::Method { owner, is_static: false, .. } => {
7421 if owner.is_none() || matches!(&**owner.as_ref().unwrap(), Expr::This) {
7422 hit = true;
7423 }
7424 }
7425 _ => {}
7426 }
7427 });
7428 hit
7429}
7430
7431fn var_occurs_in(stmts: &[Stmt], var: u32) -> bool {
7446 stmts.iter().any(|st| {
7447 let mut hit = false;
7448 visit_stmt_exprs_ro(st, &mut |e| {
7449 if !hit {
7450 visit_exprs(e, &mut |x| {
7451 if let Expr::Local { var: v, .. } = x {
7452 if *v == var {
7453 hit = true;
7454 }
7455 }
7456 });
7457 }
7458 });
7459 hit
7460 })
7461}
7462
7463pub struct CtorHelper {
7473 pub name: String,
7474 pub ret: JavaType,
7475 pub params: Vec<(JavaType, String)>,
7476 pub body: Stmt,
7477 pub vt: VarTable,
7478}
7479
7480fn ctor_helpers() -> &'static std::sync::Mutex<std::collections::HashMap<String, Vec<CtorHelper>>> {
7481 static H: std::sync::OnceLock<std::sync::Mutex<std::collections::HashMap<String, Vec<CtorHelper>>>> =
7482 std::sync::OnceLock::new();
7483 H.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::default()))
7484}
7485
7486pub struct CtorRelay {
7491 pub formals: Vec<JavaType>,
7493 pub is_super: bool,
7494}
7495
7496type RelayMap = std::collections::HashMap<String, Vec<(String, CtorRelay)>>;
7497
7498fn ctor_relays() -> &'static std::sync::Mutex<RelayMap> {
7499 static H: std::sync::OnceLock<std::sync::Mutex<RelayMap>> = std::sync::OnceLock::new();
7500 H.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::default()))
7501}
7502
7503pub fn take_ctor_relays(class: &str) -> Vec<(String, CtorRelay)> {
7506 let mut m = ctor_relays().lock().unwrap_or_else(|e| e.into_inner());
7507 let mut v = m.remove(class).unwrap_or_default();
7508 v.sort_by(|a, b| a.0.cmp(&b.0));
7509 v
7510}
7511
7512fn array_init(args: Vec<Expr>) -> Expr {
7513 Expr::NewArray {
7514 elem: TypeRef::J(JavaType::Object(std::sync::Arc::from("java/lang/Object"))),
7515 dims: Vec::new(),
7516 trailing_dims: 0,
7517 init: Some(args),
7518 }
7519}
7520
7521fn helper_name(class: &str, desc: &str) -> String {
7524 let mut h: u64 = 0xcbf29ce484222325;
7525 for b in class.as_bytes().iter().chain(b"::").chain(desc.as_bytes()) {
7526 h ^= *b as u64;
7527 h = h.wrapping_mul(0x100000001b3);
7528 }
7529 format!("resolve${:x}", (h >> 16) & 0xffff_ffff)
7530}
7531
7532pub fn take_ctor_helpers(class: &str) -> Vec<CtorHelper> {
7535 let mut m = ctor_helpers().lock().unwrap_or_else(|e| e.into_inner());
7536 let mut v = m.remove(class).unwrap_or_default();
7537 v.sort_by(|a, b| a.name.cmp(&b.name));
7538 v
7539}
7540
7541fn strip_delegations_deep(stmts: &mut Vec<Stmt>) {
7542 stmts.retain(|s| !is_bare_ctor_call(s));
7543 for s in stmts.iter_mut() {
7544 strip_delegation_one(s);
7545 }
7546}
7547
7548fn strip_delegation_one(st: &mut Stmt) {
7549 if is_bare_ctor_call(st) {
7550 *st = Stmt::Block(Vec::new());
7551 return;
7552 }
7553 match st {
7554 Stmt::Block(v) => strip_delegations_deep(v),
7555 Stmt::If { then_stmt, else_stmt, .. } => {
7556 strip_delegation_one(then_stmt);
7557 if let Some(e) = else_stmt {
7558 strip_delegation_one(e);
7559 }
7560 }
7561 Stmt::While { body, .. }
7562 | Stmt::DoWhile { body, .. }
7563 | Stmt::ForEach { body, .. }
7564 | Stmt::Synchronized { body, .. }
7565 | Stmt::Labeled { body, .. } => strip_delegation_one(body),
7566 Stmt::For { init, body, .. } => {
7567 strip_delegations_deep(init);
7568 strip_delegation_one(body);
7569 }
7570 Stmt::Switch { cases, default, .. } => {
7571 for c in cases.iter_mut() {
7572 strip_delegations_deep(&mut c.body);
7573 }
7574 if let Some(d) = default {
7575 strip_delegation_one(d);
7576 }
7577 }
7578 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
7579 strip_delegation_one(body);
7580 for c in catches.iter_mut() {
7581 strip_delegation_one(&mut c.body);
7582 }
7583 if let Some(f) = finally {
7584 strip_delegation_one(f);
7585 }
7586 }
7587 _ => {}
7588 }
7589}
7590
7591#[allow(clippy::ptr_arg)]
7592fn rewrite_bare_returns(stmts: &mut Vec<Stmt>, val: &Expr) {
7593 for s in stmts.iter_mut() {
7594 rewrite_bare_returns_one(s, val);
7595 }
7596}
7597
7598fn rewrite_bare_returns_one(st: &mut Stmt, val: &Expr) {
7599 match st {
7600 Stmt::Return(None) => *st = Stmt::Return(Some(val.clone())),
7601 Stmt::Block(v) => rewrite_bare_returns(v, val),
7602 Stmt::If { then_stmt, else_stmt, .. } => {
7603 rewrite_bare_returns_one(then_stmt, val);
7604 if let Some(e) = else_stmt {
7605 rewrite_bare_returns_one(e, val);
7606 }
7607 }
7608 Stmt::While { body, .. }
7609 | Stmt::DoWhile { body, .. }
7610 | Stmt::ForEach { body, .. }
7611 | Stmt::Synchronized { body, .. }
7612 | Stmt::Labeled { body, .. } => rewrite_bare_returns_one(body, val),
7613 Stmt::For { init, body, .. } => {
7614 rewrite_bare_returns(init, val);
7615 rewrite_bare_returns_one(body, val);
7616 }
7617 Stmt::Switch { cases, default, .. } => {
7618 for c in cases.iter_mut() {
7619 rewrite_bare_returns(&mut c.body, val);
7620 }
7621 if let Some(d) = default {
7622 rewrite_bare_returns_one(d, val);
7623 }
7624 }
7625 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
7626 rewrite_bare_returns_one(body, val);
7627 for c in catches.iter_mut() {
7628 rewrite_bare_returns_one(&mut c.body, val);
7629 }
7630 if let Some(f) = finally {
7631 rewrite_bare_returns_one(f, val);
7632 }
7633 }
7634 _ => {}
7635 }
7636}
7637
7638fn collect_def_vars(stmts: &[Stmt], out: &mut jdc_core::FxHashSet<u32>) {
7639 for st in stmts {
7640 match st {
7641 Stmt::LocalDef { var, .. } => {
7642 out.insert(*var);
7643 }
7644 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
7645 if let Expr::Local { var, .. } = &**target {
7646 out.insert(*var);
7647 }
7648 }
7649 Stmt::Block(v) => collect_def_vars(v, out),
7650 Stmt::If { then_stmt, else_stmt, .. } => {
7651 collect_def_vars(std::slice::from_ref(then_stmt.as_ref()), out);
7652 if let Some(e) = else_stmt {
7653 collect_def_vars(std::slice::from_ref(e.as_ref()), out);
7654 }
7655 }
7656 Stmt::While { body, .. }
7657 | Stmt::DoWhile { body, .. }
7658 | Stmt::Synchronized { body, .. }
7659 | Stmt::Labeled { body, .. } => {
7660 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
7661 }
7662 Stmt::ForEach { var, body, .. } => {
7663 out.insert(*var);
7664 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
7665 }
7666 Stmt::For { init, body, .. } => {
7667 collect_def_vars(init, out);
7668 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
7669 }
7670 Stmt::Switch { cases, default, .. } => {
7671 for c in cases {
7672 collect_def_vars(&c.body, out);
7673 }
7674 if let Some(d) = default {
7675 collect_def_vars(std::slice::from_ref(d.as_ref()), out);
7676 }
7677 }
7678 Stmt::Try { body, catches, finally }
7679 | Stmt::TryWithResources { body, catches, finally, .. } => {
7680 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
7681 for c in catches {
7682 out.insert(c.var);
7683 collect_def_vars(std::slice::from_ref(c.body.as_ref()), out);
7684 }
7685 if let Some(f) = finally {
7686 collect_def_vars(std::slice::from_ref(f.as_ref()), out);
7687 }
7688 }
7689 _ => {}
7690 }
7691 }
7692}
7693
7694fn extraction_shape_ok(stmts: &[Stmt]) -> bool {
7697 stmts.iter().all(|st| match st {
7698 Stmt::Block(v) => extraction_shape_ok(v),
7699 Stmt::LocalDef { .. }
7700 | Stmt::ExprStmt(_)
7701 | Stmt::Return(_)
7702 | Stmt::Throw(_)
7703 | Stmt::Assert { .. }
7704 | Stmt::Break(_)
7705 | Stmt::Continue(_) => true,
7706 Stmt::If { then_stmt, else_stmt, .. } => {
7707 ok_one(then_stmt)
7708 && else_stmt.as_deref().map(ok_one).unwrap_or(true)
7709 }
7710 Stmt::While { body, .. }
7711 | Stmt::DoWhile { body, .. }
7712 | Stmt::ForEach { body, .. }
7713 | Stmt::Synchronized { body, .. } => ok_one(body),
7714 Stmt::Labeled { body, .. } => ok_one(body),
7715 Stmt::For { init, body, .. } => extraction_shape_ok(init) && ok_one(body),
7716 Stmt::Switch { cases, default, .. } => {
7717 cases.iter().all(|c| extraction_shape_ok(&c.body))
7718 && default.as_deref().map(ok_one).unwrap_or(true)
7719 }
7720 Stmt::Try { body, catches, finally }
7721 | Stmt::TryWithResources { body, catches, finally, .. } => {
7722 ok_one(body)
7723 && catches.iter().all(|c| ok_one(&c.body))
7724 && finally.as_deref().map(ok_one).unwrap_or(true)
7725 }
7726 _ => false,
7729 })
7730}
7731
7732fn ok_one(st: &Stmt) -> bool {
7733 extraction_shape_ok(std::slice::from_ref(st))
7734}
7735
7736fn static_safe(stmts: &[Stmt], this_id: Option<u32>) -> bool {
7737 let mut ok = true;
7738 for st in stmts {
7739 visit_stmt_exprs_ro(st, &mut |e| {
7740 visit_exprs(e, &mut |x| {
7741 match x {
7742 Expr::This => ok = false,
7743 Expr::Local { var, .. } => {
7749 if Some(*var) == this_id {
7750 ok = false;
7751 }
7752 }
7753 Expr::Field { owner: None, is_static: false, .. } => ok = false,
7755 Expr::Method { owner: None, is_static: false, .. } => ok = false,
7756 _ => {}
7757 }
7758 });
7759 });
7760 }
7761 ok
7762}
7763
7764pub fn strip_clinit_hotfix_guard(body: &mut Stmt) {
7775 if let Stmt::Block(vs) = body {
7779 flatten_top_blocks(vs);
7780 }
7781 let Stmt::Block(stmts) = body else { return };
7782 let mut gi = 0usize;
7785 while gi < stmts.len() && matches!(&stmts[gi], Stmt::LocalDef { init: None, .. }) {
7786 gi += 1;
7787 }
7788 let Some(first) = stmts.get(gi) else { return };
7789 let Stmt::If { then_stmt, else_stmt, .. } = first else {
7790 return;
7791 };
7792 if else_stmt.is_some() {
7793 return;
7794 }
7795 let mut has_ret = false;
7796 let mut has_proxy = false;
7797 let c = then_stmt.as_ref();
7798 walk_all(c, &mut |st| {
7799 if matches!(st, Stmt::Return(None)) {
7800 has_ret = true;
7801 }
7802 });
7803 let mut cc = c.clone();
7804 walk_stmt_exprs(&mut cc, &mut |e| {
7805 deep_rewrite(e, &mut |x| {
7806 if let Expr::Method { cls, .. } = x {
7807 if cls.contains("PatchProxy") {
7808 has_proxy = true;
7809 }
7810 }
7811 });
7812 });
7813 if has_ret && has_proxy {
7814 stmts.remove(gi);
7815 }
7816}
7817
7818pub(crate) fn first_leaf_stmt(s: &Stmt) -> &Stmt {
7839 match s {
7840 Stmt::Block(v) if !v.is_empty() => first_leaf_stmt(&v[0]),
7841 other => other,
7842 }
7843}
7844
7845pub fn strip_ctor_hotfix_guards(body: &mut Stmt) {
7864 fn marker_hit(e: &Expr) -> bool {
7865 let has = |n: &str| {
7866 let l = n.to_ascii_lowercase();
7867 l.contains("hotfix")
7868 || l.contains("instantrun")
7869 || l.contains("robust")
7870 || l.contains("titan")
7871 || l.contains("patchproxy")
7872 || l.contains("changequickredirect")
7873 || l.contains("constructorcode")
7874 };
7875 let mut hit = false;
7876 visit_exprs(e, &mut |x| {
7877 match x {
7878 Expr::Method { cls, .. } | Expr::New { cls, .. } | Expr::Field { cls, .. } => {
7879 if has(cls) {
7880 hit = true;
7881 }
7882 }
7883 _ => {}
7884 }
7885 if let Expr::Field { ty, .. } = x {
7886 if let TypeRef::J(JavaType::Object(n)) = ty {
7887 if has(n) {
7888 hit = true;
7889 }
7890 }
7891 }
7892 });
7893 hit
7894 }
7895 fn is_guard(st: &Stmt) -> bool {
7899 let mut cur = st;
7900 loop {
7901 match cur {
7902 Stmt::Block(v) if v.len() == 1 => cur = &v[0],
7903 Stmt::If { .. } => break,
7904 _ => return false,
7905 }
7906 }
7907 let mut has_del = false;
7908 walk_all(cur, &mut |s2| {
7909 if is_bare_ctor_call(s2) {
7910 has_del = true;
7911 }
7912 });
7913 if !has_del {
7914 return false;
7915 }
7916 let mut has_marker = false;
7917 visit_stmt_exprs_ro(cur, &mut |e| {
7918 if marker_hit(e) {
7919 has_marker = true;
7920 }
7921 });
7922 has_marker
7923 }
7924 fn strip_in(stmts: &mut Vec<Stmt>) {
7935 stmts.retain(|st| !is_guard(st));
7936 for st in stmts.iter_mut() {
7937 if let Stmt::Block(v) = st {
7938 strip_in(v);
7939 }
7940 }
7941 }
7942 let Stmt::Block(stmts) = body else { return };
7943 strip_in(stmts);
7944}
7945
7946pub fn hoist_branch_delegations(body: &mut Stmt, vt: &VarTable) {
7959 let Stmt::Block(stmts) = body else { return };
7960 if stmts.first().is_some_and(is_bare_ctor_call) {
7961 return; }
7963 let mut dels: Vec<Expr> = Vec::new();
7964 for st in stmts.iter() {
7965 visit_stmt_exprs_ro(st, &mut |e| {
7966 if is_delegation_expr(e) {
7967 dels.push(e.clone());
7968 }
7969 });
7970 }
7971 if dels.is_empty() {
7972 return;
7973 }
7974 if !dels.iter().all(|d| *d == dels[0]) {
7975 return;
7976 }
7977 let mut bad = false;
7978 visit_exprs(&dels[0], &mut |x| {
7979 if let Expr::Local { var, .. } = x {
7980 if (*var as usize) >= vt.vars.len() || !vt.vars[*var as usize].is_param {
7981 bad = true;
7982 }
7983 }
7984 });
7985 if bad {
7986 return;
7987 }
7988 let del = dels[0].clone();
7989 strip_delegations_deep(stmts);
7990 stmts.insert(0, Stmt::ExprStmt(del));
7991}
7992
7993pub fn extract_branched_delegation_helper(
8027 body: &mut Stmt,
8028 vt: &VarTable,
8029 pool: &DexPool,
8030 class: &crate::PoolClass,
8031 desc_str: &str,
8032) {
8033 if class.is_enum() {
8034 return;
8035 }
8036 if class.nesting.enclosing_class.is_some() && !crate::ctx::nested_is_static(pool, class) {
8041 return;
8042 }
8043 let Stmt::Block(stmts) = body else { return };
8044 let mut dels: Vec<Expr> = Vec::new();
8045 for st in stmts.iter() {
8046 visit_stmt_exprs_ro(st, &mut |e| {
8047 if is_delegation_expr(e) {
8048 dels.push(e.clone());
8049 }
8050 });
8051 }
8052 if dels.is_empty() {
8053 return;
8054 }
8055 if dels.len() == 1 && stmts.first().is_some_and(is_bare_ctor_call) {
8056 return; }
8058 let template = dels[0].clone();
8059 let Expr::Method { args: t_args, .. } = &template else {
8060 return;
8061 };
8062 enum Mode {
8066 Single(u32),
8067 Position(usize),
8068 Full,
8075 }
8076 let full_gate = (|| -> Option<()> {
8083 let Expr::Method {
8084 args: a0,
8085 desc: d0,
8086 is_super: s0,
8087 cls: c0,
8088 ..
8089 } = &dels[0]
8090 else {
8091 return None;
8092 };
8093 if a0.is_empty() {
8094 return None;
8095 }
8096 for d in &dels[1..] {
8097 match d {
8098 Expr::Method {
8099 args,
8100 desc,
8101 is_super,
8102 cls,
8103 ..
8104 } if args.len() == a0.len()
8105 && **desc == **d0
8106 && is_super == s0
8107 && cls == c0 => {}
8108 _ => return None,
8109 }
8110 }
8111 if class
8112 .all_methods()
8113 .any(|m| &*m.name == "<init>" && &*m.desc == "([Ljava/lang/Object;)V")
8114 {
8115 return None;
8116 }
8117 Some(())
8118 })()
8119 .is_some();
8120 let mode = if dels.iter().all(|d| *d == dels[0]) {
8121 let mut carrier: Option<u32> = None;
8122 let mut reads = 0usize;
8123 let mut multi = false;
8124 for a in t_args.iter() {
8125 let c = a.clone();
8126 visit_exprs(&c, &mut |x| {
8127 if let Expr::Local { var, .. } = x {
8128 if (*var as usize) < vt.vars.len() && !vt.vars[*var as usize].is_param {
8129 reads += 1;
8130 match carrier {
8131 Some(v) if v == *var => {}
8132 Some(_) => multi = true,
8133 None => carrier = Some(*var),
8134 }
8135 }
8136 }
8137 });
8138 }
8139 match carrier {
8140 Some(cv) if !multi && reads == 1 => Mode::Single(cv),
8141 _ if full_gate => Mode::Full,
8142 _ => return,
8143 }
8144 } else {
8145 let positioned: Option<Mode> = (|| {
8146 let arity = t_args.len();
8147 if dels
8151 .iter()
8152 .any(|d| !matches!(d, Expr::Method { args, .. } if args.len() == arity))
8153 {
8154 return None;
8155 }
8156 let mut diff: Vec<usize> = Vec::new();
8157 for j in 0..arity {
8158 if dels.iter().any(|d| match d {
8159 Expr::Method { args, .. } => args.get(j) != t_args.get(j),
8160 _ => true,
8161 }) {
8162 diff.push(j);
8163 }
8164 }
8165 if diff.len() != 1 {
8166 return None;
8167 }
8168 let k = diff[0];
8169 let mut base0 = dels[0].clone();
8173 if let Expr::Method { args, .. } = &mut base0 {
8174 args[k] = Expr::Const(ConstVal::Null);
8175 }
8176 for d in dels.iter() {
8177 let mut c = d.clone();
8178 if let Expr::Method { args, .. } = &mut c {
8179 args[k] = Expr::Const(ConstVal::Null);
8180 }
8181 if c != base0 {
8182 return None;
8183 }
8184 }
8185 let mut carrier_ty: Option<JavaType> = None;
8188 for d in dels.iter() {
8189 let Expr::Method { args, .. } = d else {
8190 return None;
8191 };
8192 let Expr::Local { var, .. } = &args[k] else {
8193 return None;
8194 };
8195 if (*var as usize) >= vt.vars.len() || vt.vars[*var as usize].is_param {
8196 return None;
8197 }
8198 let ty = vt.vars[*var as usize].ty.erased();
8199 match &carrier_ty {
8200 Some(t) if *t == ty => {}
8201 Some(_) => return None,
8202 None => carrier_ty = Some(ty),
8203 }
8204 for (j, a) in args.iter().enumerate() {
8210 if j == k {
8211 continue;
8212 }
8213 let mut bad = false;
8214 visit_exprs(a, &mut |x| {
8215 if let Expr::Local { var: v2, .. } = x {
8216 if (*v2 as usize) >= vt.vars.len() || !vt.vars[*v2 as usize].is_param {
8217 bad = true;
8218 }
8219 }
8220 });
8221 if bad {
8222 return None;
8223 }
8224 }
8225 }
8226 Some(Mode::Position(k))
8227 })();
8228 match positioned {
8229 Some(m) => m,
8230 None if full_gate => Mode::Full,
8231 None => return,
8232 }
8233 };
8234 let l = stmts.iter().rposition(is_bare_ctor_call);
8235 if l == Some(0) {
8236 return;
8237 }
8238 let (mut extracted, mut tail): (Vec<Stmt>, Vec<Stmt>) = match l {
8239 Some(l) => (stmts[..l].to_vec(), stmts[l + 1..].to_vec()),
8240 None => (stmts.clone(), Vec::new()),
8241 };
8242 if !extraction_shape_ok(&extracted) {
8243 return;
8244 }
8245 let mut site_tails: Vec<Vec<Stmt>> = Vec::new();
8252 {
8253 let mut probe = extracted.clone();
8254 site_tails_walk(&mut probe, &mut site_tails, false);
8255 }
8256 let hoist = !site_tails.is_empty() && {
8257 let n0 = normalize_tail(&site_tails[0]);
8258 site_tails.iter().all(|t| normalize_tail(t) == n0)
8259 && (l.is_none() || normalize_tail(&tail) == n0)
8260 };
8261 if hoist {
8262 site_tails_walk(&mut extracted, &mut Vec::new(), true);
8263 if l.is_none() {
8264 tail = normalize_tail(&site_tails[0]);
8265 }
8266 }
8267 let primary_del: Expr = match l {
8270 Some(l) => match &stmts[l] {
8271 Stmt::ExprStmt(e) => e.clone(),
8272 _ => return,
8273 },
8274 None => dels[0].clone(),
8275 };
8276 let ret = match mode {
8277 Mode::Single(cv) => vt.var(cv).ty.erased(),
8278 Mode::Position(k) => match &primary_del {
8279 Expr::Method { args, .. } => match args.get(k) {
8280 Some(Expr::Local { var, .. }) => vt.var(*var).ty.erased(),
8281 _ => return,
8282 },
8283 _ => return,
8284 },
8285 Mode::Full => JavaType::Array(Box::new(JavaType::Object(std::sync::Arc::from(
8286 "java/lang/Object",
8287 )))),
8288 };
8289 match mode {
8290 Mode::Single(cv) => {
8291 strip_delegations_deep(&mut extracted);
8292 let cv_local = Expr::Local {
8293 var: cv,
8294 ty: vt.var(cv).ty.clone(),
8295 };
8296 rewrite_bare_returns(&mut extracted, &cv_local);
8297 if !always_returns(&extracted) {
8298 extracted.push(Stmt::Return(Some(cv_local)));
8299 }
8300 }
8301 Mode::Position(k) => {
8302 fill_returns_by_position(&mut extracted, &|args: &[Expr]| args.get(k).cloned());
8306 let fallback = match &primary_del {
8307 Expr::Method { args, .. } => args[k].clone(),
8308 _ => return,
8309 };
8310 rewrite_bare_returns(&mut extracted, &fallback);
8311 if !always_returns(&extracted) {
8312 extracted.push(Stmt::Return(Some(fallback)));
8313 }
8314 }
8315 Mode::Full => {
8316 fill_returns_by_position(&mut extracted, &|args: &[Expr]| {
8323 Some(array_init(args.to_vec()))
8324 });
8325 if l.is_none() {
8326 if !always_returns(&extracted) {
8333 let cut = (1..=extracted.len())
8334 .find(|&i| always_returns(&extracted[..i]));
8335 match cut {
8336 Some(i) => extracted.truncate(i),
8337 None => return,
8338 }
8339 }
8340 }
8341 let fallback_args = match &primary_del {
8342 Expr::Method { args, .. } => args.clone(),
8343 _ => return,
8344 };
8345 let fallback = array_init(fallback_args);
8346 rewrite_bare_returns(&mut extracted, &fallback);
8347 if !always_returns(&extracted) {
8348 extracted.push(Stmt::Return(Some(fallback)));
8349 }
8350 }
8351 }
8352 let this_id = vt
8353 .vars
8354 .iter()
8355 .find(|v| v.is_param && v.name == "this")
8356 .map(|v| v.id);
8357 if !static_safe(&extracted, this_id) {
8358 return;
8359 }
8360 let mut defs: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
8361 collect_def_vars(&extracted, &mut defs);
8362 if defs.iter().any(|v| var_occurs_in(&tail, *v)) {
8363 return;
8364 }
8365 let params: Vec<(u32, JavaType, String)> = vt
8366 .vars
8367 .iter()
8368 .filter(|v| v.is_param && v.name != "this")
8369 .map(|v| (v.id, v.ty.erased(), v.name.clone()))
8370 .collect();
8371 let name = helper_name(&class.name, desc_str);
8372 let call = Expr::Method {
8373 owner: None,
8374 cls: std::sync::Arc::from(class.name.as_str()),
8375 name: std::sync::Arc::from(name.as_str()),
8376 desc: std::sync::Arc::new(jdc_core::types::MethodDescriptor {
8377 ret: ret.clone(),
8378 args: params.iter().map(|p| p.1.clone()).collect(),
8379 }),
8380 args: params
8381 .iter()
8382 .map(|(id, ty, _)| Expr::Local {
8383 var: *id,
8384 ty: TypeRef::J(ty.clone()),
8385 })
8386 .collect(),
8387 is_static: true,
8388 is_interface: false,
8389 is_special: false,
8390 is_super: false,
8391 is_dynamic: false,
8392 type_args: Vec::new(),
8393 };
8394 let mut new_del = primary_del.clone();
8395 match mode {
8396 Mode::Single(cv) => {
8397 deep_rewrite(&mut new_del, &mut |x| {
8398 if let Expr::Local { var, .. } = x {
8399 if *var == cv {
8400 *x = call.clone();
8401 }
8402 }
8403 });
8404 }
8405 Mode::Position(k) => {
8406 let Expr::Method { args, .. } = &mut new_del else {
8407 return;
8408 };
8409 if k >= args.len() {
8410 return;
8411 }
8412 args[k] = call.clone();
8413 }
8414 Mode::Full => {
8415 new_del = Expr::Method {
8419 owner: None,
8420 cls: std::sync::Arc::from(class.name.as_str()),
8421 name: "<init>".into(),
8422 desc: std::sync::Arc::new(jdc_core::types::MethodDescriptor {
8423 args: vec![JavaType::Array(Box::new(JavaType::Object(
8424 std::sync::Arc::from("java/lang/Object"),
8425 )))],
8426 ret: JavaType::Void,
8427 }),
8428 args: vec![call.clone()],
8429 is_static: false,
8430 is_interface: false,
8431 is_special: true,
8432 is_super: false,
8433 is_dynamic: false,
8434 type_args: Vec::new(),
8435 };
8436 }
8437 }
8438 {
8439 let mut m = ctor_helpers().lock().unwrap_or_else(|e| e.into_inner());
8440 let v = m.entry(class.name.clone()).or_default();
8441 if !v.iter().any(|h| h.name == name) {
8442 let mut hbody = Stmt::Block(extracted);
8446 invert_empty_thens(&mut hbody);
8447 v.push(CtorHelper {
8448 name,
8449 ret: ret.clone(),
8450 params: params.into_iter().map(|(_, ty, nm)| (ty, nm)).collect(),
8451 body: hbody,
8452 vt: vt.clone(),
8453 });
8454 if matches!(mode, Mode::Full) {
8455 let Expr::Method {
8456 desc: d0,
8457 is_super: s0,
8458 ..
8459 } = &primary_del
8460 else {
8461 return;
8462 };
8463 let mut rm = ctor_relays().lock().unwrap_or_else(|e| e.into_inner());
8464 let rv = rm.entry(class.name.clone()).or_default();
8465 if !rv.iter().any(|(d, _)| d == desc_str) {
8466 rv.push((
8467 desc_str.to_string(),
8468 CtorRelay {
8469 formals: d0.args.clone(),
8470 is_super: *s0,
8471 },
8472 ));
8473 }
8474 }
8475 } else {
8476 return; }
8478 }
8479 *stmts = std::iter::once(Stmt::ExprStmt(new_del)).chain(tail).collect();
8480}
8481
8482fn fill_returns_by_position(
8489 stmts: &mut Vec<Stmt>,
8490 pick: &dyn Fn(&[Expr]) -> Option<Expr>,
8491) {
8492 let mut i = 0;
8493 while i < stmts.len() {
8494 if is_bare_ctor_call(&stmts[i]) {
8495 let karg = match &stmts[i] {
8496 Stmt::ExprStmt(Expr::Method { args, .. }) => pick(args),
8497 _ => None,
8498 };
8499 if let Some(v) = karg {
8500 let mut j = i + 1;
8501 let mut found = None;
8502 while j < stmts.len() {
8503 if matches!(stmts[j], Stmt::Return(None)) {
8504 found = Some(j);
8505 break;
8506 }
8507 if is_bare_ctor_call(&stmts[j]) {
8508 break; }
8510 j += 1;
8511 }
8512 match found {
8513 Some(j) => {
8514 stmts[j] = Stmt::Return(Some(v));
8515 stmts.remove(i);
8516 continue;
8517 }
8518 None => {
8519 stmts[i] = Stmt::Return(Some(v));
8520 stmts.truncate(i + 1);
8521 break;
8522 }
8523 }
8524 }
8525 }
8526 fill_returns_one(&mut stmts[i], pick);
8527 i += 1;
8528 }
8529}
8530
8531fn fill_returns_one(st: &mut Stmt, pick: &dyn Fn(&[Expr]) -> Option<Expr>) {
8532 match st {
8533 Stmt::Block(v) => fill_returns_by_position(v, pick),
8534 Stmt::If { then_stmt, else_stmt, .. } => {
8535 fill_returns_stmt(then_stmt, pick);
8536 if let Some(e) = else_stmt {
8537 fill_returns_stmt(e, pick);
8538 }
8539 }
8540 Stmt::While { body, .. }
8541 | Stmt::DoWhile { body, .. }
8542 | Stmt::ForEach { body, .. }
8543 | Stmt::Synchronized { body, .. }
8544 | Stmt::Labeled { body, .. } => fill_returns_stmt(body, pick),
8545 Stmt::For { init, body, .. } => {
8546 fill_returns_by_position(init, pick);
8547 fill_returns_stmt(body, pick);
8548 }
8549 Stmt::Switch { cases, default, .. } => {
8550 for c in cases.iter_mut() {
8551 fill_returns_by_position(&mut c.body, pick);
8552 }
8553 if let Some(d) = default {
8554 fill_returns_stmt(d, pick);
8555 }
8556 }
8557 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
8558 fill_returns_stmt(body, pick);
8559 for c in catches.iter_mut() {
8560 fill_returns_stmt(&mut c.body, pick);
8561 }
8562 if let Some(f) = finally {
8563 fill_returns_stmt(f, pick);
8564 }
8565 }
8566 _ => {}
8567 }
8568}
8569
8570fn fill_returns_stmt(st: &mut Stmt, pick: &dyn Fn(&[Expr]) -> Option<Expr>) {
8571 if is_bare_ctor_call(st) {
8572 if let Stmt::ExprStmt(Expr::Method { args, .. }) = st {
8575 if let Some(v) = pick(args) {
8576 *st = Stmt::Return(Some(v));
8577 return;
8578 }
8579 }
8580 }
8581 fill_returns_one(st, pick);
8582}
8583
8584fn normalize_tail(t: &[Stmt]) -> Vec<Stmt> {
8585 let mut v = t.to_vec();
8586 while matches!(v.last(), Some(Stmt::Return(None))) {
8587 v.pop();
8588 }
8589 v
8590}
8591
8592fn site_tails_walk(stmts: &mut Vec<Stmt>, out: &mut Vec<Vec<Stmt>>, carve: bool) {
8597 let mut i = 0;
8598 while i < stmts.len() {
8599 if is_bare_ctor_call(&stmts[i]) {
8600 let mut j = i + 1;
8601 let mut end = stmts.len();
8602 while j < stmts.len() {
8603 if matches!(stmts[j], Stmt::Return(None)) {
8604 end = j;
8605 break;
8606 }
8607 if is_bare_ctor_call(&stmts[j]) {
8608 end = j;
8609 break;
8610 }
8611 j += 1;
8612 }
8613 out.push(stmts[i + 1..end].to_vec());
8614 if carve {
8615 stmts.splice(i + 1..end, []);
8616 }
8617 i += 1;
8618 continue;
8619 }
8620 site_tails_one(&mut stmts[i], out, carve);
8621 i += 1;
8622 }
8623}
8624
8625fn site_tails_one(st: &mut Stmt, out: &mut Vec<Vec<Stmt>>, carve: bool) {
8626 if is_bare_ctor_call(st) {
8627 out.push(Vec::new());
8628 return;
8629 }
8630 match st {
8631 Stmt::Block(v) => site_tails_walk(v, out, carve),
8632 Stmt::If { then_stmt, else_stmt, .. } => {
8633 site_tails_one(then_stmt, out, carve);
8634 if let Some(e) = else_stmt {
8635 site_tails_one(e, out, carve);
8636 }
8637 }
8638 Stmt::While { body, .. }
8639 | Stmt::DoWhile { body, .. }
8640 | Stmt::ForEach { body, .. }
8641 | Stmt::Synchronized { body, .. }
8642 | Stmt::Labeled { body, .. } => site_tails_one(body, out, carve),
8643 Stmt::For { init, body, .. } => {
8644 site_tails_walk(init, out, carve);
8645 site_tails_one(body, out, carve);
8646 }
8647 Stmt::Switch { cases, default, .. } => {
8648 for c in cases.iter_mut() {
8649 site_tails_walk(&mut c.body, out, carve);
8650 }
8651 if let Some(d) = default {
8652 site_tails_one(d, out, carve);
8653 }
8654 }
8655 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
8656 site_tails_one(body, out, carve);
8657 for c in catches.iter_mut() {
8658 site_tails_one(&mut c.body, out, carve);
8659 }
8660 if let Some(f) = finally {
8661 site_tails_one(f, out, carve);
8662 }
8663 }
8664 _ => {}
8665 }
8666}
8667
8668fn always_returns(stmts: &[Stmt]) -> bool {
8674 let Some(last) = stmts
8675 .iter()
8676 .rev()
8677 .find(|s| !matches!(s, Stmt::Block(v) if v.is_empty()))
8678 else {
8679 return false;
8680 };
8681 match last {
8682 Stmt::Return(Some(_)) | Stmt::Throw(_) => true,
8683 Stmt::Block(v) => always_returns(v),
8684 Stmt::If { then_stmt, else_stmt, .. } => match else_stmt {
8685 Some(e) => always_returns_one(then_stmt) && always_returns_one(e),
8686 None => false,
8687 },
8688 Stmt::Labeled { body, .. } | Stmt::Synchronized { body, .. } => {
8689 always_returns_one(body)
8690 }
8691 Stmt::Switch { cases, default, .. } => {
8692 default.as_ref().is_some_and(|d| always_returns_one(d))
8693 && cases.iter().all(|c| always_returns(&c.body))
8694 }
8695 Stmt::Try { body, catches, finally }
8696 | Stmt::TryWithResources { body, catches, finally, .. } => {
8697 finally.as_ref().is_some_and(|f| always_returns_one(f))
8698 || (always_returns_one(body)
8699 && catches.iter().all(|c| always_returns_one(&c.body)))
8700 }
8701 _ => false,
8702 }
8703}
8704
8705fn always_returns_one(st: &Stmt) -> bool {
8706 always_returns(std::slice::from_ref(st))
8707}
8708
8709fn inline_ctor_arg_defs(
8710 call: &mut Stmt,
8711 prefix: &[&Stmt],
8712 vt: &VarTable,
8713 consumed: &mut Vec<(u32, usize)>,
8714) -> bool {
8715 let Stmt::ExprStmt(cexpr) = call else {
8716 return false;
8717 };
8718 fn subst(e: &mut Expr, v: u32, val: &Option<Expr>) -> bool {
8721 let mut ok = true;
8722 deep_rewrite(e, &mut |x| {
8723 if let Expr::Local { var, .. } = x {
8724 if *var == v {
8725 match val {
8726 Some(e2) => *x = e2.clone(),
8727 None => ok = false,
8728 }
8729 }
8730 }
8731 });
8732 ok
8733 }
8734 let mut seen: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
8735 for _ in 0..32 {
8736 let mut need: Option<u32> = None;
8737 visit_exprs(cexpr, &mut |x| {
8738 if need.is_none() {
8739 if let Expr::Local { var, .. } = x {
8740 if (*var as usize) < vt.vars.len() && !vt.vars[*var as usize].is_param {
8741 need = Some(*var);
8742 }
8743 }
8744 }
8745 });
8746 let Some(v) = need else { break };
8747 if !seen.insert(v) {
8748 return false; }
8750 let mut val: Option<Expr> = None;
8762 let mut ok = true;
8763 for (si, st) in prefix.iter().enumerate() {
8764 match st {
8765 Stmt::LocalDef { var, init: None, .. } if *var == v => {}
8769 Stmt::LocalDef { var, init, .. } if *var == v => {
8770 let Some(e) = init else { ok = false; break };
8771 let mut e2 = (*e).clone();
8772 if !subst(&mut e2, v, &val) {
8773 ok = false;
8774 break;
8775 }
8776 val = Some(e2);
8777 consumed.push((v, si));
8778 }
8779 Stmt::ExprStmt(Expr::Assign { target, op, .. })
8780 if matches!(&**target, Expr::Local { var: tv, .. } if *tv == v) =>
8781 {
8782 if !matches!(op, AssignOp::Plain) {
8783 ok = false; break;
8785 }
8786 let Stmt::ExprStmt(Expr::Assign { value, .. }) = st else {
8787 unreachable!()
8788 };
8789 let mut e2 = value.as_ref().clone();
8790 if !subst(&mut e2, v, &val) {
8791 ok = false;
8792 break;
8793 }
8794 val = Some(e2);
8795 consumed.push((v, si));
8796 }
8797 _ => {
8798 let mut touched = false;
8799 visit_all_exprs(st, &mut |x| {
8800 if let Expr::Local { var, .. } = x {
8801 if *var == v {
8802 touched = true;
8803 }
8804 }
8805 });
8806 if touched {
8807 ok = false;
8808 break;
8809 }
8810 }
8811 }
8812 }
8813 if !ok {
8814 return false;
8815 }
8816 let Some(de) = val else { return false };
8817 if contains_this_access(&de) {
8818 return false;
8819 }
8820 let mut de_nodes = 0usize;
8822 visit_exprs(&de, &mut |_| de_nodes += 1);
8823 if de_nodes > 256 {
8824 return false;
8825 }
8826 deep_rewrite(cexpr, &mut |x| {
8827 if let Expr::Local { var, .. } = x {
8828 if *var == v {
8829 *x = de.clone();
8830 }
8831 }
8832 });
8833 let mut nodes = 0usize;
8838 visit_exprs(cexpr, &mut |_| nodes += 1);
8839 if nodes > 2_048 {
8840 return false;
8841 }
8842 }
8843 true
8844}
8845
8846fn fold_sb_chain(stmts: &mut Vec<Stmt>, pos: usize) -> usize {
8859 let mut sbv: Option<u32> = None;
8861 let mut def_i: Option<usize> = None;
8862 let mut def_init: Option<Expr> = None;
8863 for (i, st) in stmts[..pos].iter().enumerate() {
8864 let cand: Option<(u32, &Expr)> = match st {
8865 Stmt::LocalDef {
8866 var,
8867 init: Some(e),
8868 ..
8869 } => Some((*var, e)),
8870 Stmt::ExprStmt(Expr::Assign {
8871 target,
8872 value,
8873 op: AssignOp::Plain,
8874 ..
8875 }) => match &**target {
8876 Expr::Local { var, .. } => Some((*var, value.as_ref())),
8877 _ => None,
8878 },
8879 _ => None,
8880 };
8881 if let Some((v, init)) = cand {
8882 if let Expr::New { cls, .. } = init {
8883 if cls.as_ref() == "java/lang/StringBuilder" {
8884 sbv = Some(v);
8885 def_i = Some(i);
8886 def_init = Some(init.clone());
8887 break;
8888 }
8889 }
8890 }
8891 }
8892 let (Some(sbv), Some(def_i), Some(init)) = (sbv, def_i, def_init) else {
8893 return pos;
8894 };
8895 if contains_this_access(&init) {
8896 return pos;
8897 }
8898 let mut appends: Vec<usize> = Vec::new();
8900 for (i, st) in stmts[..pos].iter().enumerate() {
8901 if let Stmt::ExprStmt(Expr::Method {
8902 owner: Some(o),
8903 cls,
8904 name,
8905 ..
8906 }) = st
8907 {
8908 if matches!(&**o, Expr::Local { var, .. } if *var == sbv)
8909 && cls.as_ref() == "java/lang/StringBuilder"
8910 && &**name == "append"
8911 {
8912 appends.push(i);
8913 }
8914 }
8915 }
8916 let mut post_read = false;
8918 for st in stmts.iter().skip(pos + 1) {
8919 visit_all_exprs(st, &mut |e| {
8920 if let Expr::Local { var, .. } = e {
8921 if *var == sbv {
8922 post_read = true;
8923 }
8924 }
8925 });
8926 }
8927 if post_read {
8928 return pos;
8929 }
8930 for (i, st) in stmts[..pos].iter().enumerate() {
8933 if i == def_i || appends.contains(&i) {
8934 if let Stmt::ExprStmt(Expr::Method { args, .. }) = st {
8936 if args.iter().any(contains_this_access) {
8937 return pos;
8938 }
8939 }
8940 continue;
8941 }
8942 let mut leak = false;
8943 visit_all_exprs(st, &mut |e| {
8944 if let Expr::Local { var, .. } = e {
8945 if *var == sbv {
8946 leak = true;
8947 }
8948 }
8949 });
8950 if leak {
8951 return pos;
8952 }
8953 }
8954 let mut local_reads = 0usize;
8956 let mut tostring_reads = 0usize;
8957 visit_all_exprs(&stmts[pos], &mut |e| match e {
8958 Expr::Local { var, .. } if *var == sbv => local_reads += 1,
8959 Expr::Method {
8960 owner: Some(o),
8961 name,
8962 args,
8963 ..
8964 } if args.is_empty()
8965 && &**name == "toString"
8966 && matches!(&**o, Expr::Local { var, .. } if *var == sbv) =>
8967 {
8968 tostring_reads += 1;
8969 }
8970 _ => {}
8971 });
8972 if local_reads != 1 || tostring_reads != 1 {
8973 return pos;
8974 }
8975 let mut chain = init;
8977 for ai in &appends {
8978 if let Stmt::ExprStmt(Expr::Method {
8979 owner: _,
8980 cls,
8981 name,
8982 desc,
8983 args,
8984 is_static,
8985 is_interface,
8986 is_special,
8987 is_super,
8988 is_dynamic,
8989 type_args,
8990 ..
8991 }) = &stmts[*ai]
8992 {
8993 chain = Expr::Method {
8994 owner: Some(Box::new(chain)),
8995 cls: cls.clone(),
8996 name: name.clone(),
8997 desc: desc.clone(),
8998 args: args.clone(),
8999 is_static: *is_static,
9000 is_interface: *is_interface,
9001 is_special: *is_special,
9002 is_super: *is_super,
9003 is_dynamic: *is_dynamic,
9004 type_args: type_args.clone(),
9005 };
9006 }
9007 }
9008 let mut replaced = false;
9009 walk_stmt_exprs(&mut stmts[pos], &mut |e| {
9010 deep_rewrite(e, &mut |x| {
9011 if let Expr::Method {
9012 owner: Some(o),
9013 name,
9014 args,
9015 ..
9016 } = x
9017 {
9018 if args.is_empty()
9019 && &**name == "toString"
9020 && matches!(&**o, Expr::Local { var, .. } if *var == sbv)
9021 {
9022 **o = chain.clone();
9023 replaced = true;
9024 }
9025 }
9026 });
9027 });
9028 if !replaced {
9029 return pos; }
9031 let mut removed: Vec<usize> = appends.clone();
9033 removed.push(def_i);
9034 removed.sort_unstable();
9035 removed.dedup();
9036 for i in removed.iter().rev() {
9037 stmts.remove(*i);
9038 }
9039 pos - removed.len()
9040}
9041
9042pub fn fix_ctor_super_first(body: &mut Stmt, vt: &VarTable) {
9043 let Stmt::Block(stmts) = body else { return };
9044 if stmts.is_empty() || is_bare_ctor_call(stmts.first().unwrap()) {
9045 return;
9046 }
9047 if let Some(pos0) = stmts.iter().position(is_bare_ctor_call) {
9049 let pos = fold_sb_chain(stmts, pos0);
9050 let mut call = stmts[pos].clone();
9051 let prefix: Vec<&Stmt> = stmts[..pos].iter().collect();
9052 let mut consumed: Vec<(u32, usize)> = Vec::new();
9053 if !inline_ctor_arg_defs(&mut call, &prefix, vt, &mut consumed) {
9054 return;
9055 }
9056 stmts.remove(pos);
9057 let mut drop_idx: Vec<usize> = consumed
9064 .iter()
9065 .filter(|(v, _)| !var_occurs_in(&stmts[pos..], *v))
9066 .map(|(_, k)| *k)
9067 .collect();
9068 drop_idx.sort_unstable();
9069 drop_idx.dedup();
9070 for k in drop_idx.iter().rev() {
9071 stmts.remove(*k);
9072 }
9073 stmts.insert(0, call);
9074 return;
9075 }
9076 let mut found: Option<(usize, usize)> = None;
9086 for (i, st) in stmts.iter().enumerate() {
9087 if let Stmt::Block(inner) = st {
9088 if let Some(p) = inner.iter().position(is_bare_ctor_call) {
9089 if inner[..p]
9090 .iter()
9091 .all(|s| matches!(s, Stmt::LocalDef { .. } | Stmt::ExprStmt(_)))
9092 {
9093 found = Some((i, p));
9094 break;
9095 }
9096 }
9097 }
9098 }
9099 if let Some((i, p)) = found {
9100 let mut call = match &stmts[i] {
9101 Stmt::Block(inner) => inner[p].clone(),
9102 _ => unreachable!(),
9103 };
9104 let mut prefix: Vec<&Stmt> = stmts[..i].iter().collect();
9105 if let Stmt::Block(inner) = &stmts[i] {
9106 prefix.extend(inner[..p].iter());
9107 }
9108 let mut consumed: Vec<(u32, usize)> = Vec::new();
9109 if !inline_ctor_arg_defs(&mut call, &prefix, vt, &mut consumed) {
9110 return;
9111 }
9112 let mut ks: Vec<usize> = Vec::new();
9119 {
9120 let inner_tail: &[Stmt] = match &stmts[i] {
9121 Stmt::Block(inner) if p < inner.len() => &inner[p + 1..],
9122 _ => &[],
9123 };
9124 for (v, k) in &consumed {
9125 if !var_occurs_in(inner_tail, *v) && !var_occurs_in(&stmts[i + 1..], *v) {
9126 ks.push(*k);
9127 }
9128 }
9129 }
9130 ks.sort_unstable();
9131 ks.dedup();
9132 if let Stmt::Block(inner) = &mut stmts[i] {
9133 inner.remove(p);
9134 for k in ks.iter().rev().filter(|k| **k >= i) {
9135 inner.remove(k - i);
9136 }
9137 }
9138 for k in ks.iter().rev().filter(|k| **k < i) {
9139 stmts.remove(*k);
9140 }
9141 stmts.insert(0, call);
9142 }
9143}
9144
9145fn is_delegation_expr(e: &Expr) -> bool {
9154 matches!(
9155 e,
9156 Expr::Method { name, is_special: true, owner, .. }
9157 if &**name == "<init>"
9158 && (owner.is_none() || matches!(owner.as_deref(), Some(Expr::This)))
9159 )
9160}
9161
9162pub(crate) fn is_bare_ctor_call(s: &Stmt) -> bool {
9163 matches!(s, Stmt::ExprStmt(e) if is_delegation_expr(e))
9164}
9165
9166pub(crate) fn local_is_written(body: &Stmt, var: u32) -> bool {
9170 let mut hit = false;
9171 visit_stmt_exprs_ro(body, &mut |e| {
9172 if hit {
9173 return;
9174 }
9175 visit_exprs(e, &mut |x| {
9176 if hit {
9177 return;
9178 }
9179 match x {
9180 Expr::Assign { target, .. }
9181 if matches!(&**target, Expr::Local { var: v, .. } if *v == var) =>
9182 {
9183 hit = true;
9184 }
9185 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. }
9186 if matches!(&**e, Expr::Local { var: v, .. } if *v == var) =>
9187 {
9188 hit = true;
9189 }
9190 _ => {}
9191 }
9192 });
9193 });
9194 hit
9195}
9196
9197pub(crate) fn rewrite_inner_ctor_outer_param(
9207 body: &mut Stmt,
9208 param0: u32,
9209 outer_display: &str,
9210 outer_ty: &TypeRef,
9211 eligible: &[String],
9212) {
9213 walk_stmt_exprs(body, &mut |e| {
9217 if let Expr::Method { name, cls, args, .. } = e {
9218 if &**name == "<init>"
9219 && eligible.iter().any(|c| c.as_str() == cls.as_ref())
9220 && args.first().is_some_and(|a| matches!(a, Expr::Local { var: v, .. } if *v == param0))
9221 {
9222 args.remove(0);
9223 }
9224 }
9225 });
9226 let text = format!("{outer_display}.this");
9228 let ty = outer_ty.clone();
9229 walk_stmt_exprs(body, &mut |e| {
9230 deep_rewrite_reads(e, &mut |x| {
9231 if let Expr::Local { var: v, .. } = x {
9232 if *v == param0 {
9233 *x = Expr::RawT(text.clone(), ty.clone());
9234 }
9235 }
9236 });
9237 });
9238}
9239
9240fn stmts_count_writes(stmts: &[Stmt], var: u32) -> usize {
9288 let mut n = 0usize;
9289 for s in stmts {
9290 visit_stmt_exprs_ro(s, &mut |e| {
9291 visit_exprs(e, &mut |x| match x {
9292 Expr::Assign { target, .. }
9293 if matches!(&**target, Expr::Local { var: v, .. } if *v == var) =>
9294 {
9295 n += 1;
9296 }
9297 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. }
9298 if matches!(&**e, Expr::Local { var: v, .. } if *v == var) =>
9299 {
9300 n += 1;
9301 }
9302 _ => {}
9303 });
9304 });
9305 }
9306 n
9307}
9308
9309fn prefix_written_vars(stmts: &[Stmt]) -> jdc_core::FxHashSet<u32> {
9313 let mut set = jdc_core::FxHashSet::default();
9314 for s in stmts {
9315 let mut c = s.clone();
9316 walk_stmt_exprs(&mut c, &mut |e| {
9317 deep_rewrite(e, &mut |x| match x {
9318 Expr::Assign { target, .. } => {
9319 if let Expr::Local { var, .. } = &**target {
9320 set.insert(*var);
9321 }
9322 }
9323 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => {
9324 if let Expr::Local { var, .. } = &**e {
9325 set.insert(*var);
9326 }
9327 }
9328 _ => {}
9329 });
9330 });
9331 if let Stmt::LocalDef { var, .. } = s {
9332 set.insert(*var);
9333 }
9334 }
9335 set
9336}
9337
9338fn pre_this_safe(e: &Expr, prefix_vars: &jdc_core::FxHashSet<u32>) -> bool {
9342 match e {
9343 Expr::Local { var, .. } => !prefix_vars.contains(var),
9344 Expr::Const(_) => true,
9345 Expr::Cast { e, .. } => pre_this_safe(e, prefix_vars),
9346 Expr::Bin { l, r, .. } => {
9347 pre_this_safe(l, prefix_vars) && pre_this_safe(r, prefix_vars)
9348 }
9349 Expr::Cond { c, t, f } => {
9350 pre_this_safe(c, prefix_vars)
9351 && pre_this_safe(t, prefix_vars)
9352 && pre_this_safe(f, prefix_vars)
9353 }
9354 Expr::Field { owner, is_static, .. } => {
9355 *is_static
9356 && owner
9357 .as_ref()
9358 .is_none_or(|o| pre_this_safe(o, prefix_vars))
9359 }
9360 Expr::Method {
9361 owner,
9362 args,
9363 is_static,
9364 ..
9365 } => {
9366 args.iter().all(|a| pre_this_safe(a, prefix_vars))
9367 && match owner {
9368 Some(o) => pre_this_safe(o, prefix_vars),
9369 None => *is_static,
9370 }
9371 }
9372 Expr::New { args, .. } => args.iter().all(|a| pre_this_safe(a, prefix_vars)),
9380 Expr::NewArray { dims, init, .. } => {
9381 dims.iter().all(|d| pre_this_safe(d, prefix_vars))
9382 && init
9383 .as_ref()
9384 .map(|l| l.iter().all(|x| pre_this_safe(x, prefix_vars)))
9385 .unwrap_or(true)
9386 }
9387 Expr::Un { e, .. } | Expr::InstanceOf { e, .. } => pre_this_safe(e, prefix_vars),
9389 Expr::ArrayIndex { array, index } => {
9390 pre_this_safe(array, prefix_vars) && pre_this_safe(index, prefix_vars)
9391 }
9392 _ => false,
9393 }
9394}
9395
9396fn single_assign_to_v(s: &Stmt, v: u32) -> Option<Expr> {
9400 match s {
9401 Stmt::ExprStmt(Expr::Assign {
9402 target,
9403 op: AssignOp::Plain,
9404 value,
9405 }) if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v) => {
9406 Some((**value).clone())
9407 }
9408 Stmt::Block(xs) if xs.len() == 1 => single_assign_to_v(&xs[0], v),
9409 _ => None,
9410 }
9411}
9412
9413pub(crate) fn flatten_top_blocks(stmts: &mut Vec<Stmt>) {
9435 fn rec(s: Stmt, out: &mut Vec<Stmt>) {
9436 match s {
9437 Stmt::Block(inner) => {
9438 for x in inner {
9439 rec(x, out);
9440 }
9441 }
9442 other => out.push(other),
9443 }
9444 }
9445 if !stmts.iter().any(|s| matches!(s, Stmt::Block(_))) {
9446 return;
9447 }
9448 let mut out: Vec<Stmt> = Vec::with_capacity(stmts.len());
9449 for s in std::mem::take(stmts) {
9450 rec(s, &mut out);
9451 }
9452 *stmts = out;
9453}
9454
9455fn fold_default_arg_bridge(stmts: &mut Vec<Stmt>) -> bool {
9456 flatten_top_blocks(stmts);
9457 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
9458 return false;
9459 };
9460 if pos == 0 {
9461 return false;
9462 }
9463 let Stmt::ExprStmt(Expr::Method { args, .. }) = &stmts[pos] else {
9464 return false;
9465 };
9466 let mut arg_vars: Vec<u32> = Vec::new();
9467 let mut reads = 0usize;
9468 for a in args {
9469 let mut c = a.clone();
9470 deep_rewrite_reads(&mut c, &mut |x| {
9471 if let Expr::Local { var, .. } = x {
9472 reads += 1;
9473 if !arg_vars.contains(var) {
9474 arg_vars.push(*var);
9475 }
9476 }
9477 });
9478 }
9479 if arg_vars.is_empty() || reads != arg_vars.len() {
9480 return false; }
9482 for &v in &arg_vars {
9485 if stmts_read_var(&stmts[pos + 1..], v) != 0
9486 || stmts_count_writes(&stmts[pos + 1..], v) != 0
9487 {
9488 return false;
9489 }
9490 }
9491 let prefix_vars = prefix_written_vars(&stmts[..pos]);
9492 let mut repl: Vec<(u32, Expr)> = Vec::new();
9493 let mut drop: Vec<usize> = Vec::new();
9494 for &v in &arg_vars {
9495 let mut base: Option<(usize, Expr)> = None;
9497 let mut decl: Option<usize> = None;
9498 let mut condf: Option<(usize, Expr, Option<Expr>, Option<Expr>)> = None;
9499 let mut ok = true;
9500 for (i, s) in stmts[..pos].iter().enumerate() {
9501 match s {
9502 Stmt::LocalDef { var, init, .. } if *var == v => match init {
9503 None => {
9504 if decl.is_some() {
9505 ok = false;
9506 break;
9507 }
9508 decl = Some(i);
9509 }
9510 Some(e) => {
9511 if base.is_some() {
9512 ok = false;
9513 break;
9514 }
9515 base = Some((i, e.clone()));
9516 }
9517 },
9518 Stmt::ExprStmt(Expr::Assign {
9519 target,
9520 op: AssignOp::Plain,
9521 value,
9522 }) if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v) => {
9523 if base.is_some() {
9524 ok = false;
9525 break;
9526 }
9527 base = Some((i, (**value).clone()));
9528 }
9529 Stmt::If {
9530 cond,
9531 then_stmt,
9532 else_stmt,
9533 } => {
9534 let t = single_assign_to_v(then_stmt, v);
9535 let f = else_stmt.as_ref().and_then(|b| single_assign_to_v(b, v));
9536 let then_touches = t.is_some()
9537 || stmts_read_var(std::slice::from_ref(&**then_stmt), v) != 0
9538 || stmts_count_writes(std::slice::from_ref(&**then_stmt), v) != 0;
9539 let else_touches = else_stmt.as_ref().is_some_and(|b| {
9540 f.is_some()
9541 || stmts_read_var(std::slice::from_ref(b), v) != 0
9542 || stmts_count_writes(std::slice::from_ref(b), v) != 0
9543 });
9544 if !then_touches && !else_touches {
9545 continue; }
9547 if condf.is_some()
9552 || base.is_none()
9553 || (then_touches && t.is_none())
9554 || (else_touches && f.is_none())
9555 {
9556 ok = false;
9557 break;
9558 }
9559 let cond_stmt = Stmt::ExprStmt(cond.clone());
9560 if stmts_read_var(std::slice::from_ref(&cond_stmt), v) != 0 {
9561 ok = false;
9562 break;
9563 }
9564 condf = Some((i, cond.clone(), t, f));
9565 }
9566 _ => {}
9567 }
9568 }
9569 if !ok {
9570 continue;
9571 }
9572 let Some((bi, be)) = base else { continue };
9573 let want_writes = 1
9576 + condf
9577 .as_ref()
9578 .map(|(_, _, t, f)| t.is_some() as usize + f.is_some() as usize)
9579 .unwrap_or(0);
9580 if stmts_count_writes(&stmts[..pos], v) != want_writes
9581 || stmts_read_var(&stmts[..pos], v) != 0
9582 {
9583 continue;
9584 }
9585 let rep = match condf {
9586 Some((ci, c, t, f)) => {
9587 drop.push(ci);
9588 match (t, f) {
9589 (Some(t), Some(f)) => Expr::Cond {
9590 c: Box::new(c),
9591 t: Box::new(t),
9592 f: Box::new(f),
9593 },
9594 (Some(t), None) => Expr::Cond {
9595 c: Box::new(c),
9596 t: Box::new(t),
9597 f: Box::new(be.clone()),
9598 },
9599 (None, Some(f)) => Expr::Cond {
9600 c: Box::new(c),
9601 t: Box::new(be.clone()),
9602 f: Box::new(f),
9603 },
9604 (None, None) => unreachable!("condf requires a branch assign"),
9605 }
9606 }
9607 None => be.clone(),
9608 };
9609 if !pre_this_safe(&rep, &prefix_vars) {
9610 continue;
9611 }
9612 drop.push(bi);
9613 if let Some(d) = decl {
9614 drop.push(d);
9615 }
9616 repl.push((v, rep));
9617 }
9618 if repl.is_empty() {
9619 return false;
9620 }
9621 if let Stmt::ExprStmt(Expr::Method { args, .. }) = &mut stmts[pos] {
9622 for a in args.iter_mut() {
9623 deep_rewrite_reads(a, &mut |x| {
9624 if let Expr::Local { var, .. } = x {
9625 if let Some((_, e)) = repl.iter().find(|(vv, _)| vv == var) {
9626 *x = e.clone();
9627 }
9628 }
9629 });
9630 }
9631 }
9632 drop.sort_unstable();
9633 drop.dedup();
9634 for i in drop.into_iter().rev() {
9635 stmts.remove(i);
9636 }
9637 true
9638}
9639
9640pub fn rewrite_kotlin_facades(body: &mut Stmt, pool: &crate::DexPool) {
9648 let map = pool.kotlin_facade_map();
9649 if map.is_empty() {
9650 return;
9651 }
9652 walk_stmt_exprs(body, &mut |e| {
9653 match e {
9654 Expr::Method {
9655 cls,
9656 name,
9657 desc,
9658 is_static,
9659 ..
9660 } if *is_static => {
9661 if let Some((base, cover)) = map.get(cls.as_ref()) {
9662 let hit = match cover {
9663 None => true,
9664 Some(c) => c.0.contains(&(name.to_string(), desc.to_string())),
9665 };
9666 if hit {
9667 *cls = std::sync::Arc::from(base.as_str());
9668 }
9669 }
9670 }
9671 Expr::Field {
9672 cls,
9673 name,
9674 is_static,
9675 ..
9676 } if *is_static => {
9677 if let Some((base, cover)) = map.get(cls.as_ref()) {
9678 let hit = match cover {
9679 None => true,
9680 Some(c) => c.1.contains(name.as_ref()),
9681 };
9682 if hit {
9683 *cls = std::sync::Arc::from(base.as_str());
9684 }
9685 }
9686 }
9687 _ => {}
9688 }
9689 });
9690}
9691
9692pub fn fold_enum_default_arg_bridge(body: &mut Stmt) {
9700 let Stmt::Block(stmts) = body else { return };
9701 fold_default_arg_bridge(stmts);
9702}
9703
9704pub fn fix_ctor_delegation_arg_defs(body: &mut Stmt) {
9705 let Stmt::Block(stmts) = body else { return };
9706 fold_default_arg_bridge(stmts);
9709 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
9713 return;
9714 };
9715 if pos == 0 {
9716 return;
9717 }
9718 let mut arg_vars: Vec<u32> = Vec::new();
9721 let mut total_reads = 0usize;
9722 {
9723 let Stmt::ExprStmt(Expr::Method { args, .. }) = &stmts[pos] else {
9724 return;
9725 };
9726 for a in args {
9727 let mut c = a.clone();
9728 deep_rewrite_reads(&mut c, &mut |x| {
9729 if let Expr::Local { var: v, .. } = x {
9730 total_reads += 1;
9731 if !arg_vars.contains(v) {
9732 arg_vars.push(*v);
9733 }
9734 }
9735 });
9736 }
9737 }
9738 if arg_vars.is_empty() || total_reads != arg_vars.len() {
9739 return;
9740 }
9741 let prefix_defs: Vec<u32> = stmts[..pos]
9744 .iter()
9745 .filter_map(|s| match s {
9746 Stmt::LocalDef { var, init: Some(_), .. } => Some(*var),
9747 _ => None,
9748 })
9749 .collect();
9750 let mut plan: Vec<(u32, usize, Option<usize>)> = Vec::new();
9755 for v in arg_vars {
9756 let defs: Vec<usize> = stmts[..pos]
9757 .iter()
9758 .enumerate()
9759 .filter(|(i, _)| {
9760 matches!(&stmts[*i], Stmt::LocalDef { var, init: Some(_), .. } if *var == v)
9761 })
9762 .map(|(i, _)| i)
9763 .collect();
9764 if defs.len() > 1 {
9765 return; }
9767 let Some(&q) = defs.first() else {
9768 continue; };
9770 let init = match &stmts[q] {
9771 Stmt::LocalDef { init: Some(e), .. } => e.clone(),
9772 _ => return,
9773 };
9774 if stmts_read_var(&stmts[q + 1..pos], v) != 0 {
9775 return; }
9777 let mut init_vars: Vec<u32> = Vec::new();
9780 deep_rewrite_reads(&mut { init.clone() }, &mut |x| {
9781 if let Expr::Local { var: vv, .. } = x {
9782 if !init_vars.contains(vv) {
9783 init_vars.push(*vv);
9784 }
9785 }
9786 });
9787 if init_vars.iter().any(|iv| prefix_defs.contains(iv)) {
9788 return;
9789 }
9790 match stmts[pos + 1..]
9791 .iter()
9792 .position(|s| {
9793 matches!(s, Stmt::ExprStmt(Expr::Assign { target, .. })
9794 if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v))
9795 }) {
9796 Some(w) => {
9797 if stmts_read_var(&stmts[pos + 1..pos + 1 + w], v) != 0 {
9799 return;
9800 }
9801 plan.push((v, q, Some(pos + 1 + w)));
9802 }
9803 None => {
9804 if stmts_read_var(&stmts[pos + 1..], v) != 0 {
9806 return;
9807 }
9808 plan.push((v, q, None));
9809 }
9810 }
9811 }
9812 if plan.is_empty() {
9813 return;
9814 }
9815 let values: Vec<(u32, Expr)> = plan
9817 .iter()
9818 .map(|(_v, q, _)| match &stmts[*q] {
9819 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e.clone()),
9820 _ => unreachable!("plan entries carry a LocalDef with init"),
9821 })
9822 .collect();
9823 if let Stmt::ExprStmt(Expr::Method { args, .. }) = &mut stmts[pos] {
9824 for a in args {
9825 deep_rewrite_reads(a, &mut |x| {
9826 if let Expr::Local { var: v, .. } = x {
9827 if let Some((_, e)) = values.iter().find(|(vv, _)| vv == v) {
9828 *x = e.clone();
9829 }
9830 }
9831 });
9832 }
9833 }
9834 for (v, _, w) in &plan {
9838 if let Some(w) = w {
9839 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = &mut stmts[*w] {
9840 if matches!(&**target, Expr::Local { var: vv, .. } if vv == v) {
9841 let taken = std::mem::replace(&mut **value, Expr::Const(ConstVal::Null));
9842 stmts[*w] = Stmt::LocalDef {
9843 var: *v,
9844 init: Some(taken),
9845 is_final: false,
9846 force_type: false,
9847 };
9848 }
9849 }
9850 }
9851 }
9852 let mut order: Vec<usize> = plan.iter().map(|(_, q, _)| *q).collect();
9853 order.sort_unstable();
9854 order.reverse();
9855 for q in order {
9856 stmts.remove(q);
9857 }
9858}
9859
9860pub fn fix_bool_returns(vt: &VarTable, body: &mut Stmt) {
9871 walk_mut_deep(body, &mut |st| {
9872 if let Stmt::Return(Some(e)) = st {
9873 let numeric = match &*e {
9874 Expr::Local { var, ty } => {
9875 let t = if (*var as usize) < vt.vars.len() {
9876 vt.var(*var).ty.erased()
9877 } else {
9878 ty.erased()
9879 };
9880 matches!(
9881 t,
9882 JavaType::Int
9883 | JavaType::Long
9884 | JavaType::Short
9885 | JavaType::Byte
9886 | JavaType::Char
9887 )
9888 }
9889 other => matches!(
9890 other.type_ref().erased(),
9891 JavaType::Int | JavaType::Long | JavaType::Short | JavaType::Byte
9892 | JavaType::Char
9893 ),
9894 };
9895 if numeric {
9896 let taken = e.clone();
9897 *e = Expr::Bin {
9898 op: BinOp::Ne,
9899 l: Box::new(taken),
9900 r: Box::new(Expr::Const(ConstVal::Int(0))),
9901 ty: Some(TypeRef::J(JavaType::Boolean)),
9902 };
9903 }
9904 }
9905 });
9906}
9907
9908pub fn fix_int_returns(vt: &VarTable, body: &mut Stmt) {
9909 walk_mut_deep(body, &mut |st| {
9910 if let Stmt::Return(Some(e)) = st {
9911 let is_bool = match e {
9912 Expr::Local { var, .. } => {
9913 (*var as usize) < vt.vars.len()
9914 && vt.vars[*var as usize].ty.erased() == JavaType::Boolean
9915 }
9916 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
9917 _ => false,
9918 };
9919 if is_bool {
9920 *e = Expr::Cond {
9921 c: Box::new(e.clone()),
9922 t: Box::new(Expr::Const(ConstVal::Int(1))),
9923 f: Box::new(Expr::Const(ConstVal::Int(0))),
9924 };
9925 }
9926 }
9927 });
9928}
9929
9930pub fn split_generations(vt: &mut VarTable, body: &mut Stmt, pool: &DexPool) {
9946 let mut counter: u32 = 0;
9947 let mut gen: std::collections::HashMap<u32, u32> = std::collections::HashMap::default();
9948 split_walk_stmt(body, vt, &mut gen, &mut counter, pool);
9949}
9950
9951fn split_needed(vt: &VarTable, target: u32, value: &Expr, pool: &DexPool) -> bool {
9953 let n = vt.vars.len();
9954 if target as usize >= n {
9955 return false;
9956 }
9957 let declared = vt.vars[target as usize].ty.erased();
9958 let vt_is_ref = |t: &JavaType| matches!(t, JavaType::Object(_) | JavaType::Array(_));
9959 fn side_bool(e: &Expr, vt: &VarTable) -> bool {
9968 match e {
9969 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
9970 matches!(vt.var(*var).ty.erased(), JavaType::Boolean)
9971 }
9972 Expr::Const(ConstVal::Int(0 | 1)) => true,
9973 Expr::Bin { op: BinOp::Or | BinOp::And | BinOp::Xor, l, r, .. } => {
9974 side_bool(l, vt) && side_bool(r, vt)
9975 }
9976 other => matches!(other.type_ref().erased(), JavaType::Boolean),
9977 }
9978 }
9979 let value_ty = match value {
9980 Expr::Local { var, ty } if (*var as usize) < vt.vars.len() => vt.var(*var).ty.erased(),
9981 Expr::Bin {
9982 op: BinOp::Or | BinOp::And | BinOp::Xor,
9983 l,
9984 r,
9985 ..
9986 } if side_bool(l, vt) && side_bool(r, vt) => JavaType::Boolean,
9987 other => other.type_ref().erased(),
9988 };
9989 if vt_is_ref(&declared) && vt_is_ref(&value_ty) {
9990 let obj = JavaType::Object("java/lang/Object".into());
10000 if declared == value_ty || declared == obj || value_ty == obj {
10001 return false;
10002 }
10003 if let (JavaType::Object(v), JavaType::Object(d)) = (&value_ty, &declared) {
10016 if matches!(value, Expr::New { .. })
10017 && (pool.get(v).is_none() || pool.get(d).is_none())
10018 {
10019 return false;
10020 }
10021 }
10022 true
10023 } else {
10024 if vt_is_ref(&declared) != vt_is_ref(&value_ty) {
10028 return true;
10029 }
10030 let d_bool = matches!(declared, JavaType::Boolean);
10038 let v_bool = matches!(value_ty, JavaType::Boolean);
10039 let zero_one = matches!(value, Expr::Const(ConstVal::Int(0 | 1)));
10040 d_bool != v_bool && !zero_one
10041 }
10042}
10043
10044fn rewrite_gen_reads(e: &mut Expr, gen: &std::collections::HashMap<u32, u32>, vt: &VarTable) {
10045 deep_rewrite(e, &mut |x| {
10046 if let Expr::Local { var, ty } = x {
10047 if let Some(&to) = gen.get(var) {
10048 if to != *var {
10049 *var = to;
10050 *ty = vt.var(to).ty.clone();
10051 }
10052 }
10053 }
10054 });
10055}
10056
10057fn do_split(
10058 vt: &mut VarTable,
10059 target: &mut Expr,
10060 old: u32,
10061 value_ty: &JavaType,
10062 gen: &mut std::collections::HashMap<u32, u32>,
10063 counter: &mut u32,
10064) {
10065 *counter += 1;
10066 let info = &vt.vars[old as usize];
10067 let id = vt.vars.len() as u32;
10068 let name = format!("{}_g{}", info.name, counter);
10069 let slot = info.slot;
10070 let is_param = false;
10078 vt.vars.push(jdc_core::var::VarInfo {
10079 id,
10080 slot,
10081 name,
10082 ty: TypeRef::J(value_ty.clone()),
10083 is_param,
10084 range_start: 0,
10085 range_end: u16::MAX,
10086 synthetic_name: true,
10087 });
10088 while vt.by_slot.len() <= slot as usize {
10089 vt.by_slot.push(Vec::new());
10090 }
10091 vt.by_slot[slot as usize].push((0, u16::MAX, id));
10092 gen.insert(old, id);
10093 if let Expr::Local { var, ty, .. } = target {
10094 *var = id;
10095 *ty = TypeRef::J(value_ty.clone());
10096 }
10097}
10098
10099fn rename_gen_back(s: &mut Stmt, from: u32, to: u32, vt: &VarTable) {
10111 if from == to || (to as usize) >= vt.vars.len() {
10112 return;
10113 }
10114 let to_ty = vt.vars[to as usize].ty.clone();
10115 walk_mut_deep(s, &mut |st| {
10116 if let Stmt::LocalDef { var, .. } = st {
10117 if *var == from {
10118 *var = to;
10119 }
10120 }
10121 });
10122 rewrite_exprs(s, &mut |e| {
10123 deep_rewrite(e, &mut |x| {
10124 if let Expr::Local { var, ty } = x {
10125 if *var == from {
10126 *var = to;
10127 *ty = to_ty.clone();
10128 }
10129 }
10130 });
10131 });
10132}
10133
10134fn drop_gen_slot(vt: &mut VarTable, dead: u32) {
10138 if (dead as usize) >= vt.vars.len() {
10139 return;
10140 }
10141 let slot = vt.vars[dead as usize].slot as usize;
10142 if let Some(seg) = vt.by_slot.get_mut(slot) {
10143 seg.retain(|&(_, _, id)| id != dead);
10144 }
10145}
10146
10147fn undo_type_safe(vt: &VarTable, k: u32, v: u32) -> bool {
10156 if (k as usize) >= vt.vars.len() || (v as usize) >= vt.vars.len() {
10157 return false;
10158 }
10159 let kt = vt.vars[k as usize].ty.erased();
10160 let vt_ty = vt.vars[v as usize].ty.erased();
10161 if kt == vt_ty {
10162 return true;
10163 }
10164 let is_prim = |t: &JavaType| {
10165 matches!(
10166 t,
10167 JavaType::Int
10168 | JavaType::Long
10169 | JavaType::Short
10170 | JavaType::Byte
10171 | JavaType::Char
10172 | JavaType::Float
10173 | JavaType::Double
10174 | JavaType::Boolean
10175 )
10176 };
10177 is_prim(&kt) && is_prim(&vt_ty)
10178}
10179
10180fn split_walk_stmt(
10181 st: &mut Stmt,
10182 vt: &mut VarTable,
10183 gen: &mut std::collections::HashMap<u32, u32>,
10184 counter: &mut u32,
10185 pool: &DexPool,
10186) {
10187 match st {
10188 Stmt::Block(v) => {
10189 for x in v.iter_mut() {
10190 split_walk_stmt(x, vt, gen, counter, pool);
10191 }
10192 }
10193 Stmt::ExprStmt(e) => {
10194 rewrite_gen_reads(e, gen, vt);
10195 if let Expr::Assign {
10196 target,
10197 value,
10198 op: AssignOp::Plain,
10199 ..
10200 } = e
10201 {
10202 if let Expr::Local { var, .. } = &**target {
10203 let old = *var;
10204 if split_needed(vt, old, value, pool) {
10205 let value_ty = value.type_ref().erased();
10206 do_split(vt, target, old, &value_ty, gen, counter);
10207 }
10208 }
10209 }
10210 }
10211 Stmt::LocalDef { var, init, .. } => {
10212 if let Some(e) = init {
10213 rewrite_gen_reads(e, gen, vt);
10214 if split_needed(vt, *var, e, pool) {
10215 let value_ty = e.type_ref().erased();
10219 let old = *var;
10220 let mut fake = Expr::Local {
10221 var: old,
10222 ty: vt.vars[old as usize].ty.clone(),
10223 };
10224 do_split(vt, &mut fake, old, &value_ty, gen, counter);
10225 if let Expr::Local { var: nv, .. } = fake {
10226 *var = nv;
10227 }
10228 }
10229 }
10230 }
10231 Stmt::If {
10232 cond,
10233 then_stmt,
10234 else_stmt,
10235 ..
10236 } => {
10237 rewrite_gen_reads(cond, gen, vt);
10238 let then_floor = vt.vars.len() as u32;
10239 let mut g_then = gen.clone();
10240 split_walk_stmt(then_stmt, vt, &mut g_then, counter, pool);
10241 let else_floor = vt.vars.len() as u32;
10242 let mut g_else = gen.clone();
10243 if let Some(e) = else_stmt {
10244 split_walk_stmt(e, vt, &mut g_else, counter, pool);
10245 }
10246 let then_reads = (else_floor > then_floor)
10263 .then(|| read_counts_all(std::slice::from_ref(&**then_stmt)));
10264 let mut undo_then: Vec<(u32, u32)> = g_then
10265 .iter()
10266 .filter(|(&k, &v)| {
10267 v >= then_floor
10268 && v < else_floor
10269 && g_else.get(&k).copied() == gen.get(&k).copied()
10270 && undo_type_safe(vt, k, v)
10271 && then_reads
10272 .as_ref()
10273 .map(|m| m.get(&v).copied().unwrap_or(0) == 0)
10274 .unwrap_or(true)
10275 })
10276 .map(|(&k, &v)| (v, k))
10277 .collect();
10278 undo_then.sort_unstable_by_key(|&(v, _)| std::cmp::Reverse(v));
10279 for &(v, k) in &undo_then {
10280 rename_gen_back(then_stmt, v, k, vt);
10281 drop_gen_slot(vt, v);
10282 }
10283 if let Some(e) = else_stmt.as_deref_mut() {
10284 let else_reads = ((vt.vars.len() as u32) > else_floor)
10285 .then(|| read_counts_all(std::slice::from_ref(&*e)));
10286 let mut undo_else: Vec<(u32, u32)> = g_else
10287 .iter()
10288 .filter(|(&k, &v)| {
10289 v >= else_floor
10290 && g_then.get(&k).copied() == gen.get(&k).copied()
10291 && undo_type_safe(vt, k, v)
10292 && else_reads
10293 .as_ref()
10294 .map(|m| m.get(&v).copied().unwrap_or(0) == 0)
10295 .unwrap_or(true)
10296 })
10297 .map(|(&k, &v)| (v, k))
10298 .collect();
10299 undo_else.sort_unstable_by_key(|&(v, _)| std::cmp::Reverse(v));
10300 for &(v, k) in &undo_else {
10301 rename_gen_back(e, v, k, vt);
10302 drop_gen_slot(vt, v);
10303 }
10304 }
10305 gen.retain(|k, v| g_then.get(k) == Some(v) && g_else.get(k) == Some(v));
10308 }
10309 Stmt::While { cond, body, .. } | Stmt::DoWhile { cond, body, .. } => {
10310 let entry = gen.clone();
10314 for _ in 0..3 {
10315 rewrite_gen_reads(cond, gen, vt);
10316 split_walk_stmt(body, vt, gen, counter, pool);
10317 }
10318 *gen = entry;
10321 }
10322 Stmt::For { init, body, .. } => {
10323 for x in init.iter_mut() {
10324 split_walk_stmt(x, vt, gen, counter, pool);
10325 }
10326 let entry = gen.clone();
10327 for _ in 0..3 {
10328 split_walk_stmt(body, vt, gen, counter, pool);
10329 }
10330 *gen = entry;
10331 }
10332 Stmt::ForEach { body, .. } => {
10333 let entry = gen.clone();
10334 for _ in 0..3 {
10335 split_walk_stmt(body, vt, gen, counter, pool);
10336 }
10337 *gen = entry;
10338 }
10339 Stmt::Switch { cases, default, .. } => {
10340 let entry = gen.clone();
10341 for c in cases.iter_mut() {
10342 let mut g_case = entry.clone();
10343 for x in c.body.iter_mut() {
10344 split_walk_stmt(x, vt, &mut g_case, counter, pool);
10345 }
10346 gen.retain(|k, v| g_case.get(k) == Some(v));
10347 }
10348 if let Some(d) = default {
10349 let mut g_def = entry.clone();
10350 split_walk_stmt(d, vt, &mut g_def, counter, pool);
10351 gen.retain(|k, v| g_def.get(k) == Some(v));
10352 }
10353 }
10354 Stmt::Try {
10355 body,
10356 catches,
10357 finally,
10358 } => {
10359 let entry = gen.clone();
10360 let mut g_try = entry.clone();
10361 split_walk_stmt(body, vt, &mut g_try, counter, pool);
10362 let mut g_all = g_try.clone();
10363 for c in catches.iter_mut() {
10364 let mut g_c = entry.clone();
10367 split_walk_stmt(&mut c.body, vt, &mut g_c, counter, pool);
10368 g_all.retain(|k, v| g_c.get(k) == Some(v));
10369 }
10370 *gen = g_all;
10371 if let Some(f) = finally {
10372 split_walk_stmt(f, vt, gen, counter, pool);
10373 }
10374 }
10375 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => {
10376 split_walk_stmt(body, vt, gen, counter, pool);
10377 }
10378 Stmt::Return(Some(e)) | Stmt::Throw(e) => {
10379 rewrite_gen_reads(e, gen, vt);
10380 }
10381 _ => {}
10382 }
10383}
10384
10385fn fw_super(c: &str) -> Option<&'static str> {
10392 crate::fwdb::super_of(c)
10393}
10394
10395fn exc_subtype(sub: &str, sup: &str, pool: &DexPool) -> bool {
10401 if sub == sup {
10402 return true;
10403 }
10404 if pool.is_subtype(sub, sup) {
10405 return true;
10406 }
10407 let mut cur = sub;
10408 for _ in 0..24 {
10409 let Some(next) = fw_super(cur) else { break };
10410 if next == sup {
10411 return true;
10412 }
10413 cur = next;
10414 }
10415 false
10416}
10417
10418pub fn dedupe_multicatch(s: &mut Stmt, pool: &DexPool) {
10424 dedupe_multicatch_walk(s, pool);
10425}
10426
10427fn dedupe_multicatch_walk(s: &mut Stmt, pool: &DexPool) {
10428 match s {
10429 Stmt::Try {
10430 body,
10431 catches,
10432 finally,
10433 }
10434 | Stmt::TryWithResources {
10435 body,
10436 catches,
10437 finally,
10438 ..
10439 } => {
10440 dedupe_multicatch_walk(body, pool);
10441 for c in catches.iter_mut() {
10442 if c.exc.len() > 1 {
10443 let mut keep: Vec<std::sync::Arc<str>> = Vec::with_capacity(c.exc.len());
10444 for e in c.exc.iter() {
10445 let shadowed = c.exc.iter().any(|o| {
10446 o.as_ref() != e.as_ref() && exc_subtype(e, o, pool)
10447 });
10448 if !shadowed && !keep.iter().any(|k| k.as_ref() == e.as_ref()) {
10449 keep.push(e.clone());
10450 }
10451 }
10452 c.exc = keep;
10453 }
10454 dedupe_multicatch_walk(&mut c.body, pool);
10455 }
10456 if let Some(f) = finally {
10457 dedupe_multicatch_walk(f, pool);
10458 }
10459 }
10460 Stmt::Block(v) => {
10461 for x in v.iter_mut() {
10462 dedupe_multicatch_walk(x, pool);
10463 }
10464 }
10465 Stmt::If {
10466 then_stmt,
10467 else_stmt,
10468 ..
10469 } => {
10470 dedupe_multicatch_walk(then_stmt, pool);
10471 if let Some(e) = else_stmt {
10472 dedupe_multicatch_walk(e, pool);
10473 }
10474 }
10475 Stmt::While { body, .. }
10476 | Stmt::DoWhile { body, .. }
10477 | Stmt::For { body, .. }
10478 | Stmt::ForEach { body, .. }
10479 | Stmt::Labeled { body, .. }
10480 | Stmt::Synchronized { body, .. } => dedupe_multicatch_walk(body, pool),
10481 Stmt::Switch { cases, default, .. } => {
10482 for c in cases.iter_mut() {
10483 for x in c.body.iter_mut() {
10484 dedupe_multicatch_walk(x, pool);
10485 }
10486 }
10487 if let Some(d) = default {
10488 dedupe_multicatch_walk(d, pool);
10489 }
10490 }
10491 _ => {}
10492 }
10493}
10494
10495pub fn resolve_dangling_gotos(s: &mut Stmt) {
10507 let mut labels: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
10508 walk_all(s, &mut |st| {
10511 if let Stmt::Label(id) = st {
10512 labels.insert(*id);
10513 }
10514 });
10515 resolve_gotos_walk(s, &labels, 0);
10516}
10517
10518fn resolve_gotos_walk(s: &mut Stmt, labels: &jdc_core::FxHashSet<u32>, loop_depth: u32) {
10519 let depth = match s {
10520 Stmt::While { .. } | Stmt::DoWhile { .. } | Stmt::For { .. } | Stmt::ForEach { .. } => {
10521 loop_depth + 1
10522 }
10523 _ => loop_depth,
10524 };
10525 match s {
10526 Stmt::Goto(id) => {
10527 if labels.contains(id) {
10528 return; }
10530 if depth > 0 {
10531 *s = Stmt::Break(None);
10532 } else {
10533 *s = Stmt::Comment(format!(
10534 "$DDC: unresolved jump to block {} (irreducible control flow)",
10535 id
10536 ));
10537 }
10538 }
10539 Stmt::Block(v) => {
10540 let mut i = 0usize;
10541 while i < v.len() {
10542 let dangling_here =
10547 depth == 0 && matches!(&v[i], Stmt::Goto(id) if !labels.contains(id));
10548 resolve_gotos_walk(&mut v[i], labels, depth);
10549 if dangling_here {
10550 v.truncate(i + 1);
10551 break;
10552 }
10553 i += 1;
10554 }
10555 }
10556 Stmt::If { then_stmt, else_stmt, .. } => {
10557 resolve_gotos_walk(then_stmt, labels, depth);
10558 if let Some(e) = else_stmt {
10559 resolve_gotos_walk(e, labels, depth);
10560 }
10561 }
10562 Stmt::While { body, .. }
10563 | Stmt::DoWhile { body, .. }
10564 | Stmt::For { body, .. }
10565 | Stmt::ForEach { body, .. }
10566 | Stmt::Labeled { body, .. }
10567 | Stmt::Synchronized { body, .. } => resolve_gotos_walk(body, labels, depth),
10568 Stmt::Try {
10569 body,
10570 catches,
10571 finally,
10572 } => {
10573 resolve_gotos_walk(body, labels, depth);
10574 for c in catches.iter_mut() {
10575 resolve_gotos_walk(&mut c.body, labels, depth);
10576 }
10577 if let Some(f) = finally {
10578 resolve_gotos_walk(f, labels, depth);
10579 }
10580 }
10581 Stmt::Switch { cases, default, .. } => {
10582 for c in cases.iter_mut() {
10583 for x in c.body.iter_mut() {
10584 resolve_gotos_walk(x, labels, depth);
10585 }
10586 }
10587 if let Some(d) = default {
10588 resolve_gotos_walk(d, labels, depth);
10589 }
10590 }
10591 _ => {}
10592 }
10593}
10594
10595fn read_counts_all(stmts: &[Stmt]) -> std::collections::HashMap<u32, usize> {
10600 let mut m = std::collections::HashMap::new();
10601 for s in stmts {
10602 visit_stmt_exprs_ro(s, &mut |e| {
10603 visit_exprs_reads(e, &mut |x| {
10604 if let Expr::Local { var, .. } = x {
10605 *m.entry(*var).or_insert(0) += 1;
10606 }
10607 });
10608 });
10609 }
10610 m
10611}
10612
10613fn stmts_read_var(stmts: &[Stmt], v: u32) -> usize {
10614 let mut n = 0usize;
10615 for s in stmts {
10616 visit_stmt_exprs_ro(s, &mut |e| {
10617 visit_exprs_reads(e, &mut |x| {
10618 if let Expr::Local { var: vv, .. } = x {
10619 if *vv == v {
10620 n += 1;
10621 }
10622 }
10623 });
10624 });
10625 }
10626 n
10627}
10628
10629pub fn fix_ctor_conditional_super(body: &mut Stmt) {
10630 if !matches!(body, Stmt::Block(_)) {
10633 let inner = std::mem::replace(body, Stmt::Block(Vec::new()));
10634 *body = Stmt::Block(vec![inner]);
10635 }
10636 let Stmt::Block(stmts) = body else { return };
10637 while stmts.len() == 1 {
10641 let Stmt::Block(inner) = &stmts[0] else { break };
10642 *stmts = inner.clone();
10643 }
10644 if stmts.is_empty() {
10645 return;
10646 }
10647 if is_bare_ctor_call(stmts.first().unwrap()) {
10648 dedupe_ctor_delegations(body);
10649 return;
10650 }
10651 if try_linear_super_inline(stmts) {
10652 return;
10653 }
10654 try_merge_branched_super(stmts);
10655}
10656
10657pub fn strip_secondary_delegations(body: &mut Stmt) {
10681 let Stmt::Block(stmts) = body else { return };
10682 if !stmts.first().is_some_and(is_bare_ctor_call) {
10683 return;
10684 }
10685 fn strip_in(s: &mut Stmt) {
10686 if is_bare_ctor_call(s) {
10687 *s = Stmt::Block(Vec::new());
10688 return;
10689 }
10690 match s {
10691 Stmt::Block(v) => v.iter_mut().for_each(strip_in),
10692 Stmt::If {
10693 then_stmt,
10694 else_stmt,
10695 ..
10696 } => {
10697 strip_in(then_stmt);
10698 if let Some(e) = else_stmt {
10699 strip_in(e);
10700 }
10701 }
10702 Stmt::While { body, .. }
10703 | Stmt::DoWhile { body, .. }
10704 | Stmt::ForEach { body, .. }
10705 | Stmt::Synchronized { body, .. }
10706 | Stmt::Labeled { body, .. } => strip_in(body),
10707 Stmt::For { init, body, .. } => {
10708 init.iter_mut().for_each(strip_in);
10709 strip_in(body);
10710 }
10711 Stmt::Switch { cases, default, .. } => {
10712 for c in cases.iter_mut() {
10713 c.body.iter_mut().for_each(strip_in);
10714 }
10715 if let Some(d) = default {
10716 strip_in(d);
10717 }
10718 }
10719 Stmt::Try {
10720 body,
10721 catches,
10722 finally,
10723 }
10724 | Stmt::TryWithResources {
10725 body,
10726 catches,
10727 finally,
10728 ..
10729 } => {
10730 strip_in(body);
10731 for c in catches.iter_mut() {
10732 strip_in(&mut c.body);
10733 }
10734 if let Some(f) = finally {
10735 strip_in(f);
10736 }
10737 }
10738 _ => {}
10739 }
10740 }
10741 for st in stmts.iter_mut().skip(1) {
10742 strip_in(st);
10743 }
10744}
10745
10746pub fn dedupe_ctor_delegations(body: &mut Stmt) {
10747 let Stmt::Block(stmts) = body else { return };
10748 let Some(d0) = stmts.first().and_then(|s| match s {
10749 Stmt::ExprStmt(e) if is_delegation_expr(e) => Some(e.clone()),
10750 _ => None,
10751 }) else {
10752 return;
10753 };
10754 fn rec(s: &mut Stmt, d0: &Expr) {
10755 match s {
10756 Stmt::Block(v) => {
10757 v.retain(|x| {
10758 !matches!(x, Stmt::ExprStmt(e) if is_delegation_expr(e) && same_delegation(e, d0))
10759 });
10760 for x in v.iter_mut() {
10761 rec(x, d0);
10762 }
10763 }
10764 Stmt::If { then_stmt, else_stmt, .. } => {
10765 rec(then_stmt, d0);
10766 if let Some(e) = else_stmt {
10767 rec(e, d0);
10768 }
10769 }
10770 _ => {}
10771 }
10772 }
10773 {
10777 let d0r = &d0;
10778 let mut idx = 0usize;
10779 stmts.retain(|s| {
10780 idx += 1;
10781 idx == 1
10782 || !(matches!(s, Stmt::ExprStmt(e)
10783 if is_delegation_expr(e) && same_delegation(e, d0r)))
10784 });
10785 }
10786 for s in stmts.iter_mut().skip(1) {
10787 rec(s, &d0);
10788 }
10789}
10790
10791fn same_delegation(a: &Expr, b: &Expr) -> bool {
10795 match (a, b) {
10796 (
10797 Expr::Method { name: n1, cls: c1, is_super: s1, args: a1, .. },
10798 Expr::Method { name: n2, cls: c2, is_super: s2, args: a2, .. },
10799 ) => {
10800 &**n1 == "<init>"
10801 && &**n2 == "<init>"
10802 && c1 == c2
10803 && s1 == s2
10804 && (a1 == a2 || (a1.is_empty() && a2.is_empty()))
10805 }
10806 _ => false,
10807 }
10808}
10809
10810fn ctor_call_expr(s: &Stmt) -> Option<&Expr> {
10812 match s {
10813 Stmt::ExprStmt(e) if is_delegation_expr(e) => Some(e),
10814 _ => None,
10815 }
10816}
10817
10818fn same_ctor_kind(a: &Expr, b: &Expr) -> bool {
10820 match (a, b) {
10821 (
10822 Expr::Method { cls: c1, is_super: s1, owner: o1, .. },
10823 Expr::Method { cls: c2, is_super: s2, owner: o2, .. },
10824 ) => c1 == c2 && s1 == s2 && o1 == o2,
10825 _ => false,
10826 }
10827}
10828
10829fn flat_list(s: &Stmt) -> Vec<&Stmt> {
10834 fn rec<'a>(s: &'a Stmt, out: &mut Vec<&'a Stmt>) {
10835 match s {
10836 Stmt::Block(v) => {
10837 for x in v {
10838 rec(x, out);
10839 }
10840 }
10841 other => out.push(other),
10842 }
10843 }
10844 let mut out = Vec::new();
10845 rec(s, &mut out);
10846 out
10847}
10848
10849fn strip_lost_alloc_owners(e: &mut Expr) {
10855 deep_rewrite(e, &mut |x| {
10856 if let Expr::Method { name, is_special: true, owner: Some(o), .. } = x {
10857 if &**name == "<init>" && !matches!(**o, Expr::This) {
10858 **o = Expr::Const(jdc_core::ir::expr::ConstVal::Int(0));
10859 }
10860 }
10861 });
10862}
10863
10864fn count_locals_expr(e: &Expr) -> std::collections::HashMap<u32, usize> {
10867 let mut m = std::collections::HashMap::new();
10868 count_expr_ro(e, &mut m);
10869 m
10870}
10871
10872pub(crate) fn count_locals_stmts(ss: &[Stmt]) -> std::collections::HashMap<u32, usize> {
10873 let mut m = std::collections::HashMap::new();
10874 for s in ss {
10875 visit_stmt_exprs_ro(s, &mut |e| count_expr_ro(e, &mut m));
10876 }
10877 m
10878}
10879
10880fn cnt(m: &std::collections::HashMap<u32, usize>, v: u32) -> usize {
10881 m.get(&v).copied().unwrap_or(0)
10882}
10883
10884fn inline_locals(
10888 e: &Expr,
10889 map: &std::collections::HashMap<u32, Expr>,
10890 depth: u32,
10891) -> Option<Expr> {
10892 if depth > 4 {
10893 return None;
10894 }
10895 let mut out = e.clone();
10896 let mut fail = false;
10897 deep_rewrite(&mut out, &mut |x| {
10898 if let Expr::Local { var, .. } = x {
10899 if let Some(def) = map.get(var) {
10900 match inline_locals(def, map, depth + 1) {
10901 Some(r) => *x = r,
10902 None => fail = true,
10903 }
10904 }
10905 }
10906 });
10907 if fail {
10908 None
10909 } else {
10910 Some(out)
10911 }
10912}
10913
10914fn def_of(s: &Stmt) -> Option<(u32, &Expr)> {
10916 match s {
10917 Stmt::LocalDef { var, init: Some(e), .. } => Some((*var, e)),
10918 Stmt::ExprStmt(Expr::Assign {
10919 target,
10920 value,
10921 op: jdc_core::ir::expr::AssignOp::Plain,
10922 ..
10923 }) => match &**target {
10924 Expr::Local { var, .. } => Some((*var, value)),
10925 _ => None,
10926 },
10927 _ => None,
10928 }
10929}
10930
10931fn try_linear_super_inline(stmts: &mut Vec<Stmt>) -> bool {
10933 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
10934 return false;
10935 };
10936 if pos == 0 {
10937 return false; }
10939 if !stmts[..pos]
10941 .iter()
10942 .all(|s| matches!(s, Stmt::LocalDef { .. } | Stmt::ExprStmt(_)))
10943 {
10944 return false;
10945 }
10946 let Some(call) = ctor_call_expr(&stmts[pos]) else {
10947 return false;
10948 };
10949 let Expr::Method { args, .. } = call else {
10950 return false;
10951 };
10952 let mut map: std::collections::HashMap<u32, Expr> =
10954 std::collections::HashMap::new();
10955 let mut def_idx: std::collections::HashMap<u32, usize> =
10956 std::collections::HashMap::new();
10957 for (i, s) in stmts[..pos].iter().enumerate() {
10958 if let Some((v, e)) = def_of(s) {
10959 map.insert(v, e.clone());
10960 def_idx.insert(v, i);
10961 }
10962 }
10963 let mut needed: Vec<u32> = Vec::new();
10965 for a in args {
10966 let mut probe = a.clone();
10967 strip_lost_alloc_owners(&mut probe);
10968 deep_rewrite(&mut probe, &mut |x| {
10969 if let Expr::Local { var, .. } = x {
10970 if map.contains_key(var) && !needed.contains(var) {
10971 needed.push(*var);
10972 }
10973 }
10974 });
10975 }
10976 if needed.is_empty() {
10977 return false; }
10979 let mut consumed: Vec<usize> = Vec::new();
10982 let use_counts = count_locals_stmts(stmts);
10983 for &v in &needed {
10984 let def = &map[&v];
10985 let uses = cnt(&use_counts, v);
10986 if jdc_core::ir::build::has_side_effects(def) && uses != 1 {
10987 return false;
10988 }
10989 if uses == 1 {
10990 consumed.push(def_idx[&v]);
10991 }
10992 }
10993 let mut new_args: Vec<Expr> = Vec::with_capacity(args.len());
10995 for a in args {
10996 match inline_locals(a, &map, 0) {
10997 Some(r) => new_args.push(r),
10998 None => return false,
10999 }
11000 }
11001 let mut call_stmt = stmts.remove(pos);
11004 if let Stmt::ExprStmt(Expr::Method { args: slot, .. }) = &mut call_stmt {
11005 *slot = new_args;
11006 }
11007 let mut rest: Vec<Stmt> = Vec::with_capacity(stmts.len() + 1);
11008 rest.push(call_stmt);
11009 for (i, s) in stmts.drain(..).enumerate() {
11010 let orig = if i >= pos { i + 1 } else { i };
11013 if !consumed.contains(&orig) {
11014 rest.push(s);
11015 }
11016 }
11017 *stmts = rest;
11018 true
11019}
11020
11021fn try_merge_branched_super(stmts: &mut Vec<Stmt>) -> bool {
11023 let if_positions: Vec<usize> = stmts
11027 .iter()
11028 .enumerate()
11029 .filter(|(_, s)| matches!(s, Stmt::If { .. }))
11030 .map(|(i, _)| i)
11031 .collect();
11032 for if_pos in if_positions {
11033 let gate = match &stmts[if_pos] {
11039 Stmt::If { then_stmt, else_stmt, .. } => {
11040 contains_delegation(then_stmt)
11041 || else_stmt.as_ref().is_some_and(|e| contains_delegation(e))
11042 }
11043 _ => false,
11044 };
11045 if gate && merge_at(stmts, if_pos) {
11046 return true;
11047 }
11048 }
11049 false
11050}
11051
11052fn contains_delegation(s: &Stmt) -> bool {
11055 match s {
11056 Stmt::ExprStmt(e) => is_delegation_expr(e),
11057 Stmt::Block(v) => v.iter().any(contains_delegation),
11058 Stmt::If { then_stmt, else_stmt, .. } => {
11059 contains_delegation(then_stmt)
11060 || else_stmt.as_ref().is_some_and(|e| contains_delegation(e))
11061 }
11062 _ => false,
11063 }
11064}
11065
11066
11067
11068fn merge_at(stmts: &mut Vec<Stmt>, if_pos: usize) -> bool {
11081 enum Br {
11086 Call(usize, Vec<(u32, Expr)>, Vec<usize>),
11088 Clean,
11089 Dirty,
11090 }
11091 fn split_branch(list: &[&Stmt]) -> Br {
11092 let call_pos = list.iter().position(|s| ctor_call_expr(s).is_some());
11093 let Some(c) = call_pos else {
11094 return if list.iter().any(|s| contains_delegation(s)) {
11097 Br::Dirty
11098 } else {
11099 Br::Clean
11100 };
11101 };
11102 if list.iter().skip(c + 1).any(|s| ctor_call_expr(s).is_some()) {
11103 return Br::Dirty; }
11105 let mut defs: Vec<(u32, Expr)> = Vec::new();
11106 let mut def_idxs: Vec<usize> = Vec::new();
11107 for (i, s) in list.iter().enumerate().take(c) {
11108 match def_of(s) {
11109 Some((v, e)) => {
11110 defs.push((v, e.clone()));
11111 def_idxs.push(i);
11112 }
11113 None => match s {
11114 Stmt::ExprStmt(_) | Stmt::LocalDef { .. } => {}
11117 _ => return Br::Dirty, },
11119 }
11120 }
11121 Br::Call(c, defs, def_idxs)
11122 }
11123 fn cond_def_of(s: &Stmt, scope: &[Stmt]) -> Option<(u32, Expr)> {
11125 let Stmt::If { cond, then_stmt, else_stmt: Some(e), .. } = s else {
11126 return None;
11127 };
11128 fn one<'x>(b: &'x Stmt, scope: &[Stmt]) -> Option<(u32, &'x Expr)> {
11129 let l = flat_list(b);
11130 if l.len() == 1 {
11131 return def_of(l[0]);
11132 }
11133 if l.len() == 2 {
11142 let (w, ex) = def_of(l[0])?;
11143 let Stmt::ExprStmt(Expr::Assign {
11144 target,
11145 op: AssignOp::Plain,
11146 value,
11147 }) = l[1]
11148 else {
11149 return None;
11150 };
11151 let Expr::Local { var: tv, .. } = &**target else {
11152 return None;
11153 };
11154 let Expr::Local { var: rv, .. } = &**value else {
11155 return None;
11156 };
11157 if *rv != w || *tv == w {
11158 return None;
11159 }
11160 let mut ldef_writes = 0usize;
11164 for st in scope {
11165 walk_all(st, &mut |x| {
11166 if let Stmt::LocalDef {
11167 var,
11168 init: Some(_),
11169 ..
11170 } = x
11171 {
11172 if *var == w {
11173 ldef_writes += 1;
11174 }
11175 }
11176 });
11177 }
11178 if stmts_read_var(scope, w) != 1
11179 || stmts_count_writes(scope, w) + ldef_writes != 1
11180 {
11181 return None;
11182 }
11183 return Some((*tv, ex));
11184 }
11185 None
11186 }
11187 let (v1, e1) = one(then_stmt, scope)?;
11188 let (v2, e2) = one(e, scope)?;
11189 if v1 != v2 {
11190 return None;
11191 }
11192 Some((
11193 v1,
11194 Expr::Cond {
11195 c: Box::new(cond.clone()),
11196 t: Box::new(e1.clone()),
11197 f: Box::new(e2.clone()),
11198 },
11199 ))
11200 }
11201
11202 let mut pmap: std::collections::HashMap<u32, Expr> =
11204 std::collections::HashMap::new();
11205 let mut pidx: std::collections::HashMap<u32, usize> =
11206 std::collections::HashMap::new();
11207 for (i, s) in stmts[..if_pos].iter().enumerate() {
11208 match s {
11209 Stmt::LocalDef { .. } | Stmt::ExprStmt(_) => {
11210 if let Some((v, e)) = def_of(s) {
11211 pmap.insert(v, e.clone());
11212 pidx.insert(v, i);
11213 }
11214 }
11215 Stmt::If { .. } => match cond_def_of(s, stmts) {
11216 Some((v, e)) => {
11217 pmap.insert(v, e);
11218 pidx.insert(v, i);
11219 }
11220 None => return false,
11221 },
11222 _ => return false,
11223 }
11224 }
11225 let (cond, then_v, else_v, had_else) = match &stmts[if_pos] {
11226 Stmt::If { cond, then_stmt, else_stmt, .. } => (
11227 cond.clone(),
11228 then_stmt.as_ref().clone(),
11229 else_stmt.as_ref().map_or_else(|| Stmt::Block(Vec::new()), |e| e.as_ref().clone()),
11230 else_stmt.is_some(),
11231 ),
11232 _ => return false,
11233 };
11234 fn normalize_branch(b: &mut Stmt) {
11240 let slot: &mut Stmt = match b {
11245 Stmt::Block(v) if v.len() == 1 => &mut v[0],
11246 other => other,
11247 };
11248 if !matches!(slot, Stmt::If { .. }) || !contains_delegation(slot) {
11249 return;
11250 }
11251 let taken = std::mem::replace(slot, Stmt::Block(Vec::new()));
11252 let mut tmp = vec![taken];
11253 if merge_at(&mut tmp, 0) {
11254 *slot = Stmt::Block(tmp);
11255 } else {
11256 *slot = tmp.pop().unwrap();
11257 }
11258 }
11259 let mut then_v = then_v;
11260 let mut else_v = else_v;
11261 normalize_branch(&mut then_v);
11262 normalize_branch(&mut else_v);
11263 let then_list = flat_list(&then_v);
11264 let else_list = flat_list(&else_v);
11265
11266 enum Side {
11269 Call {
11270 at: usize,
11271 defs: Vec<(u32, Expr)>,
11272 def_idxs: Vec<usize>,
11273 },
11274 Clean,
11275 }
11276 let (t_side, e_side, two_sided) = match (split_branch(&then_list), split_branch(&else_list)) {
11277 (Br::Call(tc, td, ti), Br::Call(ec, ed, ei)) => (
11278 Side::Call { at: tc, defs: td, def_idxs: ti },
11279 Side::Call { at: ec, defs: ed, def_idxs: ei },
11280 true,
11281 ),
11282 (Br::Call(tc, td, ti), Br::Clean) => (
11283 Side::Call { at: tc, defs: td, def_idxs: ti },
11284 Side::Clean,
11285 false,
11286 ),
11287 (Br::Clean, Br::Call(ec, ed, ei)) => (
11288 Side::Clean,
11289 Side::Call { at: ec, defs: ed, def_idxs: ei },
11290 false,
11291 ),
11292 _ => return false,
11293 };
11294 let (t_call, e_call) = match (&t_side, &e_side) {
11296 (Side::Call { at: ta, .. }, Side::Call { at: ea, .. }) => (
11297 ctor_call_expr(then_list[*ta]),
11298 ctor_call_expr(else_list[*ea]),
11299 ),
11300 (Side::Call { at: ta, .. }, Side::Clean) => {
11301 (ctor_call_expr(then_list[*ta]), None)
11302 }
11303 (Side::Clean, Side::Call { at: ea, .. }) => {
11304 (None, ctor_call_expr(else_list[*ea]))
11305 }
11306 _ => return false,
11307 };
11308 let (call_list, call_at) = match &t_side {
11310 Side::Call { at, .. } => (&then_list, *at),
11311 Side::Clean => match &e_side {
11312 Side::Call { at, .. } => (&else_list, *at),
11313 Side::Clean => return false,
11314 },
11315 };
11316 let (base_args, other_args) = if two_sided {
11317 let (Some(tc), Some(ec)) = (t_call, e_call) else {
11318 return false;
11319 };
11320 if !same_ctor_kind(tc, ec) {
11321 return false;
11322 }
11323 let (Expr::Method { args: a1, .. }, Expr::Method { args: a2, .. }) = (tc, ec) else {
11324 return false;
11325 };
11326 if a1.len() != a2.len() {
11327 return false;
11328 }
11329 (a1, Some(a2))
11330 } else {
11331 let c = t_call.or(e_call);
11332 let Some(c) = c else { return false };
11333 let Expr::Method { args, .. } = c else {
11334 return false;
11335 };
11336 (args, None)
11337 };
11338
11339 let mut tmap: std::collections::HashMap<u32, Expr> = pmap.clone();
11341 let mut emap: std::collections::HashMap<u32, Expr> = pmap.clone();
11342 if let Side::Call { defs, .. } = &t_side {
11343 for (v, e) in defs {
11344 tmap.insert(*v, e.clone());
11345 }
11346 }
11347 if let Side::Call { defs, .. } = &e_side {
11348 for (v, e) in defs {
11349 emap.insert(*v, e.clone());
11350 }
11351 }
11352 let Some(cond_i) = inline_locals(&cond, &pmap, 0) else {
11353 return false;
11354 };
11355 let mut then_args: Vec<Expr> = Vec::with_capacity(base_args.len());
11357 let mut else_args: Vec<Expr> = Vec::new();
11358 if two_sided {
11359 for a in base_args {
11360 match inline_locals(a, &tmap, 0) {
11361 Some(r) => then_args.push(r),
11362 None => return false,
11363 }
11364 }
11365 for a in other_args.unwrap() {
11366 match inline_locals(a, &emap, 0) {
11367 Some(r) => else_args.push(r),
11368 None => return false,
11369 }
11370 }
11371 } else {
11372 let from_then = matches!(&t_side, Side::Call { .. });
11375 for a in base_args {
11376 let r = if from_then {
11377 inline_locals(a, &tmap, 0)
11378 } else {
11379 inline_locals(a, &emap, 0)
11380 };
11381 match r {
11382 Some(r) => then_args.push(r),
11383 None => return false,
11384 }
11385 }
11386 }
11387 let primary_args = base_args;
11388 let mut merged: Vec<Expr> = Vec::with_capacity(then_args.len());
11390 let mut differ = 0usize;
11391 for (j, t) in then_args.iter().enumerate() {
11392 if two_sided {
11393 let e = &else_args[j];
11394 if t == e {
11395 merged.push(t.clone());
11396 } else {
11397 differ += 1;
11398 merged.push(Expr::Cond {
11399 c: Box::new(cond_i.clone()),
11400 t: Box::new(t.clone()),
11401 f: Box::new(e.clone()),
11402 });
11403 }
11404 } else {
11405 merged.push(t.clone());
11406 }
11407 }
11408
11409 let vars_in = |e: &Expr| -> Vec<u32> {
11411 let mut vs = Vec::new();
11412 let mut probe = e.clone();
11413 strip_lost_alloc_owners(&mut probe);
11414 deep_rewrite(&mut probe, &mut |x| {
11415 if let Expr::Local { var, .. } = x {
11416 if !vs.contains(var) {
11417 vs.push(*var);
11418 }
11419 }
11420 });
11421 vs
11422 };
11423 let branch_remainder = |list: &[&Stmt], side: &Side| -> Vec<Stmt> {
11431 match side {
11432 Side::Clean => list.iter().map(|s| (*s).clone()).collect(),
11433 Side::Call { at, def_idxs, .. } => {
11434 list.iter()
11435 .enumerate()
11436 .filter(|(i, _)| *i != *at && !def_idxs.contains(i))
11437 .map(|(_, s)| (*s).clone())
11438 .collect()
11439 }
11440 }
11441 };
11442 let tail_then_v = branch_remainder(&then_list, &t_side);
11443 let tail_else_v = branch_remainder(&else_list, &e_side);
11444 let post = &stmts[if_pos + 1..];
11445 let mut inlined: Vec<u32> = vars_in(&cond);
11446 for a in primary_args.iter().chain(else_args.iter()) {
11447 for v in vars_in(a) {
11448 if !inlined.contains(&v) {
11449 inlined.push(v);
11450 }
11451 }
11452 }
11453 inlined.retain(|v| pmap.contains_key(v) || tmap.contains_key(v) || emap.contains_key(v));
11454 for &v in &inlined {
11456 let in_branch = match (&t_side, &e_side) {
11457 (Side::Call { defs: td, .. }, Side::Call { defs: ed, .. }) => {
11458 td.iter().any(|(d, _)| *d == v) || ed.iter().any(|(d, _)| *d == v)
11459 }
11460 (Side::Call { defs, .. }, Side::Clean)
11461 | (Side::Clean, Side::Call { defs, .. }) => {
11462 defs.iter().any(|(d, _)| *d == v)
11463 }
11464 _ => false,
11465 };
11466 if pmap.contains_key(&v) && in_branch {
11467 return false;
11468 }
11469 }
11470 type Counts = std::collections::HashMap<u32, usize>;
11471 type GuardCounts = (
11472 Counts,
11473 Vec<Counts>,
11474 Vec<Counts>,
11475 Counts,
11476 Counts,
11477 Counts,
11478 Vec<(u32, Counts)>,
11479 );
11480 let (cond_counts, a1_counts, a2_counts, tail1_counts, tail2_counts, post_counts, def_counts): GuardCounts =
11481 if inlined.is_empty() {
11482 Default::default()
11483 } else {
11484 let all_defs: Vec<(u32, &Expr)> = pmap
11485 .iter()
11486 .map(|(k, v)| (*k, v))
11487 .chain(match &t_side {
11488 Side::Call { defs, .. } => defs.iter().map(|(k, v)| (*k, v)).collect(),
11489 Side::Clean => Vec::new(),
11490 })
11491 .chain(match &e_side {
11492 Side::Call { defs, .. } => defs.iter().map(|(k, v)| (*k, v)).collect(),
11493 Side::Clean => Vec::new(),
11494 })
11495 .collect();
11496 (
11497 count_locals_expr(&cond),
11498 primary_args.iter().map(count_locals_expr).collect(),
11499 else_args.iter().map(count_locals_expr).collect(),
11500 count_locals_stmts(&tail_then_v),
11501 count_locals_stmts(&tail_else_v),
11502 count_locals_stmts(post),
11503 all_defs.into_iter().map(|(v, e)| (v, count_locals_expr(e))).collect(),
11504 )
11505 };
11506 for &v in &inlined {
11507 let in_t = matches!(&t_side, Side::Call { defs, .. } if defs.iter().any(|(d, _)| *d == v));
11508 let in_e = matches!(&e_side, Side::Call { defs, .. } if defs.iter().any(|(d, _)| *d == v));
11509 let effectful = [
11510 in_t.then(|| tmap.get(&v)).flatten(),
11511 in_e.then(|| emap.get(&v)).flatten(),
11512 pmap.get(&v),
11513 ]
11514 .into_iter()
11515 .flatten()
11516 .any(jdc_core::ir::build::has_side_effects);
11517 if !effectful {
11518 continue;
11519 }
11520 let u_cond = cnt(&cond_counts, v);
11521 let u_post = cnt(&post_counts, v);
11522 let u_t1 = cnt(&tail1_counts, v);
11523 let u_t2 = cnt(&tail2_counts, v);
11524 let mut u_odef = 0usize;
11525 for (dv, dc) in &def_counts {
11526 if *dv != v && inlined.contains(dv) {
11527 u_odef += cnt(dc, v);
11528 }
11529 }
11530 let mut emit = u_cond * (1 + differ);
11531 let mut uses_args = 0usize;
11532 for j in 0..primary_args.len() {
11533 let c1 = cnt(&a1_counts[j], v);
11534 let c2 = if two_sided && j < a2_counts.len() {
11535 cnt(&a2_counts[j], v)
11536 } else {
11537 0
11538 };
11539 uses_args += c1 + c2;
11540 emit += if two_sided && then_args[j] != else_args[j] {
11541 c1 + c2
11542 } else {
11543 c1.max(c2)
11544 };
11545 }
11546 let ok = if in_t || in_e {
11547 u_cond == 0
11548 && u_post == 0
11549 && u_t1 == 0
11550 && u_t2 == 0
11551 && u_odef == 0
11552 && a1_counts.iter().map(|c| cnt(c, v)).sum::<usize>() <= 1
11553 && a2_counts.iter().map(|c| cnt(c, v)).sum::<usize>() <= 1
11554 && emit <= 2
11555 && (in_t || a1_counts.iter().all(|c| cnt(c, v) == 0))
11556 && (in_e || a2_counts.iter().all(|c| cnt(c, v) == 0))
11557 } else {
11558 u_post == 0
11559 && u_t1 == 0
11560 && u_t2 == 0
11561 && u_odef == 0
11562 && ((u_cond == 1 && uses_args == 0 && emit <= 2)
11563 || (u_cond == 0 && emit <= 1))
11564 };
11565 if !ok {
11566 return false;
11567 }
11568 }
11569
11570 let mut call_stmt = call_list[call_at].clone();
11572 if let Stmt::ExprStmt(Expr::Method { args: slot, .. }) = &mut call_stmt {
11573 *slot = merged;
11574 }
11575 let residual = |v: u32| -> usize {
11576 cnt(&tail1_counts, v) + cnt(&tail2_counts, v) + cnt(&post_counts, v)
11577 };
11578 let mut kept_prelude: Vec<Stmt> = Vec::new();
11579 for (i, s) in stmts[..if_pos].iter().enumerate() {
11580 let consumed = match s {
11581 Stmt::LocalDef { var, .. } => Some(*var),
11582 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
11583 Expr::Local { var, .. } => Some(*var),
11584 _ => None,
11585 },
11586 Stmt::If { .. } => cond_def_of(s, stmts).map(|(v, _)| v),
11587 _ => None,
11588 };
11589 if let Some(v) = consumed {
11590 if inlined.contains(&v) && pidx.get(&v) == Some(&i) && residual(v) == 0 {
11591 continue;
11592 }
11593 }
11594 kept_prelude.push(s.clone());
11595 }
11596 let build_keep = |list: &[&Stmt], side: &Side| -> Vec<Stmt> {
11597 match side {
11598 Side::Clean => list.iter().map(|s| (*s).clone()).collect(),
11599 Side::Call { at, .. } => {
11600 let mut out = Vec::new();
11601 for (i, s) in list.iter().enumerate() {
11602 if i == *at {
11603 continue;
11604 }
11605 if i < *at {
11606 if let Some((v, _)) = def_of(s) {
11607 if inlined.contains(&v) && residual(v) == 0 {
11608 continue;
11609 }
11610 }
11611 }
11612 out.push((*s).clone());
11613 }
11614 out
11615 }
11616 }
11617 };
11618 let then_keep = build_keep(&then_list, &t_side);
11619 let else_keep = build_keep(&else_list, &e_side);
11620 let else_part = if else_keep.is_empty() && !had_else {
11621 None
11622 } else {
11623 Some(Box::new(Stmt::Block(else_keep)))
11624 };
11625 let new_if = Stmt::If {
11626 cond: cond_i,
11627 then_stmt: Box::new(Stmt::Block(then_keep)),
11628 else_stmt: else_part,
11629 };
11630 let if_empty = match &new_if {
11631 Stmt::If { then_stmt, else_stmt, .. } => {
11632 let be = |s: &Stmt| matches!(s, Stmt::Block(v) if v.is_empty());
11633 be(then_stmt) && else_stmt.as_ref().is_none_or(|e| be(e))
11634 }
11635 _ => false,
11636 };
11637 let mut out: Vec<Stmt> = Vec::with_capacity(stmts.len());
11638 out.push(call_stmt);
11639 out.extend(kept_prelude);
11640 if !if_empty {
11641 out.push(new_if);
11642 }
11643 out.extend(stmts[if_pos + 1..].to_vec());
11644 *stmts = out;
11645 true
11646}
11647
11648
11649pub fn strip_enum_ctor_super(body: &mut Stmt) {
11650 fn is_enum_super(s: &Stmt) -> bool {
11651 matches!(
11652 s,
11653 Stmt::ExprStmt(Expr::Method { name, is_super: true, .. }) if &**name == "<init>"
11654 )
11655 }
11656 fn strip_deep(stmts: &mut Vec<Stmt>) {
11666 stmts.retain(|s| !is_enum_super(s));
11667 for st in stmts.iter_mut() {
11668 strip_one(st);
11669 }
11670 }
11671 fn strip_one(st: &mut Stmt) {
11672 match st {
11673 Stmt::Block(v) => strip_deep(v),
11674 Stmt::If { then_stmt, else_stmt, .. } => {
11675 strip_one(then_stmt);
11676 if let Some(e) = else_stmt {
11677 strip_one(e);
11678 }
11679 }
11680 Stmt::While { body, .. }
11681 | Stmt::DoWhile { body, .. }
11682 | Stmt::ForEach { body, .. }
11683 | Stmt::Synchronized { body, .. }
11684 | Stmt::Labeled { body, .. } => strip_one(body),
11685 Stmt::For { init, body, .. } => {
11686 strip_deep(init);
11687 strip_one(body);
11688 }
11689 Stmt::Switch { cases, default, .. } => {
11690 for c in cases.iter_mut() {
11691 strip_deep(&mut c.body);
11692 }
11693 if let Some(d) = default {
11694 strip_one(d);
11695 }
11696 }
11697 Stmt::Try { body, catches, finally }
11698 | Stmt::TryWithResources { body, catches, finally, .. } => {
11699 strip_one(body);
11700 for c in catches.iter_mut() {
11701 strip_one(&mut c.body);
11702 }
11703 if let Some(f) = finally {
11704 strip_one(f);
11705 }
11706 }
11707 _ => {}
11708 }
11709 }
11710 let Stmt::Block(stmts) = body else {
11711 return;
11712 };
11713 strip_deep(stmts);
11714}
11715
11716pub fn ensure_declared(body: &mut Stmt, vt: &VarTable) {
11717 let n = vt.vars.len().max(1);
11721
11722 let mut seen: HashSet<u32> = HashSet::default();
11725 demote_dup_decls(body, &mut seen, vt);
11726
11727 let mut def_block: Vec<u32> = vec![u32::MAX; n];
11735 let mut sizes: Vec<u32> = vec![0];
11736 let mut next_id: u32 = 1;
11737 scope_pass_a(body, 0, &mut next_id, &mut def_block, &mut sizes);
11738 sizes[0] = next_id;
11739 let mut within: Vec<u32> = vec![0; n];
11740 let mut total: Vec<u32> = vec![0; n];
11741 let mut next_b: u32 = 1;
11742 scope_pass_b(body, 0, &mut next_b, &def_block, &sizes, &mut within, &mut total);
11743 let mut hoist: HashSet<u32> = HashSet::default();
11744 for v in 0..n {
11745 if def_block[v] != u32::MAX && def_block[v] != 0 && within[v] < total[v] {
11746 hoist.insert(v as u32);
11747 }
11748 }
11749 if !hoist.is_empty() {
11750 let mut seed: HashSet<u32> = hoist.iter().copied().collect();
11751 demote_dup_decls(body, &mut seed, vt);
11752 }
11753
11754 let mut declared: Vec<bool> = vec![false; n];
11755 let mut is_param: Vec<bool> = vec![false; n];
11756 for v in &vt.vars {
11757 if v.is_param && (v.id as usize) < n {
11758 is_param[v.id as usize] = true;
11759 }
11760 }
11761 let mut is_catch: Vec<bool> = vec![false; n];
11767 walk_all(body, &mut |st| {
11768 if let Stmt::Try { catches, .. } = st {
11769 for c in catches {
11770 if (c.var as usize) < n {
11771 is_catch[c.var as usize] = true;
11772 }
11773 }
11774 }
11775 });
11776 walk_all(body, &mut |st| {
11777 if let Stmt::LocalDef { var, .. } = st {
11778 if (*var as usize) < n {
11779 declared[*var as usize] = true;
11780 }
11781 }
11782 });
11783 let mut used: HashSet<u32> = HashSet::default();
11784 stmt_collect_vars(body, &mut used, true);
11788
11789 let mut needs_decl: Vec<u32> = used
11792 .into_iter()
11793 .filter(|v| {
11794 let i = *v as usize;
11795 i >= n
11796 || ((!declared[i] && !is_param[i] && !is_catch[i]) || hoist.contains(v))
11797 })
11798 .collect();
11799 needs_decl.sort_unstable();
11800 needs_decl.dedup();
11801
11802 if !needs_decl.is_empty() {
11803 let mut decls: Vec<Stmt> = needs_decl
11804 .into_iter()
11805 .map(|v| Stmt::LocalDef {
11806 var: v,
11807 init: None,
11808 is_final: false,
11809 force_type: true,
11810 })
11811 .collect();
11812 decls.reverse();
11813 match body {
11814 Stmt::Block(v) => {
11815 for d in decls {
11816 v.insert(0, d);
11817 }
11818 }
11819 other => {
11820 let inner = std::mem::replace(other, Stmt::Block(vec![]));
11821 *other = Stmt::Block({
11822 let mut vs = decls;
11823 vs.push(inner);
11824 vs
11825 });
11826 }
11827 }
11828 }
11829}
11830
11831fn demote_dup_decls(s: &mut Stmt, seen: &mut HashSet<u32>, vt: &VarTable) {
11832 match s {
11833 Stmt::Block(v) => {
11836 for x in v.iter_mut() {
11837 demote_dup_decls(x, seen, vt);
11838 }
11839 }
11840 Stmt::LocalDef { var, init, .. } => {
11841 if seen.contains(var) {
11842 let ty = vt.var(*var).ty.clone();
11843 if let Some(e) = init.take() {
11844 *s = Stmt::ExprStmt(Expr::Assign {
11845 target: Box::new(Expr::Local { var: *var, ty }),
11846 op: AssignOp::Plain,
11847 value: Box::new(e),
11848 });
11849 } else {
11850 *s = Stmt::Block(vec![]);
11851 }
11852 } else {
11853 seen.insert(*var);
11854 }
11855 }
11856 other => {
11857 walk_mut(other, &mut |x| demote_dup_decls(x, seen, vt));
11858 }
11859 }
11860}
11861
11862#[allow(dead_code)]
11863fn unused(_: &Vec<CaseGroup>, _: &Catch) {}
11864
11865pub fn apply_local_names(vt: &mut VarTable, body: &Stmt) {
11879 use std::collections::{HashMap, HashSet};
11880
11881 let mut by_call: HashMap<u32, Vec<String>> = HashMap::new();
11884 let mut by_intrinsics: HashMap<u32, String> = HashMap::new();
11885
11886 visit_all_exprs(body, &mut |e| {
11887 if let Expr::Method { cls, name, args, .. } = e {
11888 if (cls.as_ref() == "kotlin/jvm/internal/Intrinsics"
11889 || cls.as_ref() == "kotlin/jvm/internal/IntrinsicsKt")
11890 && matches!(name.as_ref(), "checkNotNullParameter" | "checkParameterIsNotNull")
11891 && args.len() == 2
11892 {
11893 if let (Expr::Local { var, .. }, Expr::Const(ConstVal::Str(s))) = (&args[0], &args[1])
11894 {
11895 by_intrinsics
11896 .entry(*var)
11897 .or_insert_with(|| sanitize_name(s).unwrap_or_default());
11898 }
11899 }
11900 }
11901 });
11902
11903 walk_all(body, &mut |st| {
11904 let (var, value) = match st {
11905 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e),
11906 Stmt::ExprStmt(Expr::Assign { target, value, .. })
11907 if matches!(&**target, Expr::Local { .. }) =>
11908 {
11909 let Expr::Local { var, .. } = &**target else { return };
11910 (*var, value.as_ref())
11911 }
11912 _ => return,
11913 };
11914 if let Some(n) = defining_call_name(value) {
11915 by_call.entry(var).or_default().push(n);
11916 }
11917 });
11918
11919 let sanit = |n: &str| crate::classdec::java_ident(n).into_owned();
11930 let mut taken: HashSet<String> = HashSet::default();
11931 for v in vt.vars.iter() {
11936 if v.synthetic_name {
11941 taken.insert(sanit(&v.name));
11942 }
11943 }
11944 for v in vt.vars.iter_mut() {
11945 if v.synthetic_name {
11946 continue;
11947 }
11948 if !taken.insert(sanit(&v.name)) {
11949 let base = sanit(&v.name);
11950 for i in 2.. {
11951 let cand = format!("{base}{i}");
11952 if taken.insert(cand.clone()) {
11953 v.name = cand;
11954 break;
11955 }
11956 }
11957 }
11958 }
11959 let claim = |taken: &mut HashSet<String>, want: &str| -> String {
11960 let w = crate::classdec::java_ident(want).into_owned();
11961 if taken.insert(w.clone()) {
11962 return w;
11963 }
11964 for i in 2.. {
11965 let cand = format!("{w}{i}");
11966 if taken.insert(cand.clone()) {
11967 return cand;
11968 }
11969 }
11970 w
11971 };
11972
11973 for info in vt.vars.iter_mut() {
11974 if !info.synthetic_name {
11975 continue;
11976 }
11977 if let Some(n) = by_intrinsics.get(&info.id) {
11980 if !n.is_empty() {
11981 info.name = claim(&mut taken, n);
11982 continue;
11983 }
11984 }
11985 if let Some(names) = by_call.get(&info.id) {
11987 let unique: HashSet<&String> = names.iter().collect();
11988 if unique.len() == 1 {
11989 let n = unique.into_iter().next().unwrap();
11990 if !is_java_keyword(n) {
11991 info.name = claim(&mut taken, n);
11992 continue;
11993 }
11994 }
11995 }
11996 let want = type_alias(&info.ty).map(str::to_string).or_else(|| simple_type_name(&info.ty));
11999 if let Some(n) = want {
12000 info.name = claim(&mut taken, &n);
12001 }
12002 }
12003
12004 let mut seen: HashSet<String> = HashSet::default();
12009 for v in vt.vars.iter_mut() {
12010 if !seen.insert(sanit(&v.name)) {
12011 let base = sanit(&v.name);
12012 for i in 2.. {
12013 let cand = format!("{base}{i}");
12014 if seen.insert(cand.clone()) {
12015 v.name = cand;
12016 break;
12017 }
12018 }
12019 }
12020 }
12021}
12022
12023fn simple_type_name(ty: &TypeRef) -> Option<String> {
12025 let TypeRef::J(JavaType::Object(n)) = ty else {
12026 return None;
12027 };
12028 let simple = n.rsplit('/').next().unwrap_or(n);
12029 let simple = simple.rsplit('$').next().unwrap_or(simple);
12030 let mut chars = simple.chars();
12033 let mut out = String::with_capacity(simple.len());
12034 let mut lowered = false;
12035 for c in chars.by_ref() {
12036 if c.is_ascii_alphabetic() && !lowered {
12037 out.push(c.to_ascii_lowercase());
12038 lowered = true;
12039 } else {
12040 out.push(c);
12041 }
12042 if lowered {
12043 break;
12044 }
12045 }
12046 out.extend(chars);
12047 sanitize_name(&out)
12048}
12049
12050fn defining_call_name(e: &Expr) -> Option<String> {
12054 match e {
12055 Expr::Method { name, .. } => {
12056 let base = name
12057 .strip_prefix("get")
12058 .or_else(|| name.strip_prefix("is"))
12059 .filter(|rest| rest.chars().next().is_some_and(|c| c.is_ascii_uppercase()))
12060 .unwrap_or(name);
12061 let first = base.chars().next()?;
12062 let mut out = String::with_capacity(base.len());
12063 out.push(first.to_ascii_lowercase());
12064 out.extend(base.chars().skip(1));
12065 sanitize_name(&out)
12066 }
12067 Expr::New { cls, .. } => {
12068 let simple = cls.rsplit('/').next().unwrap_or(cls);
12069 let simple = simple.rsplit('$').next().unwrap_or(simple);
12070 let first = simple.chars().next()?;
12071 let mut out = String::with_capacity(simple.len());
12072 out.push(first.to_ascii_lowercase());
12073 out.extend(simple.chars().skip(1));
12074 sanitize_name(&out)
12075 }
12076 _ => None,
12077 }
12078}
12079
12080fn sanitize_name(s: &str) -> Option<String> {
12082 let mut out = String::with_capacity(s.len());
12083 for c in s.chars() {
12084 if c.is_ascii_alphanumeric() || c == '_' || c == '$' {
12085 out.push(c);
12086 } else {
12087 out.push('_');
12088 }
12089 }
12090 if out.len() < 2
12091 || out.chars().next().is_some_and(|c| c.is_ascii_digit())
12092 || is_java_keyword(&out)
12093 {
12094 return None;
12095 }
12096 Some(out)
12097}
12098
12099fn type_alias(ty: &TypeRef) -> Option<&'static str> {
12102 let TypeRef::J(JavaType::Object(n)) = ty else {
12103 return None;
12104 };
12105 Some(match n.as_ref() {
12106 "java/lang/String" | "kotlin/String" => "str",
12107 "java/lang/Class" => "cls",
12108 "java/lang/Throwable" => "th",
12109 "java/lang/Object" | "kotlin/Any" => "obj",
12110 "java/util/Iterator" | "kotlin/collections/Iterator" => "it",
12111 "java/lang/Boolean" => "bool",
12112 "java/lang/Integer" => "num",
12113 "java/lang/Character" => "ch",
12114 "java/lang/Byte" => "b",
12115 "java/lang/Short" => "sh",
12116 "java/lang/Float" => "f",
12117 "java/lang/Double" => "d",
12118 "java/lang/Long" => "i",
12119 "java/lang/StringBuilder" => "sb",
12120 "java/util/ArrayList" => "list",
12121 "java/util/HashMap" => "map",
12122 "android/content/Context" => "context",
12123 "android/content/Intent" => "intent",
12124 "android/os/Bundle" => "bundle",
12125 "android/view/View" => "view",
12126 "android/graphics/Bitmap" => "bitmap",
12127 "android/view/ViewGroup" => "viewGroup",
12128 _ => return None,
12129 })
12130}
12131
12132pub(crate) fn visit_all_exprs<F: FnMut(&Expr)>(s: &Stmt, f: &mut F) {
12134 walk_all(s, &mut |st| {
12135 let exprs: Vec<&Expr> = match st {
12136 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
12137 let mut v: Vec<&Expr> = vec![value];
12138 if !matches!(&**target, Expr::Local { .. }) {
12139 v.push(target);
12140 }
12141 v
12142 }
12143 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
12144 vec![e]
12145 }
12146 Stmt::Return(Some(e)) => vec![e],
12147 Stmt::LocalDef { init: Some(e), .. } => vec![e],
12148 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
12149 vec![cond]
12150 }
12151 _ => Vec::new(),
12152 };
12153 for e in exprs {
12154 visit_exprs(e, f);
12155 }
12156 });
12157}
12158
12159pub fn platform_constants(body: &mut Stmt) {
12164 if !crate::platform::installed() {
12165 return;
12166 }
12167 rewrite_exprs(body, &mut |e| {
12168 if let Expr::Method { cls, name, desc, args, .. } = e {
12169 for a in args.iter_mut() {
12170 let (value, is_long) = match a {
12171 Expr::Const(ConstVal::Int(v)) => (*v as i64, false),
12172 Expr::Const(ConstVal::Long(v)) => (*v, true),
12173 _ => continue,
12174 };
12175 if let Some(named) =
12176 crate::platform::named_constant(cls, name, &desc_to_string(desc), 0, value)
12177 {
12178 let _ = is_long;
12179 *a = Expr::Raw(named);
12180 }
12181 }
12182 }
12183 });
12184}
12185
12186fn desc_to_string(d: &jdc_core::types::MethodDescriptor) -> String {
12188 let mut out = String::from("(");
12189 for a in &d.args {
12190 type_to_desc(a, &mut out);
12191 }
12192 out.push(')');
12193 type_to_desc(&d.ret, &mut out);
12194 out
12195}
12196
12197fn type_to_desc(t: &jdc_core::types::JavaType, out: &mut String) {
12198 match t {
12199 JavaType::Void => out.push('V'),
12200 JavaType::Boolean => out.push('Z'),
12201 JavaType::Byte => out.push('B'),
12202 JavaType::Char => out.push('C'),
12203 JavaType::Short => out.push('S'),
12204 JavaType::Int => out.push('I'),
12205 JavaType::Float => out.push('F'),
12206 JavaType::Long => out.push('J'),
12207 JavaType::Double => out.push('D'),
12208 JavaType::Object(n) => {
12209 out.push('L');
12210 out.push_str(n);
12211 out.push(';');
12212 }
12213 JavaType::Array(inner) => {
12214 out.push('[');
12215 type_to_desc(inner, out);
12216 }
12217 }
12218}
12219
12220pub fn remove_kotlin_checks(s: &mut Stmt) {
12226 walk_mut_deep(s, &mut |st| {
12227 let Stmt::Block(v) = st else { return };
12228 v.retain(|x| !is_kotlin_check(x));
12229 });
12230}
12231
12232fn is_kotlin_check(s: &Stmt) -> bool {
12233 let Stmt::ExprStmt(e) = s else { return false };
12234 let Expr::Method { cls, name, .. } = e else { return false };
12235 (cls.as_ref() == "kotlin/jvm/internal/Intrinsics" || cls.as_ref() == "kotlin/jvm/internal/IntrinsicsKt__Jdk7Kt")
12236 && matches!(
12237 name.as_ref(),
12238 "checkNotNullParameter"
12239 | "checkParameterIsNotNull"
12240 | "checkExpressionValueIsNotNull"
12241 | "checkNotNullExpressionValue"
12242 | "checkReturnedValueIsNotNull"
12243 | "checkFieldIsNotNull"
12244 | "checkNotNull"
12245 )
12246}
12247
12248fn inline_ref_ok(pool: &DexPool, host: &str, target: &str, member: Option<(&str, bool)>) -> bool {
12268 let hpkg = match host.rfind('/') {
12269 Some(i) => &host[..i],
12270 None => "",
12271 };
12272 let mut cur = target;
12273 loop {
12274 let tpkg = match cur.rfind('/') {
12275 Some(i) => &cur[..i],
12276 None => "",
12277 };
12278 if tpkg != hpkg {
12279 if let Some(pc) = pool.get(cur) {
12280 if pc.access & crate::access::ACC_PUBLIC == 0 {
12281 return false;
12282 }
12283 if cur == target {
12284 if let Some((mname, is_method)) = member {
12285 let ok = if is_method {
12286 pc.all_methods().any(|m| {
12287 &*m.name == mname
12288 && m.access & crate::access::ACC_PUBLIC != 0
12289 })
12290 } else {
12291 pc.static_fields
12292 .iter()
12293 .chain(pc.instance_fields.iter())
12294 .any(|f| {
12295 f.name == mname
12296 && f.access & crate::access::ACC_PUBLIC != 0
12297 })
12298 };
12299 if !ok
12304 && (if is_method {
12305 pc.all_methods().any(|m| &*m.name == mname)
12306 } else {
12307 pc.static_fields
12308 .iter()
12309 .chain(pc.instance_fields.iter())
12310 .any(|f| f.name == mname)
12311 })
12312 {
12313 return false;
12314 }
12315 }
12316 }
12317 }
12318 }
12319 match cur.rfind('$') {
12320 Some(i) if i > 0 => cur = &cur[..i],
12321 _ => break,
12322 }
12323 }
12324 true
12325}
12326
12327pub fn inline_accessors(s: &mut Stmt, pool: &DexPool, host: &str) {
12328 rewrite_exprs(s, &mut |e| {
12329 let Expr::Method { cls, name, desc, args, is_static, .. } = e else { return };
12330 if !*is_static || args.is_empty() {
12331 return;
12332 }
12333 let Some(target) = pool.get(cls) else { return };
12334 let desc_s = desc_to_string(desc);
12335 let Some(m) = target.find_method(name, &desc_s) else { return };
12336 if !(m.is_static() && m.access & access::ACC_SYNTHETIC != 0) {
12337 return;
12338 }
12339 let Some(dex) = pool.dex(m.dex_idx) else { return };
12340 let code = match pool.accessor_code(m.dex_idx, m.code_off) {
12345 Some(bytes) => ddc_dex::CodeItem::parse(&bytes, 0),
12346 None => dex.code_at(m.code_off),
12347 };
12348 let Some(code) = code else { return };
12349 let insns: Vec<&Insn> =
12350 code.insns.iter().filter(|i| !matches!(i.kind, InsnKind::Nop)).collect();
12351 let core: Vec<&Insn> = insns
12353 .iter()
12354 .copied()
12355 .filter(|i| !is_const_only_trace(&i.kind, &insns))
12356 .collect();
12357 match accessor_shape(&dex, &core, &code) {
12358 Some(Shape::Identity(arg_i)) => {
12359 if let Some(a) = args.get(arg_i) {
12360 *e = a.clone();
12361 }
12362 }
12363 Some(Shape::FieldRead { arg, cls: field_cls, field, field_ty }) => {
12364 if !inline_ref_ok(pool, host, &field_cls, Some((&field, false))) {
12365 return;
12366 }
12367 if let Some(owner) = args.get(arg).cloned().map(Box::new) {
12368 let ty = TypeRef::J(desc_type(&field_ty));
12369 let name = jdc_core::rename::field_display(
12375 &field_cls,
12376 &field,
12377 &field_ty,
12378 )
12379 .map(std::sync::Arc::from)
12380 .unwrap_or_else(|| field.into());
12381 *e = Expr::Field {
12382 owner: Some(owner),
12383 cls: field_cls.into(),
12384 name,
12385 ty,
12386 is_static: false,
12387 };
12388 }
12389 }
12390 Some(Shape::Forward { cls: tcls, name: tname, desc: tdesc, instance }) => {
12391 if !inline_ref_ok(pool, host, &tcls, Some((&tname, true))) {
12392 return;
12393 }
12394 let disp = jdc_core::rename::field_display(
12398 &tcls,
12399 &tname,
12400 &tdesc.to_string(),
12401 )
12402 .map(std::sync::Arc::from)
12403 .unwrap_or_else(|| tname.into());
12404 *cls = tcls.into();
12405 *name = disp;
12406 *desc = std::sync::Arc::new(tdesc);
12407 if instance && !args.is_empty() {
12408 let recv = args.remove(0);
12410 if let Expr::Method { owner, .. } = e {
12411 *owner = Some(Box::new(recv));
12412 }
12413 }
12414 }
12415 None => {}
12416 }
12417 });
12418}
12419
12420enum Shape {
12422 Identity(usize),
12424 FieldRead { arg: usize, cls: String, field: String, field_ty: String },
12426 Forward { cls: String, name: String, desc: MethodDescriptor, instance: bool },
12429}
12430
12431fn field_of(dex: &ddc_dex::DexFile, field_idx: u32) -> Option<(String, String, String)> {
12433 let f = dex.field(field_idx);
12434 Some((
12435 dex.class_name(f.class_idx),
12438 dex.string(f.name_idx).to_string(),
12439 dex.type_name(f.type_idx).to_string(),
12441 ))
12442}
12443
12444fn param_index(code: &ddc_dex::CodeItem, reg: u16) -> Option<usize> {
12447 let rs = code.registers_size as usize;
12448 let ins = code.ins_size as usize;
12449 let r = reg as usize;
12450 let first = rs.checked_sub(ins)?;
12451 (r >= first && r < rs).then(|| r - first)
12452}
12453
12454fn accessor_shape(
12455 dex: &ddc_dex::DexFile,
12456 core: &[&Insn],
12457 code: &ddc_dex::CodeItem,
12458) -> Option<Shape> {
12459 let reads = |rest: &[&Insn], r: u16| {
12469 rest.iter()
12470 .any(|i| crate::lift::src_regs(&i.kind).contains(&r))
12471 };
12472 let core: &[&Insn] = match core.split_first() {
12473 Some((first, rest))
12474 if matches!(first.kind, InsnKind::Const { dst, .. } if !reads(rest, dst)) =>
12475 {
12476 rest
12477 }
12478 _ => core,
12479 };
12480 let core: &[&Insn] = match core.split_last() {
12481 Some((last, rest))
12482 if matches!(last.kind, InsnKind::Const { dst, .. } if !reads(rest, dst)) =>
12483 {
12484 rest
12485 }
12486 _ => core,
12487 };
12488 match core {
12489 [Insn { kind: InsnKind::Return { src }, .. }] => {
12491 let idx = param_index(code, *src)?;
12492 Some(Shape::Identity(idx))
12493 }
12494 [
12496 Insn { kind: InsnKind::IGet { dst, obj, field_idx }, .. },
12497 Insn { kind: InsnKind::Return { src }, .. },
12498 ] if dst == src => {
12499 let arg = param_index(code, *obj)?;
12500 let (cls, field, field_ty) = field_of(dex, *field_idx)?;
12501 Some(Shape::FieldRead { arg, cls, field, field_ty })
12502 }
12503 [Insn { kind: InsnKind::Invoke { method_idx, regs, kind, .. }, .. }, rest @ ..] if rest.len() <= 2 => {
12505 if !regs.iter().all(|r| param_index(code, *r).is_some()) || regs.is_empty() {
12507 return None;
12508 }
12509 if rest.len() == 2 {
12512 let ok = matches!(rest[0].kind, InsnKind::MoveResult { .. })
12513 && matches!(rest[1].kind, InsnKind::Return { .. } | InsnKind::ReturnVoid);
12514 if !ok {
12515 return None;
12516 }
12517 }
12518 let mid = dex.method(*method_idx);
12519 let cls = dex.class_name(mid.class_idx);
12522 let name = dex.string(mid.name_idx).to_string();
12523 let mut s = String::from("(");
12524 for t in dex.proto_params(mid.proto_idx) {
12525 s.push_str(dex.type_name(*t));
12526 }
12527 s.push(')');
12528 s.push_str(dex.type_name(dex.proto(mid.proto_idx).return_type_idx));
12529 let desc = parse_method_descriptor(&s)?;
12530 Some(Shape::Forward { cls, name, desc, instance: !matches!(kind, InvokeKind::Static) })
12531 }
12532 _ => None,
12533 }
12534}
12535
12536
12537fn is_const_only_trace(kind: &InsnKind, insns: &[&Insn]) -> bool {
12543 let InsnKind::Invoke { kind, regs, .. } = kind else { return false };
12544 if !matches!(kind, InvokeKind::Static) || regs.is_empty() {
12545 return false;
12546 }
12547 regs.iter().all(|r| const_loaded(*r, insns, 0))
12548}
12549
12550fn const_loaded(r: u16, insns: &[&Insn], depth: u8) -> bool {
12553 if depth > 3 {
12554 return false;
12555 }
12556 insns.iter().any(|i| match &i.kind {
12557 InsnKind::Const { dst, .. } => *dst == r,
12558 InsnKind::Move { dst, src } => *dst == r && const_loaded(*src, insns, depth + 1),
12559 _ => false,
12560 })
12561}