1#![allow(clippy::collapsible_match)]
14
15use jdc_core::FxHashSet as HashSet;
16
17use jdc_core::ir::build::has_side_effects;
21use jdc_core::emit::is_java_keyword;
22use jdc_core::ir::expr::{AssignOp, BinOp, ConcatPart, ConstVal, Expr, TypeRef, UnOp};
23use jdc_core::ir::stmt::{CaseGroup, Catch, Stmt};
24use jdc_core::types::{JavaType, MethodDescriptor};
25use jdc_core::var::VarTable;
26
27use crate::lift::MethodEnv;
28use crate::{access, desc_type, DexPool};
29use ddc_dex::insn::{Insn, InsnKind, InvokeKind};
30use jdc_core::types::parse_method_descriptor;
31
32pub fn rewrite_exprs<F: FnMut(&mut Expr)>(s: &mut Stmt, f: &mut F) {
38 walk_stmt_exprs(s, f);
39}
40
41pub(crate) fn walk_stmt_exprs<F: FnMut(&mut Expr)>(s: &mut Stmt, f: &mut F) {
42 match s {
43 Stmt::Block(v) => {
44 for x in v.iter_mut() {
45 walk_stmt_exprs(x, f);
46 }
47 }
48 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
49 f(e);
50 }
51 Stmt::Return(Some(e)) => f(e),
52 Stmt::LocalDef { init, .. } => {
53 if let Some(e) = init {
54 f(e);
55 }
56 }
57 Stmt::If {
58 cond,
59 then_stmt,
60 else_stmt,
61 } => {
62 f(cond);
63 walk_stmt_exprs(then_stmt, f);
64 if let Some(e) = else_stmt {
65 walk_stmt_exprs(e, f);
66 }
67 }
68 Stmt::While { cond, body } => {
69 f(cond);
70 walk_stmt_exprs(body, f);
71 }
72 Stmt::DoWhile { body, cond } => {
73 walk_stmt_exprs(body, f);
74 f(cond);
75 }
76 Stmt::For {
77 init,
78 cond,
79 update,
80 body,
81 } => {
82 for x in init.iter_mut() {
83 walk_stmt_exprs(x, f);
84 }
85 if let Some(c) = cond {
86 f(c);
87 }
88 for u in update.iter_mut() {
89 f(u);
90 }
91 walk_stmt_exprs(body, f);
92 }
93 Stmt::ForEach { iterable, body, .. } => {
94 f(iterable);
95 walk_stmt_exprs(body, f);
96 }
97 Stmt::Switch {
98 selector,
99 cases,
100 default,
101 ..
102 } => {
103 f(selector);
104 for c in cases {
105 for x in c.body.iter_mut() {
106 walk_stmt_exprs(x, f);
107 }
108 if let Some(g) = &mut c.guard {
109 f(g);
110 }
111 }
112 if let Some(d) = default {
113 walk_stmt_exprs(d, f);
114 }
115 }
116 Stmt::Try {
117 body,
118 catches,
119 finally,
120 } => {
121 walk_stmt_exprs(body, f);
122 for c in catches {
123 walk_stmt_exprs(&mut c.body, f);
124 }
125 if let Some(fl) = finally {
126 walk_stmt_exprs(fl, f);
127 }
128 }
129 Stmt::TryWithResources {
130 resources,
131 body,
132 catches,
133 finally,
134 } => {
135 for x in resources.iter_mut() {
136 walk_stmt_exprs(x, f);
137 }
138 walk_stmt_exprs(body, f);
139 for c in catches {
140 walk_stmt_exprs(&mut c.body, f);
141 }
142 if let Some(fl) = finally {
143 walk_stmt_exprs(fl, f);
144 }
145 }
146 Stmt::Assert { cond, msg } => {
147 f(cond);
148 if let Some(m) = msg {
149 f(m);
150 }
151 }
152 Stmt::Synchronized { lock, body } => {
153 f(lock);
154 walk_stmt_exprs(body, f);
155 }
156 Stmt::TernaryValue { e } => f(e),
157 Stmt::Labeled { body, .. } => walk_stmt_exprs(body, f),
158 Stmt::Goto(_) | Stmt::Label(_) | Stmt::Break(_) | Stmt::Continue(_) => {}
159 _ => {}
160 }
161}
162
163pub(crate) fn visit_stmt_exprs_ro<F: FnMut(&Expr)>(s: &Stmt, f: &mut F) {
168 match s {
169 Stmt::Block(v) => {
170 for x in v {
171 visit_stmt_exprs_ro(x, f);
172 }
173 }
174 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
175 f(e);
176 }
177 Stmt::Return(Some(e)) => f(e),
178 Stmt::LocalDef { init: Some(e), .. } => f(e),
179 Stmt::If {
180 cond,
181 then_stmt,
182 else_stmt,
183 } => {
184 f(cond);
185 visit_stmt_exprs_ro(then_stmt, f);
186 if let Some(e) = else_stmt {
187 visit_stmt_exprs_ro(e, f);
188 }
189 }
190 Stmt::While { cond, body } => {
191 f(cond);
192 visit_stmt_exprs_ro(body, f);
193 }
194 Stmt::DoWhile { body, cond } => {
195 visit_stmt_exprs_ro(body, f);
196 f(cond);
197 }
198 Stmt::For {
199 init,
200 cond,
201 update,
202 body,
203 } => {
204 for x in init {
205 visit_stmt_exprs_ro(x, f);
206 }
207 if let Some(c) = cond {
208 f(c);
209 }
210 for u in update {
211 f(u);
212 }
213 visit_stmt_exprs_ro(body, f);
214 }
215 Stmt::ForEach { iterable, body, .. } => {
216 f(iterable);
217 visit_stmt_exprs_ro(body, f);
218 }
219 Stmt::Switch {
220 selector,
221 cases,
222 default,
223 ..
224 } => {
225 f(selector);
226 for c in cases {
227 for x in &c.body {
228 visit_stmt_exprs_ro(x, f);
229 }
230 if let Some(g) = &c.guard {
231 f(g);
232 }
233 }
234 if let Some(d) = default {
235 visit_stmt_exprs_ro(d, f);
236 }
237 }
238 Stmt::Try {
239 body,
240 catches,
241 finally,
242 } => {
243 visit_stmt_exprs_ro(body, f);
244 for c in catches {
245 visit_stmt_exprs_ro(&c.body, f);
246 }
247 if let Some(fl) = finally {
248 visit_stmt_exprs_ro(fl, f);
249 }
250 }
251 Stmt::TryWithResources {
252 resources,
253 body,
254 catches,
255 finally,
256 ..
257 } => {
258 for x in resources {
259 visit_stmt_exprs_ro(x, f);
260 }
261 visit_stmt_exprs_ro(body, f);
262 for c in catches {
263 visit_stmt_exprs_ro(&c.body, f);
264 }
265 if let Some(fl) = finally {
266 visit_stmt_exprs_ro(fl, f);
267 }
268 }
269 Stmt::Assert { cond, msg } => {
270 f(cond);
271 if let Some(m) = msg {
272 f(m);
273 }
274 }
275 Stmt::Synchronized { lock, body } => {
276 f(lock);
277 visit_stmt_exprs_ro(body, f);
278 }
279 Stmt::TernaryValue { e } => f(e),
280 Stmt::Labeled { body, .. } => visit_stmt_exprs_ro(body, f),
281 _ => {}
282 }
283}
284
285fn count_expr_ro(e: &Expr, m: &mut jdc_core::FxHashMap<u32, usize>) {
291 if let Expr::Local { var, .. } = e {
292 *m.entry(*var).or_insert(0) += 1;
293 return;
294 }
295 if let Expr::Method {
296 name,
297 is_special: true,
298 owner: Some(o),
299 args,
300 ..
301 } = e
302 {
303 if &**name == "<init>" && !matches!(**o, Expr::This) {
304 for a in args {
305 count_expr_ro(a, m);
306 }
307 return;
308 }
309 }
310 for_each_child(e, &mut |c| count_expr_ro(c, m));
311}
312
313fn visit_exprs<F: FnMut(&Expr)>(e: &Expr, f: &mut F) {
315 f(e);
316 for_each_child(e, &mut |c| visit_exprs(c, f));
317}
318
319fn for_each_child<F: FnMut(&Expr)>(e: &Expr, f: &mut F) {
320 match e {
321 Expr::Un { e, .. } | Expr::Cast { e, .. } | Expr::InstanceOf { e, .. } => f(e),
322 Expr::Bin { l, r, .. } => {
323 f(l);
324 f(r);
325 }
326 Expr::Cond { c, t, f: fe } => {
327 f(c);
328 f(t);
329 f(fe);
330 }
331 Expr::Assign { target, value, .. } => {
332 f(target);
333 f(value);
334 }
335 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => f(e),
336 Expr::Field { owner, .. } => {
337 if let Some(o) = owner {
338 f(o);
339 }
340 }
341 Expr::Method { owner, args, .. } => {
342 if let Some(o) = owner {
343 f(o);
344 }
345 for a in args {
346 f(a);
347 }
348 }
349 Expr::ArrayIndex { array, index } => {
350 f(array);
351 f(index);
352 }
353 Expr::New { args, .. } => {
354 for a in args {
355 f(a);
356 }
357 }
358 Expr::NewArray { dims, init, .. } => {
359 for d in dims {
360 f(d);
361 }
362 if let Some(v) = init {
363 for x in v {
364 f(x);
365 }
366 }
367 }
368 Expr::NewMultiArray { dims, .. } => {
369 for d in dims {
370 f(d);
371 }
372 }
373 Expr::StringConcat(parts) => {
374 for p in parts {
375 if let ConcatPart::Str(x) = p {
376 f(x);
377 }
378 }
379 }
380 Expr::Invokedynamic { args, .. } => {
381 for a in args {
382 f(a);
383 }
384 }
385 Expr::AnonNew { args, .. } => {
386 for a in args {
387 f(a);
388 }
389 }
390 _ => {}
391 }
392}
393
394fn for_each_child_mut<F: FnMut(&mut Expr)>(e: &mut Expr, f: &mut F) {
396 match e {
397 Expr::Un { e, .. } | Expr::Cast { e, .. } | Expr::InstanceOf { e, .. } => f(e),
398 Expr::Bin { l, r, .. } => {
399 f(l);
400 f(r);
401 }
402 Expr::Cond { c, t, f: fe } => {
403 f(c);
404 f(t);
405 f(fe);
406 }
407 Expr::Assign { target, value, .. } => {
408 f(target);
409 f(value);
410 }
411 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => f(e),
412 Expr::Field { owner, .. } => {
413 if let Some(o) = owner {
414 f(o);
415 }
416 }
417 Expr::Method { owner, args, .. } => {
418 if let Some(o) = owner {
419 f(o);
420 }
421 for a in args.iter_mut() {
422 f(a);
423 }
424 }
425 Expr::ArrayIndex { array, index } => {
426 f(array);
427 f(index);
428 }
429 Expr::New { args, .. } => {
430 for a in args.iter_mut() {
431 f(a);
432 }
433 }
434 Expr::NewArray { dims, init, .. } => {
435 for d in dims.iter_mut() {
436 f(d);
437 }
438 if let Some(v) = init {
439 for x in v.iter_mut() {
440 f(x);
441 }
442 }
443 }
444 Expr::NewMultiArray { dims, .. } => {
445 for d in dims.iter_mut() {
446 f(d);
447 }
448 }
449 Expr::StringConcat(parts) => {
450 for p in parts {
451 if let ConcatPart::Str(x) = p {
452 f(x);
453 }
454 }
455 }
456 Expr::Invokedynamic { args, .. } => {
457 for a in args.iter_mut() {
458 f(a);
459 }
460 }
461 Expr::AnonNew { args, .. } => {
462 for a in args.iter_mut() {
463 f(a);
464 }
465 }
466 _ => {}
467 }
468}
469
470pub(crate) fn deep_rewrite<F: FnMut(&mut Expr)>(e: &mut Expr, f: &mut F) {
472 f(e);
473 for_each_child_mut(e, &mut |c| deep_rewrite(c, f));
474}
475
476fn visit_exprs_reads<F: FnMut(&Expr)>(e: &Expr, f: &mut F) {
488 f(e);
489 match e {
490 Expr::Assign { target, value, .. } => {
491 if !matches!(**target, Expr::Local { .. }) {
492 visit_exprs_reads(target, f);
493 }
494 visit_exprs_reads(value, f);
495 }
496 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
497 if !matches!(**inner, Expr::Local { .. }) {
498 visit_exprs_reads(inner, f);
499 }
500 }
501 _ => for_each_child(e, &mut |c| visit_exprs_reads(c, f)),
502 }
503}
504
505fn deep_rewrite_reads<F: FnMut(&mut Expr)>(e: &mut Expr, f: &mut F) {
506 f(e);
507 match e {
508 Expr::Assign { target, value, .. } => {
509 if !matches!(**target, Expr::Local { .. }) {
510 deep_rewrite_reads(target, f);
511 }
512 deep_rewrite_reads(value, f);
513 }
514 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
515 if !matches!(**inner, Expr::Local { .. }) {
516 deep_rewrite_reads(inner, f);
517 }
518 }
519 _ => for_each_child_mut(e, &mut |c| deep_rewrite_reads(c, f)),
520 }
521}
522
523fn collect_vars(e: &Expr, out: &mut HashSet<u32>) {
524 visit_exprs(e, &mut |x| {
525 if let Expr::Local { var, .. } = x {
526 out.insert(*var);
527 }
528 });
529}
530
531fn stmt_collect_vars(s: &Stmt, out: &mut HashSet<u32>, assignments: bool) {
536 match s {
537 Stmt::Block(v) => {
538 for x in v {
539 stmt_collect_vars(x, out, assignments);
540 }
541 }
542 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
543 if let Expr::Local { var, .. } = &**target {
544 if assignments {
545 out.insert(*var);
546 }
547 } else {
548 collect_vars(target, out);
549 }
550 collect_vars(value, out);
551 }
552 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
553 collect_vars(e, out)
554 }
555 Stmt::Return(Some(e)) => collect_vars(e, out),
556 Stmt::LocalDef { var, init, .. } => {
557 if assignments {
558 out.insert(*var);
559 }
560 if let Some(e) = init {
561 collect_vars(e, out);
562 }
563 }
564 Stmt::If {
565 cond,
566 then_stmt,
567 else_stmt,
568 } => {
569 collect_vars(cond, out);
570 stmt_collect_vars(then_stmt, out, assignments);
571 if let Some(e) = else_stmt {
572 stmt_collect_vars(e, out, assignments);
573 }
574 }
575 Stmt::While { cond, body } => {
576 collect_vars(cond, out);
577 stmt_collect_vars(body, out, assignments);
578 }
579 Stmt::DoWhile { body, cond } => {
580 stmt_collect_vars(body, out, assignments);
581 collect_vars(cond, out);
582 }
583 Stmt::For {
584 init,
585 cond,
586 update,
587 body,
588 } => {
589 for x in init {
590 stmt_collect_vars(x, out, assignments);
591 }
592 if let Some(c) = cond {
593 collect_vars(c, out);
594 }
595 for u in update {
596 collect_vars(u, out);
597 }
598 stmt_collect_vars(body, out, assignments);
599 }
600 Stmt::ForEach {
601 var,
602 iterable,
603 body,
604 ..
605 } => {
606 if assignments {
607 out.insert(*var);
608 }
609 collect_vars(iterable, out);
610 stmt_collect_vars(body, out, assignments);
611 }
612 Stmt::Switch {
613 selector,
614 cases,
615 default,
616 ..
617 } => {
618 collect_vars(selector, out);
619 for c in cases {
620 for x in &c.body {
621 stmt_collect_vars(x, out, assignments);
622 }
623 }
624 if let Some(d) = default {
625 stmt_collect_vars(d, out, assignments);
626 }
627 }
628 Stmt::Try {
629 body,
630 catches,
631 finally,
632 } => {
633 stmt_collect_vars(body, out, assignments);
634 for c in catches {
635 stmt_collect_vars(&c.body, out, assignments);
636 }
637 if let Some(f) = finally {
638 stmt_collect_vars(f, out, assignments);
639 }
640 }
641 Stmt::TryWithResources {
642 resources,
643 body,
644 catches,
645 finally,
646 } => {
647 for x in resources {
648 stmt_collect_vars(x, out, assignments);
649 }
650 stmt_collect_vars(body, out, assignments);
651 for c in catches {
652 stmt_collect_vars(&c.body, out, assignments);
653 }
654 if let Some(f) = finally {
655 stmt_collect_vars(f, out, assignments);
656 }
657 }
658 Stmt::Synchronized { lock, body } => {
659 collect_vars(lock, out);
660 stmt_collect_vars(body, out, assignments);
661 }
662 Stmt::Assert { cond, msg } => {
663 collect_vars(cond, out);
664 if let Some(m) = msg {
665 collect_vars(m, out);
666 }
667 }
668 Stmt::Labeled { body, .. } => stmt_collect_vars(body, out, assignments),
669 _ => {}
670 }
671}
672
673pub(crate) fn walk_all<F: FnMut(&Stmt)>(s: &Stmt, f: &mut F) {
674 f(s);
675 match s {
676 Stmt::Block(v) => {
677 for x in v {
678 walk_all(x, f);
679 }
680 }
681 Stmt::If {
682 then_stmt,
683 else_stmt,
684 ..
685 } => {
686 walk_all(then_stmt, f);
687 if let Some(e) = else_stmt {
688 walk_all(e, f);
689 }
690 }
691 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => walk_all(body, f),
692 Stmt::For { init, body, .. } => {
693 for x in init {
694 walk_all(x, f);
695 }
696 walk_all(body, f);
697 }
698 Stmt::ForEach { body, .. } => walk_all(body, f),
699 Stmt::Switch { cases, default, .. } => {
700 for c in cases {
701 for x in &c.body {
702 walk_all(x, f);
703 }
704 }
705 if let Some(d) = default {
706 walk_all(d, f);
707 }
708 }
709 Stmt::Try {
710 body,
711 catches,
712 finally,
713 } => {
714 walk_all(body, f);
715 for c in catches {
716 walk_all(&c.body, f);
717 }
718 if let Some(fl) = finally {
719 walk_all(fl, f);
720 }
721 }
722 Stmt::TryWithResources {
723 resources,
724 body,
725 catches,
726 finally,
727 } => {
728 for x in resources {
729 walk_all(x, f);
730 }
731 walk_all(body, f);
732 for c in catches {
733 walk_all(&c.body, f);
734 }
735 if let Some(fl) = finally {
736 walk_all(fl, f);
737 }
738 }
739 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => walk_all(body, f),
740 _ => {}
741 }
742}
743
744pub fn bind_catches(s: &mut Stmt, vt: &mut VarTable) {
751 let mut defined: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
756 for v in &vt.vars {
757 if v.is_param {
758 defined.insert(v.id);
759 }
760 }
761 collect_defined_locals(s, &mut defined);
762 let reads_all = count_locals_stmts(std::slice::from_ref(s));
763 let mut assigned: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
767 collect_assigned_locals(s, &mut assigned);
768 let mut fallback_bound: Vec<u32> = Vec::new();
769 bind_catches_walk(s, vt, &defined, &reads_all, &assigned, &mut fallback_bound);
770 if !fallback_bound.is_empty() {
773 remove_bare_decls(s, &fallback_bound);
774 }
775}
776
777fn collect_defined_locals(s: &Stmt, out: &mut jdc_core::FxHashSet<u32>) {
778 crate::passes::walk_all(s, &mut |st| {
779 match st {
780 Stmt::LocalDef { var, .. } => {
781 out.insert(*var);
782 }
783 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
784 if let Expr::Local { var, .. } = &**target {
785 out.insert(*var);
786 }
787 }
788 _ => {}
789 }
790 });
791}
792
793fn collect_bare_decls(s: &Stmt, out: &mut Vec<u32>) {
796 walk_all(s, &mut |st| {
797 if let Stmt::LocalDef { var, init: None, .. } = st {
798 if !out.contains(var) {
799 out.push(*var);
800 }
801 }
802 });
803}
804
805
806fn collect_assigned_locals(s: &Stmt, out: &mut jdc_core::FxHashSet<u32>) {
811 crate::passes::walk_all(s, &mut |st| {
812 match st {
813 Stmt::LocalDef { var, init: Some(_), .. } => {
814 out.insert(*var);
815 }
816 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
817 if let Expr::Local { var, .. } = &**target {
818 out.insert(*var);
819 }
820 }
821 _ => {}
822 }
823 });
824}
825
826fn first_thrown_unassigned_local(
829 body: &Stmt,
830 vt: &VarTable,
831 assigned: &jdc_core::FxHashSet<u32>,
832) -> Option<u32> {
833 let mut found: Option<u32> = None;
834 let c = body.clone();
835 walk_all(&c, &mut |st| {
836 if let Stmt::Throw(th) = st {
837 if let Expr::Local { var, .. } = th {
838 let v = *var;
839 if !assigned.contains(&v) {
840 if let JavaType::Object(o) = vt.var(v).ty.erased() {
841 if o.as_ref() == "java/lang/Throwable" {
842 found = Some(v);
843 }
844 }
845 }
846 }
847 }
848 });
849 found
850}
851
852fn remove_first_leaf(s: &mut Stmt) {
856 if let Stmt::Block(v) = s {
857 if let Some(f) = v.first_mut() {
858 remove_first_leaf(f);
859 }
860 while matches!(v.first(), Some(Stmt::Block(b)) if b.is_empty()) {
861 v.remove(0);
862 }
863 } else {
864 *s = Stmt::Block(vec![]);
865 }
866}
867
868fn remove_bare_decls(s: &mut Stmt, vars: &[u32]) {
871 let vars: jdc_core::FxHashSet<u32> = vars.iter().copied().collect();
872 walk_mut_deep(s, &mut |st| {
873 if let Stmt::Block(v) = st {
874 v.retain(|x| {
875 !matches!(x, Stmt::LocalDef { var, init: None, .. } if vars.contains(var))
876 });
877 }
878 });
879}
880
881fn first_undefined_local_read(
882 body: &Stmt,
883 defined: &jdc_core::FxHashSet<u32>,
884) -> Option<u32> {
885 let mut found: Option<u32> = None;
886 visit_stmt_exprs_ro(body, &mut |e| {
887 if found.is_none() {
888 visit_exprs(e, &mut |x| {
889 if found.is_none() {
890 if let Expr::Local { var, .. } = x {
891 if !defined.contains(var) {
892 found = Some(*var);
893 }
894 }
895 }
896 });
897 }
898 });
899 found
900}
901
902#[allow(clippy::too_many_arguments)]
903fn bind_catches_walk(
904 s: &mut Stmt,
905 vt: &mut VarTable,
906 defined: &jdc_core::FxHashSet<u32>,
907 reads_all: &jdc_core::FxHashMap<u32, usize>,
908 assigned: &jdc_core::FxHashSet<u32>,
909 fallback_bound: &mut Vec<u32>,
910) {
911 match s {
912 Stmt::Try {
913 body,
914 catches,
915 finally,
916 } => {
917 bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound);
918 for c in catches.iter_mut() {
919 if c.var == u32::MAX {
920 let stored = match first_leaf_stmt(c.body.as_ref()) {
927 Stmt::LocalDef { var, init: None, .. } => Some(*var),
939 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
943 match (&**target, &**value) {
944 (Expr::Local { var, .. }, Expr::Local { var: src, .. })
945 if !defined.contains(src) =>
946 {
947 Some(*var)
948 }
949 _ => None,
950 }
951 }
952 _ => None,
953 };
954 if let Some(v) = stored {
955 let exc = c
956 .exc
957 .first()
958 .cloned()
959 .unwrap_or_else(|| "java/lang/Throwable".into());
960 let slot = vt.var(v).slot;
961 let name = "e".to_string();
962 let new_var =
963 vt.add_catch_var(slot, name, TypeRef::J(JavaType::Object(exc)));
964 remove_first_leaf(c.body.as_mut());
965 rewrite_local_refs(c.body.as_mut(), v, new_var);
966 c.var = new_var;
967 } else if let Some(v) = first_undefined_local_read(c.body.as_ref(), defined)
968 .filter(|v| {
969 let in_catch = count_locals_stmts(std::slice::from_ref(c.body.as_ref()));
975 reads_all.get(v).copied().unwrap_or(0)
976 == in_catch.get(v).copied().unwrap_or(0)
977 })
978 .filter(|v| {
979 let ty = vt.var(*v).ty.erased();
987 let exc = c
988 .exc
989 .first()
990 .cloned()
991 .unwrap_or_else(|| "java/lang/Throwable".into());
992 matches!(&ty, JavaType::Object(o)
993 if o.as_ref() == exc.as_ref()
994 || o.as_ref() == "java/lang/Throwable")
995 })
996 {
997 c.var = v;
1007 fallback_bound.push(v);
1008 } else if let Some(v) =
1009 first_thrown_unassigned_local(c.body.as_ref(), vt, assigned)
1010 .filter(|v| {
1011 let in_catch =
1012 count_locals_stmts(std::slice::from_ref(c.body.as_ref()));
1013 reads_all.get(v).copied().unwrap_or(0)
1014 == in_catch.get(v).copied().unwrap_or(0)
1015 })
1016 .filter(|v| {
1017 let ty = vt.var(*v).ty.erased();
1021 let exc = c
1022 .exc
1023 .first()
1024 .cloned()
1025 .unwrap_or_else(|| "java/lang/Throwable".into());
1026 matches!(&ty, JavaType::Object(o)
1027 if o.as_ref() == exc.as_ref()
1028 || o.as_ref() == "java/lang/Throwable")
1029 })
1030 {
1031 c.var = v;
1037 fallback_bound.push(v);
1038 }
1039 }
1040 if c.var != u32::MAX {
1041 let in_catch =
1056 count_locals_stmts(std::slice::from_ref(c.body.as_ref()));
1057 let mut twins: Vec<u32> = Vec::new();
1058 collect_bare_decls(c.body.as_ref(), &mut twins);
1059 let mut swept: Vec<u32> = Vec::new();
1060 for x in twins {
1061 if x == c.var || assigned.contains(&x) {
1062 continue;
1063 }
1064 if reads_all.get(&x).copied().unwrap_or(0)
1065 != in_catch.get(&x).copied().unwrap_or(0)
1066 {
1067 continue;
1068 }
1069 if !matches!(vt.var(x).ty.erased(),
1070 JavaType::Object(o) if o.as_ref() == "java/lang/Throwable")
1071 {
1072 continue;
1073 }
1074 rewrite_local_refs(c.body.as_mut(), x, c.var);
1075 swept.push(x);
1076 }
1077 if !swept.is_empty() {
1078 remove_bare_decls(c.body.as_mut(), &swept);
1079 }
1080 }
1081 bind_catches_walk(&mut c.body, vt, defined, reads_all, assigned, fallback_bound);
1082 }
1083 if let Some(f) = finally {
1084 bind_catches_walk(f.as_mut(), vt, defined, reads_all, assigned, fallback_bound);
1085 }
1086 }
1087 Stmt::Block(v) => {
1088 for x in v.iter_mut() {
1089 bind_catches_walk(x, vt, defined, reads_all, assigned, fallback_bound);
1090 }
1091 }
1092 Stmt::If {
1093 then_stmt,
1094 else_stmt,
1095 ..
1096 } => {
1097 bind_catches_walk(then_stmt, vt, defined, reads_all, assigned, fallback_bound);
1098 if let Some(e) = else_stmt {
1099 bind_catches_walk(e, vt, defined, reads_all, assigned, fallback_bound);
1100 }
1101 }
1102 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound),
1103 Stmt::For { init, body, .. } => {
1104 for x in init.iter_mut() {
1105 bind_catches_walk(x, vt, defined, reads_all, assigned, fallback_bound);
1106 }
1107 bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound);
1108 }
1109 Stmt::ForEach { body, .. }
1110 | Stmt::Labeled { body, .. }
1111 | Stmt::Synchronized { body, .. } => bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound),
1112 Stmt::Switch { cases, default, .. } => {
1113 for c in cases.iter_mut() {
1114 for x in c.body.iter_mut() {
1115 bind_catches_walk(x, vt, defined, reads_all, assigned, fallback_bound);
1116 }
1117 }
1118 if let Some(d) = default {
1119 bind_catches_walk(d, vt, defined, reads_all, assigned, fallback_bound);
1120 }
1121 }
1122 _ => {}
1123 }
1124}
1125
1126fn rewrite_local_refs(s: &mut Stmt, from: u32, to: u32) {
1127 rewrite_exprs(s, &mut |e| {
1128 deep_rewrite(e, &mut |x| {
1129 if let Expr::Local { var, .. } = x {
1130 if *var == from {
1131 *var = to;
1132 }
1133 }
1134 });
1135 });
1136}
1137
1138pub fn cleanup(s: &mut Stmt) {
1140 jdc_core::ir::stmt::flatten(s);
1141 prune_dead_tails(s);
1142 strip_empty(s);
1143 merge_singleton_stmts(s);
1144}
1145
1146fn seq_terminates(s: &Stmt) -> bool {
1157 match s {
1158 Stmt::Return(_) | Stmt::Throw(_) | Stmt::Break(_) | Stmt::Continue(_) => {
1159 true
1160 }
1161 Stmt::Block(v) => v.iter().any(seq_terminates),
1162 Stmt::If {
1163 then_stmt,
1164 else_stmt,
1165 ..
1166 } => {
1167 seq_terminates(then_stmt)
1168 && else_stmt
1169 .as_ref()
1170 .map(|e| seq_terminates(e))
1171 .unwrap_or(false)
1172 }
1173 Stmt::Try {
1174 body,
1175 catches,
1176 finally,
1177 }
1178 | Stmt::TryWithResources {
1179 body,
1180 catches,
1181 finally,
1182 ..
1183 } => {
1184 if let Some(f) = finally {
1185 if seq_terminates(f) {
1186 return true;
1187 }
1188 }
1189 seq_terminates(body) && catches.iter().all(|c| seq_terminates(&c.body))
1190 }
1191 Stmt::Labeled { body, .. } | Stmt::Synchronized { body, .. } => {
1192 seq_terminates(body)
1193 }
1194 Stmt::While { .. } | Stmt::DoWhile { .. } => {
1199 jdc_core::analysis::dead_end_infinite_while(s)
1200 }
1201 _ => false,
1202 }
1203}
1204
1205fn prune_seq(v: &mut Vec<Stmt>) {
1206 if v.iter()
1207 .any(|x| matches!(x, Stmt::Label(_) | Stmt::Goto(_)))
1208 {
1209 return;
1210 }
1211 if let Some(i) = v.iter().position(seq_terminates) {
1212 v.truncate(i + 1);
1213 }
1214}
1215
1216pub fn prune_dead_tails(s: &mut Stmt) {
1217 match s {
1218 Stmt::Block(v) => {
1219 for x in v.iter_mut() {
1220 prune_dead_tails(x);
1221 }
1222 prune_seq(v);
1223 }
1224 Stmt::If {
1225 then_stmt,
1226 else_stmt,
1227 ..
1228 } => {
1229 prune_dead_tails(then_stmt);
1230 if let Some(e) = else_stmt {
1231 prune_dead_tails(e);
1232 }
1233 }
1234 Stmt::While { body, .. }
1235 | Stmt::DoWhile { body, .. }
1236 | Stmt::ForEach { body, .. }
1237 | Stmt::Labeled { body, .. }
1238 | Stmt::Synchronized { body, .. } => prune_dead_tails(body),
1239 Stmt::For { init, body, .. } => {
1240 for x in init.iter_mut() {
1241 prune_dead_tails(x);
1242 }
1243 prune_seq(init);
1244 prune_dead_tails(body);
1245 }
1246 Stmt::Switch {
1247 cases, default, ..
1248 } => {
1249 for c in cases {
1250 for x in c.body.iter_mut() {
1251 prune_dead_tails(x);
1252 }
1253 prune_seq(&mut c.body);
1254 }
1255 if let Some(d) = default {
1256 prune_dead_tails(d);
1257 }
1258 }
1259 Stmt::Try {
1260 body,
1261 catches,
1262 finally,
1263 }
1264 | Stmt::TryWithResources {
1265 body,
1266 catches,
1267 finally,
1268 ..
1269 } => {
1270 prune_dead_tails(body);
1271 for c in catches {
1272 prune_dead_tails(&mut c.body);
1273 }
1274 if let Some(f) = finally {
1275 prune_dead_tails(f);
1276 }
1277 }
1278 _ => {}
1279 }
1280}
1281
1282pub fn strip_phantom_field_writes(body: &mut Stmt, pool: &DexPool) {
1298 fn phantom(pool: &DexPool, cls: &str, name: &str) -> bool {
1299 let Some(pc) = pool.get(cls) else {
1303 return false;
1304 };
1305 fn declares(pc: &crate::PoolClass, cls: &str, name: &str) -> bool {
1313 pc.static_fields
1314 .iter()
1315 .chain(pc.instance_fields.iter())
1316 .any(|f| {
1317 f.name.as_str() == name
1318 || jdc_core::rename::field_display(cls, &f.name, &f.desc)
1319 .is_some_and(|d| d == name)
1320 })
1321 }
1322 if declares(pc, cls, name) {
1323 return false;
1324 }
1325 let mut cur: Option<String> = pc.super_name.clone();
1335 let mut hops = 0u32;
1336 while let Some(s) = cur {
1337 hops += 1;
1338 if hops > 64 {
1339 return false;
1340 }
1341 if let Some(sup) = pool.get(&s) {
1342 if declares(sup, &s, name) {
1343 return false;
1344 }
1345 cur = sup.super_name.clone();
1346 } else {
1347 if !crate::fwdb::exists(&s) {
1348 return false;
1349 }
1350 let mut found = false;
1351 crate::fwdb::for_each_field(&s, |f, _| {
1352 if f == name {
1353 found = true;
1354 }
1355 });
1356 if found {
1357 return false;
1358 }
1359 cur = crate::fwdb::super_of(&s).map(|x| x.to_string());
1361 }
1362 }
1363 true
1364 }
1365 fn droppable_value(pool: &DexPool, e: &Expr) -> bool {
1366 match e {
1367 Expr::Const(_) | Expr::Local { .. } => true,
1368 Expr::Field { cls, name, .. } => phantom(pool, cls, name),
1369 Expr::Cast { e, .. } => droppable_value(pool, e),
1370 _ => false,
1371 }
1372 }
1373 fn is_phantom_write(pool: &DexPool, st: &Stmt) -> bool {
1374 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = st {
1375 if let Expr::Field { cls, name, .. } = &**target {
1376 return phantom(pool, cls, name) && droppable_value(pool, value);
1377 }
1378 }
1379 false
1380 }
1381 walk_mut_deep(body, &mut |st| match st {
1385 Stmt::Block(v) => v.retain(|x| !is_phantom_write(pool, x)),
1386 Stmt::Switch { cases, .. } => {
1387 for c in cases.iter_mut() {
1388 c.body.retain(|x| !is_phantom_write(pool, x));
1389 }
1390 }
1391 Stmt::For { init, .. } => init.retain(|x| !is_phantom_write(pool, x)),
1392 Stmt::TryWithResources { resources, .. } => {
1393 resources.retain(|x| !is_phantom_write(pool, x))
1394 }
1395 _ => {}
1396 });
1397 if is_phantom_write(pool, body) {
1398 *body = Stmt::Block(vec![]);
1399 }
1400}
1401
1402pub fn init_bare_decls(body: &mut Stmt, vt: &VarTable) {
1414 walk_mut_deep(body, &mut |st| {
1415 if let Stmt::LocalDef { var, init, .. } = st {
1416 if init.is_some() {
1417 return;
1418 }
1419 let lit = match vt.var(*var).ty.erased() {
1420 JavaType::Boolean => "false",
1421 JavaType::Int
1422 | JavaType::Byte
1423 | JavaType::Short
1424 | JavaType::Char => "0",
1425 JavaType::Long => "0L",
1426 JavaType::Float => "0.0f",
1427 JavaType::Double => "0.0d",
1428 JavaType::Void => return,
1429 JavaType::Object(_) | JavaType::Array(_) => "null",
1430 };
1431 *init = Some(Expr::Raw(lit.to_string()));
1432 }
1433 });
1434}
1435
1436fn strip_empty(s: &mut Stmt) {
1437 match s {
1438 Stmt::Block(v) => {
1439 v.retain(|x| !x.is_empty_block());
1440 for x in v.iter_mut() {
1441 strip_empty(x);
1442 }
1443 }
1444 Stmt::If {
1445 then_stmt,
1446 else_stmt,
1447 ..
1448 } => {
1449 strip_empty(then_stmt);
1450 if let Some(e) = else_stmt {
1451 strip_empty(e);
1452 if e.is_empty_block() {
1453 *else_stmt = None;
1454 }
1455 }
1456 }
1457 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => strip_empty(body),
1458 Stmt::For { init, body, .. } => {
1459 init.retain(|x| !x.is_empty_block());
1460 strip_empty(body);
1461 }
1462 Stmt::ForEach { body, .. } => strip_empty(body),
1463 Stmt::Switch { cases, default, .. } => {
1464 for c in cases {
1465 c.body.retain(|x| !x.is_empty_block());
1466 }
1467 if let Some(d) = default {
1468 strip_empty(d);
1469 }
1470 }
1471 Stmt::Try {
1472 body,
1473 catches,
1474 finally,
1475 } => {
1476 strip_empty(body);
1477 for c in catches {
1478 strip_empty(&mut c.body);
1479 }
1480 if let Some(f) = finally {
1481 strip_empty(f);
1482 }
1483 }
1484 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => strip_empty(body),
1485 _ => {}
1486 }
1487}
1488
1489fn merge_singleton_stmts(s: &mut Stmt) {
1490 if let Stmt::Block(v) = s {
1491 for x in v.iter_mut() {
1492 merge_singleton_stmts(x);
1493 }
1494 }
1495}
1496
1497pub fn prune_unreachable(s: &mut Stmt) {
1499 if let Stmt::Block(v) = s {
1500 let mut cut: Option<usize> = None;
1501 for (i, x) in v.iter().enumerate() {
1502 if matches!(x, Stmt::Return(_) | Stmt::Throw(_)) {
1503 cut = Some(i + 1);
1504 break;
1505 }
1506 }
1507 if let Some(c) = cut {
1508 if c < v.len() {
1509 v.truncate(c);
1510 }
1511 }
1512 for x in v.iter_mut() {
1513 prune_unreachable(x);
1514 }
1515 } else {
1516 walk_mut(s, &mut prune_unreachable);
1517 }
1518}
1519
1520fn walk_mut<F: FnMut(&mut Stmt)>(s: &mut Stmt, f: &mut F) {
1521 match s {
1522 Stmt::Block(v) => {
1523 for x in v.iter_mut() {
1524 f(x);
1525 }
1526 }
1527 Stmt::If {
1528 then_stmt,
1529 else_stmt,
1530 ..
1531 } => {
1532 f(then_stmt);
1533 if let Some(e) = else_stmt {
1534 f(e);
1535 }
1536 }
1537 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => f(body),
1538 Stmt::For { init, body, .. } => {
1539 for x in init.iter_mut() {
1540 f(x);
1541 }
1542 f(body);
1543 }
1544 Stmt::ForEach { body, .. } => f(body),
1545 Stmt::Switch { cases, default, .. } => {
1546 for c in cases {
1547 for x in c.body.iter_mut() {
1548 f(x);
1549 }
1550 }
1551 if let Some(d) = default {
1552 f(d);
1553 }
1554 }
1555 Stmt::Try {
1556 body,
1557 catches,
1558 finally,
1559 } => {
1560 f(body);
1561 for c in catches {
1562 f(&mut c.body);
1563 }
1564 if let Some(fl) = finally {
1565 f(fl);
1566 }
1567 }
1568 Stmt::TryWithResources {
1569 resources,
1570 body,
1571 catches,
1572 finally,
1573 } => {
1574 for r in resources.iter_mut() {
1575 f(r);
1576 }
1577 f(body);
1578 for c in catches {
1579 f(&mut c.body);
1580 }
1581 if let Some(fl) = finally {
1582 f(fl);
1583 }
1584 }
1585 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => f(body),
1586 _ => {}
1587 }
1588}
1589
1590pub fn count_localdefs(s: &Stmt, out: &mut usize) {
1592 if matches!(s, Stmt::LocalDef { .. }) {
1593 *out += 1;
1594 }
1595 walk_all(s, &mut |x| {
1596 if matches!(x, Stmt::LocalDef { .. }) {
1597 *out += 1;
1598 }
1599 });
1600}
1601
1602pub fn strip_trailing_void_return(s: &mut Stmt) {
1604 if let Stmt::Block(v) = s {
1605 loop {
1606 match v.last_mut() {
1607 Some(Stmt::Return(None)) => {
1608 v.pop();
1609 }
1610 Some(Stmt::Block(_)) => {
1615 let last = v.last_mut().unwrap();
1616 let before = match last {
1617 Stmt::Block(b) => b.len(),
1618 _ => 0,
1619 };
1620 strip_trailing_void_return(last);
1621 let after = match last {
1622 Stmt::Block(b) => b.len(),
1623 _ => 0,
1624 };
1625 if after == before {
1626 break; }
1628 }
1629 _ => break,
1630 }
1631 }
1632 }
1633}
1634
1635pub fn invert_empty_thens(s: &mut Stmt) {
1640 walk_mut_deep(s, &mut |st| {
1641 if let Stmt::If {
1642 cond,
1643 then_stmt,
1644 else_stmt,
1645 } = st
1646 {
1647 let then_empty = matches!(&**then_stmt, Stmt::Block(v) if v.is_empty());
1648 if then_empty {
1649 if let Some(els) = else_stmt.take() {
1650 let c = std::mem::replace(cond, Expr::Const(ConstVal::Int(0)));
1651 *cond = jdc_core::convert::negate(c);
1652 *then_stmt = els;
1653 }
1654 }
1655 }
1656 });
1657}
1658
1659pub fn fold_short_circuits(s: &mut Stmt) {
1670 for _ in 0..8 {
1671 let mut changed = false;
1672 walk_mut_deep(s, &mut |st| {
1673 if try_fold_diamond(st) {
1674 changed = true;
1675 }
1676 });
1677 if !changed {
1678 break;
1679 }
1680 }
1681}
1682
1683fn try_fold_diamond(st: &mut Stmt) -> bool {
1684 enum Shape {
1685 ThenAnd,
1686 ThenAndNot,
1687 ElseOr,
1688 ElseOrNot,
1689 BareAnd,
1690 BareAndNot,
1691 }
1692 let shape = match &*st {
1696 Stmt::If {
1699 then_stmt,
1700 else_stmt: None,
1701 ..
1702 } => match &**then_stmt {
1703 Stmt::If {
1704 then_stmt: t2,
1705 else_stmt: None,
1706 ..
1707 } if !matches!(&**t2, Stmt::Block(v) if v.is_empty()) => Some(Shape::BareAnd),
1708 Stmt::If {
1709 then_stmt: t2,
1710 else_stmt: Some(_),
1711 ..
1712 } if matches!(&**t2, Stmt::Block(v) if v.is_empty()) => Some(Shape::BareAndNot),
1713 _ => None,
1714 },
1715 Stmt::If {
1716 then_stmt,
1717 else_stmt: Some(els),
1718 ..
1719 } => match (&**then_stmt, &**els) {
1720 (Stmt::If { else_stmt: Some(f2), .. }, _) if f2.as_ref() == els.as_ref() => {
1721 Some(Shape::ThenAnd)
1722 }
1723 (Stmt::If { then_stmt: t2, else_stmt: Some(_), .. }, _)
1724 if t2.as_ref() == els.as_ref() =>
1725 {
1726 Some(Shape::ThenAndNot)
1727 }
1728 (_, Stmt::If { then_stmt: t2, else_stmt: Some(_), .. })
1729 if t2.as_ref() == then_stmt.as_ref() =>
1730 {
1731 Some(Shape::ElseOr)
1732 }
1733 (_, Stmt::If { else_stmt: Some(f2), .. })
1734 if f2.as_ref() == then_stmt.as_ref() =>
1735 {
1736 Some(Shape::ElseOrNot)
1737 }
1738 _ => None,
1739 },
1740 _ => None,
1741 };
1742 let Some(shape) = shape else {
1743 return false;
1744 };
1745
1746 let taken = std::mem::replace(st, Stmt::Block(vec![]));
1747 if matches!(shape, Shape::BareAnd | Shape::BareAndNot) {
1749 let Stmt::If {
1750 cond: c1,
1751 then_stmt,
1752 else_stmt: None,
1753 } = taken
1754 else {
1755 unreachable!()
1756 };
1757 let Stmt::If {
1758 cond: c2,
1759 then_stmt: t2,
1760 else_stmt: f2,
1761 } = *then_stmt
1762 else {
1763 unreachable!()
1764 };
1765 let sc = |op: BinOp, l: Expr, r: Expr| Expr::Bin {
1766 op,
1767 l: Box::new(l),
1768 r: Box::new(r),
1769 ty: None,
1770 };
1771 match shape {
1772 Shape::BareAnd => {
1774 *st = Stmt::If {
1775 cond: sc(BinOp::LogAnd, c1, c2),
1776 then_stmt: t2,
1777 else_stmt: None,
1778 };
1779 }
1780 _ => {
1782 let body = f2.unwrap();
1783 *st = Stmt::If {
1784 cond: sc(
1785 BinOp::LogAnd,
1786 c1,
1787 Expr::Un {
1788 op: UnOp::Not,
1789 e: Box::new(c2),
1790 },
1791 ),
1792 then_stmt: body,
1793 else_stmt: None,
1794 };
1795 }
1796 }
1797 return true;
1798 }
1799 let Stmt::If {
1800 cond: c1,
1801 then_stmt,
1802 else_stmt: Some(els),
1803 } = taken
1804 else {
1805 unreachable!("shape probe guaranteed an If with else")
1806 };
1807 let sc = |op: BinOp, l: Expr, r: Expr| Expr::Bin {
1808 op,
1809 l: Box::new(l),
1810 r: Box::new(r),
1811 ty: None,
1812 };
1813 let not = |e: Expr| Expr::Un {
1814 op: UnOp::Not,
1815 e: Box::new(e),
1816 };
1817 match shape {
1818 Shape::BareAnd | Shape::BareAndNot => unreachable!("handled above"),
1819 Shape::ThenAnd | Shape::ThenAndNot => {
1820 let Stmt::If {
1821 cond: c2,
1822 then_stmt: t2,
1823 else_stmt: Some(f2),
1824 } = *then_stmt
1825 else {
1826 unreachable!()
1827 };
1828 let (nc, body, alt) = match shape {
1829 Shape::ThenAnd => (sc(BinOp::LogAnd, c1, c2), t2, f2),
1831 _ => (sc(BinOp::LogAnd, c1, not(c2)), f2, t2),
1833 };
1834 *st = Stmt::If {
1835 cond: nc,
1836 then_stmt: body,
1837 else_stmt: Some(alt),
1838 };
1839 }
1840 Shape::ElseOr | Shape::ElseOrNot => {
1841 let Stmt::If {
1842 cond: c2,
1843 then_stmt: t2,
1844 else_stmt: Some(f2),
1845 } = *els
1846 else {
1847 unreachable!()
1848 };
1849 let body = then_stmt;
1852 let (nc, alt) = match shape {
1853 Shape::ElseOr => (sc(BinOp::LogOr, c1, c2), f2),
1855 _ => (sc(BinOp::LogOr, c1, not(c2)), t2),
1857 };
1858 *st = Stmt::If {
1859 cond: nc,
1860 then_stmt: body,
1861 else_stmt: Some(alt),
1862 };
1863 }
1864 }
1865 true
1866}
1867
1868pub fn strip_clinit_returns(s: &mut Stmt) {
1874 walk_mut_deep(s, &mut |st| {
1875 if let Stmt::Block(v) = st {
1876 v.retain(|x| !matches!(x, Stmt::Return(None)));
1877 }
1878 });
1879}
1880
1881pub fn prepend_comment(s: &mut Stmt, text: String) {
1882 let stmt = Stmt::Comment(text);
1883 match s {
1884 Stmt::Block(v) => v.insert(0, stmt),
1885 other => {
1886 let inner = std::mem::replace(other, Stmt::Block(vec![]));
1887 *other = Stmt::Block(vec![stmt, inner]);
1888 }
1889 }
1890}
1891
1892fn is_obj_expr(e: &Expr, obj_var: &dyn Fn(u32) -> bool) -> bool {
1900 match e {
1901 Expr::Local { var, .. } => obj_var(*var),
1902 Expr::Const(_) => false,
1903 _ => e.type_ref().erased().is_reference(),
1904 }
1905}
1906
1907fn null_side_rewrite(x: &mut Expr, obj_var: &dyn Fn(u32) -> bool) {
1912 if let Expr::Bin { op, l, r, .. } = x {
1913 if !matches!(op, BinOp::Eq | BinOp::Ne) {
1914 return;
1915 }
1916 let l_zero = matches!(&**l, Expr::Const(ConstVal::Int(0)));
1917 let r_zero = matches!(&**r, Expr::Const(ConstVal::Int(0)));
1918 if l_zero && !r_zero && is_obj_expr(r, obj_var) {
1919 **l = Expr::Const(ConstVal::Null);
1920 } else if r_zero && !l_zero && is_obj_expr(l, obj_var) {
1921 **r = Expr::Const(ConstVal::Null);
1922 }
1923 }
1924}
1925
1926pub fn fused_expr_rewrites(s: &mut Stmt, vt: &VarTable) {
1927 let mut obj_vars: Vec<bool> = Vec::with_capacity(vt.vars.len());
1929 for v in &vt.vars {
1930 obj_vars.push(v.ty.erased().is_reference());
1931 }
1932 rewrite_exprs(s, &mut |e| {
1933 deep_rewrite(e, &mut |x| {
1934 if let Expr::Invokedynamic { name, args, .. } = x {
1936 if name.starts_with('\0') && args.len() == 2 {
1937 let (cls, ty) = match name.as_str() {
1938 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
1939 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
1940 _ => ("java/lang/Double", JavaType::Double),
1941 };
1942 let desc = MethodDescriptor {
1943 args: vec![ty.clone(), ty],
1944 ret: JavaType::Int,
1945 };
1946 *x = Expr::Method {
1947 owner: None,
1948 cls: cls.into(),
1949 name: "compare".into(),
1950 desc: std::sync::Arc::new(desc),
1951 args: args.clone(),
1952 is_static: true,
1953 is_interface: false,
1954 is_special: false,
1955 is_super: false,
1956 is_dynamic: false,
1957 type_args: vec![],
1958 };
1959 return;
1960 }
1961 }
1962 null_side_rewrite(x, &|v| obj_vars.get(v as usize).copied().unwrap_or(false));
1965 if let Expr::Bin { op, l, r, .. } = x {
1967 if matches!(
1968 op,
1969 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
1970 ) {
1971 let l_const = matches!(&**l, Expr::Const(_));
1972 let r_var = matches!(&**r, Expr::Local { .. });
1973 if l_const && r_var {
1974 std::mem::swap(l, r);
1975 }
1976 }
1977 }
1978 });
1979 });
1980 walk_mut_deep(s, &mut |st| {
1988 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = st {
1989 let tgt_is_obj = match &**target {
1990 Expr::Local { var, .. } => {
1991 obj_vars.get(*var as usize).copied().unwrap_or(false)
1992 }
1993 other => other.type_ref().erased().is_reference(),
1994 };
1995 if tgt_is_obj {
1996 if let Expr::Const(ConstVal::Int(0)) = &**value {
1997 **value = Expr::Const(ConstVal::Null);
1998 }
1999 }
2000 } else if let Stmt::LocalDef { var, init, .. } = st {
2001 if obj_vars.get(*var as usize).copied().unwrap_or(false) {
2002 if let Some(iv) = init {
2003 if let Expr::Const(ConstVal::Int(0)) = iv {
2004 *iv = Expr::Const(ConstVal::Null);
2005 }
2006 }
2007 }
2008 }
2009 });
2010}
2011
2012#[allow(dead_code)]
2015pub fn desugar_cmp_residuals(s: &mut Stmt) {
2016 rewrite_exprs(s, &mut |e| {
2017 deep_rewrite(e, &mut |x| {
2018 if let Expr::Invokedynamic { name, args, .. } = x {
2019 if name.starts_with('\0') && args.len() == 2 {
2020 let (cls, ty) = match name.as_str() {
2021 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
2022 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
2023 _ => ("java/lang/Double", JavaType::Double),
2024 };
2025 let desc = jdc_core::types::MethodDescriptor {
2026 args: vec![ty.clone(), ty],
2027 ret: JavaType::Int,
2028 };
2029 *x = Expr::Method {
2030 owner: None,
2031 cls: cls.into(),
2032 name: "compare".into(),
2033 desc: std::sync::Arc::new(desc),
2034 args: args.clone(),
2035 is_static: true,
2036 is_interface: false,
2037 is_special: false,
2038 is_super: false,
2039 is_dynamic: false,
2040 type_args: vec![],
2041 };
2042 }
2043 }
2044 });
2045 });
2046}
2047
2048pub fn ternary_fold(s: &mut Stmt) {
2050 let mut changed = true;
2051 let mut guard = 0;
2052 while changed && guard < 8 {
2053 changed = false;
2054 guard += 1;
2055 ternary_fold_walk(s, &mut changed);
2056 strip_empty(s);
2057 }
2058}
2059
2060fn ternary_fold_walk(s: &mut Stmt, changed: &mut bool) {
2061 if let Stmt::Block(v) = s {
2062 for x in v.iter_mut() {
2063 ternary_fold_walk(x, changed);
2064 }
2065 return;
2066 }
2067 let Stmt::If {
2068 cond,
2069 then_stmt,
2070 else_stmt,
2071 } = s
2072 else {
2073 return;
2074 };
2075 ternary_fold_walk(then_stmt, changed);
2076 if let Some(e) = else_stmt {
2077 ternary_fold_walk(e, changed);
2078 }
2079 let Some(else_b) = else_stmt else { return };
2080 let target_of = |st: &Stmt| -> Option<u32> {
2081 let Stmt::ExprStmt(Expr::Assign { target, .. }) = st else {
2082 return None;
2083 };
2084 if let Expr::Local { var, .. } = &**target {
2085 Some(*var)
2086 } else {
2087 None
2088 }
2089 };
2090 let (Stmt::Block(tv), Stmt::Block(ev)) = (then_stmt.as_mut(), else_b.as_mut()) else {
2091 return;
2092 };
2093 if tv.len() != 1 || ev.len() != 1 {
2094 return;
2095 }
2096 let (Some(vt_), Some(ve)) = (target_of(&tv[0]), target_of(&ev[0])) else {
2097 return;
2098 };
2099 if vt_ != ve {
2100 return;
2101 }
2102 let mut touched = HashSet::default();
2106 let (Stmt::ExprStmt(a_then), Stmt::ExprStmt(a_else)) = (&tv[0], &ev[0]) else {
2107 return;
2108 };
2109 if let Expr::Assign { value, .. } = a_then {
2110 collect_vars(value, &mut touched);
2111 }
2112 if let Expr::Assign { value, .. } = a_else {
2113 collect_vars(value, &mut touched);
2114 }
2115 if touched.contains(&vt_) {
2116 return;
2117 }
2118 let value_then = match a_then {
2119 Expr::Assign { value, .. } => value.clone(),
2120 _ => return,
2121 };
2122 let value_else = match a_else {
2123 Expr::Assign { value, .. } => value.clone(),
2124 _ => return,
2125 };
2126 let cond_e = cond.clone();
2127 let folded = Stmt::ExprStmt(Expr::Assign {
2128 target: Box::new(Expr::Local {
2129 var: vt_,
2130 ty: vt_join_ty(&value_then, &value_else),
2131 }),
2132 op: AssignOp::Plain,
2133 value: Box::new(Expr::Cond {
2134 c: Box::new(cond_e),
2135 t: value_then,
2136 f: value_else,
2137 }),
2138 });
2139 *s = Stmt::Block(vec![folded]);
2140 *changed = true;
2141}
2142
2143fn vt_join_ty(a: &Expr, b: &Expr) -> TypeRef {
2144 let ta = a.type_ref();
2145 let tb = b.type_ref();
2146 if ta.erased() == tb.erased() {
2147 return ta;
2148 }
2149 TypeRef::J(JavaType::Object("java/lang/Object".into()))
2150}
2151
2152pub fn fold_string_builders(s: &mut Stmt, vt: &VarTable) {
2159 let n_vars = vt.vars.len().max(1);
2161 let mut counts = vec![0usize; n_vars];
2162 count_assignments(s, &mut counts);
2163 let mut values: Vec<Option<Expr>> = vec![None; n_vars];
2164 record_assignments(s, &counts, &mut values);
2165
2166 let mut appended_stmts: HashSet<u32> = HashSet::default();
2168 walk_all(s, &mut |st| {
2169 if let Stmt::ExprStmt(Expr::Method {
2170 cls,
2171 name,
2172 owner,
2173 args,
2174 ..
2175 }) = st
2176 {
2177 if name.as_ref() == "append" && is_string_builder(cls) && args.len() == 1 {
2178 if let Some(Expr::Local { var, .. }) = owner.as_deref() {
2179 appended_stmts.insert(*var);
2180 }
2181 }
2182 }
2183 });
2184
2185 rewrite_exprs(s, &mut |e| {
2186 fold_concat_in_expr(e, &values, &appended_stmts);
2187 });
2188
2189 for _ in 0..8 {
2193 let mut reads: HashSet<u32> = HashSet::default();
2194 stmt_collect_vars(s, &mut reads, false);
2195 if drop_unused_assigns(s, &reads) == 0 {
2196 break;
2197 }
2198 }
2199}
2200
2201pub fn count_stmts_deep(s: &Stmt, out: &mut usize) {
2203 *out += 1;
2204 walk_all(s, &mut |_| {
2205 *out += 1;
2206 });
2207}
2208
2209struct VarAnalysis {
2211 assigns: Vec<usize>,
2212 reads: Vec<usize>,
2213 values: Vec<Option<Expr>>,
2215}
2216
2217fn analyze_vars(s: &Stmt, a: &mut VarAnalysis) {
2218 walk_all(s, &mut |st| {
2219 let exprs: Vec<&Expr> = match st {
2220 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
2221 if let Expr::Local { var, .. } = &**target {
2222 grow_to(&mut a.assigns, *var);
2223 let idx = *var as usize;
2224 a.assigns[idx] += 1;
2225 if a.assigns[idx] == 1 {
2226 if idx >= a.values.len() {
2227 a.values.resize(idx + 1, None);
2228 }
2229 a.values[idx] = Some((**value).clone());
2230 } else if idx < a.values.len() {
2231 a.values[idx] = None;
2232 }
2233 }
2234 let mut v: Vec<&Expr> = vec![value];
2235 if !matches!(&**target, Expr::Local { .. }) {
2236 v.push(target);
2237 }
2238 v
2239 }
2240 Stmt::ExprStmt(e @ (Expr::PreIncDec { .. } | Expr::PostIncDec { .. })) => {
2241 let inner = match e {
2246 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => e,
2247 _ => unreachable!(),
2248 };
2249 if let Expr::Local { var, .. } = &**inner {
2250 grow_to(&mut a.assigns, *var);
2251 let idx = *var as usize;
2252 a.assigns[idx] += 1;
2253 if idx < a.values.len() {
2254 a.values[idx] = None;
2255 }
2256 return;
2257 }
2258 vec![e]
2259 }
2260 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
2261 vec![e]
2262 }
2263 Stmt::Return(Some(e)) => vec![e],
2264 Stmt::LocalDef {
2265 var, init: Some(e), ..
2266 } => {
2267 grow_to(&mut a.assigns, *var);
2268 let idx = *var as usize;
2269 a.assigns[idx] += 1;
2270 if a.assigns[idx] == 1 {
2271 if idx >= a.values.len() {
2272 a.values.resize(idx + 1, None);
2273 }
2274 a.values[idx] = Some(e.clone());
2275 } else if idx < a.values.len() {
2276 a.values[idx] = None;
2277 }
2278 vec![e]
2279 }
2280 Stmt::If { cond, .. } => vec![cond],
2281 Stmt::While { cond, .. } => vec![cond],
2282 Stmt::DoWhile { cond, .. } => vec![cond],
2283 _ => Vec::new(),
2284 };
2285 for e in exprs {
2286 visit_exprs(e, &mut |x| {
2287 if let Expr::Local { var, .. } = x {
2288 grow_to(&mut a.reads, *var);
2289 a.reads[*var as usize] += 1;
2290 }
2291 });
2292 }
2293 });
2294 let n = a.assigns.len().max(a.reads.len()).max(a.values.len());
2296 a.assigns.resize(n, 0);
2297 a.reads.resize(n, 0);
2298 a.values.resize(n, None);
2299}
2300
2301fn grow_to(v: &mut Vec<usize>, var: u32) {
2302 if var as usize >= v.len() {
2303 v.resize(var as usize + 1, 0);
2304 }
2305}
2306
2307fn count_assignments(s: &Stmt, counts: &mut Vec<usize>) {
2308 walk_all(s, &mut |st| match st {
2309 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
2310 if let Expr::Local { var, .. } = &**target {
2311 grow_to(counts, *var);
2312 counts[*var as usize] += 1;
2313 }
2314 }
2315 Stmt::LocalDef {
2316 var, init: Some(_), ..
2317 } => {
2318 grow_to(counts, *var);
2319 counts[*var as usize] += 1;
2320 }
2321 _ => {}
2322 });
2323}
2324
2325fn record_assignments(s: &Stmt, counts: &[usize], out: &mut Vec<Option<Expr>>) {
2326 walk_all(s, &mut |st| match st {
2327 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
2328 if let Expr::Local { var, .. } = &**target {
2329 if counts.get(*var as usize).copied().unwrap_or(0) == 1 {
2330 if *var as usize >= out.len() {
2331 out.resize(*var as usize + 1, None);
2332 }
2333 out[*var as usize] = Some((**value).clone());
2334 }
2335 }
2336 }
2337 Stmt::LocalDef {
2338 var, init: Some(e), ..
2339 } if counts.get(*var as usize).copied().unwrap_or(0) == 1 => {
2340 if *var as usize >= out.len() {
2341 out.resize(*var as usize + 1, None);
2342 }
2343 out[*var as usize] = Some(e.clone());
2344 }
2345 _ => {}
2346 });
2347}
2348
2349fn fold_concat_in_expr(e: &mut Expr, values: &[Option<Expr>], appended: &HashSet<u32>) {
2350 deep_rewrite(e, &mut |x| {
2351 if let Expr::Method {
2352 cls,
2353 name,
2354 args,
2355 owner,
2356 ..
2357 } = x
2358 {
2359 if name.as_ref() == "toString" && args.is_empty() && is_string_builder(cls) {
2360 if let Some(o) = owner {
2361 let mut has_stmt_appends = false;
2364 visit_exprs(o, &mut |y| {
2365 if let Expr::Local { var, .. } = y {
2366 if appended.contains(var) {
2367 has_stmt_appends = true;
2368 }
2369 }
2370 });
2371 if has_stmt_appends {
2372 return;
2373 }
2374 let resolved = resolve_local(o, values);
2375 if let Some(parts) = collect_sb_parts(&resolved, values, 0) {
2376 if !parts.is_empty() {
2385 *x = Expr::StringConcat(parts);
2386 }
2387 }
2388 }
2389 }
2390 }
2391 });
2392}
2393
2394fn is_string_builder(cls: &str) -> bool {
2395 cls == "java/lang/StringBuilder" || cls == "java/lang/StringBuffer"
2396}
2397
2398fn resolve_local(e: &Expr, values: &[Option<Expr>]) -> Expr {
2399 match e {
2400 Expr::Local { var, ty } => values
2401 .get(*var as usize)
2402 .and_then(|o| o.clone())
2403 .unwrap_or_else(|| Expr::Local {
2404 var: *var,
2405 ty: ty.clone(),
2406 }),
2407 other => other.clone(),
2408 }
2409}
2410
2411fn collect_sb_parts(e: &Expr, values: &[Option<Expr>], depth: u32) -> Option<Vec<ConcatPart>> {
2413 if depth > 16 {
2414 return None;
2415 }
2416 match e {
2417 Expr::Method {
2418 cls,
2419 name,
2420 args,
2421 owner,
2422 ..
2423 } if name.as_ref() == "append" && is_string_builder(cls) && args.len() == 1 => {
2424 let mut parts =
2425 collect_sb_parts(&resolve_local(owner.as_deref()?, values), values, depth + 1)?;
2426 parts.push(ConcatPart::Str(args[0].clone()));
2427 Some(parts)
2428 }
2429 Expr::New {
2430 cls,
2431 args,
2432 raw: false,
2433 ..
2434 } if is_string_builder(cls) => {
2435 let mut parts = Vec::new();
2436 for a in args {
2437 if let Expr::Const(ConstVal::Str(sv)) = a {
2438 parts.push(ConcatPart::Const(sv.to_string()));
2439 } else {
2440 parts.push(ConcatPart::Str(a.clone()));
2441 }
2442 }
2443 Some(parts)
2444 }
2445 _ => None,
2446 }
2447}
2448
2449fn drop_unused_assigns(s: &mut Stmt, reads: &HashSet<u32>) -> usize {
2450 let mut dropped = 0usize;
2451 walk_mut_deep(s, &mut |st| {
2452 if let Stmt::Block(v) = st {
2453 let before = v.len();
2454 v.retain(|x| match x {
2455 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
2458 Expr::Local { var, .. } => reads.contains(var) || !is_sbish(value),
2459 _ => true,
2460 },
2461 Stmt::LocalDef { var, init, .. } => {
2462 reads.contains(var) || !init.as_ref().map(is_sbish).unwrap_or(false)
2463 }
2464 _ => true,
2465 });
2466 dropped += before - v.len();
2467 }
2468 });
2469 dropped
2470}
2471
2472fn is_sbish(e: &Expr) -> bool {
2473 match e {
2474 Expr::New {
2475 cls, raw: false, ..
2476 } => is_string_builder(cls),
2477 Expr::Method { name, cls, .. } => name.as_ref() == "append" && is_string_builder(cls),
2478 _ => false,
2479 }
2480}
2481
2482fn walk_mut_deep<F: FnMut(&mut Stmt)>(s: &mut Stmt, f: &mut F) {
2483 f(s);
2484 walk_mut(s, &mut |x| walk_mut_deep(x, f));
2485}
2486
2487pub fn drop_empty_finallies(body: &mut Stmt) {
2503 fn is_empty_block(s: &Stmt) -> bool {
2504 match s {
2505 Stmt::Block(v) => v.iter().all(is_empty_block),
2506 _ => false,
2507 }
2508 }
2509 walk_mut_deep(body, &mut |st| {
2510 let Stmt::Try {
2511 body: b,
2512 catches,
2513 finally,
2514 } = st
2515 else {
2516 return;
2517 };
2518 if !catches.is_empty() {
2519 return;
2520 }
2521 match finally {
2522 None => {}
2526 Some(f) if is_empty_block(f) => {}
2527 Some(_) => return,
2528 }
2529 let inner = std::mem::replace(&mut **b, Stmt::Block(vec![]));
2530 *st = inner;
2531 });
2532}
2533
2534pub fn drop_pure_value_stmts(body: &mut Stmt) {
2539 fn is_pure(s: &Stmt) -> bool {
2542 let Stmt::ExprStmt(e) = s else {
2543 return false;
2544 };
2545 !has_side_effects(e)
2546 }
2547 walk_mut_deep(body, &mut |st| {
2548 if is_pure(st) {
2551 *st = Stmt::Block(vec![]);
2552 return;
2553 }
2554 match st {
2555 Stmt::Switch { cases, .. } => {
2556 for c in cases.iter_mut() {
2557 c.body.retain(|x| !is_pure(x));
2558 }
2559 }
2560 Stmt::For { init, .. } => init.retain(|x| !is_pure(x)),
2561 _ => {}
2562 }
2563 });
2564}
2565
2566pub fn drop_dead_raw_news(body: &mut Stmt) {
2567 fn is_orphan(s: &Stmt) -> bool {
2568 matches!(s, Stmt::ExprStmt(Expr::New { raw: true, .. }))
2569 }
2570 walk_mut_deep(body, &mut |st| match st {
2571 Stmt::Block(v) => v.retain(|x| !is_orphan(x)),
2572 Stmt::Switch { cases, .. } => {
2573 for c in cases.iter_mut() {
2574 c.body.retain(|x| !is_orphan(x));
2575 }
2576 }
2577 Stmt::For { init, .. } => init.retain(|x| !is_orphan(x)),
2578 Stmt::TryWithResources { resources, .. } => resources.retain(|x| !is_orphan(x)),
2579 _ => {}
2580 });
2581}
2582
2583pub fn fold_synchronized(s: &mut Stmt) {
2587 if std::env::var("DDC_DBG_SYNC").is_ok() {
2588 walk_all(s, &mut |st| {
2589 if let Stmt::Try { catches, .. } = st {
2590 for c in catches.iter() {
2591 eprintln!("[sync] catch var={} body={:.600}", c.var, format!("{:?}", c.body));
2592 }
2593 }
2594 });
2595 }
2596 fold_sync_walk(s);
2597}
2598
2599
2600fn fold_sync_walk(s: &mut Stmt) {
2601 walk_mut_deep(s, &mut |st| {
2602 let Stmt::Block(v) = st else { return };
2603 let mut i = 0;
2604 while i < v.len() {
2605 #[allow(unused_assignments)]
2609 let mut advance = true;
2610 if i + 1 < v.len() {
2611 if let Some((repl, consumed)) = try_sync_at(v, i) {
2612 advance = !matches!(repl.first(), Some(Stmt::Synchronized { .. }));
2613 let _ = v.drain(i..i + consumed);
2614 let n = repl.len();
2615 v.splice(i..i, repl);
2616 if advance {
2617 i += n;
2618 }
2619 continue;
2620 }
2621 }
2622 if let Some((repl, consumed)) = try_sync_in_try(v, i) {
2623 let _ = v.drain(i..i + consumed);
2624 let n = repl.len();
2625 v.splice(i..i, repl);
2626 i += n;
2627 continue;
2628 }
2629 if let Some((repl, consumed)) = try_sync_deep(v, i) {
2630 let _ = v.drain(i..i + consumed);
2631 let n = repl.len();
2632 v.splice(i..i, repl);
2633 i += n;
2634 continue;
2635 }
2636 if let Some((repl, consumed)) = try_sync_absorb_after(v, i) {
2637 let _ = v.drain(i..i + consumed);
2638 let n = repl.len();
2639 v.splice(i..i, repl);
2640 i += n;
2641 continue;
2642 }
2643 i += 1;
2644 }
2645 });
2646}
2647
2648fn is_pure_read_expr(e: &Expr) -> bool {
2652 let mut pure = true;
2653 visit_exprs(e, &mut |x| match x {
2654 Expr::Const(_)
2655 | Expr::Local { .. }
2656 | Expr::This
2657 | Expr::Field { .. } => {}
2658 _ => pure = false,
2659 });
2660 pure
2661}
2662
2663fn resolve_lock(e: &Expr, defs: &jdc_core::FxHashMap<u32, Expr>, depth: u32) -> Expr {
2668 if depth >= 4 {
2669 return e.clone();
2670 }
2671 if let Expr::Local { var, .. } = e {
2672 if let Some(d) = defs.get(var) {
2673 return resolve_lock(d, defs, depth + 1);
2674 }
2675 }
2676 e.clone()
2677}
2678
2679fn lock_same(a: &Expr, b: &Expr) -> bool {
2680 match (a, b) {
2681 (Expr::This, Expr::This) => true,
2682 (Expr::Local { var: x, .. }, Expr::Local { var: y, .. }) => x == y,
2683 (
2684 Expr::Field {
2685 owner: oa,
2686 cls: ca,
2687 name: na,
2688 ..
2689 },
2690 Expr::Field {
2691 owner: ob,
2692 cls: cb,
2693 name: nb,
2694 ..
2695 },
2696 ) => {
2697 ca == cb
2698 && na == nb
2699 && match (oa, ob) {
2700 (None, None) => true,
2701 (Some(ra), Some(rb)) => lock_same(ra, rb),
2702 _ => false,
2703 }
2704 }
2705 (Expr::Const(x), Expr::Const(y)) => x == y,
2706 _ => false,
2707 }
2708}
2709
2710fn lock_equiv(a: &Expr, b: &Expr, defs: &jdc_core::FxHashMap<u32, Expr>) -> bool {
2711 lock_same(&resolve_lock(a, defs, 0), &resolve_lock(b, defs, 0))
2712}
2713
2714fn flatten_leaves<'a>(s: &'a Stmt, out: &mut Vec<&'a Stmt>) {
2715 match s {
2716 Stmt::Block(v) => {
2717 for x in v {
2718 flatten_leaves(x, out);
2719 }
2720 }
2721 _ => out.push(s),
2722 }
2723}
2724
2725fn strip_lock_exits(s: &mut Stmt, lock: &Expr, defs: &jdc_core::FxHashMap<u32, Expr>) {
2729 walk_mut_deep(s, &mut |st| {
2730 let Stmt::Block(v) = st else { return };
2731 v.retain(|x| !matches!(x, Stmt::MonitorExit(e) if lock_equiv(e, lock, defs)));
2732 });
2733}
2734
2735fn try_sync_at(v: &[Stmt], i: usize) -> Option<(Vec<Stmt>, usize)> {
2736 let mut defs: jdc_core::FxHashMap<u32, Expr> = jdc_core::FxHashMap::default();
2738 for s in &v[..i] {
2739 if let Stmt::LocalDef { var, init: Some(e), .. } = s {
2740 defs.insert(*var, e.clone());
2741 }
2742 }
2743 let (pre, enter_e, mut j, mut absorbed): (Vec<Stmt>, &Expr, usize, Vec<Stmt>) = match &v[i] {
2747 Stmt::MonitorEnter(e) => (Vec::new(), e, i + 1, Vec::new()),
2748 Stmt::Block(b) => {
2749 let split = b
2750 .iter()
2751 .position(|x| matches!(x, Stmt::MonitorEnter(_)))?;
2752 let e = match &b[split] {
2753 Stmt::MonitorEnter(e) => e,
2754 _ => unreachable!(),
2755 };
2756 let mut absorbed: Vec<Stmt> = Vec::new();
2757 let mut k = split + 1;
2758 while let Some(Stmt::LocalDef { var, init: Some(ex), .. }) = b.get(k) {
2759 if !is_pure_read_expr(ex) {
2760 break;
2761 }
2762 defs.insert(*var, ex.clone());
2763 absorbed.push(b[k].clone());
2764 k += 1;
2765 }
2766 if k != b.len() {
2769 return None;
2770 }
2771 let pre: Vec<Stmt> = b[..split].to_vec();
2772 for s in &pre {
2773 if let Stmt::LocalDef { var, init: Some(e2), .. } = s {
2774 defs.insert(*var, e2.clone());
2775 }
2776 }
2777 (pre, e, i + 1, absorbed)
2778 }
2779 _ => return None,
2780 };
2781 let mut absorbed_vars: Vec<u32> = Vec::new();
2785 while let Some(Stmt::LocalDef { var, init: Some(e), .. }) = v.get(j) {
2786 if !is_pure_read_expr(e) {
2787 break;
2788 }
2789 defs.insert(*var, e.clone());
2790 absorbed_vars.push(*var);
2791 absorbed.push(v[j].clone());
2792 j += 1;
2793 }
2794 let Stmt::Try {
2795 body,
2796 catches,
2797 finally,
2798 } = v.get(j)?
2799 else {
2800 return None;
2801 };
2802 if finally.is_some() || catches.len() != 1 {
2803 return None;
2804 }
2805 let c = &catches[0];
2806 if !c.exc.is_empty() {
2807 return None;
2808 }
2809 let mut flat: Vec<&Stmt> = Vec::new();
2812 flatten_leaves(c.body.as_ref(), &mut flat);
2813 if flat.len() < 2 {
2814 return None;
2815 }
2816 let Stmt::MonitorExit(exit_e) = flat[0] else {
2817 return None;
2818 };
2819 if !lock_equiv(enter_e, exit_e, &defs) {
2820 return None;
2821 }
2822 if !matches!(flat[flat.len() - 1], Stmt::Throw(_)) {
2823 return None;
2824 }
2825 for mid in &flat[1..flat.len() - 1] {
2826 if !matches!(mid, Stmt::MonitorEnter(_) | Stmt::MonitorExit(_)) {
2827 return None;
2828 }
2829 }
2830 if !absorbed_vars.is_empty() {
2833 let outside = count_locals_stmts(&v[j + 1..]);
2834 if absorbed_vars
2835 .iter()
2836 .any(|av| outside.get(av).copied().unwrap_or(0) > 0)
2837 {
2838 return None;
2839 }
2840 }
2841 let mut body_stmt = (**body).clone();
2842 strip_lock_exits(&mut body_stmt, enter_e, &defs);
2843 absorbed.push(body_stmt);
2844 let mut consumed = j - i + 1;
2845 if let Some(Stmt::MonitorExit(e)) = v.get(j + 1) {
2848 if lock_equiv(e, enter_e, &defs) {
2849 consumed += 1;
2850 }
2851 }
2852 let mut repl = pre;
2853 repl.push(Stmt::Synchronized {
2854 lock: enter_e.clone(),
2855 body: Box::new(Stmt::Block(absorbed)),
2856 });
2857 Some((repl, consumed))
2858}
2859
2860fn try_sync_in_try(v: &[Stmt], i: usize) -> Option<(Vec<Stmt>, usize)> {
2866 let Stmt::Try {
2867 body,
2868 catches,
2869 finally,
2870 } = &v[i]
2871 else {
2872 return None;
2873 };
2874 if finally.is_some() || catches.len() != 1 {
2875 return None;
2876 }
2877 let c = &catches[0];
2878 if !c.exc.is_empty() {
2879 return None;
2880 }
2881 let mut flat: Vec<&Stmt> = Vec::new();
2882 flatten_leaves(c.body.as_ref(), &mut flat);
2883 if flat.len() < 2 {
2884 return None;
2885 }
2886 let Stmt::MonitorExit(_) = flat[0] else {
2887 return None;
2888 };
2889 if !matches!(flat[flat.len() - 1], Stmt::Throw(_)) {
2890 return None;
2891 }
2892 for mid in &flat[1..flat.len() - 1] {
2893 if !matches!(mid, Stmt::MonitorEnter(_) | Stmt::MonitorExit(_)) {
2894 return None;
2895 }
2896 }
2897 let Stmt::Block(bv) = body.as_ref() else {
2901 return None;
2902 };
2903 let mut defs: jdc_core::FxHashMap<u32, Expr> = jdc_core::FxHashMap::default();
2904 for s in &v[..i] {
2905 if let Stmt::LocalDef { var, init: Some(e), .. } = s {
2906 defs.insert(*var, e.clone());
2907 }
2908 }
2909 let mut pre: Vec<Stmt> = Vec::new();
2910 let mut enter_e: Option<Expr> = None;
2911 let mut absorbed: Vec<Stmt> = Vec::new();
2912 let mut absorbed_vars: Vec<u32> = Vec::new();
2913 let mut rest_start: Option<usize> = None;
2914 for (p, el) in bv.iter().enumerate() {
2915 match el {
2916 Stmt::MonitorEnter(e) => {
2917 enter_e = Some(e.clone());
2918 rest_start = Some(p + 1);
2919 break;
2920 }
2921 Stmt::Block(inner) => {
2922 let split = inner
2923 .iter()
2924 .position(|x| matches!(x, Stmt::MonitorEnter(_)));
2925 if let Some(sp) = split {
2926 let mut k = sp + 1;
2927 let mut ok = true;
2928 let mut tail: Vec<Stmt> = Vec::new();
2929 while let Some(Stmt::LocalDef { var: _, init: Some(ex), .. }) = inner.get(k) {
2930 if !is_pure_read_expr(ex) {
2931 ok = false;
2932 break;
2933 }
2934 tail.push(inner[k].clone());
2935 k += 1;
2936 }
2937 if ok && k == inner.len() {
2938 for s in &inner[..sp] {
2939 if let Stmt::LocalDef { var, init: Some(ex), .. } = s {
2940 defs.insert(*var, ex.clone());
2941 }
2942 }
2943 pre.extend(inner[..sp].iter().cloned());
2944 enter_e = Some(match &inner[sp] {
2945 Stmt::MonitorEnter(e) => e.clone(),
2946 _ => unreachable!(),
2947 });
2948 absorbed = tail;
2949 rest_start = Some(p + 1);
2950 break;
2951 }
2952 }
2953 for s in inner.iter() {
2955 if let Stmt::LocalDef { var, init: Some(ex), .. } = s {
2956 defs.insert(*var, ex.clone());
2957 }
2958 }
2959 pre.push(el.clone());
2960 }
2961 other => {
2962 if let Stmt::LocalDef { var, init: Some(ex), .. } = other {
2963 defs.insert(*var, ex.clone());
2964 }
2965 pre.push(other.clone());
2966 }
2967 }
2968 }
2969 let enter_e = enter_e?;
2970 let rs = rest_start?;
2971 let mut q = rs;
2973 while let Some(Stmt::LocalDef { var, init: Some(ex), .. }) = bv.get(q) {
2974 if !is_pure_read_expr(ex) {
2975 break;
2976 }
2977 defs.insert(*var, ex.clone());
2978 absorbed_vars.push(*var);
2979 absorbed.push(bv[q].clone());
2980 q += 1;
2981 }
2982 let rest: Vec<Stmt> = bv[q..].to_vec();
2983 if rest.is_empty() {
2984 return None;
2985 }
2986 if pre
2989 .iter()
2990 .any(|s| !matches!(s, Stmt::LocalDef { init: Some(e), .. } if is_pure_read_expr(e)))
2991 {
2992 return None;
2993 }
2994 let Stmt::MonitorExit(exit_e) = flat[0] else {
2996 return None;
2997 };
2998 if !lock_equiv(&enter_e, exit_e, &defs) {
2999 return None;
3000 }
3001 let outside_vars: Vec<u32> = pre
3003 .iter()
3004 .chain(absorbed.iter())
3005 .filter_map(|s| match s {
3006 Stmt::LocalDef { var, .. } => Some(*var),
3007 _ => None,
3008 })
3009 .collect();
3010 if !outside_vars.is_empty() {
3011 let outside = count_locals_stmts(&v[i + 1..]);
3012 if outside_vars
3013 .iter()
3014 .any(|av| outside.get(av).copied().unwrap_or(0) > 0)
3015 {
3016 return None;
3017 }
3018 }
3019 let mut body_stmts = pre;
3020 body_stmts.extend(absorbed);
3021 body_stmts.extend(rest);
3022 let mut sync_body = Stmt::Block(body_stmts);
3023 strip_lock_exits(&mut sync_body, &enter_e, &defs);
3024 let mut consumed = 1;
3025 if let Some(Stmt::MonitorExit(e)) = v.get(i + 1) {
3026 if lock_equiv(e, &enter_e, &defs) {
3027 consumed += 1;
3028 }
3029 }
3030 Some((
3031 vec![Stmt::Synchronized {
3032 lock: enter_e,
3033 body: Box::new(sync_body),
3034 }],
3035 consumed,
3036 ))
3037}
3038
3039fn try_sync_absorb_after(v: &[Stmt], i: usize) -> Option<(Vec<Stmt>, usize)> {
3049 let Stmt::Synchronized { lock, body } = &v[i] else {
3050 return None;
3051 };
3052 let Stmt::Try {
3053 body: tbody,
3054 catches,
3055 finally,
3056 } = v.get(i + 1)?
3057 else {
3058 return None;
3059 };
3060 if finally.is_some() {
3061 return None;
3062 }
3063 let defs = defs_probe(v, i, body);
3069 let mut absorbed_mid: Vec<Stmt> = Vec::new();
3070 let mut rest_after: Option<Stmt> = None;
3071 let mut found = false;
3072 let mut scan = i + 2;
3073 'scan: while scan < v.len() && scan < i + 14 {
3074 match &v[scan] {
3075 Stmt::MonitorExit(e) => {
3076 if !lock_equiv(e, lock, &defs) {
3077 return None;
3078 }
3079 found = true;
3080 scan += 1;
3081 break;
3082 }
3083 Stmt::Block(bv) => {
3084 for (k, s) in bv.iter().enumerate() {
3085 match s {
3086 Stmt::MonitorExit(e) => {
3087 if !lock_equiv(e, lock, &defs) {
3088 return None;
3089 }
3090 absorbed_mid.push(Stmt::Block(bv[..k].to_vec()));
3091 let rest = &bv[k + 1..];
3092 rest_after = (!rest.is_empty())
3093 .then(|| Stmt::Block(rest.to_vec()));
3094 found = true;
3095 scan += 1;
3096 break 'scan;
3097 }
3098 Stmt::ExprStmt(e) if mid_safe(e) || is_local_commit(e) => {}
3099 _ => break 'scan,
3100 }
3101 }
3102 absorbed_mid.push(v[scan].clone());
3103 scan += 1;
3104 }
3105 Stmt::ExprStmt(e) if mid_safe(e) || is_local_commit(e) => {
3106 absorbed_mid.push(v[scan].clone());
3107 scan += 1;
3108 }
3109 _ => break,
3110 }
3111 }
3112 if !found {
3113 return None;
3114 }
3115 let consumed = scan - i;
3116 let mut t = (**tbody).clone();
3119 let defs = defs_probe(v, i, body);
3120 strip_lock_exits(&mut t, lock, &defs);
3121 let mut cs = Vec::with_capacity(catches.len());
3122 for c in catches.iter() {
3123 let mut cb = (*c.body).clone();
3124 strip_lock_exits(&mut cb, lock, &defs);
3125 let bare_rethrow = c.exc.is_empty() && {
3129 let mut leaves: Vec<&Stmt> = Vec::new();
3130 flatten_leaves(&cb, &mut leaves);
3131 matches!(leaves.as_slice(), [Stmt::Throw(Expr::Local { var, .. })] if *var == c.var)
3132 };
3133 if bare_rethrow {
3134 continue;
3135 }
3136 let mut c2 = c.clone();
3137 *c2.body = cb;
3138 cs.push(c2);
3139 }
3140 let mut new_body = (**body).clone();
3141 let adopted = Stmt::Try {
3142 body: Box::new(t),
3143 catches: cs,
3144 finally: None,
3145 };
3146 match &mut new_body {
3147 Stmt::Block(bv) => bv.push(adopted),
3148 other => {
3149 let inner = std::mem::replace(other, Stmt::Block(vec![]));
3150 *other = Stmt::Block(vec![inner, adopted]);
3151 }
3152 }
3153 if let Stmt::Block(bv) = &mut new_body {
3154 bv.extend(absorbed_mid);
3155 }
3156 let sync = Stmt::Synchronized {
3157 lock: lock.clone(),
3158 body: Box::new(new_body),
3159 };
3160 let mut repl = vec![sync];
3161 if let Some(r) = rest_after {
3162 repl.push(r);
3163 }
3164 Some((repl, consumed))
3165}
3166
3167fn mid_safe(e: &Expr) -> bool {
3171 !has_side_effects(e)
3172}
3173
3174fn is_local_commit(e: &Expr) -> bool {
3176 match e {
3177 Expr::Assign { target, op: AssignOp::Plain, .. } => {
3178 matches!(&**target, Expr::Local { .. })
3179 }
3180 _ => false,
3181 }
3182}
3183
3184fn defs_probe<'a>(
3187 v: &'a [Stmt],
3188 i: usize,
3189 body: &'a Stmt,
3190) -> jdc_core::FxHashMap<u32, Expr> {
3191 let mut defs: jdc_core::FxHashMap<u32, Expr> = jdc_core::FxHashMap::default();
3192 for s in &v[..i] {
3193 if let Stmt::LocalDef { var, init: Some(e), .. } = s {
3194 defs.insert(*var, e.clone());
3195 }
3196 }
3197 if let Stmt::Block(bv) = body {
3198 for s in bv {
3199 if let Stmt::LocalDef { var, init: Some(e), .. } = s {
3200 defs.insert(*var, e.clone());
3201 }
3202 }
3203 }
3204 defs
3205}
3206
3207
3208fn try_sync_deep(v: &[Stmt], i: usize) -> Option<(Vec<Stmt>, usize)> {
3217 let Stmt::Try {
3218 body,
3219 catches,
3220 finally,
3221 } = &v[i]
3222 else {
3223 return None;
3224 };
3225 if finally.is_some() || catches.len() != 1 {
3226 return None;
3227 }
3228 let c = &catches[0];
3229 if !c.exc.is_empty() {
3230 return None;
3231 }
3232 let mut flat: Vec<&Stmt> = Vec::new();
3233 flatten_leaves(c.body.as_ref(), &mut flat);
3234 if flat.len() < 2 {
3235 return None;
3236 }
3237 let catch_exit = match flat[0] {
3238 Stmt::MonitorExit(e) => e.clone(),
3239 _ => return None,
3240 };
3241 if !matches!(flat[flat.len() - 1], Stmt::Throw(_)) {
3242 return None;
3243 }
3244 for mid in &flat[1..flat.len() - 1] {
3245 if !matches!(mid, Stmt::MonitorEnter(_) | Stmt::MonitorExit(_)) {
3246 return None;
3247 }
3248 }
3249 let mut defs0: jdc_core::FxHashMap<u32, Expr> = jdc_core::FxHashMap::default();
3250 for s in &v[..i] {
3251 if let Stmt::LocalDef { var, init: Some(e), .. } = s {
3252 defs0.insert(*var, e.clone());
3253 }
3254 }
3255 let mut body_mut = (**body).clone();
3256 let folded = match &mut body_mut {
3257 Stmt::Block(b) => try_fold_spine(b, &catch_exit, &defs0),
3258 Stmt::If {
3259 then_stmt,
3260 else_stmt,
3261 ..
3262 } => {
3263 let mut hit = false;
3264 if let Stmt::Block(b) = &mut **then_stmt {
3265 hit |= try_fold_spine(b, &catch_exit, &defs0);
3266 }
3267 if !hit {
3268 if let Some(els) = else_stmt {
3269 if let Stmt::Block(b) = &mut **els {
3270 hit |= try_fold_spine(b, &catch_exit, &defs0);
3271 }
3272 }
3273 }
3274 hit
3275 }
3276 _ => false,
3277 };
3278 if !folded {
3279 return None;
3280 }
3281 let throw = flat[flat.len() - 1].clone();
3283 let mut catches_out = catches.clone();
3284 *catches_out[0].body = Stmt::Block(vec![throw]);
3285 let mut consumed = 1;
3286 if let Some(Stmt::MonitorExit(e)) = v.get(i + 1) {
3287 if lock_equiv(e, &catch_exit, &defs0) {
3288 consumed += 1;
3289 }
3290 }
3291 Some((
3292 vec![Stmt::Try {
3293 body: Box::new(body_mut),
3294 catches: catches_out,
3295 finally: None,
3296 }],
3297 consumed,
3298 ))
3299}
3300
3301fn try_fold_spine(
3307 seq: &mut Vec<Stmt>,
3308 catch_exit: &Expr,
3309 defs_in: &jdc_core::FxHashMap<u32, Expr>,
3310) -> bool {
3311 for p in (0..seq.len()).rev() {
3312 if let Stmt::MonitorEnter(e0) = &seq[p] {
3314 let mut defs = defs_in.clone();
3315 for s in &seq[..p] {
3316 if let Stmt::LocalDef { var, init: Some(ex), .. } = s {
3317 defs.insert(*var, ex.clone());
3318 }
3319 }
3320 for s in &seq[p + 1..] {
3323 match s {
3324 Stmt::LocalDef { var, init: Some(ex), .. } if is_pure_read_expr(ex) => {
3325 defs.insert(*var, ex.clone());
3326 }
3327 _ => break,
3328 }
3329 }
3330 if !lock_equiv(e0, catch_exit, &defs) {
3331 return false;
3332 }
3333 let enter_e = e0.clone();
3334 let rest: Vec<Stmt> = seq.split_off(p + 1);
3335 let mut body = Stmt::Block(rest);
3336 strip_lock_exits(&mut body, &enter_e, &defs);
3337 seq[p] = Stmt::Synchronized {
3338 lock: enter_e,
3339 body: Box::new(body),
3340 };
3341 return true;
3342 }
3343 let is_last = p + 1 == seq.len();
3346 let mut trial = seq[p].clone();
3347 let hit = match &mut trial {
3348 Stmt::Block(b) => try_fold_spine(b, catch_exit, defs_in),
3349 Stmt::If {
3350 then_stmt,
3351 else_stmt,
3352 ..
3353 } => {
3354 let mut hit = false;
3355 if let Stmt::Block(b) = &mut **then_stmt {
3356 hit |= try_fold_spine(b, catch_exit, defs_in);
3357 }
3358 if !hit {
3359 if let Some(els) = else_stmt {
3360 if let Stmt::Block(b) = &mut **els {
3361 hit |= try_fold_spine(b, catch_exit, defs_in);
3362 }
3363 }
3364 }
3365 hit
3366 }
3367 _ => false,
3368 };
3369 if hit {
3370 if is_last {
3371 seq[p] = trial;
3372 return true;
3373 }
3374 return false;
3375 }
3376 }
3377 false
3378}
3379
3380pub fn forward_single_use(s: &mut Stmt, _vt: &VarTable) {
3385 let mut analysis = VarAnalysis {
3389 assigns: Vec::new(),
3390 reads: Vec::new(),
3391 values: Vec::new(),
3392 };
3393 analyze_vars(s, &mut analysis);
3394 let mut assigns = analysis.assigns;
3395 let mut reads = analysis.reads;
3396 let n_vars = assigns.len().max(_vt.vars.len()).max(1);
3399 assigns.resize(n_vars, 0);
3400 reads.resize(n_vars, 0);
3401 let mut values = analysis.values;
3402 values.resize(n_vars, None);
3403
3404 let mut cand = vec![false; n_vars];
3409 let mut edges: Vec<Vec<u32>> = vec![Vec::new(); n_vars];
3410 for (v, cand_v) in cand.iter_mut().enumerate() {
3411 if assigns.get(v).copied().unwrap_or(0) != 1 || reads.get(v).copied().unwrap_or(0) != 1 {
3412 continue;
3413 }
3414 if let Some(val) = values.get(v).and_then(|o| o.as_ref()) {
3415 let mut refs = HashSet::default();
3416 collect_vars(val, &mut refs);
3417 if refs.iter().any(|r| assigns[*r as usize] > 1) {
3418 continue;
3419 }
3420 edges[v] = refs.into_iter().collect();
3424 }
3425 *cand_v = true;
3426 }
3427
3428 let mut color = vec![0u8; n_vars]; let mut bad = vec![false; n_vars];
3446 for root in 0..n_vars {
3447 if color[root] != 0 || edges[root].is_empty() {
3448 continue;
3449 }
3450 color[root] = 1;
3451 let mut stack: Vec<(usize, usize)> = vec![(root, 0)];
3452 while let Some(top) = stack.last_mut() {
3453 let v = top.0;
3454 let i = top.1;
3455 top.1 += 1;
3456 if i < edges[v].len() {
3457 let r = edges[v][i] as usize;
3458 if color[r] == 1 {
3459 for &(pn, _) in &stack {
3460 bad[pn] = true;
3461 }
3462 } else if color[r] == 0 {
3463 color[r] = 1;
3464 stack.push((r, 0));
3465 } else if bad[r] {
3466 for &(pn, _) in &stack {
3467 bad[pn] = true;
3468 }
3469 }
3470 } else {
3471 color[v] = 2;
3472 stack.pop();
3473 }
3474 }
3475 }
3476 let single: Vec<bool> = (0..n_vars).map(|v| cand[v] && !bad[v]).collect();
3477 if !single.iter().any(|&b| b) {
3478 return;
3479 }
3480 let pure: Vec<bool> = (0..n_vars)
3481 .map(|v| {
3482 single[v]
3483 && values
3484 .get(v)
3485 .and_then(|o| o.as_ref())
3486 .map(|e| !has_side_effects(e))
3487 .unwrap_or(false)
3488 })
3489 .collect();
3490 let impure: Vec<bool> = (0..n_vars).map(|v| single[v] && !pure[v]).collect();
3491
3492 if pure.iter().any(|&b| b) {
3494 let vals: Vec<Option<Expr>> = (0..n_vars)
3495 .map(|v| {
3496 if pure[v] {
3497 values.get(v).cloned().flatten()
3498 } else {
3499 None
3500 }
3501 })
3502 .collect();
3503 rewrite_exprs(s, &mut |e| {
3504 deep_rewrite_reads(e, &mut |x| {
3505 if let Expr::Local { var, .. } = x {
3506 if let Some(Some(v)) = vals.get(*var as usize) {
3507 *x = v.clone();
3508 }
3509 }
3510 });
3511 });
3512 drop_defs(s, &pure);
3513 }
3514
3515 if impure.iter().any(|&b| b) {
3518 forward_adjacent_impure(s, &values, &impure);
3519 }
3520}
3521
3522fn count_reads(s: &Stmt, out: &mut Vec<usize>) {
3524 walk_all(s, &mut |st| {
3525 let exprs: Vec<&Expr> = match st {
3526 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
3529 let mut v: Vec<&Expr> = vec![value];
3530 if !matches!(&**target, Expr::Local { .. }) {
3531 v.push(target);
3532 }
3533 v
3534 }
3535 Stmt::ExprStmt(e @ (Expr::PreIncDec { .. } | Expr::PostIncDec { .. })) => {
3538 let inner = match e {
3539 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => e,
3540 _ => unreachable!(),
3541 };
3542 if matches!(**inner, Expr::Local { .. }) {
3543 Vec::new()
3544 } else {
3545 vec![e]
3546 }
3547 }
3548 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
3549 vec![e]
3550 }
3551 Stmt::Return(Some(e)) => vec![e],
3552 Stmt::LocalDef { init: Some(e), .. } => vec![e],
3553 Stmt::If { cond, .. } => vec![cond],
3554 Stmt::While { cond, .. } => vec![cond],
3555 Stmt::DoWhile { cond, .. } => vec![cond],
3556 Stmt::Switch { selector, .. } => vec![selector],
3562 Stmt::ForEach { iterable, .. } => vec![iterable],
3563 Stmt::Synchronized { lock, .. } => vec![lock],
3564 Stmt::For { cond, update, .. } => {
3565 let mut v: Vec<&Expr> = Vec::with_capacity(update.len() + 1);
3566 if let Some(c) = cond {
3567 v.push(c);
3568 }
3569 v.extend(update.iter());
3570 v
3571 }
3572 Stmt::Assert { cond, msg } => {
3573 let mut v: Vec<&Expr> = vec![cond];
3574 if let Some(m) = msg {
3575 v.push(m);
3576 }
3577 v
3578 }
3579 _ => Vec::new(),
3580 };
3581 for e in exprs {
3582 visit_exprs(e, &mut |x| {
3583 if let Expr::Local { var, .. } = x {
3584 grow_to(out, *var);
3585 out[*var as usize] += 1;
3586 }
3587 });
3588 }
3589 });
3590}
3591
3592fn drop_defs(s: &mut Stmt, vars: &[bool]) {
3593 walk_mut_deep(s, &mut |st| {
3594 if let Stmt::Block(v) = st {
3595 v.retain(|x| match x {
3596 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
3597 Expr::Local { var, .. } => !vars.get(*var as usize).copied().unwrap_or(false),
3598 _ => true,
3599 },
3600 Stmt::LocalDef { var, .. } => !vars.get(*var as usize).copied().unwrap_or(false),
3601 _ => true,
3602 });
3603 }
3604 });
3605}
3606
3607fn forward_adjacent_impure(s: &mut Stmt, values: &[Option<Expr>], impure: &[bool]) {
3609 if let Stmt::Block(v) = s {
3610 let mut i = 0;
3611 while i + 1 < v.len() {
3612 let def_var = match &v[i] {
3613 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
3614 Expr::Local { var, .. } => Some(*var),
3615 _ => None,
3616 },
3617 Stmt::LocalDef { var, .. } => Some(*var),
3618 _ => None,
3619 };
3620 if let Some(var) = def_var {
3621 if impure.get(var as usize).copied().unwrap_or(false) {
3622 let mut reads_here = vec![0usize; values.len().max(1)];
3624 count_reads(&v[i + 1], &mut reads_here);
3625 if reads_here.get(var as usize).copied().unwrap_or(0) == 1 {
3626 let val = match &v[i] {
3630 Stmt::ExprStmt(Expr::Assign { value, .. }) => (**value).clone(),
3631 Stmt::LocalDef { init: Some(e), .. } => (*e).clone(),
3632 _ => {
3633 i += 1;
3634 continue;
3635 }
3636 };
3637 rewrite_exprs(&mut v[i + 1], &mut |e| {
3638 deep_rewrite_reads(e, &mut |x| {
3639 if let Expr::Local { var: v2, .. } = x {
3640 if *v2 == var {
3641 *x = val.clone();
3642 }
3643 }
3644 });
3645 });
3646 v.remove(i);
3647 i = i.saturating_sub(1);
3650 continue;
3651 }
3652 }
3653 }
3654 i += 1;
3655 }
3656 }
3657 walk_mut(s, &mut |x| forward_adjacent_impure(x, values, impure));
3658}
3659
3660pub fn infer_types(vt: &mut VarTable, body: &mut Stmt, ret: &JavaType, env: &MethodEnv) {
3666 let n = vt.vars.len();
3667 let mut evidence: Vec<Vec<(JavaType, bool)>> = vec![Vec::new(); n];
3675 let ev = |evidence: &mut Vec<Vec<(JavaType, bool)>>, var: u32, t: JavaType, strong: bool| {
3676 if (var as usize) < evidence.len() {
3677 evidence[var as usize].push((t, strong));
3678 }
3679 };
3680
3681 walk_all(body, &mut |st| match st {
3682 Stmt::LocalDef { var, init, .. } => {
3683 if let Some(e) = init {
3684 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
3685 let strong = match e {
3693 Expr::Const(_) => false,
3694 Expr::Local { var: src, .. } => {
3695 let src_ty = vt.vars.get(*src as usize).map(|v| v.ty.erased());
3696 let tgt_ty = vt.vars.get(*var as usize).map(|v| v.ty.erased());
3697 match (src_ty, tgt_ty) {
3698 (Some(s), Some(g)) => {
3699 (s.is_numeric() && g.is_numeric())
3700 || (s.is_reference() && g.is_reference())
3701 }
3702 _ => false,
3703 }
3704 }
3705 _ => true,
3706 };
3707 ev(&mut evidence, *var, e.type_ref().erased(), strong);
3708 }
3709 }
3710 Stmt::ExprStmt(e) => {
3711 if let Expr::Assign { target, value, .. } = e {
3718 if let Expr::Field { owner: Some(o), cls, is_static: false, .. } = &**target {
3725 if cls.is_empty() {
3726 } else if let Expr::Local { var, .. } = &**o {
3728 ev(
3729 &mut evidence,
3730 *var,
3731 JavaType::Object(cls.clone()),
3732 true,
3733 );
3734 }
3735 }
3736 if let Expr::Local { var, .. } = &**target {
3737 let strong = match &**value {
3738 Expr::Const(_) => false,
3739 Expr::Local { var: src, .. } => {
3740 let src_ty = vt.vars.get(*src as usize).map(|v| v.ty.erased());
3747 let tgt_ty = vt.vars.get(*var as usize).map(|v| v.ty.erased());
3748 match (src_ty, tgt_ty) {
3749 (Some(s), Some(g)) => {
3750 (s.is_numeric() && g.is_numeric())
3751 || (s.is_reference() && g.is_reference())
3752 }
3753 _ => false,
3754 }
3755 }
3756 _ => true,
3757 };
3758 ev(&mut evidence, *var, value.type_ref().erased(), strong);
3759 }
3760 }
3761 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
3762 }
3763 Stmt::Throw(e) => {
3764 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
3765 if let Expr::Local { var, .. } = e {
3766 ev(
3767 &mut evidence,
3768 *var,
3769 JavaType::Object("java/lang/Throwable".into()),
3770 true,
3771 );
3772 }
3773 }
3774 Stmt::Return(Some(e)) => {
3775 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
3776 if let Expr::Local { var, .. } = e {
3777 ev(&mut evidence, *var, ret.clone(), true);
3778 }
3779 }
3780 Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
3781 if let Expr::Local { var, .. } = e {
3782 ev(
3783 &mut evidence,
3784 *var,
3785 JavaType::Object("java/lang/Object".into()),
3786 true,
3787 );
3788 }
3789 }
3790 Stmt::ForEach {
3791 var,
3792 iterable,
3793 is_array,
3794 ..
3795 } => {
3796 if let Expr::Local { var: v0, .. } = iterable {
3797 if *is_array {
3798 ev(&mut evidence, *v0, JavaType::Array(Box::new(JavaType::Int)), true);
3799 } else {
3800 ev(
3801 &mut evidence,
3802 *v0,
3803 JavaType::Object("java/lang/Object".into()),
3804 true,
3805 );
3806 }
3807 }
3808 let _ = var;
3809 }
3810 _ => {}
3811 });
3812 let _ = env;
3813
3814 let mut derefed: Vec<bool> = vec![false; vt.vars.len()];
3824 mark_derefs(body, &mut derefed);
3825 let mut arr_derefed: Vec<bool> = vec![false; vt.vars.len()];
3830 visit_all_exprs(body, &mut |x| {
3831 if let Expr::ArrayIndex { array, .. } = x {
3832 if let Expr::Local { var, .. } = &**array {
3833 if (*var as usize) < arr_derefed.len() {
3834 arr_derefed[*var as usize] = true;
3835 }
3836 }
3837 }
3838 if let Expr::Bin { op, l, r, .. } = x {
3846 if matches!(op, BinOp::Eq | BinOp::Ne)
3847 && (matches!(&**r, Expr::Const(ConstVal::Null))
3848 || matches!(&**l, Expr::Const(ConstVal::Null)))
3849 {
3850 for side in [l, r] {
3851 if let Expr::Local { var, .. } = &**side {
3852 ev(
3853 &mut evidence,
3854 *var,
3855 JavaType::Object("java/lang/Object".into()),
3856 true,
3857 );
3858 }
3859 }
3860 }
3861 }
3862 });
3863 const FINAL_JDK: &[&str] = &[
3864 "java/lang/Boolean",
3865 "java/lang/String",
3866 "java/lang/Integer",
3867 "java/lang/Long",
3868 "java/lang/Short",
3869 "java/lang/Byte",
3870 "java/lang/Character",
3871 "java/lang/Float",
3872 "java/lang/Double",
3873 ];
3874 for (i, evs) in evidence.iter().enumerate() {
3875 let info = &vt.vars[i];
3876 if info.is_param {
3877 continue;
3878 }
3879 let strong: Vec<JavaType> = evs
3880 .iter()
3881 .filter(|(_, s)| *s)
3882 .map(|(t, _)| t.clone())
3883 .collect();
3884 let all: Vec<JavaType> = evs.iter().map(|(t, _)| t.clone()).collect();
3885 if info.ty.erased().is_reference() {
3886 if strong.is_empty() {
3892 let refs: std::collections::HashSet<&str> = all
3893 .iter()
3894 .filter_map(|t| match t {
3895 JavaType::Object(n) if n.as_ref() != "java/lang/Object" => {
3896 Some(n.as_ref())
3897 }
3898 _ => None,
3899 })
3900 .collect();
3901 let mixed = all.iter().any(|t| t.is_numeric());
3902 if refs.len() >= 2 || (mixed && !refs.is_empty()) || (mixed && all.iter().any(|t| matches!(t, JavaType::Array(_)))) {
3903 vt.vars[i].ty =
3904 TypeRef::J(JavaType::Object("java/lang/Object".into()));
3905 continue;
3906 }
3907 }
3908 if arr_derefed[i] {
3911 let arr = strong
3912 .iter()
3913 .chain(all.iter())
3914 .find(|t| matches!(t, JavaType::Array(_)))
3915 .cloned();
3916 if let Some(t) = arr {
3917 vt.vars[i].ty = TypeRef::J(t);
3918 continue;
3919 }
3920 }
3921 let deref_pick = || {
3925 if !derefed[i] {
3926 return None;
3927 }
3928 strong
3929 .iter()
3930 .find(|t| {
3931 matches!(t, JavaType::Object(n)
3932 if !n.is_empty()
3933 && n.as_ref() != "java/lang/Object"
3934 && !FINAL_JDK.contains(&n.as_ref()))
3935 })
3936 .cloned()
3937 };
3938 if let Some(t) = deref_pick()
3939 .or_else(|| pick_object(&strong))
3940 .or_else(|| pick_object(&all))
3941 {
3942 vt.vars[i].ty = TypeRef::J(t);
3943 }
3944 } else if let Some(t) = pick_object(&strong) {
3945 vt.vars[i].ty = TypeRef::J(t);
3949 } else if strong.contains(&JavaType::Boolean) {
3950 vt.vars[i].ty = TypeRef::J(JavaType::Boolean);
3954 } else if !all.is_empty() && all.iter().all(|t| t.is_numeric()) {
3955 if let Some(t) = pick_numeric(&all) {
3956 vt.vars[i].ty = TypeRef::J(t);
3957 }
3958 } else {
3959 let refs = all.iter().filter(|t| t.is_reference()).count();
3963 let nums = all.iter().filter(|t| t.is_numeric()).count();
3964 if refs > 0 && nums > 0 {
3965 vt.vars[i].ty =
3966 TypeRef::J(JavaType::Object("java/lang/Object".into()));
3967 } else if let Some(t) = pick_object(&strong).or_else(|| pick_object(&all)) {
3968 vt.vars[i].ty = TypeRef::J(t);
3969 }
3970 }
3971 }
3972
3973 let types: Vec<&TypeRef> = vt.vars.iter().map(|v| &v.ty).collect();
3978 rewrite_exprs(body, &mut |e| {
3979 deep_rewrite(e, &mut |x| {
3980 if let Expr::Local { var, ty } = x {
3981 if let Some(want) = types.get(*var as usize) {
3982 if ty != *want {
3983 *ty = (*want).clone();
3984 }
3985 }
3986 }
3987 });
3988 });
3989
3990 coerce_num_consts(body, &types);
3993}
3994
3995fn coerce_num_consts(body: &mut Stmt, types: &[&TypeRef]) {
3996 walk_mut_deep(body, &mut |st| {
3997 let (var, val): (u32, &mut Expr) = match st {
3998 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &mut **target {
3999 Expr::Local { var, .. } => (*var, value),
4000 _ => return,
4001 },
4002 Stmt::LocalDef {
4003 var, init: Some(e), ..
4004 } => (*var, e),
4005 _ => return,
4006 };
4007 let want = match types.get(var as usize) {
4008 Some(TypeRef::J(t)) => t.clone(),
4009 _ => return,
4010 };
4011 let rewritten = match &*val {
4012 Expr::Const(ConstVal::Long(l)) => match &want {
4013 JavaType::Double => Some(Expr::Const(ConstVal::Double(*l as f64))),
4014 JavaType::Float => Some(Expr::Const(ConstVal::Float(*l as f32))),
4015 _ => None,
4016 },
4017 Expr::Const(ConstVal::Int(i)) => match &want {
4018 JavaType::Double => Some(Expr::Const(ConstVal::Double(*i as f64))),
4019 JavaType::Float => Some(Expr::Const(ConstVal::Float(*i as f32))),
4020 _ => None,
4021 },
4022 _ => None,
4023 };
4024 if let Some(e) = rewritten {
4025 *val = e;
4026 }
4027 });
4028}
4029
4030fn pick_object(evs: &[JavaType]) -> Option<JavaType> {
4031 evs.iter()
4032 .find(|t| {
4033 t.is_reference()
4034 && !matches!(t, JavaType::Object(n) if n.is_empty() || n.as_ref() == "java/lang/Object")
4035 })
4036 .cloned()
4037}
4038
4039fn pick_numeric(evs: &[JavaType]) -> Option<JavaType> {
4040 if evs.is_empty() {
4041 return None;
4042 }
4043 let mut t = evs[0].clone();
4044 for x in &evs[1..] {
4045 t = join_numeric(&t, x);
4046 }
4047 Some(t)
4048}
4049
4050fn join_numeric(a: &JavaType, b: &JavaType) -> JavaType {
4051 if a == b {
4052 return a.clone();
4053 }
4054 match (a, b) {
4055 (JavaType::Double, _) | (_, JavaType::Double) => JavaType::Double,
4056 (JavaType::Float, _) | (_, JavaType::Float) => JavaType::Float,
4057 (JavaType::Long, _) | (_, JavaType::Long) => JavaType::Long,
4058 (JavaType::Int, x) if x.is_integral() => JavaType::Int,
4059 (x, JavaType::Int) if x.is_integral() => JavaType::Int,
4060 (x, y) if x.is_integral() && y.is_integral() => JavaType::Int,
4061 _ => JavaType::Int,
4062 }
4063}
4064
4065fn expr_evidence<F: FnMut(u32, JavaType)>(e: &Expr, f: &mut F) {
4067 visit_exprs(e, &mut |x| match x {
4068 Expr::Method {
4069 owner,
4070 args,
4071 desc,
4072 is_static,
4073 cls,
4074 ..
4075 } => {
4076 if !*is_static {
4077 if let Some(o) = owner {
4078 if let Expr::Local { var, .. } = &**o {
4079 f(*var, JavaType::Object(cls.clone()));
4080 }
4081 }
4082 }
4083 for (i, a) in args.iter().enumerate() {
4084 if let Expr::Local { var, .. } = a {
4085 if let Some(t) = desc.args.get(i) {
4086 f(*var, t.clone());
4087 }
4088 }
4089 }
4090 }
4091 Expr::Field {
4092 owner,
4093 cls,
4094 ty,
4095 is_static,
4096 ..
4097 } => {
4098 if !*is_static && !cls.is_empty() {
4099 if let Some(o) = owner {
4100 if let Expr::Local { var, .. } = &**o {
4101 f(*var, JavaType::Object(cls.clone()));
4111 }
4112 }
4113 }
4114 let _ = ty;
4115 }
4116 Expr::Bin { op, l, r, .. }
4121 if matches!(op, BinOp::Eq | BinOp::Ne)
4122 && (matches!(&**r, Expr::Const(ConstVal::Null))
4123 || matches!(&**l, Expr::Const(ConstVal::Null))) =>
4124 {
4125 for side in [l, r] {
4126 if let Expr::Local { var, .. } = &**side {
4127 f(*var, JavaType::Object("java/lang/Object".into()));
4128 }
4129 }
4130 }
4131 Expr::ArrayIndex { array, .. } => {
4132 if let Expr::Local { var, ty, .. } = &**array {
4133 let t = ty.erased();
4138 if matches!(t, JavaType::Array(_)) {
4139 f(*var, t);
4140 } else {
4141 f(*var, JavaType::Array(Box::new(JavaType::Int)));
4142 }
4143 }
4144 }
4145 Expr::Assign { .. } => {
4146 }
4149 Expr::Cast { e: inner, .. } => {
4150 let _ = inner;
4156 }
4157 Expr::InstanceOf { e: inner, .. } => {
4158 let _ = inner;
4159 }
4160 _ => {}
4161 });
4162}
4163
4164pub fn mark_field_owner_concrete(vt: &mut VarTable, body: &mut Stmt) {
4176 let mut owners: HashSet<u32> = HashSet::default();
4177 rewrite_exprs(body, &mut |e| {
4178 visit_exprs(e, &mut |x| {
4179 if let Expr::Field {
4180 owner: Some(o),
4181 is_static: false,
4182 ..
4183 } = x
4184 {
4185 if let Expr::Local { var, .. } = &**o {
4186 owners.insert(*var);
4187 }
4188 }
4189 });
4190 });
4191 for var in owners {
4192 let i = var as usize;
4193 if i >= vt.vars.len() {
4194 continue;
4195 }
4196 if let JavaType::Object(cls) = vt.vars[i].ty.erased() {
4197 if let Some(last) = cls.rsplit('$').next() {
4198 if !last.is_empty() && last.chars().all(|c| c.is_ascii_digit()) {
4199 vt.force_concrete_vars.insert(var);
4200 }
4201 }
4202 }
4203 }
4204 let concrete: Vec<u32> = vt
4213 .vars
4214 .iter()
4215 .filter(|v| {
4216 if let JavaType::Object(cls) = v.ty.erased() {
4217 cls.rsplit('$')
4218 .next()
4219 .is_some_and(|l| !l.is_empty() && l.chars().all(|c| c.is_ascii_digit()))
4220 } else {
4221 false
4222 }
4223 })
4224 .map(|v| v.id)
4225 .collect();
4226 for var in concrete {
4227 vt.force_concrete_vars.insert(var);
4228 }
4229}
4230
4231pub fn insert_object_narrowing_casts(
4243 vt: &VarTable,
4244 body: &mut Stmt,
4245 pool: &DexPool,
4246 ret: &JavaType,
4247) {
4248 let value_ty = |e: &Expr| -> JavaType {
4251 match e {
4252 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4253 other => other.type_ref().erased(),
4254 }
4255 };
4256 let is_top_object = |e: &Expr| -> bool {
4257 value_ty(e) == JavaType::Object("java/lang/Object".into())
4258 };
4259 let specific_ref = |ty: &TypeRef| -> Option<TypeRef> {
4263 match ty.erased() {
4264 JavaType::Object(c) if c.as_ref() != "java/lang/Object" => Some(ty.clone()),
4265 JavaType::Array(_) => Some(ty.clone()),
4266 _ => None,
4267 }
4268 };
4269 let castable = |e: &Expr| -> bool {
4270 is_top_object(e)
4271 && !matches!(e, Expr::Const(_) | Expr::Cast { .. } | Expr::InstanceOf { .. })
4272 };
4273 let needs_downcast = |e: &Expr, tt: &JavaType| -> bool {
4283 if matches!(e, Expr::Const(_) | Expr::Cast { .. } | Expr::InstanceOf { .. }) {
4284 return false;
4285 }
4286 let v = value_ty(e);
4287 let (JavaType::Object(vn), JavaType::Object(tn)) = (&v, tt) else {
4288 return false;
4289 };
4290 if vn.as_ref() == "java/lang/Object" || tn.as_ref() == "java/lang/Object" {
4291 return false;
4292 }
4293 vn != tn && !pool.is_subtype(vn, tn)
4294 };
4295 walk_mut_deep(body, &mut |st| match st {
4296 Stmt::ExprStmt(Expr::Assign {
4297 target,
4298 value,
4299 op: AssignOp::Plain,
4300 ..
4301 }) => {
4302 let tgt = match &**target {
4303 Expr::Local { var, .. } => vt.var(*var).ty.clone(),
4304 Expr::Field { ty, .. } => ty.clone(),
4305 Expr::ArrayIndex { array, .. } => {
4312 let aty = match &**array {
4313 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4314 other => other.type_ref().erased(),
4315 };
4316 match aty {
4317 JavaType::Array(inner) => TypeRef::J(*inner),
4318 _ => return,
4319 }
4320 }
4321 _ => return,
4322 };
4323 if let Some(t) = specific_ref(&tgt) {
4324 if castable(value) || needs_downcast(value, &tgt.erased()) {
4325 let v = std::mem::replace(value, Box::new(Expr::This));
4326 **value = Expr::Cast { ty: t, e: v };
4327 }
4328 }
4329 }
4330 Stmt::LocalDef { var, init: Some(value), .. } => {
4331 if let Some(t) = specific_ref(&vt.var(*var).ty) {
4332 let tt = vt.var(*var).ty.erased();
4333 if castable(value) || needs_downcast(value, &tt) {
4334 let v = std::mem::replace(value, Expr::This);
4335 *value = Expr::Cast {
4336 ty: t,
4337 e: Box::new(v),
4338 };
4339 }
4340 }
4341 }
4342 Stmt::Return(Some(value)) => {
4343 if let Some(t) = specific_ref(&TypeRef::J(ret.clone())) {
4344 if castable(value) || needs_downcast(value, ret) {
4345 let v = std::mem::replace(value, Expr::This);
4346 *value = Expr::Cast { ty: t, e: Box::new(v) };
4347 }
4348 }
4349 }
4350 _ => {}
4351 });
4352 walk_stmt_exprs(body, &mut |e| {
4359 deep_rewrite(e, &mut |x| {
4360 let (args, arg_tys): (&mut Vec<Expr>, &[JavaType]) = match x {
4361 Expr::Method { desc, args, is_dynamic: false, .. } => {
4362 (args, desc.args.as_slice())
4363 }
4364 Expr::New { arg_tys, args, .. } => (args, arg_tys.as_slice()),
4368 _ => return,
4369 };
4370 for (i, a) in args.iter_mut().enumerate() {
4371 let Some(formal) = arg_tys.get(i) else {
4372 continue;
4373 };
4374 let Some(t) = specific_ref(&TypeRef::J(formal.clone())) else {
4375 continue;
4376 };
4377 if !(castable(a) || needs_downcast(a, formal)) {
4378 continue;
4379 }
4380 let v = std::mem::replace(a, Expr::Const(ConstVal::Null));
4381 *a = Expr::Cast { ty: t, e: Box::new(v) };
4382 }
4383 });
4384 });
4385}
4386
4387pub fn fix_primitive_assign_casts(vt: &VarTable, body: &mut Stmt, ret_ty: &JavaType) {
4396 fn numlike(t: &JavaType) -> bool {
4397 matches!(
4398 t,
4399 JavaType::Byte
4400 | JavaType::Short
4401 | JavaType::Int
4402 | JavaType::Char
4403 | JavaType::Long
4404 | JavaType::Float
4405 | JavaType::Double
4406 )
4407 }
4408 fn val_ty(e: &Expr, vt: &VarTable) -> JavaType {
4409 match e {
4410 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
4411 vt.var(*var).ty.erased()
4412 }
4413 other => other.type_ref().erased(),
4414 }
4415 }
4416 fn wrap(ty: &JavaType, value: &mut Expr) {
4417 if matches!(value, Expr::Cast { .. }) {
4418 return;
4419 }
4420 if val_ty_static(value) == *ty {
4421 return;
4422 }
4423 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
4424 *value = Expr::Cast {
4425 ty: TypeRef::J(ty.clone()),
4426 e: Box::new(taken),
4427 };
4428 }
4429 fn val_ty_static(e: &Expr) -> JavaType {
4430 e.type_ref().erased()
4431 }
4432 fn coerce(tt: &JavaType, value: &mut Expr, vt: &VarTable) {
4440 match (&*value, tt) {
4445 (Expr::Const(ConstVal::Int(b)), JavaType::Float) => {
4446 *value = Expr::Const(ConstVal::Float(f32::from_bits(*b as u32)));
4447 return;
4448 }
4449 (Expr::Const(ConstVal::Long(b)), JavaType::Double) => {
4450 *value = Expr::Const(ConstVal::Double(f64::from_bits(*b as u64)));
4451 return;
4452 }
4453 (Expr::Const(ConstVal::Float(f)), JavaType::Int) => {
4454 *value = Expr::Const(ConstVal::Int(f.to_bits() as i32));
4455 return;
4456 }
4457 (Expr::Const(ConstVal::Double(d)), JavaType::Long) => {
4458 *value = Expr::Const(ConstVal::Long(d.to_bits() as i64));
4459 return;
4460 }
4461 _ => {}
4462 }
4463 let vt_val = val_ty(value, vt);
4464 let tt_bool = matches!(tt, JavaType::Boolean);
4465 let val_bool = matches!(vt_val, JavaType::Boolean);
4466 if tt_bool && !val_bool && numlike(&vt_val) {
4467 if matches!(
4468 value,
4469 Expr::Const(_)
4470 | Expr::Bin { op: BinOp::Eq | BinOp::Ne, .. }
4471 | Expr::Un { op: UnOp::Not, .. }
4472 ) {
4473 return;
4478 }
4479 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
4480 *value = Expr::Bin {
4481 op: BinOp::Ne,
4482 l: Box::new(taken),
4483 r: Box::new(Expr::Const(ConstVal::Int(0))),
4484 ty: Some(TypeRef::J(JavaType::Boolean)),
4485 };
4486 } else if !tt_bool && val_bool && numlike(tt) {
4487 if matches!(value, Expr::Cast { .. }) {
4488 return;
4489 }
4490 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
4491 *value = Expr::Cast {
4492 ty: TypeRef::J(tt.clone()),
4493 e: Box::new(Expr::Cond {
4494 c: Box::new(taken),
4495 t: Box::new(Expr::Const(ConstVal::Int(1))),
4496 f: Box::new(Expr::Const(ConstVal::Int(0))),
4497 }),
4498 };
4499 } else if numlike(tt) && numlike(&vt_val) {
4500 wrap(tt, value);
4501 }
4502 }
4503 walk_mut_deep(body, &mut |st| match st {
4504 Stmt::ExprStmt(Expr::Assign {
4505 target,
4506 op: AssignOp::Plain,
4507 value,
4508 }) => {
4509 let tt = match &**target {
4510 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
4511 vt.var(*var).ty.erased()
4512 }
4513 Expr::Field { ty, .. } => ty.erased(),
4514 Expr::ArrayIndex { array, .. } => match array.type_ref().erased() {
4518 JavaType::Array(inner) => *inner,
4519 _ => return,
4520 },
4521 _ => return,
4522 };
4523 coerce(&tt, value, vt);
4524 }
4525 Stmt::LocalDef {
4526 var,
4527 init: Some(value),
4528 ..
4529 } => {
4530 if (*var as usize) >= vt.vars.len() {
4531 return;
4532 }
4533 let tt = vt.var(*var).ty.erased();
4534 coerce(&tt, value, vt);
4535 }
4536 Stmt::Return(Some(e)) => {
4537 if !numlike(ret_ty) || !numlike(&val_ty(e, vt)) {
4538 return;
4539 }
4540 wrap(ret_ty, e);
4541 }
4542 _ => {}
4543 });
4544}
4545
4546pub fn fix_shadowed_super_fields(
4570 body: &mut Stmt,
4571 vt: &VarTable,
4572 pool: &DexPool,
4573 class_name: &str,
4574) {
4575 fn display(pool: &DexPool, cls: &str, name: &str) -> Option<String> {
4579 let pc = pool.get(cls)?;
4580 let f = pc
4581 .static_fields
4582 .iter()
4583 .chain(pc.instance_fields.iter())
4584 .find(|f| f.name.as_str() == name)?;
4585 Some(
4586 jdc_core::rename::field_display(cls, name, &f.desc)
4587 .map(|d| d.to_string())
4588 .unwrap_or_else(|| name.to_string()),
4589 )
4590 }
4591 fn owner_ct(
4592 o: &Option<Box<Expr>>,
4593 vt: &VarTable,
4594 class_name: &str,
4595 ) -> Option<std::sync::Arc<str>> {
4596 match o.as_deref() {
4597 None | Some(Expr::This) => Some(std::sync::Arc::from(class_name)),
4598 Some(Expr::Local { var, .. }) => match vt.var(*var).ty.erased() {
4599 JavaType::Object(n) => Some(n),
4600 _ => None,
4601 },
4602 Some(other) => match other.type_ref().erased() {
4603 JavaType::Object(n) => Some(n),
4604 _ => None,
4605 },
4606 }
4607 }
4608 walk_stmt_exprs(body, &mut |e| {
4609 deep_rewrite(e, &mut |x| {
4610 let Expr::Field {
4611 owner,
4612 cls,
4613 name,
4614 is_static: false,
4615 ..
4616 } = x
4617 else {
4618 return;
4619 };
4620 let Some(ot) = owner_ct(owner, vt, class_name) else {
4621 return;
4622 };
4623 if cls.as_ref() == ot.as_ref() {
4624 return;
4625 }
4626 let Some(ref_display) = display(pool, cls, name) else {
4627 return;
4628 };
4629 let mut cur = ot.to_string();
4633 let mut shadowed = false;
4634 let mut found = false;
4635 for _ in 0..64 {
4636 if display(pool, &cur, name).as_deref() == Some(ref_display.as_str()) {
4637 shadowed = true;
4638 }
4639 let Some(pc) = pool.get(&cur) else {
4640 return;
4641 };
4642 let Some(sup) = pc.super_name.clone() else {
4643 return;
4644 };
4645 if sup.as_str() == cls.as_ref() {
4646 found = true;
4647 break;
4648 }
4649 cur = sup;
4650 }
4651 if !found || !shadowed {
4652 return;
4653 }
4654 let ty = TypeRef::J(JavaType::Object(cls.clone()));
4655 match owner {
4656 Some(o) => {
4657 if let Expr::Cast { ty: ct, .. } = &mut **o {
4658 *ct = ty;
4659 } else {
4660 let taken = std::mem::replace(&mut **o, Expr::This);
4661 **o = Expr::Cast {
4662 ty,
4663 e: Box::new(taken),
4664 };
4665 }
4666 }
4667 None => {
4668 *owner = Some(Box::new(Expr::Cast {
4669 ty,
4670 e: Box::new(Expr::This),
4671 }))
4672 }
4673 }
4674 });
4675 });
4676}
4677
4678pub fn fix_field_owner_downcasts(body: &mut Stmt, vt: &VarTable, pool: &DexPool) {
4697 fn owner_internal(o: &Expr, vt: &VarTable) -> Option<std::sync::Arc<str>> {
4698 let t = match o {
4699 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4700 other => other.type_ref().erased(),
4701 };
4702 match t {
4703 JavaType::Object(n) => Some(n),
4704 _ => None,
4705 }
4706 }
4707 fn declares_up(pool: &DexPool, class: &str, name: &str) -> bool {
4708 let mut cur = Some(class.to_string());
4709 let mut hops = 0;
4710 while let Some(c) = cur {
4711 hops += 1;
4712 if hops > 64 {
4713 return false;
4714 }
4715 let Some(pc) = pool.get(&c) else { return false };
4716 if pc
4717 .instance_fields
4718 .iter()
4719 .chain(pc.static_fields.iter())
4720 .any(|f| f.name.as_str() == name)
4721 {
4722 return true;
4723 }
4724 cur = pc.super_name.clone();
4725 }
4726 false
4727 }
4728 walk_stmt_exprs(body, &mut |e| {
4729 deep_rewrite(e, &mut |x| {
4730 if let Expr::Field {
4731 owner: Some(o),
4732 cls,
4733 name,
4734 is_static: false,
4735 ..
4736 } = x
4737 {
4738 let Some(ot) = owner_internal(o, vt) else {
4739 return;
4740 };
4741 if ot.as_ref() == cls.as_ref() || !pool.is_subtype(cls, &ot) {
4742 return;
4743 }
4744 if declares_up(pool, &ot, name) || !declares_up(pool, cls, name) {
4745 return;
4746 }
4747 let ty = TypeRef::J(JavaType::Object(cls.clone()));
4748 if let Expr::Cast { ty: ct, .. } = &mut **o {
4749 *ct = ty;
4750 } else {
4751 let taken = std::mem::replace(&mut **o, Expr::This);
4752 **o = Expr::Cast { ty, e: Box::new(taken) };
4753 }
4754 }
4755 let (tgt_ty, value) = match x {
4757 Expr::Assign { target, op: AssignOp::Plain, value } => {
4758 let t = match &**target {
4759 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4760 Expr::Field { ty, .. } => ty.erased(),
4761 _ => return,
4762 };
4763 (t, value)
4764 }
4765 _ => return,
4766 };
4767 let JavaType::Object(sn) = tgt_ty else { return };
4768 if let Expr::Cast { ty, .. } = &mut **value {
4769 if let TypeRef::J(JavaType::Object(tn)) = ty {
4770 if tn.as_ref() != sn.as_ref() && pool.is_subtype(&sn, tn) {
4771 *ty = TypeRef::J(JavaType::Object(sn.clone()));
4772 }
4773 }
4774 }
4775 });
4776 });
4777 walk_mut_deep(body, &mut |st| {
4779 if let Stmt::LocalDef { var, init: Some(value), .. } = st {
4780 let tt = if (*var as usize) < vt.vars.len() {
4781 vt.vars[*var as usize].ty.erased()
4782 } else {
4783 return;
4784 };
4785 let JavaType::Object(sn) = tt else { return };
4786 if let Expr::Cast { ty, .. } = value {
4787 if let TypeRef::J(JavaType::Object(tn)) = ty {
4788 if tn.as_ref() != sn.as_ref() && pool.is_subtype(&sn, tn) {
4789 *ty = TypeRef::J(JavaType::Object(sn.clone()));
4790 }
4791 }
4792 }
4793 }
4794 });
4795}
4796
4797pub fn fix_incomparable_equality(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
4804 fn side_ty(e: &Expr, vt: &VarTable) -> JavaType {
4805 match e {
4806 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
4807 vt.var(*var).ty.erased()
4808 }
4809 other => other.type_ref().erased(),
4810 }
4811 }
4812 let incomparable = |a: &JavaType, b: &JavaType| -> bool {
4813 use JavaType::{Array, Object};
4814 match (a, b) {
4815 (Object(x), Object(y)) => {
4816 if x == y || x.as_ref() == "java/lang/Object" || y.as_ref() == "java/lang/Object"
4817 {
4818 return false;
4819 }
4820 let xi = pool.get(x).map(|c| c.is_interface()).unwrap_or(true);
4821 let yi = pool.get(y).map(|c| c.is_interface()).unwrap_or(true);
4822 if xi || yi {
4826 return false;
4827 }
4828 !pool.is_subtype(x, y) && !pool.is_subtype(y, x)
4829 }
4830 (Array(_), Object(y)) | (Object(y), Array(_)) => {
4831 y.as_ref() != "java/lang/Object"
4832 && y.as_ref() != "java/lang/Cloneable"
4833 && y.as_ref() != "java/io/Serializable"
4834 && !pool.get(y).map(|c| c.is_interface()).unwrap_or(false)
4835 }
4836 _ => false,
4837 }
4838 };
4839 let is_ref = |t: &JavaType| matches!(t, JavaType::Object(_) | JavaType::Array(_));
4840 walk_stmt_exprs(body, &mut |e| {
4841 deep_rewrite(e, &mut |x| {
4842 if let Expr::Bin {
4843 op: BinOp::Eq | BinOp::Ne,
4844 l,
4845 r,
4846 ..
4847 } = x
4848 {
4849 let lt = side_ty(l, vt);
4850 let rt = side_ty(r, vt);
4851 if is_ref(<) && matches!(&**r, Expr::Const(ConstVal::Int(0))) {
4857 **r = Expr::Const(ConstVal::Null);
4858 return;
4859 }
4860 if is_ref(&rt) && matches!(&**l, Expr::Const(ConstVal::Int(0))) {
4861 **l = Expr::Const(ConstVal::Null);
4862 return;
4863 }
4864 if incomparable(<, &rt) {
4865 let taken = std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
4866 **l = Expr::Cast {
4867 ty: TypeRef::J(JavaType::Object("java/lang/Object".into())),
4868 e: taken,
4869 };
4870 }
4871 }
4872 });
4873 });
4874}
4875
4876pub fn fix_primitive_arg_bridges(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
4890 fn val_ty(e: &Expr, vt: &VarTable) -> JavaType {
4891 match e {
4892 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4893 other => other.type_ref().erased(),
4894 }
4895 }
4896 #[allow(clippy::type_complexity)]
4897 let is_bool = |t: &JavaType| matches!(t, JavaType::Boolean);
4898 let is_num = |t: &JavaType| {
4899 matches!(
4900 t,
4901 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
4902 | JavaType::Long | JavaType::Float | JavaType::Double
4903 )
4904 };
4905 fn bridge(actual: &JavaType, formal: &JavaType, arg: &Expr) -> Option<Expr> {
4910 match (arg, formal) {
4920 (Expr::Const(ConstVal::Int(b)), JavaType::Float) => {
4921 return Some(Expr::Const(ConstVal::Float(f32::from_bits(*b as u32))));
4922 }
4923 (Expr::Const(ConstVal::Long(b)), JavaType::Double) => {
4924 return Some(Expr::Const(ConstVal::Double(f64::from_bits(*b as u64))));
4925 }
4926 (Expr::Const(ConstVal::Float(f)), JavaType::Int) => {
4927 return Some(Expr::Const(ConstVal::Int(f.to_bits() as i32)));
4928 }
4929 (Expr::Const(ConstVal::Double(d)), JavaType::Long) => {
4930 return Some(Expr::Const(ConstVal::Long(d.to_bits() as i64)));
4931 }
4932 _ => {}
4933 }
4934 if matches!(actual, JavaType::Boolean)
4935 && matches!(
4936 formal,
4937 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
4938 | JavaType::Long | JavaType::Float | JavaType::Double
4939 )
4940 {
4941 Some(Expr::Cast {
4942 ty: TypeRef::J(formal.clone()),
4943 e: Box::new(Expr::Cond {
4944 c: Box::new(arg.clone()),
4945 t: Box::new(Expr::Const(ConstVal::Int(1))),
4946 f: Box::new(Expr::Const(ConstVal::Int(0))),
4947 }),
4948 })
4949 } else if matches!(formal, JavaType::Boolean)
4950 && matches!(
4951 actual,
4952 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
4953 | JavaType::Long | JavaType::Float | JavaType::Double
4954 )
4955 {
4956 Some(Expr::Bin {
4957 op: BinOp::Ne,
4958 l: Box::new(arg.clone()),
4959 r: Box::new(Expr::Const(ConstVal::Int(0))),
4960 ty: Some(TypeRef::J(JavaType::Boolean)),
4961 })
4962 } else if matches!(
4963 actual,
4964 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
4965 | JavaType::Long | JavaType::Float | JavaType::Double
4966 ) && matches!(
4967 formal,
4968 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
4969 | JavaType::Long | JavaType::Float | JavaType::Double
4970 ) && !implicit_widening(actual, formal)
4971 {
4972 Some(Expr::Cast {
4981 ty: TypeRef::J(formal.clone()),
4982 e: Box::new(arg.clone()),
4983 })
4984 } else {
4985 None
4986 }
4987 }
4988 fn fw_resolvable(n: &str) -> bool {
5004 crate::fwdb::exists(n)
5005 }
5006 fn null_arg_cast(a: &mut Expr, f: &JavaType, pool: &DexPool) -> bool {
5007 if !matches!(&*a, Expr::Const(ConstVal::Null)) {
5008 return false;
5009 }
5010 if let JavaType::Object(n) = f {
5011 let resolvable = pool.get(n).is_some() || fw_resolvable(n);
5017 if n.as_ref() != "java/lang/Object" && resolvable {
5018 *a = Expr::Cast {
5019 ty: TypeRef::J(f.clone()),
5020 e: Box::new(Expr::Const(ConstVal::Null)),
5021 };
5022 return true;
5023 }
5024 } else if let JavaType::Array(_) = f {
5025 let mut e: &JavaType = f;
5032 while let JavaType::Array(inner) = e {
5033 e = &**inner;
5034 }
5035 let resolvable = match e {
5036 JavaType::Object(n) => pool.get(n).is_some() || fw_resolvable(n),
5037 _ => true, };
5039 if resolvable {
5040 *a = Expr::Cast {
5041 ty: TypeRef::J(f.clone()),
5042 e: Box::new(Expr::Const(ConstVal::Null)),
5043 };
5044 return true;
5045 }
5046 }
5047 false
5050 }
5051 fn implicit_widening(a: &JavaType, f: &JavaType) -> bool {
5054 use JavaType::*;
5055 match (a, f) {
5056 (Byte, Short) | (Byte, Int) | (Byte, Long) | (Byte, Float) | (Byte, Double) => true,
5057 (Short, Int) | (Short, Long) | (Short, Float) | (Short, Double) => true,
5058 (Char, Int) | (Char, Long) | (Char, Float) | (Char, Double) => true,
5059 (Int, Long) | (Int, Float) | (Int, Double) => true,
5060 (Long, Float) | (Long, Double) => true,
5061 (Float, Double) => true,
5062 (x, y) => x == y,
5063 }
5064 }
5065 let overload_competes = |cls: &str, name: &str, args: &[JavaType]| -> bool {
5074 if let Some(c) = pool.get(cls) {
5075 return c.all_methods().any(|m| {
5076 &*m.name == name
5077 && m.access & (crate::access::ACC_BRIDGE | crate::access::ACC_SYNTHETIC) == 0
5083 && m.parsed_desc()
5084 .is_some_and(|d| d.args.len() == args.len() && d.args != args)
5085 });
5086 }
5087 let _ = args;
5096 false
5097 };
5098 let ctor_formals = |cls: &str, n: usize| -> Option<Vec<JavaType>> {
5101 let c = pool.get(cls)?;
5102 let mut out: Option<Vec<JavaType>> = None;
5103 for m in c.all_methods() {
5104 if &*m.name != "<init>" {
5105 continue;
5106 }
5107 let Some(d) = m.parsed_desc() else { continue };
5108 if d.args.len() != n {
5109 continue;
5110 }
5111 match &out {
5112 None => out = Some(d.args.clone()),
5113 Some(prev) if *prev != d.args => return None,
5114 Some(_) => {}
5115 }
5116 }
5117 out
5118 };
5119 walk_stmt_exprs(body, &mut |e| {
5120 deep_rewrite(e, &mut |x| match x {
5121 Expr::Method {
5122 cls,
5123 name,
5124 desc,
5125 args,
5126 is_static,
5127 ..
5128 } => {
5129 if args.len() != desc.args.len() {
5130 return;
5131 }
5132 let _ = is_static;
5133 let mut competes: Option<bool> = None;
5134 for (a, f) in args.iter_mut().zip(desc.args.iter()) {
5135 if null_arg_cast(a, f, pool) {
5136 continue;
5137 }
5138 if matches!(a, Expr::Cast { .. }) {
5139 continue;
5140 }
5141 let subtype_arg = match (&val_ty(a, vt), f) {
5152 (JavaType::Object(an), JavaType::Object(fn_)) => {
5153 (an != fn_ && pool.is_subtype(an, fn_))
5158 || (fn_.as_ref() == "java/lang/Object"
5159 && an.as_ref() != "java/lang/Object")
5160 }
5161 (JavaType::Array(_), JavaType::Object(fn_))
5162 if fn_.as_ref() == "java/lang/Object" =>
5163 {
5164 true
5165 }
5166 _ => false,
5167 };
5168 let null_obj =
5182 matches!(a, Expr::Const(ConstVal::Null)) && matches!(f, JavaType::Object(_));
5183 let framework_owner = pool.get(cls.as_ref()).is_none();
5184 let fw_null_ambiguous = framework_owner
5185 && matches!(
5186 (cls.as_ref(), name.as_ref()),
5187 ("java/lang/StringBuilder", "append")
5188 | ("java/lang/StringBuffer", "append")
5189 | ("java/io/PrintStream", "println")
5190 | ("java/io/PrintStream", "print")
5191 | ("java/io/PrintWriter", "println")
5192 | ("java/io/PrintWriter", "print")
5193 | ("java/lang/String", "valueOf")
5194 );
5195 let fw_subtype_pinnable = framework_owner
5206 && subtype_arg
5207 && !matches!(f, JavaType::Object(fn_) if fn_.as_ref() == "java/lang/Object")
5208 && matches!(
5209 (cls.as_ref(), name.as_ref()),
5210 ("android/content/Intent", "putExtra")
5211 | ("java/util/concurrent/ScheduledExecutorService", "schedule")
5212 | ("java/util/concurrent/ExecutorService", "submit")
5213 | ("java/util/concurrent/ExecutorService", "invokeAll")
5214 );
5215 if fw_subtype_pinnable
5216 || (subtype_arg
5217 && *competes.get_or_insert_with(|| {
5218 overload_competes(cls, name, &desc.args)
5219 }))
5220 || (null_obj
5221 && (fw_null_ambiguous
5222 || *competes.get_or_insert_with(|| {
5223 overload_competes(cls, name, &desc.args)
5224 })))
5225 {
5226 let old =
5227 std::mem::replace(a, Expr::Const(ConstVal::Null));
5228 *a = Expr::Cast {
5229 ty: TypeRef::J(f.clone()),
5230 e: Box::new(old),
5231 };
5232 continue;
5233 }
5234 if !is_bool(&val_ty(a, vt)) && !is_num(&val_ty(a, vt)) {
5235 continue;
5236 }
5237 if !is_bool(f) && !is_num(f) {
5238 continue;
5239 }
5240 if let Some(b) = bridge(&val_ty(a, vt), f, a) {
5241 *a = b;
5242 }
5243 }
5244 }
5245 Expr::New { cls, args, arg_tys, .. } => {
5246 let formals_owned = if arg_tys.len() == args.len() && !arg_tys.is_empty() {
5247 Some(arg_tys.clone())
5248 } else {
5249 ctor_formals(cls, args.len())
5250 };
5251 if let Some(formals) = formals_owned {
5252 for (a, f) in args.iter_mut().zip(formals.iter()) {
5253 if null_arg_cast(a, f, pool) {
5254 continue;
5255 }
5256 if matches!(a, Expr::Cast { .. }) {
5257 continue;
5258 }
5259 if !is_bool(f) && !is_num(f) {
5260 continue;
5261 }
5262 if let Some(b) = bridge(&val_ty(a, vt), f, a) {
5263 *a = b;
5264 }
5265 }
5266 }
5267 }
5268 _ => {}
5269 });
5270 });
5271}
5272
5273pub fn deshadow_locals(vt: &mut VarTable, pool: &crate::DexPool, class_name: &str, body: &Stmt) {
5285 let segs = pool.root_pkg_segs();
5286 let obscured = crate::classdec::obscured_simples_snapshot();
5290 let self_pkg = class_name.rfind('/').map(|i| &class_name[..i]).unwrap_or("");
5297 let mut same_pkg_simples: jdc_core::FxHashSet<String> = jdc_core::FxHashSet::default();
5298 visit_all_exprs(body, &mut |x| {
5299 let cls = match x {
5300 Expr::Field { cls, is_static: true, .. } => Some(cls.as_ref()),
5301 Expr::Method { cls, is_static: true, .. } => Some(cls.as_ref()),
5302 _ => None,
5303 };
5304 if let Some(c) = cls {
5305 let pkg = c.rfind('/').map(|i| &c[..i]).unwrap_or("");
5306 if pkg == self_pkg {
5307 let simple = &c[c.rfind('/').map(|i| i + 1).unwrap_or(0)..];
5308 let head = simple.split('$').next().unwrap_or(simple);
5309 if !head.is_empty() {
5310 same_pkg_simples.insert(head.to_string());
5311 }
5312 }
5313 }
5314 });
5315 let hit = |name: &str| {
5316 segs.contains(name) || obscured.contains(name) || same_pkg_simples.contains(name)
5317 };
5318 if (segs.is_empty() && obscured.is_empty() && same_pkg_simples.is_empty())
5319 || !vt.vars.iter().any(|v| hit(&v.name))
5320 {
5321 return;
5322 }
5323 let mut taken: jdc_core::FxHashSet<String> =
5324 vt.vars.iter().map(|v| v.name.clone()).collect();
5325 for v in vt.vars.iter_mut() {
5326 if !hit(&v.name) {
5327 continue;
5328 }
5329 let mut k = 0u32;
5330 let cand = loop {
5331 k += 1;
5332 let c = if k == 1 {
5333 format!("{}x", v.name)
5334 } else {
5335 format!("{}x{}", v.name, k)
5336 };
5337 if !taken.contains(&c) {
5338 break c;
5339 }
5340 };
5341 taken.insert(cand.clone());
5342 v.name = cand;
5343 }
5344}
5345
5346pub fn fix_ctor_this_aliases(body: &mut Stmt, vt: &VarTable) {
5372 let Some(this_var) = vt
5373 .vars
5374 .iter()
5375 .find(|v| v.is_param && v.name == "this")
5376 .map(|v| v.id)
5377 else {
5378 return;
5379 };
5380 let mut analysis = VarAnalysis {
5381 assigns: Vec::new(),
5382 reads: Vec::new(),
5383 values: Vec::new(),
5384 };
5385 analyze_vars(body, &mut analysis);
5386 enum Src {
5397 This,
5398 Loc(u32),
5399 Other,
5400 }
5401 let mut sources: jdc_core::FxHashMap<u32, Vec<Src>> = jdc_core::FxHashMap::default();
5402 walk_all(body, &mut |st| {
5403 let (tgt, val): (u32, &Expr) = match st {
5404 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
5405 Expr::Local { var, .. } => (*var, &**value),
5406 _ => return,
5407 },
5408 Stmt::LocalDef { var, init: Some(value), .. } => (*var, value),
5409 _ => return,
5410 };
5411 let s = match val {
5412 Expr::This => Src::This,
5413 Expr::Local { var: src, .. } if *src == this_var => Src::This,
5414 Expr::Local { var: src, .. } => Src::Loc(*src),
5415 _ => Src::Other,
5416 };
5417 sources.entry(tgt).or_default().push(s);
5418 });
5419 let n = vt
5420 .vars
5421 .len()
5422 .max(analysis.assigns.len())
5423 .max(sources.keys().map(|k| *k as usize + 1).max().unwrap_or(0));
5424 let mut alias = vec![false; n];
5425 loop {
5426 let mut changed = false;
5427 for (v, srcs) in &sources {
5428 let vi = *v as usize;
5429 if vi >= n || alias[vi] || srcs.is_empty() {
5430 continue;
5431 }
5432 let ok = srcs.iter().all(|s| match s {
5433 Src::This => true,
5434 Src::Loc(w) => (*w as usize) < n && alias[*w as usize],
5435 Src::Other => false,
5436 });
5437 if ok {
5438 alias[vi] = true;
5439 changed = true;
5440 }
5441 }
5442 if !changed {
5443 break;
5444 }
5445 }
5446 if !alias.iter().any(|&b| b) {
5447 return;
5448 }
5449 walk_stmt_exprs(body, &mut |e| {
5450 deep_rewrite_reads(e, &mut |x| {
5451 if let Expr::Local { var, .. } = x {
5452 if (*var as usize) < n && alias[*var as usize] {
5453 *x = Expr::This;
5454 }
5455 }
5456 });
5457 });
5458}
5459
5460pub fn fix_this0_owners(body: &mut Stmt, class_name: &str) {
5461 walk_stmt_exprs(body, &mut |e| {
5462 deep_rewrite(e, &mut |x| {
5463 if let Expr::Field { owner: Some(_), cls, name, .. } = x {
5464 if name.as_ref() == "this$0" && cls.as_ref() == class_name {
5465 if let Expr::Field { owner, .. } = x {
5466 *owner = None;
5467 }
5468 }
5469 }
5470 });
5471 });
5472}
5473
5474pub fn rescue_primitive_receivers(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
5475 fn prim(t: &JavaType) -> bool {
5476 match t {
5477 JavaType::Int
5478 | JavaType::Long
5479 | JavaType::Short
5480 | JavaType::Byte
5481 | JavaType::Char
5482 | JavaType::Float
5483 | JavaType::Double
5484 | JavaType::Boolean => true,
5485 JavaType::Object(n) => matches!(
5489 n.as_ref(),
5490 "java/lang/Boolean"
5491 | "java/lang/String"
5492 | "java/lang/Integer"
5493 | "java/lang/Long"
5494 | "java/lang/Short"
5495 | "java/lang/Byte"
5496 | "java/lang/Character"
5497 | "java/lang/Float"
5498 | "java/lang/Double"
5499 ),
5500 _ => false,
5501 }
5502 }
5503 walk_stmt_exprs(body, &mut |e| {
5504 deep_rewrite(e, &mut |x| {
5505 let (recv_var, recv_ty) = {
5507 let o: &Expr = match &*x {
5508 Expr::Method { owner: Some(o), .. } => o,
5509 Expr::Field { owner: Some(o), is_static: false, .. } => o,
5510 _ => return,
5511 };
5512 let Expr::Local { var, ty } = o else {
5513 return;
5514 };
5515 let vt_ty = if (*var as usize) < vt.vars.len() {
5516 vt.var(*var).ty.erased()
5517 } else {
5518 ty.erased()
5519 };
5520 if !prim(&vt_ty) {
5521 return;
5522 }
5523 (*var, vt_ty)
5524 };
5525 let cls: std::sync::Arc<str> = match &*x {
5526 Expr::Method { cls, .. } | Expr::Field { cls, .. } => cls.clone(),
5527 _ => return,
5528 };
5529 if let JavaType::Object(n) = &recv_ty {
5538 if n.as_ref() == cls.as_ref() || pool.is_subtype(n.as_ref(), cls.as_ref()) {
5539 return;
5540 }
5541 }
5542 let mut cands: Vec<u32> = Vec::new();
5544 for v in &vt.vars {
5545 if let JavaType::Object(n) = v.ty.erased() {
5546 if n.as_ref() == cls.as_ref()
5547 || pool.is_subtype(n.as_ref(), cls.as_ref())
5548 {
5549 cands.push(v.id);
5550 }
5551 }
5552 }
5553 let id = if cands.len() == 1 {
5554 cands[0]
5555 } else if cands.len() > 1 {
5556 let slot = if (recv_var as usize) < vt.vars.len() {
5561 Some(vt.var(recv_var).slot)
5562 } else {
5563 None
5564 };
5565 let mut hits: Vec<u32> = match slot {
5566 Some(sl) => cands
5567 .iter()
5568 .copied()
5569 .filter(|&c| {
5570 c != recv_var
5571 && (c as usize) < vt.vars.len()
5572 && vt.var(c).slot == sl
5573 })
5574 .collect(),
5575 None => Vec::new(),
5576 };
5577 if hits.is_empty() {
5578 hits = cands
5581 .iter()
5582 .copied()
5583 .filter(|&c| c != recv_var)
5584 .collect();
5585 }
5586 hits.sort_unstable();
5587 hits.dedup();
5588 if hits.is_empty() {
5589 return;
5590 }
5591 *hits.last().unwrap()
5592 } else {
5593 if cands.is_empty() {
5600 return;
5601 }
5602 let slot = if (recv_var as usize) < vt.vars.len() {
5603 vt.var(recv_var).slot
5604 } else {
5605 return;
5606 };
5607 let mut hits: Vec<u32> = cands
5608 .into_iter()
5609 .filter(|&c| {
5610 c != recv_var
5611 && (c as usize) < vt.vars.len()
5612 && vt.var(c).slot == slot
5613 })
5614 .collect();
5615 hits.sort_unstable();
5616 hits.dedup();
5617 if hits.len() != 1 {
5618 return;
5619 }
5620 hits[0]
5621 };
5622 let o: &mut Box<Expr> = match x {
5624 Expr::Method { owner: Some(o), .. } => o,
5625 Expr::Field { owner: Some(o), is_static: false, .. } => o,
5626 _ => return,
5627 };
5628 if let Expr::Local { var, ty } = &mut **o {
5629 if *var == recv_var {
5630 *var = id;
5631 *ty = vt.vars[id as usize].ty.clone();
5632 }
5633 }
5634 });
5635 });
5636
5637}
5638
5639pub fn rescue_arg_return_swaps(
5645 body: &mut Stmt,
5646 vt: &VarTable,
5647 pool: &crate::DexPool,
5648 ret_ty: &JavaType,
5649) {
5650 let mut top_ids: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
5658 for v in &vt.vars {
5659 if v.is_param {
5660 top_ids.insert(v.id);
5661 }
5662 }
5663 if let Stmt::Block(stmts) = body {
5664 for st in stmts.iter() {
5665 if let Stmt::LocalDef { var, .. } = st {
5666 top_ids.insert(*var);
5667 }
5668 }
5669 }
5670 let ret_wants_ref = match ret_ty {
5671 JavaType::Object(n) => n.as_ref() != "java/lang/Object",
5672 JavaType::Array(_) => true,
5673 _ => false,
5674 };
5675 let try_swap = |a: &mut Expr, want: &JavaType| {
5676 let Expr::Local { var, ty } = &mut *a else {
5677 return;
5678 };
5679 if (*var as usize) >= vt.vars.len() {
5680 return;
5681 }
5682 let avt = vt.var(*var).ty.erased();
5683 let avt_ref = matches!(avt, JavaType::Object(_) | JavaType::Array(_));
5684 let want_arr = matches!(want, JavaType::Array(_));
5685 if avt_ref && !(avt == JavaType::Object("java/lang/Object".into())) {
5688 return;
5689 }
5690 if avt_ref && !matches!(want, JavaType::Object(_)) {
5691 return;
5692 }
5693 let slot = vt.var(*var).slot;
5694 let mut hits: Vec<u32> = Vec::new();
5695 for v in &vt.vars {
5696 if v.slot != slot || !top_ids.contains(&v.id) || v.id == *var {
5697 continue;
5698 }
5699 let t = v.ty.erased();
5700 let ok = match (&t, want) {
5701 (JavaType::Object(n), JavaType::Object(w)) => {
5702 n.as_ref() == w.as_ref()
5703 || pool.is_subtype(n.as_ref(), w.as_ref())
5704 }
5705 (JavaType::Array(_), JavaType::Array(_)) => want_arr,
5706 _ => false,
5707 };
5708 if ok {
5709 hits.push(v.id);
5710 }
5711 }
5712 hits.sort_unstable();
5713 hits.dedup();
5714 if hits.is_empty() {
5715 return;
5716 }
5717 let id = *hits.last().unwrap();
5723 *var = id;
5724 *ty = vt.vars[id as usize].ty.clone();
5725 };
5726 walk_stmt_exprs(body, &mut |e| {
5727 deep_rewrite(e, &mut |x| {
5728 match x {
5729 Expr::Method { desc, args, is_dynamic: false, .. } => {
5730 for (i, a) in args.iter_mut().enumerate() {
5731 if let Some(f) = desc.args.get(i) {
5732 if matches!(f, JavaType::Object(n) if n.as_ref() != "java/lang/Object")
5733 || matches!(f, JavaType::Array(_))
5734 {
5735 try_swap(a, f);
5736 }
5737 }
5738 }
5739 }
5740 Expr::New { arg_tys, args, .. } => {
5741 for (i, a) in args.iter_mut().enumerate() {
5742 if let Some(f) = arg_tys.get(i) {
5743 if matches!(f, JavaType::Object(n) if n.as_ref() != "java/lang/Object")
5744 || matches!(f, JavaType::Array(_))
5745 {
5746 try_swap(a, f);
5747 }
5748 }
5749 }
5750 }
5751 _ => {}
5752 }
5753 });
5754 });
5755 if ret_wants_ref {
5756 walk_mut_deep(body, &mut |st| {
5757 if let Stmt::Return(Some(e)) = st {
5758 try_swap(e, ret_ty);
5759 }
5760 });
5761 }
5762}
5763
5764pub fn fix_ref_null_assigns(vt: &VarTable, body: &mut Stmt) {
5779 let is_ref_var = |var: u32| -> bool {
5780 (var as usize) < vt.vars.len()
5781 && matches!(
5782 vt.vars[var as usize].ty.erased(),
5783 JavaType::Object(_) | JavaType::Array(_)
5784 )
5785 };
5786 walk_mut_deep(body, &mut |st| match st {
5787 Stmt::ExprStmt(Expr::Assign {
5788 target,
5789 value,
5790 op: AssignOp::Plain,
5791 ..
5792 }) => {
5793 if let Expr::Local { var, .. } = &**target {
5794 if is_ref_var(*var) && matches!(&**value, Expr::Const(ConstVal::Int(0))) {
5795 **value = Expr::Const(ConstVal::Null);
5796 }
5797 }
5798 }
5799 Stmt::LocalDef {
5800 var,
5801 init: Some(e),
5802 ..
5803 } => {
5804 if is_ref_var(*var) && matches!(e, Expr::Const(ConstVal::Int(0))) {
5805 *e = Expr::Const(ConstVal::Null);
5806 }
5807 }
5808 _ => {}
5809 });
5810}
5811
5812pub fn fix_ref_array_null_consts(body: &mut Stmt) {
5813 walk_stmt_exprs(body, &mut |e| {
5814 deep_rewrite(e, &mut |x| {
5815 if let Expr::NewArray { elem, init: Some(list), .. } = x {
5816 if matches!(elem.erased(), JavaType::Object(_) | JavaType::Array(_)) {
5817 for slot in list.iter_mut() {
5818 if matches!(&*slot, Expr::Const(ConstVal::Int(0))) {
5819 *slot = Expr::Const(ConstVal::Null);
5820 }
5821 }
5822 }
5823 }
5824 });
5825 });
5826}
5827
5828pub fn fix_null_sentinels(body: &mut Stmt, vt: &VarTable, ret: &JavaType) {
5867 #[derive(Clone, Copy, PartialEq)]
5868 enum WK {
5869 Zero,
5870 Copy(u32),
5871 Other,
5872 }
5873 fn is_ref_jt(t: &JavaType) -> bool {
5874 matches!(t, JavaType::Object(_) | JavaType::Array(_))
5875 }
5876 fn cls_write(e: &Expr) -> WK {
5877 match e {
5878 Expr::Const(ConstVal::Int(0)) => WK::Zero,
5879 Expr::Local { var, .. } => WK::Copy(*var),
5880 _ => WK::Other,
5881 }
5882 }
5883 fn expr_writes(e: &Expr, writes: &mut jdc_core::FxHashMap<u32, Vec<WK>>) {
5884 visit_exprs(e, &mut |x| match x {
5885 Expr::Assign { target, op, value } => {
5886 if let Expr::Local { var, .. } = &**target {
5887 let k = if matches!(op, AssignOp::Plain) {
5888 cls_write(value)
5889 } else {
5890 WK::Other
5891 };
5892 writes.entry(*var).or_default().push(k);
5893 }
5894 }
5895 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
5896 if let Expr::Local { var, .. } = &**inner {
5897 writes.entry(*var).or_default().push(WK::Other);
5898 }
5899 }
5900 _ => {}
5901 });
5902 }
5903 fn collect_writes(
5904 s: &Stmt,
5905 writes: &mut jdc_core::FxHashMap<u32, Vec<WK>>,
5906 has_zero: &mut bool,
5907 ) {
5908 fn note(e: &Expr, has_zero: &mut bool, writes: &mut jdc_core::FxHashMap<u32, Vec<WK>>) {
5909 visit_exprs(e, &mut |x| {
5910 if let Expr::Const(ConstVal::Int(0)) = x {
5911 *has_zero = true;
5912 }
5913 });
5914 expr_writes(e, writes);
5915 }
5916 match s {
5917 Stmt::Block(v) => {
5918 for x in v {
5919 collect_writes(x, writes, has_zero);
5920 }
5921 }
5922 Stmt::LocalDef { var, init, .. } => {
5923 if let Some(e) = init {
5924 if matches!(e, Expr::Const(ConstVal::Int(0))) {
5925 *has_zero = true;
5926 }
5927 writes.entry(*var).or_default().push(cls_write(e));
5928 }
5929 }
5930 Stmt::ExprStmt(e) => note(e, has_zero, writes),
5931 Stmt::Return(e) => {
5932 if let Some(x) = e {
5933 note(x, has_zero, writes);
5934 }
5935 }
5936 Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
5937 note(e, has_zero, writes);
5938 }
5939 Stmt::TernaryValue { e } => note(e, has_zero, writes),
5940 Stmt::If { cond, then_stmt, else_stmt } => {
5941 note(cond, has_zero, writes);
5942 collect_writes(then_stmt, writes, has_zero);
5943 if let Some(b) = else_stmt {
5944 collect_writes(b, writes, has_zero);
5945 }
5946 }
5947 Stmt::While { cond, body } | Stmt::DoWhile { body, cond } => {
5948 note(cond, has_zero, writes);
5949 collect_writes(body, writes, has_zero);
5950 }
5951 Stmt::For { init, cond, update, body } => {
5952 for x in init {
5953 collect_writes(x, writes, has_zero);
5954 }
5955 if let Some(c) = cond {
5956 note(c, has_zero, writes);
5957 }
5958 for u in update {
5959 note(u, has_zero, writes);
5960 }
5961 collect_writes(body, writes, has_zero);
5962 }
5963 Stmt::ForEach { var, iterable, body, .. } => {
5964 writes.entry(*var).or_default().push(WK::Other);
5965 note(iterable, has_zero, writes);
5966 collect_writes(body, writes, has_zero);
5967 }
5968 Stmt::Switch { selector, cases, default, .. } => {
5969 note(selector, has_zero, writes);
5970 for c in cases {
5971 if let Some(g) = &c.guard {
5972 note(g, has_zero, writes);
5973 }
5974 for x in &c.body {
5975 collect_writes(x, writes, has_zero);
5976 }
5977 }
5978 if let Some(d) = default {
5979 collect_writes(d, writes, has_zero);
5980 }
5981 }
5982 Stmt::Try { body, catches, finally } => {
5983 collect_writes(body, writes, has_zero);
5984 for c in catches {
5985 writes.entry(c.var).or_default().push(WK::Other);
5986 collect_writes(&c.body, writes, has_zero);
5987 }
5988 if let Some(f) = finally {
5989 collect_writes(f, writes, has_zero);
5990 }
5991 }
5992 Stmt::TryWithResources { resources, body, catches, finally, .. } => {
5993 for r in resources {
5994 collect_writes(r, writes, has_zero);
5995 }
5996 collect_writes(body, writes, has_zero);
5997 for c in catches {
5998 writes.entry(c.var).or_default().push(WK::Other);
5999 collect_writes(&c.body, writes, has_zero);
6000 }
6001 if let Some(f) = finally {
6002 collect_writes(f, writes, has_zero);
6003 }
6004 }
6005 Stmt::Assert { cond, msg } => {
6006 note(cond, has_zero, writes);
6007 if let Some(m) = msg {
6008 note(m, has_zero, writes);
6009 }
6010 }
6011 Stmt::Synchronized { lock, body } => {
6012 note(lock, has_zero, writes);
6013 collect_writes(body, writes, has_zero);
6014 }
6015 Stmt::Labeled { body, .. } => collect_writes(body, writes, has_zero),
6016 _ => {}
6017 }
6018 }
6019
6020 let mut writes: jdc_core::FxHashMap<u32, Vec<WK>> =
6021 jdc_core::FxHashMap::default();
6022 let mut has_zero = false;
6023 collect_writes(body, &mut writes, &mut has_zero);
6024
6025 let mut eligible: HashSet<u32> = HashSet::default();
6027 let int_typed = |v: u32| {
6028 (v as usize) < vt.vars.len() && vt.vars[v as usize].ty.erased() == JavaType::Int
6029 };
6030 loop {
6031 let mut changed = false;
6032 for (&v, ws) in writes.iter() {
6033 if eligible.contains(&v) || ws.is_empty() || !int_typed(v) {
6034 continue;
6035 }
6036 if ws.iter().all(|k| match k {
6037 WK::Zero => true,
6038 WK::Copy(u) => *u != v && eligible.contains(u),
6039 WK::Other => false,
6040 }) {
6041 eligible.insert(v);
6042 changed = true;
6043 }
6044 }
6045 if !changed {
6046 break;
6047 }
6048 }
6049 if eligible.is_empty() && !has_zero {
6050 return; }
6052
6053 #[derive(Clone, Copy, PartialEq)]
6054 enum Mode {
6055 Scan,
6056 Rewrite,
6057 }
6058 struct St<'a> {
6059 mode: Mode,
6060 vt: &'a VarTable,
6061 eligible: &'a HashSet<u32>,
6062 ref_read: HashSet<u32>,
6063 dead: HashSet<u32>,
6064 sentinels: HashSet<u32>,
6065 n_lit: usize,
6066 n_var: usize,
6067 }
6068 fn arg_ctx(t: &JavaType) -> Option<JavaType> {
6072 match t {
6073 JavaType::Object(n) if n.as_ref() == "java/lang/Object" => None,
6074 t if is_ref_jt(t) => Some(t.clone()),
6075 _ => None,
6076 }
6077 }
6078 fn walk_expr(e: &mut Expr, ctx: Option<JavaType>, st: &mut St) {
6079 walk_expr_c(e, ctx, false, st);
6080 }
6081 fn walk_expr_c(e: &mut Expr, ctx: Option<JavaType>, cast_arg: bool, st: &mut St) {
6087 let slot_ref = ctx.as_ref().is_some_and(is_ref_jt);
6088 fn mk_null(ctx: &Option<JavaType>, cast_arg: bool) -> Expr {
6089 match (cast_arg, ctx) {
6090 (true, Some(t)) => Expr::Cast {
6091 ty: TypeRef::J(t.clone()),
6092 e: Box::new(Expr::Const(ConstVal::Null)),
6093 },
6094 _ => Expr::Const(ConstVal::Null),
6095 }
6096 }
6097 match e {
6098 Expr::Const(ConstVal::Int(0)) if slot_ref => {
6099 if st.mode == Mode::Rewrite {
6100 *e = mk_null(&ctx, cast_arg);
6101 st.n_lit += 1;
6102 }
6103 return;
6104 }
6105 Expr::Local { var, .. } => {
6106 if st.eligible.contains(var) {
6107 if slot_ref {
6108 st.ref_read.insert(*var);
6109 } else {
6110 st.dead.insert(*var);
6111 }
6112 }
6113 if st.mode == Mode::Rewrite && slot_ref && st.sentinels.contains(var) {
6114 *e = mk_null(&ctx, cast_arg);
6115 st.n_var += 1;
6116 }
6117 return;
6118 }
6119 _ => {}
6120 }
6121 match e {
6122 Expr::Method { owner, desc, args, .. } => {
6123 if let Some(o) = owner {
6124 walk_expr(o, None, st);
6125 }
6126 for (i, a) in args.iter_mut().enumerate() {
6127 let c = desc.args.get(i).and_then(arg_ctx);
6128 walk_expr_c(a, c, true, st);
6129 }
6130 }
6131 Expr::Invokedynamic { desc, args, .. } => {
6132 for (i, a) in args.iter_mut().enumerate() {
6133 let c = desc.args.get(i).and_then(arg_ctx);
6134 walk_expr_c(a, c, true, st);
6135 }
6136 }
6137 Expr::New { args, arg_tys, .. } => {
6138 for (i, a) in args.iter_mut().enumerate() {
6139 let c = arg_tys.get(i).and_then(arg_ctx);
6140 walk_expr_c(a, c, true, st);
6141 }
6142 }
6143 Expr::AnonNew { args, .. } => {
6144 for a in args.iter_mut() {
6145 walk_expr(a, None, st);
6146 }
6147 }
6148 Expr::Assign { target, value, .. } => {
6149 if let Expr::Local { var: u, .. } = &**target {
6153 if st.eligible.contains(u) {
6154 return;
6155 }
6156 }
6157 let vctx = match &**target {
6158 Expr::Field { ty, .. } => {
6159 let t = ty.erased();
6160 is_ref_jt(&t).then_some(t)
6161 }
6162 Expr::ArrayIndex { array, .. } => match array.type_ref().erased() {
6163 JavaType::Array(inner) if is_ref_jt(&inner) => Some(*inner),
6164 _ => None,
6165 },
6166 _ => None,
6167 };
6168 walk_expr(target, None, st);
6169 walk_expr(value, vctx, st);
6170 }
6171 Expr::Cast { ty, e: inner } => {
6172 let t = ty.erased();
6173 let c = is_ref_jt(&t).then_some(t);
6174 walk_expr(inner, c, st);
6175 }
6176 Expr::InstanceOf { e: inner, .. } => {
6177 walk_expr(inner, Some(JavaType::Object("java/lang/Object".into())), st);
6178 }
6179 Expr::Cond { c, t, f } => {
6180 walk_expr(c, None, st);
6181 let tc = ctx.clone();
6182 walk_expr(t, tc, st);
6183 walk_expr(f, ctx, st);
6184 }
6185 Expr::NewArray { elem, dims, init, .. } => {
6186 for d in dims.iter_mut() {
6187 walk_expr(d, None, st);
6188 }
6189 if let Some(list) = init {
6190 let et = elem.erased();
6191 let c = is_ref_jt(&et).then_some(et);
6192 for slot in list.iter_mut() {
6193 walk_expr(slot, c.clone(), st);
6194 }
6195 }
6196 }
6197 Expr::NewMultiArray { dims, .. } => {
6198 for d in dims.iter_mut() {
6199 walk_expr(d, None, st);
6200 }
6201 }
6202 Expr::ArrayIndex { array, index } => {
6203 walk_expr(array, None, st);
6204 walk_expr(index, None, st);
6205 }
6206 Expr::Field { owner, .. } => {
6207 if let Some(o) = owner {
6208 walk_expr(o, None, st);
6209 }
6210 }
6211 Expr::Bin { l, r, .. } => {
6212 walk_expr(l, None, st);
6213 walk_expr(r, None, st);
6214 }
6215 Expr::Un { e: inner, .. } => walk_expr(inner, None, st),
6216 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
6217 walk_expr(inner, None, st);
6218 }
6219 Expr::StringConcat(parts) => {
6220 for p in parts.iter_mut() {
6221 if let ConcatPart::Str(x) = p {
6222 walk_expr(x, None, st);
6223 }
6224 }
6225 }
6226 Expr::Lambda(l) => {
6227 for c in l.captures.iter_mut() {
6228 walk_expr(c, None, st);
6229 }
6230 }
6231 _ => {}
6232 }
6233 }
6234 fn walk_stmt(s: &mut Stmt, st: &mut St, ret: &JavaType) {
6235 match s {
6236 Stmt::Block(v) => {
6237 for x in v.iter_mut() {
6238 walk_stmt(x, st, ret);
6239 }
6240 }
6241 Stmt::ExprStmt(e) => walk_expr(e, None, st),
6242 Stmt::LocalDef { var, init, .. } => {
6243 if let Some(e) = init {
6244 let ctx = if (*var as usize) < st.vt.vars.len() {
6245 let t = st.vt.vars[*var as usize].ty.erased();
6246 is_ref_jt(&t).then_some(t)
6247 } else {
6248 None
6249 };
6250 walk_expr(e, ctx, st);
6251 }
6252 }
6253 Stmt::Return(e) => {
6254 if let Some(x) = e {
6255 let c = is_ref_jt(ret).then(|| ret.clone());
6256 walk_expr(x, c, st);
6257 }
6258 }
6259 Stmt::Throw(e) => {
6260 walk_expr(e, Some(JavaType::Object("java/lang/Throwable".into())), st);
6261 }
6262 Stmt::If { cond, then_stmt, else_stmt } => {
6263 walk_expr(cond, None, st);
6264 walk_stmt(then_stmt, st, ret);
6265 if let Some(b) = else_stmt {
6266 walk_stmt(b, st, ret);
6267 }
6268 }
6269 Stmt::While { cond, body } => {
6270 walk_expr(cond, None, st);
6271 walk_stmt(body, st, ret);
6272 }
6273 Stmt::DoWhile { body, cond } => {
6274 walk_stmt(body, st, ret);
6275 walk_expr(cond, None, st);
6276 }
6277 Stmt::For { init, cond, update, body } => {
6278 for x in init.iter_mut() {
6279 walk_stmt(x, st, ret);
6280 }
6281 if let Some(c) = cond {
6282 walk_expr(c, None, st);
6283 }
6284 for u in update.iter_mut() {
6285 walk_expr(u, None, st);
6286 }
6287 walk_stmt(body, st, ret);
6288 }
6289 Stmt::ForEach { iterable, body, .. } => {
6290 walk_expr(iterable, None, st);
6291 walk_stmt(body, st, ret);
6292 }
6293 Stmt::Switch { selector, cases, default, .. } => {
6294 walk_expr(selector, None, st);
6295 for c in cases.iter_mut() {
6296 if let Some(g) = &mut c.guard {
6297 walk_expr(g, None, st);
6298 }
6299 for x in c.body.iter_mut() {
6300 walk_stmt(x, st, ret);
6301 }
6302 }
6303 if let Some(d) = default {
6304 walk_stmt(d, st, ret);
6305 }
6306 }
6307 Stmt::Try { body, catches, finally } => {
6308 walk_stmt(body, st, ret);
6309 for c in catches.iter_mut() {
6310 walk_stmt(&mut c.body, st, ret);
6311 }
6312 if let Some(f) = finally {
6313 walk_stmt(f, st, ret);
6314 }
6315 }
6316 Stmt::TryWithResources { resources, body, catches, finally, .. } => {
6317 for r in resources.iter_mut() {
6318 walk_stmt(r, st, ret);
6319 }
6320 walk_stmt(body, st, ret);
6321 for c in catches.iter_mut() {
6322 walk_stmt(&mut c.body, st, ret);
6323 }
6324 if let Some(f) = finally {
6325 walk_stmt(f, st, ret);
6326 }
6327 }
6328 Stmt::Assert { cond, msg } => {
6329 walk_expr(cond, None, st);
6330 if let Some(m) = msg {
6331 walk_expr(m, None, st);
6332 }
6333 }
6334 Stmt::Synchronized { lock, body } => {
6335 walk_expr(lock, Some(JavaType::Object("java/lang/Object".into())), st);
6336 walk_stmt(body, st, ret);
6337 }
6338 Stmt::TernaryValue { e } => walk_expr(e, None, st),
6339 Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
6340 walk_expr(e, Some(JavaType::Object("java/lang/Object".into())), st);
6341 }
6342 Stmt::Labeled { body, .. } => walk_stmt(body, st, ret),
6343 _ => {}
6344 }
6345 }
6346
6347 let mut st = St {
6348 mode: Mode::Scan,
6349 vt,
6350 eligible: &eligible,
6351 ref_read: HashSet::default(),
6352 dead: HashSet::default(),
6353 sentinels: HashSet::default(),
6354 n_lit: 0,
6355 n_var: 0,
6356 };
6357 if !eligible.is_empty() {
6358 walk_stmt(body, &mut st, ret);
6359 st.sentinels = eligible
6360 .iter()
6361 .copied()
6362 .filter(|v| st.ref_read.contains(v) && !st.dead.contains(v))
6363 .collect();
6364 }
6365 if st.sentinels.is_empty() && !has_zero {
6366 return;
6367 }
6368 st.mode = Mode::Rewrite;
6369 walk_stmt(body, &mut st, ret);
6370 if std::env::var_os("DDC_SENT").is_some() {
6371 eprintln!(
6372 "[sent] elig={} confirmed={} lit={} var={}",
6373 eligible.len(),
6374 st.sentinels.len(),
6375 st.n_lit,
6376 st.n_var
6377 );
6378 }
6379}
6380
6381pub fn fix_int_operand_bridges(body: &mut Stmt, vt: &VarTable) {
6389 fn side_bool(e: &Expr, vt: &VarTable) -> bool {
6390 match e {
6391 Expr::Local { var, .. } => {
6392 matches!(vt.var(*var).ty.erased(), JavaType::Boolean)
6393 }
6394 Expr::Bin {
6404 op: BinOp::And | BinOp::Or | BinOp::Xor,
6405 l,
6406 r,
6407 ..
6408 } => side_bool(l, vt) && side_bool(r, vt),
6409 other => matches!(other.type_ref().erased(), JavaType::Boolean),
6410 }
6411 }
6412 fn side_int(e: &Expr, vt: &VarTable) -> bool {
6413 if side_bool(e, vt) {
6417 return false;
6418 }
6419 matches!(
6420 match e {
6421 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
6422 other => other.type_ref().erased(),
6423 },
6424 JavaType::Int | JavaType::Short | JavaType::Byte | JavaType::Char | JavaType::Long
6425 )
6426 }
6427 fn wrap(e: Box<Expr>) -> Box<Expr> {
6428 Box::new(Expr::Cond {
6429 c: e,
6430 t: Box::new(Expr::Const(ConstVal::Int(1))),
6431 f: Box::new(Expr::Const(ConstVal::Int(0))),
6432 })
6433 }
6434 walk_stmt_exprs(body, &mut |e| {
6435 deep_rewrite(e, &mut |x| {
6436 if let Expr::Assign { target, value, op: AssignOp::Plain } = x {
6443 let tgt_ty: Option<JavaType> = match &**target {
6444 Expr::Field { ty, .. } => Some(ty.erased()),
6445 Expr::ArrayIndex { array, .. } => {
6446 match array.type_ref().erased() {
6447 JavaType::Array(el) => Some(el.as_ref().clone()),
6448 _ => None,
6449 }
6450 }
6451 Expr::Local { var, .. } => {
6456 if (*var as usize) < vt.vars.len() {
6457 Some(vt.var(*var).ty.erased())
6458 } else {
6459 None
6460 }
6461 }
6462 _ => None,
6463 };
6464 if let Some(t) = tgt_ty {
6465 let tgt_int = matches!(
6466 t,
6467 JavaType::Int
6468 | JavaType::Short
6469 | JavaType::Byte
6470 | JavaType::Char
6471 | JavaType::Long
6472 );
6473 if tgt_int && side_bool(value, vt) && !side_int(value, vt) {
6474 let taken = std::mem::replace(
6475 value,
6476 Box::new(Expr::Const(ConstVal::Null)),
6477 );
6478 let mut wrapped = wrap(taken);
6479 if !matches!(t, JavaType::Int | JavaType::Long) {
6480 wrapped = Box::new(Expr::Cast {
6481 ty: TypeRef::J(t.clone()),
6482 e: wrapped,
6483 });
6484 }
6485 *value = wrapped;
6486 } else if matches!(t, JavaType::Boolean)
6487 && side_int(value, vt)
6488 && !side_bool(value, vt)
6489 {
6490 let taken = std::mem::replace(
6491 value,
6492 Box::new(Expr::Const(ConstVal::Null)),
6493 );
6494 **value = Expr::Bin {
6495 op: BinOp::Ne,
6496 l: taken,
6497 r: Box::new(Expr::Const(ConstVal::Int(0))),
6498 ty: Some(TypeRef::J(JavaType::Boolean)),
6499 };
6500 }
6501 }
6502 }
6503 if let Expr::Cast { ty, e } = x {
6509 let t = ty.erased();
6510 let t_num = matches!(
6511 t,
6512 JavaType::Int
6513 | JavaType::Short
6514 | JavaType::Byte
6515 | JavaType::Char
6516 | JavaType::Long
6517 | JavaType::Float
6518 | JavaType::Double
6519 );
6520 if t_num && side_bool(e, vt) && !side_int(e, vt) {
6521 let taken = std::mem::replace(e, Box::new(Expr::Const(ConstVal::Null)));
6522 **e = Expr::Cond {
6523 c: taken,
6524 t: Box::new(Expr::Const(ConstVal::Int(1))),
6525 f: Box::new(Expr::Const(ConstVal::Int(0))),
6526 };
6527 } else if matches!(t, JavaType::Boolean)
6528 && side_int(e, vt)
6529 && !side_bool(e, vt)
6530 {
6531 let taken = std::mem::replace(e, Box::new(Expr::Const(ConstVal::Null)));
6532 *x = Expr::Bin {
6533 op: BinOp::Ne,
6534 l: taken,
6535 r: Box::new(Expr::Const(ConstVal::Int(0))),
6536 ty: Some(TypeRef::J(JavaType::Boolean)),
6537 };
6538 }
6539 }
6540 if let Expr::Bin { op, l, r, .. } = x {
6549 if matches!(op, BinOp::Eq | BinOp::Ne) {
6550 if matches!(&**r, Expr::Const(ConstVal::Null))
6551 && side_int(l, vt)
6552 && !side_bool(l, vt)
6553 {
6554 **r = Expr::Const(ConstVal::Int(0));
6555 } else if matches!(&**l, Expr::Const(ConstVal::Null))
6556 && side_int(r, vt)
6557 && !side_bool(r, vt)
6558 {
6559 **l = Expr::Const(ConstVal::Int(0));
6560 } else if matches!(&**r, Expr::Const(ConstVal::Null))
6561 && side_bool(l, vt)
6562 {
6563 let taken =
6564 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
6565 *x = if matches!(op, BinOp::Eq) {
6566 Expr::Un { op: UnOp::Not, e: taken }
6567 } else {
6568 *taken
6569 };
6570 } else if matches!(&**l, Expr::Const(ConstVal::Null))
6571 && side_bool(r, vt)
6572 {
6573 let taken =
6574 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
6575 *x = if matches!(op, BinOp::Eq) {
6576 Expr::Un { op: UnOp::Not, e: taken }
6577 } else {
6578 *taken
6579 };
6580 }
6581 }
6582 }
6583 if let Expr::ArrayIndex { index, .. } = x {
6587 if side_bool(index, vt) && !side_int(index, vt) {
6588 let taken =
6589 std::mem::replace(index, Box::new(Expr::Const(ConstVal::Null)));
6590 *index = wrap(taken);
6591 }
6592 }
6593 if let Expr::NewArray { dims, .. } = x {
6599 for d in dims.iter_mut() {
6600 if side_bool(d, vt) && !side_int(d, vt) {
6601 let taken =
6602 std::mem::replace(d, Expr::Const(ConstVal::Null));
6603 *d = *wrap(Box::new(taken));
6604 }
6605 }
6606 }
6607 if let Expr::Bin { op, l, r, .. } = x {
6608 let int_kind = matches!(
6609 op,
6610 BinOp::Or
6611 | BinOp::And
6612 | BinOp::Xor
6613 | BinOp::Add
6614 | BinOp::Sub
6615 | BinOp::Mul
6616 | BinOp::Div
6617 | BinOp::Rem
6618 | BinOp::Shl
6619 | BinOp::Shr
6620 | BinOp::Ushr
6621 | BinOp::Eq
6622 | BinOp::Ne
6623 | BinOp::Lt
6624 | BinOp::Ge
6625 | BinOp::Gt
6626 | BinOp::Le
6627 );
6628 if !int_kind {
6629 return;
6630 }
6631 if matches!(
6644 op,
6645 BinOp::Or
6646 | BinOp::And
6647 | BinOp::Xor
6648 | BinOp::Add
6649 | BinOp::Sub
6650 | BinOp::Mul
6651 | BinOp::Div
6652 | BinOp::Rem
6653 | BinOp::Shl
6654 | BinOp::Shr
6655 | BinOp::Ushr
6656 ) {
6657 if matches!(&**r, Expr::Const(ConstVal::Null))
6658 && (side_bool(l, vt) || side_int(l, vt))
6659 {
6660 **r = Expr::Const(ConstVal::Int(0));
6661 } else if matches!(&**l, Expr::Const(ConstVal::Null))
6662 && (side_bool(r, vt) || side_int(r, vt))
6663 {
6664 **l = Expr::Const(ConstVal::Int(0));
6665 }
6666 }
6667 let arith = matches!(
6677 op,
6678 BinOp::Add
6679 | BinOp::Sub
6680 | BinOp::Mul
6681 | BinOp::Div
6682 | BinOp::Rem
6683 | BinOp::Shl
6684 | BinOp::Shr
6685 | BinOp::Ushr
6686 );
6687 let ordered = matches!(op, BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le);
6688 if arith || ordered {
6689 let side_str = |e: &Expr| {
6690 matches!(
6691 match e {
6692 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
6693 other => other.type_ref().erased(),
6694 },
6695 JavaType::Object(ref s) if s.as_ref() == "java/lang/String"
6696 )
6697 };
6698 let concat = arith && (side_str(l) || side_str(r));
6699 if !concat {
6700 if side_bool(l, vt) {
6701 let taken =
6702 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
6703 *l = wrap(taken);
6704 }
6705 if side_bool(r, vt) {
6706 let taken =
6707 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
6708 *r = wrap(taken);
6709 }
6710 }
6711 return;
6712 }
6713 if side_bool(l, vt) && side_int(r, vt) && !side_bool(r, vt) {
6716 let taken = std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
6717 *l = wrap(taken);
6718 } else if side_bool(r, vt) && side_int(l, vt) && !side_bool(l, vt) {
6719 let taken = std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
6720 *r = wrap(taken);
6721 }
6722 }
6723 });
6724 });
6725}
6726
6727pub fn fold_value_diamonds(body: &mut Stmt, vt: &VarTable) {
6765 let Stmt::Block(stmts) = body else { return };
6766 fold_diamonds_in(stmts, vt);
6767}
6768
6769fn diamond_types_ok(a: &Expr, b: &Expr) -> bool {
6770 fn num(t: &JavaType) -> bool {
6771 matches!(
6772 t,
6773 JavaType::Byte
6774 | JavaType::Short
6775 | JavaType::Int
6776 | JavaType::Char
6777 | JavaType::Long
6778 | JavaType::Float
6779 | JavaType::Double
6780 )
6781 }
6782 if let Some(other) = if matches!(a, Expr::Const(ConstVal::Null)) {
6790 Some(b)
6791 } else if matches!(b, Expr::Const(ConstVal::Null)) {
6792 Some(a)
6793 } else {
6794 None
6795 } {
6796 return !num(&other.type_ref().erased());
6797 }
6798 let ta = a.type_ref().erased();
6799 let tb = b.type_ref().erased();
6800 ta == tb || (num(&ta) && num(&tb))
6801}
6802
6803fn single_local_assign(st: &Stmt) -> Option<(u32, Expr, Expr)> {
6811 let mut inner = st;
6812 while let Stmt::Block(v) = inner {
6813 if v.len() != 1 {
6814 return None;
6815 }
6816 inner = &v[0];
6817 }
6818 match inner {
6819 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain }) => {
6820 match &**target {
6821 Expr::Local { var, .. } => Some((*var, *target.clone(), *value.clone())),
6822 _ => None,
6823 }
6824 }
6825 _ => None,
6826 }
6827}
6828
6829fn arm_assign_inlined(st: &Stmt) -> Option<(u32, Expr, Expr)> {
6838 if let Some(r) = single_local_assign(st) {
6839 return Some(r);
6840 }
6841 let mut inner = st;
6842 while let Stmt::Block(v) = inner {
6843 if v.len() == 1 {
6844 inner = &v[0];
6845 } else {
6846 break;
6847 }
6848 }
6849 let Stmt::Block(v) = inner else { return None };
6850 if v.len() != 2 {
6851 return None;
6852 }
6853 let Stmt::LocalDef {
6854 var: x,
6855 init: Some(init),
6856 ..
6857 } = &v[0]
6858 else {
6859 return None;
6860 };
6861 let (var, tgt, val) = single_local_assign(&v[1])?;
6862 let mut count = 0usize;
6863 let mut c = val.clone();
6864 deep_rewrite(&mut c, &mut |e| {
6865 if let Expr::Local { var: lv, .. } = e {
6866 if lv == x {
6867 count += 1;
6868 }
6869 }
6870 });
6871 if count != 1 {
6872 return None;
6873 }
6874 let mut out = val.clone();
6875 deep_rewrite(&mut out, &mut |e| {
6876 if let Expr::Local { var: lv, .. } = e {
6877 if lv == x {
6878 *e = init.clone();
6879 }
6880 }
6881 });
6882 Some((var, tgt, out))
6883}
6884
6885fn mk_ternary_assign(tgt: Expr, c: Expr, a: Expr, b: Expr) -> Stmt {
6886 Stmt::ExprStmt(Expr::Assign {
6887 target: Box::new(tgt),
6888 op: AssignOp::Plain,
6889 value: Box::new(Expr::Cond {
6890 c: Box::new(c),
6891 t: Box::new(a),
6892 f: Box::new(b),
6893 }),
6894 })
6895}
6896
6897fn same_render_var(v1: u32, v2: u32, vt: &VarTable) -> bool {
6902 let (a, b) = (vt.vars.get(v1 as usize), vt.vars.get(v2 as usize));
6903 match (a, b) {
6904 (Some(x), Some(y)) => x.name == y.name && x.ty.erased() == y.ty.erased(),
6905 _ => false,
6906 }
6907}
6908
6909fn canon_stmts_eq(a: &[Stmt], b: &[Stmt], vt: &VarTable) -> bool {
6916 if a.len() != b.len() {
6917 return false;
6918 }
6919 let mut map: std::collections::HashMap<String, u32> = std::collections::HashMap::default();
6920 let canon = |s: &Stmt, map: &mut std::collections::HashMap<String, u32>| -> Stmt {
6921 let mut c = s.clone();
6922 walk_stmt_exprs(&mut c, &mut |e| {
6923 deep_rewrite(e, &mut |x| {
6924 if let Expr::Local { var, .. } = x {
6925 let key = match vt.vars.get(*var as usize) {
6926 Some(vi) => format!("{}#{:?}", vi.name, vi.ty.erased()),
6927 None => format!("v{}", var),
6928 };
6929 let id = *map.entry(key).or_insert(*var);
6930 *x = Expr::Local {
6931 var: id,
6932 ty: vt.var(id).ty.clone(),
6933 };
6934 }
6935 });
6936 });
6937 c
6938 };
6939 a.iter()
6940 .zip(b.iter())
6941 .all(|(x, y)| canon(x, &mut map) == canon(y, &mut map))
6942}
6943
6944fn fold_diamonds_in(stmts: &mut Vec<Stmt>, vt: &VarTable) {
6945 let mut i = 0usize;
6946 while i < stmts.len() {
6947 match &mut stmts[i] {
6948 Stmt::Block(v) => fold_diamonds_in(v, vt),
6949 Stmt::If { then_stmt, else_stmt, .. } => {
6950 if let Stmt::Block(v) = &mut **then_stmt {
6951 fold_diamonds_in(v, vt);
6952 }
6953 if let Some(e) = else_stmt {
6954 if let Stmt::Block(v) = &mut **e {
6955 fold_diamonds_in(v, vt);
6956 }
6957 }
6958 }
6959 _ => {}
6960 }
6961 let shape1 = if let Stmt::If { cond, then_stmt, else_stmt: Some(el), .. } = &stmts[i] {
6963 match (arm_assign_inlined(then_stmt), arm_assign_inlined(el)) {
6964 (Some((v1, tgt, a)), Some((v2, _, b)))
6965 if v1 == v2 && diamond_types_ok(&a, &b) =>
6966 {
6967 Some(mk_ternary_assign(tgt, cond.clone(), a, b))
6968 }
6969 _ => None,
6970 }
6971 } else {
6972 None
6973 };
6974 if let Some(f) = shape1 {
6975 stmts[i] = f;
6976 i += 1;
6977 continue;
6978 }
6979 let shape3 = if let Stmt::If {
6992 cond,
6993 then_stmt,
6994 else_stmt: Some(el),
6995 ..
6996 } = &stmts[i]
6997 {
6998 let arm = |st: &Stmt| -> Option<((u32, Expr, Expr), Vec<Stmt>)> {
6999 let Stmt::Block(v) = st else { return None };
7000 if v.len() < 2 {
7001 return None;
7002 }
7003 let first = single_local_assign(&v[0])?;
7004 Some((first, v[1..].to_vec()))
7005 };
7006 match (arm(then_stmt), arm(el)) {
7007 (Some(((v1, tgt1, a), mut rest_t)), Some(((v2, _t2, b), rest_e)))
7008 if !rest_t.is_empty()
7009 && is_bare_ctor_call(first_leaf_stmt(&rest_t[0]))
7010 && stmts_count_writes(&rest_t, v1) == 0
7011 && stmts_count_writes(&rest_t, v2) == 0
7012 && diamond_types_ok(&a, &b) =>
7013 {
7014 if v1 == v2 || same_render_var(v1, v2, vt) {
7015 if canon_stmts_eq(&rest_t, &rest_e, vt) {
7017 Some((mk_ternary_assign(tgt1, cond.clone(), a, b), rest_t))
7018 } else {
7019 None
7020 }
7021 } else if stmts_read_var(&rest_t, v1) == 1
7022 && stmts_read_var(&rest_e, v2) == 1
7023 && stmts_read_var(&rest_t, v2) == 0
7024 && stmts_read_var(&rest_e, v1) == 0
7025 {
7026 let mut unified = rest_e.clone();
7034 for st in unified.iter_mut() {
7035 let mut c2 = st.clone();
7036 walk_stmt_exprs(&mut c2, &mut |e| {
7037 deep_rewrite(e, &mut |x| {
7038 if let Expr::Local { var, .. } = x {
7039 if *var == v2 {
7040 *x = Expr::Local {
7041 var: v1,
7042 ty: vt.var(v1).ty.clone(),
7043 };
7044 }
7045 }
7046 });
7047 });
7048 *st = c2;
7049 }
7050 if unified != rest_t {
7051 None
7052 } else {
7053 let tern = Expr::Cond {
7054 c: Box::new(cond.clone()),
7055 t: Box::new(a.clone()),
7056 f: Box::new(b.clone()),
7057 };
7058 for st in rest_t.iter_mut() {
7059 let mut c2 = st.clone();
7060 walk_stmt_exprs(&mut c2, &mut |e| {
7061 deep_rewrite(e, &mut |x| {
7062 if let Expr::Local { var, .. } = x {
7063 if *var == v1 {
7064 *x = tern.clone();
7065 }
7066 }
7067 });
7068 });
7069 *st = c2;
7070 }
7071 let first = rest_t.remove(0);
7075 Some((first, rest_t))
7076 }
7077 } else {
7078 None
7079 }
7080 }
7081 _ => None,
7082 }
7083 } else {
7084 None
7085 };
7086 if let Some((folded, rest)) = shape3 {
7087 stmts[i] = folded;
7088 stmts.splice(i + 1..i + 1, rest);
7089 i += 1;
7090 continue;
7091 }
7092 if i + 1 < stmts.len() {
7102 let shape2 = {
7122 let s_i = single_local_assign(&stmts[i]);
7123 if let Stmt::If {
7124 cond,
7125 then_stmt,
7126 else_stmt,
7127 ..
7128 } = &stmts[i + 1]
7129 {
7130 let then_a = single_local_assign(then_stmt);
7131 let else_a = else_stmt.as_deref().and_then(single_local_assign);
7132 let then_empty =
7133 matches!(&**then_stmt, Stmt::Block(v) if v.is_empty());
7134 let else_empty = else_stmt
7135 .as_deref()
7136 .map(|e| matches!(e, Stmt::Block(v) if v.is_empty()))
7137 .unwrap_or(true);
7138 let try_fold = |over: Option<(u32, Expr, Expr)>,
7139 keep_over: bool|
7140 -> Option<Stmt> {
7141 let (v2, tgt2, a) = over?;
7142 let (v1, _t1, b) = s_i.as_ref()?;
7143 if !(*v1 == v2 || same_render_var(*v1, v2, vt)) {
7144 return None;
7145 }
7146 let keep = if *v1 == v2 {
7147 b.clone()
7148 } else {
7149 Expr::Local {
7150 var: *v1,
7151 ty: vt.var(*v1).ty.clone(),
7152 }
7153 };
7154 if !diamond_types_ok(&a, &keep) {
7155 return None;
7156 }
7157 if keep_over {
7158 Some(mk_ternary_assign(tgt2, cond.clone(), keep, a))
7159 } else {
7160 Some(mk_ternary_assign(tgt2, cond.clone(), a, keep))
7161 }
7162 };
7163 if then_a.is_some() && else_empty {
7164 try_fold(then_a, false)
7165 } else if else_a.is_some() && then_empty {
7166 try_fold(else_a, true)
7167 } else {
7168 None
7169 }
7170 } else {
7171 None
7172 }
7173 };
7174 let mut lead_fold: Option<(Stmt, Stmt)> = None;
7183 if shape2.is_none() && i + 1 < stmts.len() {
7184 let s_i2 = single_local_assign(&stmts[i]);
7185 let leading = |st: &Stmt| -> Option<((u32, Expr, Expr), Vec<Stmt>)> {
7186 let Stmt::Block(v) = st else { return None };
7187 if v.len() < 2 {
7188 return None;
7189 }
7190 let first = single_local_assign(&v[0])?;
7191 Some((first, v[1..].to_vec()))
7192 };
7193 if let (Some((v1, _t1, b)), Stmt::If { cond, then_stmt, else_stmt, .. }) =
7194 (&s_i2, &stmts[i + 1])
7195 {
7196 let else_absent = else_stmt.is_none()
7197 || else_stmt
7198 .as_deref()
7199 .is_some_and(|e| matches!(e, Stmt::Block(v) if v.is_empty()));
7200 let then_absent = matches!(&**then_stmt, Stmt::Block(v) if v.is_empty());
7201 let try_lead = |over: Option<((u32, Expr, Expr), Vec<Stmt>)>,
7202 keep_over: bool|
7203 -> Option<(Stmt, Stmt)> {
7204 let ((v2, tgt2, a), rest) = over?;
7205 if !(*v1 == v2 || same_render_var(*v1, v2, vt)) {
7206 return None;
7207 }
7208 if stmts_count_writes(&rest, *v1) != 0
7209 || stmts_count_writes(&rest, v2) != 0
7210 {
7211 return None;
7212 }
7213 let keep = if *v1 == v2 {
7214 b.clone()
7215 } else {
7216 Expr::Local {
7217 var: *v1,
7218 ty: vt.var(*v1).ty.clone(),
7219 }
7220 };
7221 if !diamond_types_ok(&a, &keep) {
7222 return None;
7223 }
7224 let t = if keep_over {
7225 mk_ternary_assign(tgt2, cond.clone(), keep, a)
7226 } else {
7227 mk_ternary_assign(tgt2, cond.clone(), a, keep)
7228 };
7229 let mut new_if = stmts[i + 1].clone();
7230 if let Stmt::If { then_stmt, else_stmt, .. } = &mut new_if {
7231 if keep_over {
7232 *else_stmt = Some(Box::new(Stmt::Block(rest)));
7233 } else {
7234 **then_stmt = Stmt::Block(rest);
7235 }
7236 }
7237 Some((t, new_if))
7238 };
7239 if else_absent {
7240 lead_fold = try_lead(leading(then_stmt), false);
7241 } else if then_absent {
7242 if let Some(e) = else_stmt {
7243 lead_fold = try_lead(leading(e), true);
7244 }
7245 }
7246 }
7247 }
7248 if let Some((folded, new_if)) = lead_fold {
7249 stmts[i] = folded;
7250 stmts[i + 1] = new_if;
7251 continue;
7252 }
7253 if let Some(f) = shape2 {
7254 stmts[i] = f;
7255 stmts.remove(i + 1);
7256 continue; }
7258 }
7259 i += 1;
7260 }
7261}
7262
7263pub fn fold_bool_value_diamonds(body: &mut Stmt, vt: &VarTable) {
7264 fn is_bool_var(v: u32, vt: &VarTable) -> bool {
7265 (v as usize) < vt.vars.len()
7266 && matches!(vt.vars[v as usize].ty.erased(), JavaType::Boolean)
7267 }
7268 fn bool_const(e: &Expr) -> Option<bool> {
7269 fn cint(e: &Expr) -> Option<i32> {
7270 match e {
7271 Expr::Const(ConstVal::Int(n)) => Some(*n),
7272 _ => None,
7273 }
7274 }
7275 match e {
7276 Expr::Const(ConstVal::Int(0)) => Some(false),
7277 Expr::Const(ConstVal::Int(1)) => Some(true),
7278 Expr::Bin { op: op @ (BinOp::Eq | BinOp::Ne), l, r, .. } => {
7282 let (a, b) = (cint(l)?, cint(r)?);
7283 let eq = a == b;
7284 Some(if matches!(op, BinOp::Eq) { eq } else { !eq })
7285 }
7286 _ => None,
7287 }
7288 }
7289 fn reads_var(e: &Expr, v: u32) -> bool {
7290 let mut hit = false;
7291 visit_exprs(e, &mut |x| {
7292 if let Expr::Local { var, .. } = x {
7293 if *var == v {
7294 hit = true;
7295 }
7296 }
7297 });
7298 hit
7299 }
7300 fn bool_valued(e: &Expr, vt: &VarTable) -> bool {
7304 fn side(e: &Expr, vt: &VarTable) -> bool {
7305 bool_const(e).is_some() || bool_valued(e, vt)
7306 }
7307 match e {
7308 Expr::Local { var, .. } => is_bool_var(*var, vt),
7309 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
7310 Expr::InstanceOf { .. } => true,
7311 Expr::Un { op: UnOp::Not, e: x, .. } => bool_valued(x, vt) || bool_const(x).is_some(),
7312 Expr::Bin { op, l, r, .. } => match op {
7313 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le => true,
7314 BinOp::LogAnd | BinOp::LogOr => {
7317 side(l, vt) && side(r, vt)
7318 }
7319 _ => false,
7320 },
7321 Expr::Cond { t, f, .. } => side(t, vt) && side(f, vt),
7322 _ => false,
7323 }
7324 }
7325 fn lone_assign(
7334 b: &Stmt,
7335 v: u32,
7336 vt: &VarTable,
7337 an: &VarAnalysis,
7338 consumed: &mut HashSet<u32>,
7339 ) -> Option<Expr> {
7340 let l = flat_list(b);
7341 if l.len() == 2 {
7342 let (u, e): (u32, &Expr) = match l[0] {
7343 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e),
7344 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
7345 match &**target {
7346 Expr::Local { var, .. } => (*var, &**value),
7347 _ => return None,
7348 }
7349 }
7350 _ => return None,
7351 };
7352 if u == v || consumed.contains(&u) {
7353 return None;
7354 }
7355 if an.assigns.get(u as usize).copied().unwrap_or(0) != 1
7362 || an.reads.get(u as usize).copied().unwrap_or(0) != 1
7363 {
7364 return None;
7365 }
7366 let (tv, rv) = match l[1] {
7367 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
7368 match (&**target, &**value) {
7369 (Expr::Local { var: t, .. }, Expr::Local { var: r, .. }) => (*t, *r),
7370 _ => return None,
7371 }
7372 }
7373 Stmt::LocalDef { var, init: Some(Expr::Local { var: r, .. }), .. } => (*var, *r),
7374 _ => return None,
7375 };
7376 if tv != v || rv != u {
7377 return None;
7378 }
7379 if reads_var(e, v) || !(bool_valued(e, vt) || bool_const(e).is_some()) {
7380 return None;
7381 }
7382 consumed.insert(u);
7383 return Some(e.clone());
7384 }
7385 if l.len() != 1 {
7386 return None;
7387 }
7388 let (target, value) = match l[0] {
7389 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => (target, value),
7390 Stmt::LocalDef { var, init: Some(value), .. } if *var == v => {
7391 if reads_var(value, v) || !(bool_valued(value, vt) || bool_const(value).is_some()) {
7392 return None;
7393 }
7394 return Some(value.clone());
7395 }
7396 _ => return None,
7397 };
7398 let Expr::Local { var: tv, .. } = &**target else { return None };
7399 if *tv != v || reads_var(value, v) {
7400 return None;
7401 }
7402 if !(bool_valued(value, vt) || bool_const(value).is_some()) {
7403 return None;
7404 }
7405 Some((**value).clone())
7406 }
7407 fn not(e: Expr) -> Expr {
7408 Expr::Un { op: UnOp::Not, e: Box::new(e) }
7409 }
7410 fn simplify_bool(e: Expr) -> Expr {
7415 match e {
7416 Expr::Un { op: UnOp::Not, e: inner, .. } => match *inner {
7417 Expr::Bin { op: BinOp::Ne, l, r, ty } => {
7422 simplify_bool(Expr::Bin { op: BinOp::Eq, l, r, ty })
7423 }
7424 Expr::Bin { op: BinOp::Eq, l, r, ty } => {
7425 simplify_bool(Expr::Bin { op: BinOp::Ne, l, r, ty })
7426 }
7427 other => Expr::Un { op: UnOp::Not, e: Box::new(simplify_bool(other)) },
7428 },
7429 Expr::Bin { op: op @ (BinOp::Eq | BinOp::Ne), l, r, ty } => {
7430 fn cond01(e: &Expr) -> Option<Expr> {
7432 if let Expr::Cond { c, t, f } = e {
7433 if matches!(&**t, Expr::Const(ConstVal::Int(1)))
7434 && matches!(&**f, Expr::Const(ConstVal::Int(0)))
7435 {
7436 return Some((**c).clone());
7437 }
7438 }
7439 None
7440 }
7441 let (lc, rc) = (cond01(&l), cond01(&r));
7442 match (lc, rc) {
7443 (Some(c), None) => match (&*r, op) {
7444 (Expr::Const(ConstVal::Int(1)), BinOp::Eq) => c,
7445 (Expr::Const(ConstVal::Int(1)), BinOp::Ne) => not(c),
7446 (Expr::Const(ConstVal::Int(0)), BinOp::Eq) => not(c),
7447 (Expr::Const(ConstVal::Int(0)), BinOp::Ne) => c,
7448 _ => Expr::Bin { op, l: Box::new(c), r, ty },
7449 },
7450 (None, Some(c)) => match (&*l, op) {
7451 (Expr::Const(ConstVal::Int(1)), BinOp::Eq) => c,
7452 (Expr::Const(ConstVal::Int(1)), BinOp::Ne) => not(c),
7453 (Expr::Const(ConstVal::Int(0)), BinOp::Eq) => not(c),
7454 (Expr::Const(ConstVal::Int(0)), BinOp::Ne) => c,
7455 _ => Expr::Bin { op, l, r: Box::new(c), ty },
7456 },
7457 _ => Expr::Bin { op, l, r, ty },
7458 }
7459 }
7460 other => other,
7461 }
7462 }
7463 fn bin(op: BinOp, l: Expr, r: Expr) -> Expr {
7464 Expr::Bin { op, l: Box::new(l), r: Box::new(r), ty: None }
7465 }
7466
7467 for _round in 0..4 {
7468 let mut changed = false;
7469 let mut analysis = VarAnalysis {
7470 assigns: Vec::new(),
7471 reads: Vec::new(),
7472 values: Vec::new(),
7473 };
7474 analyze_vars(body, &mut analysis);
7475 let mut consumed: HashSet<u32> = HashSet::default();
7476 walk_mut_deep(body, &mut |st| {
7477 let Stmt::Block(list) = st else { return };
7478 let mut i = 0usize;
7479 while i + 1 < list.len() {
7480 let folded = try_at(list, i, vt, &is_bool_var, &bool_const, &reads_var, &lone_assign, &analysis, &mut consumed);
7481 match folded {
7482 Some(expr) => {
7483 let is_def = matches!(&list[i], Stmt::LocalDef { .. });
7484 let var = match &list[i] {
7485 Stmt::LocalDef { var, .. } => *var,
7486 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
7487 match &**target { Expr::Local { var, .. } => *var, _ => unreachable!() }
7488 }
7489 _ => unreachable!(),
7490 };
7491 let (is_final, force_type) = match &list[i] {
7492 Stmt::LocalDef { is_final, force_type, .. } => (*is_final, *force_type),
7493 _ => (false, false),
7494 };
7495 list[i] = if is_def {
7496 Stmt::LocalDef { var, init: Some(expr), is_final, force_type }
7497 } else {
7498 Stmt::ExprStmt(Expr::Assign {
7499 target: Box::new(Expr::Local {
7500 var,
7501 ty: TypeRef::J(JavaType::Boolean),
7502 }),
7503 op: AssignOp::Plain,
7504 value: Box::new(expr),
7505 })
7506 };
7507 list.remove(i + 1);
7508 changed = true;
7509 }
7512 None => i += 1,
7513 }
7514 }
7515 });
7516 if !changed {
7517 break;
7518 }
7519 }
7520
7521 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
7522 fn try_at(
7523 list: &[Stmt],
7524 i: usize,
7525 vt: &VarTable,
7526 is_bool_var: &dyn Fn(u32, &VarTable) -> bool,
7527 bool_const: &dyn Fn(&Expr) -> Option<bool>,
7528 reads_var: &dyn Fn(&Expr, u32) -> bool,
7529 lone_assign: &dyn Fn(&Stmt, u32, &VarTable, &VarAnalysis, &mut HashSet<u32>) -> Option<Expr>,
7530 an: &VarAnalysis,
7531 consumed: &mut HashSet<u32>,
7532 ) -> Option<Expr> {
7533 let (v, base) = match &list[i] {
7535 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
7536 match &**target {
7537 Expr::Local { var, .. } => {
7538 let b = bool_const(value)?;
7541 if !is_bool_var(*var, vt) { return None; }
7542 (*var, b)
7543 }
7544 _ => return None,
7545 }
7546 }
7547 Stmt::LocalDef { var, init: Some(e), .. } => {
7548 let b = bool_const(e)?;
7549 if !is_bool_var(*var, vt) { return None; }
7550 (*var, b)
7551 }
7552 _ => return None,
7553 };
7554 let Stmt::If { cond, then_stmt, else_stmt } = &list[i + 1] else {
7555 return None;
7556 };
7557 if reads_var(cond, v) {
7558 return None;
7559 }
7560 let mut touches_v = false;
7561 visit_exprs(cond, &mut |x| {
7562 if let Expr::Assign { target, .. } = x {
7563 if let Expr::Local { var, .. } = &**target {
7564 if *var == v { touches_v = true; }
7565 }
7566 }
7567 });
7568 if touches_v {
7569 return None;
7570 }
7571 let (cond_n, then_arm, else_arm): (Expr, &Stmt, Option<&Stmt>) =
7574 if flat_list(then_stmt).is_empty() && else_stmt.is_some() {
7575 (not(cond.clone()), else_stmt.as_ref().unwrap(), None)
7576 } else {
7577 (cond.clone(), then_stmt.as_ref(), else_stmt.as_deref())
7578 };
7579 let e1 = lone_assign(then_arm, v, vt, an, consumed)?;
7580 let e2 = match else_arm {
7581 None => None,
7582 Some(b) => Some(lone_assign(b, v, vt, an, consumed)?),
7583 };
7584 let e1c = bool_const(&e1);
7585 let c = simplify_bool(cond_n);
7586 Some(match e2 {
7587 None => {
7588 if !base {
7589 match e1c {
7590 Some(true) => c,
7591 Some(false) => return None, None => bin(BinOp::LogAnd, c, e1),
7593 }
7594 } else {
7595 match e1c {
7596 Some(false) => not(c),
7597 Some(true) => return None,
7598 None => bin(BinOp::LogOr, not(c), e1),
7599 }
7600 }
7601 }
7602 Some(e2) => {
7603 if e1 == e2 {
7605 return None; }
7607 Expr::Cond { c: Box::new(c), t: Box::new(e1), f: Box::new(e2) }
7608 }
7609 })
7610 }
7611}
7612
7613pub fn idiom_compounds(body: &mut Stmt, vt: &VarTable) {
7614 walk_mut_deep(body, &mut |st| {
7615 let Stmt::ExprStmt(e) = st else { return };
7616 let Expr::Assign { target, op: AssignOp::Plain, value } = &mut *e else {
7617 return;
7618 };
7619 enum Tgt {
7622 Loc(u32),
7623 Fld,
7624 }
7625 let tgt = match &**target {
7626 Expr::Local { var, .. } => Tgt::Loc(*var),
7627 Expr::Field { owner, .. }
7628 if owner.is_none()
7629 || matches!(&**owner.as_ref().unwrap(), Expr::This) =>
7630 {
7631 Tgt::Fld
7632 }
7633 _ => return,
7634 };
7635 let Expr::Bin { op, l, r, .. } = &mut **value else {
7636 return;
7637 };
7638 match (&tgt, &**l) {
7640 (Tgt::Loc(v), Expr::Local { var, .. }) if *var == *v => {}
7641 (Tgt::Fld, Expr::Field { cls: c2, name: n2, .. }) => {
7642 let Expr::Field { cls: c1, name: n1, .. } = &**target else {
7643 return;
7644 };
7645 if c1 != c2 || n1 != n2 {
7646 return;
7647 }
7648 }
7649 _ => return,
7650 }
7651 let one = match &**r {
7653 Expr::Const(ConstVal::Int(1)) => Some(1i64),
7654 Expr::Const(ConstVal::Int(-1)) => Some(-1),
7655 Expr::Const(ConstVal::Long(1)) => Some(1),
7656 Expr::Const(ConstVal::Long(-1)) => Some(-1),
7657 _ => None,
7658 };
7659 if *op == BinOp::Add || *op == BinOp::Sub {
7660 if let Some(mut d) = one {
7661 if *op == BinOp::Sub {
7662 d = -d;
7663 }
7664 let tgt_ty = match &tgt {
7665 Tgt::Loc(v) => vt.var(*v).ty.erased(),
7666 Tgt::Fld => {
7667 let Expr::Field { ty, .. } = &**target else {
7668 return;
7669 };
7670 ty.erased()
7671 }
7672 };
7673 let numeric = matches!(
7674 tgt_ty,
7675 JavaType::Int
7676 | JavaType::Long
7677 | JavaType::Short
7678 | JavaType::Byte
7679 | JavaType::Char
7680 | JavaType::Float
7681 | JavaType::Double
7682 );
7683 if numeric {
7684 let wide = matches!(tgt_ty, JavaType::Long);
7685 let inner = match &tgt {
7686 Tgt::Loc(v) => Expr::Local {
7687 var: *v,
7688 ty: vt.var(*v).ty.clone(),
7689 },
7690 Tgt::Fld => (**target).clone(),
7691 };
7692 *e = Expr::PostIncDec {
7693 e: Box::new(inner),
7694 delta: d,
7695 wide,
7696 };
7697 return;
7698 }
7699 }
7700 }
7701 let cop = match op {
7702 BinOp::Add => AssignOp::Add,
7703 BinOp::Sub => AssignOp::Sub,
7704 BinOp::Mul => AssignOp::Mul,
7705 BinOp::Div => AssignOp::Div,
7706 BinOp::Rem => AssignOp::Rem,
7707 BinOp::Shl => AssignOp::Shl,
7708 BinOp::Shr => AssignOp::Shr,
7709 BinOp::Ushr => AssignOp::Ushr,
7710 BinOp::And => AssignOp::And,
7711 BinOp::Or => AssignOp::Or,
7712 BinOp::Xor => AssignOp::Xor,
7713 _ => return,
7714 };
7715 let rhs = std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
7716 let new_target = match &tgt {
7717 Tgt::Loc(v) => Box::new(Expr::Local {
7718 var: *v,
7719 ty: vt.var(*v).ty.clone(),
7720 }),
7721 Tgt::Fld => target.clone(),
7722 };
7723 *e = Expr::Assign {
7724 target: new_target,
7725 op: cop,
7726 value: rhs,
7727 };
7728 });
7729}
7730
7731pub fn fix_bool_xor(body: &mut Stmt, vt: &VarTable, ret_bool: bool) {
7732 fn is_one(e: &Expr) -> bool {
7733 matches!(e, Expr::Const(ConstVal::Int(1)))
7734 }
7735 fn bty(e: &Expr, vt: &VarTable) -> bool {
7736 match e {
7737 Expr::Local { var, .. } => {
7738 matches!(vt.var(*var).ty.erased(), JavaType::Boolean)
7739 }
7740 Expr::Bin {
7749 op: BinOp::And | BinOp::Or | BinOp::Xor,
7750 l,
7751 r,
7752 ..
7753 } => bty(l, vt) && bty(r, vt),
7754 other => matches!(other.type_ref().erased(), JavaType::Boolean),
7755 }
7756 }
7757 fn inv_of_xor(x: &Expr, vt: &VarTable) -> Option<Expr> {
7762 if let Expr::Bin { op: BinOp::Xor, l, r, .. } = x {
7763 let (int_side, one_side) = if is_one(r) {
7764 (l, r)
7765 } else if is_one(l) {
7766 (r, l)
7767 } else {
7768 return None;
7769 };
7770 let _ = one_side;
7771 if !bty(int_side, vt) {
7772 return Some(Expr::Bin {
7773 op: BinOp::Eq,
7774 l: int_side.clone(),
7775 r: Box::new(Expr::Const(ConstVal::Int(0))),
7776 ty: Some(TypeRef::J(JavaType::Boolean)),
7777 });
7778 }
7779 }
7780 None
7781 }
7782 let tgt_bool = |target: &Expr, vt: &VarTable| -> bool {
7783 match target {
7784 Expr::Local { var, .. } => matches!(vt.var(*var).ty.erased(), JavaType::Boolean),
7785 Expr::Field { ty, .. } => matches!(ty.erased(), JavaType::Boolean),
7786 _ => false,
7787 }
7788 };
7789 walk_mut_deep(body, &mut |st| match st {
7790 Stmt::Return(Some(e)) if ret_bool => {
7791 if let Some(r) = inv_of_xor(e, vt) {
7792 *e = r;
7793 }
7794 }
7795 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain, .. }) => {
7796 if tgt_bool(target, vt) {
7797 if let Some(r) = inv_of_xor(value, vt) {
7798 **value = r;
7799 }
7800 }
7801 }
7802 Stmt::LocalDef { var, init: Some(value), .. }
7803 if matches!(vt.var(*var).ty.erased(), JavaType::Boolean) =>
7804 {
7805 if let Some(r) = inv_of_xor(value, vt) {
7806 *value = r;
7807 }
7808 }
7809 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
7810 if let Some(r) = inv_of_xor(cond, vt) {
7811 *cond = r;
7812 }
7813 }
7814 _ => {}
7815 });
7816 fn ity(e: &Expr, vt: &VarTable) -> bool {
7817 matches!(
7818 match e {
7819 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
7820 other => other.type_ref().erased(),
7821 },
7822 JavaType::Int | JavaType::Short | JavaType::Byte | JavaType::Char
7823 )
7824 }
7825 fn mixed_rewrite(e: &mut Expr, vt: &VarTable) {
7834 deep_rewrite(e, &mut |x| {
7835 if let Expr::Bin { op, l, r, ty } = x {
7836 match op {
7837 BinOp::Xor => {
7838 if is_one(r) && bty(l, vt) {
7839 let taken =
7840 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
7841 *x = Expr::Un { op: UnOp::Not, e: taken };
7842 } else if is_one(l) && bty(r, vt) {
7843 let taken =
7844 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
7845 *x = Expr::Un { op: UnOp::Not, e: taken };
7846 }
7847 }
7848 BinOp::Or | BinOp::And => {
7849 if bty(l, vt) && ity(r, vt) {
7850 let taken =
7851 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
7852 **r = Expr::Bin {
7853 op: BinOp::Ne,
7854 l: taken,
7855 r: Box::new(Expr::Const(ConstVal::Int(0))),
7856 ty: Some(TypeRef::J(JavaType::Boolean)),
7857 };
7858 *ty = Some(TypeRef::J(JavaType::Boolean));
7859 } else if ity(l, vt) && bty(r, vt) {
7860 let taken =
7861 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
7862 **l = Expr::Bin {
7863 op: BinOp::Ne,
7864 l: taken,
7865 r: Box::new(Expr::Const(ConstVal::Int(0))),
7866 ty: Some(TypeRef::J(JavaType::Boolean)),
7867 };
7868 *ty = Some(TypeRef::J(JavaType::Boolean));
7869 }
7870 }
7871 _ => {}
7872 }
7873 }
7874 });
7875 }
7876 walk_mut_deep(body, &mut |st| match st {
7879 Stmt::Return(Some(e)) if ret_bool => mixed_rewrite(e, vt),
7880 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain, .. }) => {
7881 if tgt_bool(target, vt) {
7882 mixed_rewrite(value, vt);
7883 }
7884 }
7885 Stmt::LocalDef { var, init: Some(e), .. }
7886 if matches!(vt.var(*var).ty.erased(), JavaType::Boolean) =>
7887 {
7888 mixed_rewrite(e, vt);
7889 }
7890 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
7891 mixed_rewrite(cond, vt);
7892 }
7893 _ => {}
7894 });
7895 walk_stmt_exprs(body, &mut |e| {
7898 deep_rewrite(e, &mut |x| {
7899 if let Expr::Cond { c, .. } = x {
7900 mixed_rewrite(c, vt);
7901 }
7902 });
7903 });
7904}
7905
7906pub fn booleanize(vt: &mut VarTable, body: &mut Stmt, ret_bool: bool) -> usize {
7912 let n0 = vt.vars.len();
7919 let mut derefed = vec![false; n0];
7920 mark_derefs(body, &mut derefed);
7921 let mut total = 0usize;
7922 for _ in 0..4 {
7923 let n = booleanize_round(vt, body, ret_bool, &derefed);
7924 if n == 0 {
7925 break;
7926 }
7927 total += n;
7928 }
7929 total
7930}
7931
7932fn mark_deref_expr(e: &Expr, derefed: &mut [bool]) {
7936 match e {
7937 Expr::Field { owner: Some(o), is_static: false, .. }
7938 | Expr::Method { owner: Some(o), .. } => {
7939 if let Expr::Local { var, .. } = &**o {
7940 if (*var as usize) < derefed.len() {
7941 derefed[*var as usize] = true;
7942 }
7943 }
7944 }
7945 Expr::ArrayIndex { array, .. } => {
7946 if let Expr::Local { var, .. } = &**array {
7947 if (*var as usize) < derefed.len() {
7948 derefed[*var as usize] = true;
7949 }
7950 }
7951 }
7952 _ => {}
7953 }
7954 for_each_child(e, &mut |c| mark_deref_expr(c, derefed));
7955}
7956
7957fn mark_derefs(body: &Stmt, derefed: &mut [bool]) {
7958 walk_all(body, &mut |st| {
7959 match st {
7960 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
7961 mark_deref_expr(e, derefed);
7962 }
7963 Stmt::Return(Some(e)) => mark_deref_expr(e, derefed),
7964 Stmt::LocalDef { init: Some(e), .. } => mark_deref_expr(e, derefed),
7965 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
7966 mark_deref_expr(cond, derefed);
7967 }
7968 Stmt::For { init, .. } => {
7969 for x in init {
7970 mark_derefs(x, derefed);
7971 }
7972 }
7973 Stmt::Switch { selector, .. } => mark_deref_expr(selector, derefed),
7974 Stmt::ForEach { iterable, .. } => mark_deref_expr(iterable, derefed),
7975 _ => {}
7976 }
7977 });
7978}
7979
7980fn booleanize_round(
7981 vt: &mut VarTable,
7982 body: &mut Stmt,
7983 ret_bool: bool,
7984 derefed: &[bool],
7985) -> usize {
7986 let n = vt.vars.len();
7987 let mut in_cond = vec![false; n];
7988
7989 walk_all(body, &mut |st| {
7992 let cond: Option<&Expr> = match st {
7993 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
7994 Some(cond)
7995 }
7996 _ => None,
7997 };
7998 if let Some(c) = cond {
7999 if let Expr::Bin {
8000 op: BinOp::Eq | BinOp::Ne,
8001 l,
8002 r,
8003 ..
8004 } = c
8005 {
8006 for (x, other) in [(l, r), (r, l)] {
8007 if let (Expr::Local { var, .. }, Expr::Const(ConstVal::Int(0))) =
8008 (&**x, &**other)
8009 {
8010 if (*var as usize) < n {
8011 in_cond[*var as usize] = true;
8012 }
8013 }
8014 }
8015 }
8016 }
8017 if ret_bool {
8022 if let Stmt::Return(Some(Expr::Local { var, .. })) = st {
8023 if (*var as usize) < n {
8024 in_cond[*var as usize] = true;
8025 }
8026 }
8027 }
8028 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = st {
8035 if let Expr::Field { ty, .. } = &**target {
8036 if ty.erased() == JavaType::Boolean {
8037 if let Expr::Local { var, .. } = &**value {
8038 if (*var as usize) < n {
8039 in_cond[*var as usize] = true;
8040 }
8041 }
8042 }
8043 }
8044 }
8045 let val: Option<&Expr> = match st {
8046 Stmt::ExprStmt(Expr::Assign { value, .. }) => Some(value),
8047 Stmt::ExprStmt(e) => Some(e),
8050 Stmt::LocalDef { init: Some(e), .. } => Some(e),
8051 Stmt::Return(Some(e)) => Some(e),
8052 _ => None,
8053 };
8054 if let Some(v) = val {
8055 visit_exprs(v, &mut |x| {
8056 if let Expr::Method { desc, args, .. } = x {
8062 for (i, a) in args.iter().enumerate() {
8063 if desc.args.get(i) == Some(&JavaType::Boolean) {
8064 if let Expr::Local { var, .. } = a {
8065 if (*var as usize) < n {
8066 in_cond[*var as usize] = true;
8067 }
8068 }
8069 }
8070 }
8071 }
8072 if let Expr::Bin { op, l, r, .. } = x {
8073 if matches!(
8074 op,
8075 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
8076 ) {
8077 for side in [l, r] {
8078 if let Expr::Local { var, .. } = &**side {
8079 if (*var as usize) < n {
8080 in_cond[*var as usize] = true;
8081 }
8082 }
8083 }
8084 }
8085 if matches!(op, BinOp::Or | BinOp::And) {
8091 let l_bool = l.type_ref().erased() == JavaType::Boolean;
8092 let r_bool = r.type_ref().erased() == JavaType::Boolean;
8093 if l_bool != r_bool {
8094 let side = if l_bool { r } else { l };
8095 if let Expr::Local { var, .. } = &**side {
8096 if (*var as usize) < n {
8097 in_cond[*var as usize] = true;
8098 }
8099 }
8100 }
8101 }
8102 }
8103 });
8104 }
8105 });
8106
8107 let mut assigned_any = vec![false; n];
8114 let mut all_bool = vec![true; n];
8115 let mut edges: Vec<(usize, usize)> = Vec::new();
8116 walk_all(body, &mut |st| {
8117 let (var, value) = match st {
8118 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
8119 Expr::Local { var, .. } => (var, &**value),
8120 _ => return,
8121 },
8122 Stmt::LocalDef {
8123 var, init: Some(e), ..
8124 } => (var, e),
8125 _ => return,
8126 };
8127 let i = *var as usize;
8128 if i < n {
8129 assigned_any[i] = true;
8130 if !is_boolean_valued(value, vt) {
8131 all_bool[i] = false;
8132 }
8133 if let Expr::Local { var: src, .. } = value {
8139 if (*src as usize) < n {
8140 edges.push((i, *src as usize));
8141 }
8142 }
8143 }
8144 });
8145 loop {
8150 let mut changed = false;
8151 for &(tgt, src) in &edges {
8152 if in_cond[tgt] && !in_cond[src] && all_bool[src] {
8153 in_cond[src] = true;
8154 changed = true;
8155 }
8156 if in_cond[src] && !in_cond[tgt] && all_bool[tgt] {
8162 in_cond[tgt] = true;
8163 changed = true;
8164 }
8165 }
8166 if !changed {
8167 break;
8168 }
8169 }
8170 let reads = count_locals_stmts(std::slice::from_ref(body));
8171 let mut boolean_vars: HashSet<u32> = HashSet::default();
8172 for i in 0..n {
8173 if vt.vars[i].is_param {
8174 continue;
8175 }
8176 if derefed[i] {
8177 continue;
8178 }
8179 if !in_cond[i] && !(reads.get(&(i as u32)).copied().unwrap_or(0) == 0) {
8180 continue;
8181 }
8182 if assigned_any[i] && all_bool[i] {
8183 boolean_vars.insert(i as u32);
8184 }
8185 }
8186 if boolean_vars.is_empty() {
8187 return 0;
8188 }
8189 for v in &boolean_vars {
8190 vt.vars[*v as usize].ty = TypeRef::J(JavaType::Boolean);
8191 }
8192 let types: Vec<&TypeRef> = vt.vars.iter().map(|v| &v.ty).collect();
8193 rewrite_exprs(body, &mut |e| {
8194 deep_rewrite(e, &mut |x| {
8195 if let Expr::Local { var, ty } = x {
8196 if let Some(want) = types.get(*var as usize) {
8197 if ty != *want {
8198 *ty = (*want).clone();
8199 }
8200 }
8201 }
8202 });
8203 });
8204
8205 fold_bool_conditions(body, &boolean_vars);
8207 boolean_vars.len()
8208}
8209
8210fn is_boolean_valued(e: &Expr, vt: &VarTable) -> bool {
8211 match e {
8212 Expr::Const(ConstVal::Int(0)) | Expr::Const(ConstVal::Int(1)) => true,
8213 Expr::Bin { op: BinOp::Or | BinOp::And, l, r, .. } => {
8220 is_boolean_valued(l, vt) || is_boolean_valued(r, vt)
8221 }
8222 Expr::Bin {
8230 op: BinOp::Xor,
8231 l,
8232 r,
8233 ..
8234 } => is_boolean_valued(l, vt) && is_boolean_valued(r, vt),
8235 Expr::Bin { op, .. } => matches!(
8236 op,
8237 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
8238 ),
8239 Expr::InstanceOf { .. } => true,
8240 Expr::Local { var, ty } => {
8246 let t = if (*var as usize) < vt.vars.len() {
8247 vt.var(*var).ty.erased()
8248 } else {
8249 ty.erased()
8250 };
8251 t == JavaType::Boolean
8252 }
8253 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
8254 Expr::Cond { t, f, .. } => is_boolean_valued(t, vt) && is_boolean_valued(f, vt),
8255 Expr::Un { op: UnOp::Not, .. } => true,
8256 Expr::Field { ty, .. } => ty.erased() == JavaType::Boolean,
8260 _ => false,
8261 }
8262}
8263
8264fn fold_bool_conditions(s: &mut Stmt, bv: &HashSet<u32>) {
8265 let fold = |e: &mut Expr| {
8266 deep_rewrite(e, &mut |x| {
8267 if let Expr::Bin { op, l, r, .. } = x {
8268 let (var_side, const_side) = match (&**l, &**r) {
8269 (Expr::Local { .. }, Expr::Const(ConstVal::Int(_))) => (l, 0),
8270 (Expr::Const(ConstVal::Int(_)), Expr::Local { .. }) => (r, 1),
8271 _ => return,
8272 };
8273 let var = match &**var_side {
8274 Expr::Local { var, .. } => *var,
8275 _ => return,
8276 };
8277 if !bv.contains(&var) {
8278 return;
8279 }
8280 let _ = const_side;
8281 match op {
8282 BinOp::Ne => {
8283 let inner = var_side.clone();
8284 *x = *inner;
8285 }
8286 BinOp::Eq => {
8287 let inner = var_side.clone();
8288 *x = Expr::Un {
8289 op: UnOp::Not,
8290 e: Box::new(*inner),
8291 };
8292 }
8293 _ => {}
8294 }
8295 }
8296 });
8297 };
8298 match s {
8299 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => fold(cond),
8300 _ => {}
8301 }
8302 walk_mut_deep(s, &mut |st| {
8303 let cond = match st {
8304 Stmt::If { cond, .. } => Some(cond),
8305 Stmt::While { cond, .. } => Some(cond),
8306 Stmt::DoWhile { cond, .. } => Some(cond),
8307 _ => None,
8308 };
8309 if let Some(c) = cond {
8310 fold(c);
8311 }
8312 });
8313}
8314
8315pub fn flip_const_compares(s: &mut Stmt) {
8317 rewrite_exprs(s, &mut |e| {
8318 deep_rewrite(e, &mut |x| {
8319 if let Expr::Bin { op, l, r, .. } = x {
8320 if matches!(
8321 op,
8322 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
8323 ) {
8324 let l_const = matches!(&**l, Expr::Const(_));
8325 let r_var = matches!(&**r, Expr::Local { .. });
8326 if l_const && r_var {
8327 std::mem::swap(l, r);
8328 }
8329 }
8330 }
8331 });
8332 });
8333}
8334
8335pub fn null_compares(vt: &VarTable, body: &mut Stmt) {
8337 let obj_vars: HashSet<u32> = vt
8338 .vars
8339 .iter()
8340 .filter(|v| v.ty.erased().is_reference())
8341 .map(|v| v.id)
8342 .collect();
8343 rewrite_exprs(body, &mut |e| {
8344 deep_rewrite(e, &mut |x| {
8345 null_side_rewrite(x, &|v| obj_vars.contains(&v));
8346 });
8347 });
8348}
8349
8350pub fn drop_dead_locals(body: &mut Stmt) {
8359 loop {
8360 let mut reads: Vec<usize> = Vec::new();
8361 count_reads(body, &mut reads);
8362 let mut dropped = 0usize;
8363 walk_mut_deep(body, &mut |st| match st {
8364 Stmt::Block(items) => prune_dead_items(items, &reads, &mut dropped),
8365 Stmt::Switch { cases, .. } => {
8375 for c in cases {
8376 prune_dead_items(&mut c.body, &reads, &mut dropped);
8377 }
8378 }
8379 Stmt::For { init, .. } => prune_dead_items(init, &reads, &mut dropped),
8380 _ => {}
8381 });
8382 if dropped == 0 {
8383 break;
8384 }
8385 }
8386}
8387
8388fn prune_dead_items(items: &mut Vec<Stmt>, reads: &[usize], dropped: &mut usize) {
8393 let dead = |v: u32| reads.get(v as usize).copied().unwrap_or(0) == 0;
8394 items.retain_mut(|x| match x {
8395 Stmt::ExprStmt(Expr::Assign { target, value, op, .. }) => {
8396 let dead_target = matches!(&**target, Expr::Local { var, .. } if dead(*var));
8397 if dead_target && matches!(*op, AssignOp::Plain) {
8398 *dropped += 1;
8399 if has_side_effects(value) {
8400 let e = std::mem::replace(value, Box::new(Expr::This));
8401 *x = Stmt::ExprStmt(*e);
8402 true
8403 } else {
8404 false
8405 }
8406 } else {
8407 true
8408 }
8409 }
8410 Stmt::LocalDef { var, init, .. } if dead(*var) => {
8411 *dropped += 1;
8412 if let Some(e) = init.take() {
8413 if has_side_effects(&e) {
8414 *x = Stmt::ExprStmt(e);
8415 return true;
8416 }
8417 }
8418 false
8419 }
8420 _ => true,
8421 });
8422}
8423
8424
8425fn stmt_shallow_vars<F: FnMut(u32)>(st: &Stmt, out: &mut F) {
8430 fn ex<F: FnMut(u32)>(e: &Expr, out: &mut F) {
8431 visit_exprs(e, &mut |x| {
8432 if let Expr::Local { var, .. } = x {
8433 out(*var);
8434 }
8435 });
8436 }
8437 match st {
8438 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
8439 ex(e, out)
8440 }
8441 Stmt::TernaryValue { e } => ex(e, out),
8442 Stmt::Return(Some(e)) => ex(e, out),
8443 Stmt::LocalDef { var, init, .. } => {
8444 out(*var);
8445 if let Some(e) = init {
8446 ex(e, out);
8447 }
8448 }
8449 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
8450 ex(cond, out)
8451 }
8452 Stmt::For { cond, update, .. } => {
8453 if let Some(c) = cond {
8454 ex(c, out);
8455 }
8456 for u in update {
8457 ex(u, out);
8458 }
8459 }
8460 Stmt::ForEach { iterable, .. } => ex(iterable, out),
8461 Stmt::Switch { selector, cases, .. } => {
8462 ex(selector, out);
8463 for cg in cases {
8464 if let Some(g) = &cg.guard {
8465 ex(g, out);
8466 }
8467 }
8468 }
8469 Stmt::Assert { cond, msg } => {
8470 ex(cond, out);
8471 if let Some(m) = msg {
8472 ex(m, out);
8473 }
8474 }
8475 Stmt::Synchronized { lock, .. } => ex(lock, out),
8476 _ => {}
8477 }
8478}
8479
8480fn scope_pass_a(st: &Stmt, cur: u32, next: &mut u32, def_block: &mut [u32], sizes: &mut Vec<u32>) {
8492 if let Stmt::LocalDef { var, .. } = st {
8493 let i = *var as usize;
8494 if i < def_block.len() && def_block[i] == u32::MAX {
8495 def_block[i] = cur;
8496 }
8497 }
8498 fn scoped(c: &Stmt, next: &mut u32, def_block: &mut [u32], sizes: &mut Vec<u32>) {
8499 let id = *next;
8500 *next += 1;
8501 sizes.push(0);
8502 scope_pass_a(c, id, next, def_block, sizes);
8503 sizes[id as usize] = *next - id;
8504 }
8505 match st {
8506 Stmt::Block(v) => {
8507 for x in v {
8508 scope_pass_a(x, cur, next, def_block, sizes);
8509 }
8510 }
8511 Stmt::If {
8512 then_stmt,
8513 else_stmt,
8514 ..
8515 } => {
8516 scoped(then_stmt, next, def_block, sizes);
8517 if let Some(e) = else_stmt {
8518 scoped(e, next, def_block, sizes);
8519 }
8520 }
8521 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => {
8522 scoped(body, next, def_block, sizes)
8523 }
8524 Stmt::For { init, body, .. } => {
8525 let id = *next;
8528 *next += 1;
8529 sizes.push(0);
8530 for x in init {
8531 scope_pass_a(x, id, next, def_block, sizes);
8532 }
8533 scoped(body, next, def_block, sizes);
8534 sizes[id as usize] = *next - id;
8535 }
8536 Stmt::ForEach { body, .. } => scoped(body, next, def_block, sizes),
8537 Stmt::Switch { cases, default, .. } => {
8538 for c in cases {
8539 let id = *next;
8544 *next += 1;
8545 sizes.push(0);
8546 for x in &c.body {
8547 if matches!(x, Stmt::Block(_)) {
8548 scoped(x, next, def_block, sizes);
8549 } else {
8550 scope_pass_a(x, id, next, def_block, sizes);
8551 }
8552 }
8553 sizes[id as usize] = *next - id;
8554 }
8555 if let Some(d) = default {
8556 scoped(d, next, def_block, sizes);
8557 }
8558 }
8559 Stmt::Try {
8560 body,
8561 catches,
8562 finally,
8563 } => {
8564 scoped(body, next, def_block, sizes);
8565 for c in catches {
8566 scoped(&c.body, next, def_block, sizes);
8567 }
8568 if let Some(f) = finally {
8569 scoped(f, next, def_block, sizes);
8570 }
8571 }
8572 Stmt::TryWithResources {
8573 resources,
8574 body,
8575 catches,
8576 finally,
8577 } => {
8578 let id = *next;
8580 *next += 1;
8581 sizes.push(0);
8582 for r in resources {
8583 scope_pass_a(r, id, next, def_block, sizes);
8584 }
8585 scoped(body, next, def_block, sizes);
8586 for c in catches {
8587 scoped(&c.body, next, def_block, sizes);
8588 }
8589 if let Some(f) = finally {
8590 scoped(f, next, def_block, sizes);
8591 }
8592 sizes[id as usize] = *next - id;
8593 }
8594 Stmt::Synchronized { body, .. } => scoped(body, next, def_block, sizes),
8595 Stmt::Labeled { body, .. } => scope_pass_a(body, cur, next, def_block, sizes),
8596 _ => {}
8597 }
8598}
8599
8600fn scope_pass_b(
8605 st: &Stmt,
8606 cur: u32,
8607 next: &mut u32,
8608 def_block: &[u32],
8609 sizes: &[u32],
8610 within: &mut [u32],
8611 total: &mut [u32],
8612) {
8613 fn cnt(
8614 v: u32,
8615 at: u32,
8616 def_block: &[u32],
8617 sizes: &[u32],
8618 within: &mut [u32],
8619 total: &mut [u32],
8620 ) {
8621 let i = v as usize;
8622 if i < total.len() {
8623 total[i] += 1;
8624 let d = def_block[i];
8625 if d != u32::MAX && d <= at && at < d + sizes[d as usize] {
8626 within[i] += 1;
8627 }
8628 }
8629 }
8630 fn scoped(
8631 c: &Stmt,
8632 next: &mut u32,
8633 def_block: &[u32],
8634 sizes: &[u32],
8635 within: &mut [u32],
8636 total: &mut [u32],
8637 ) {
8638 let id = *next;
8639 *next += 1;
8640 scope_pass_b(c, id, next, def_block, sizes, within, total);
8641 }
8642 fn ex_at(
8643 e: &Expr,
8644 at: u32,
8645 def_block: &[u32],
8646 sizes: &[u32],
8647 within: &mut [u32],
8648 total: &mut [u32],
8649 ) {
8650 visit_exprs(e, &mut |x| {
8651 if let Expr::Local { var, .. } = x {
8652 cnt(*var, at, def_block, sizes, within, total);
8653 }
8654 });
8655 }
8656 match st {
8657 Stmt::For {
8658 init,
8659 cond,
8660 update,
8661 body,
8662 } => {
8663 let id = *next;
8664 *next += 1;
8665 for x in init {
8666 scope_pass_b(x, id, next, def_block, sizes, within, total);
8667 }
8668 if let Some(c) = cond {
8669 ex_at(c, id, def_block, sizes, within, total);
8670 }
8671 for u in update {
8672 ex_at(u, id, def_block, sizes, within, total);
8673 }
8674 scoped(body, next, def_block, sizes, within, total);
8675 }
8676 Stmt::TryWithResources {
8677 resources,
8678 body,
8679 catches,
8680 finally,
8681 } => {
8682 let id = *next;
8683 *next += 1;
8684 for r in resources {
8685 scope_pass_b(r, id, next, def_block, sizes, within, total);
8686 }
8687 scoped(body, next, def_block, sizes, within, total);
8688 for c in catches {
8689 scoped(&c.body, next, def_block, sizes, within, total);
8690 }
8691 if let Some(f) = finally {
8692 scoped(f, next, def_block, sizes, within, total);
8693 }
8694 }
8695 _ => {
8696 stmt_shallow_vars(st, &mut |v| cnt(v, cur, def_block, sizes, within, total));
8697 match st {
8698 Stmt::Block(v) => {
8699 for x in v {
8700 scope_pass_b(x, cur, next, def_block, sizes, within, total);
8701 }
8702 }
8703 Stmt::If {
8704 then_stmt,
8705 else_stmt,
8706 ..
8707 } => {
8708 scoped(then_stmt, next, def_block, sizes, within, total);
8709 if let Some(e) = else_stmt {
8710 scoped(e, next, def_block, sizes, within, total);
8711 }
8712 }
8713 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => {
8714 scoped(body, next, def_block, sizes, within, total)
8715 }
8716 Stmt::ForEach { body, .. } => {
8717 scoped(body, next, def_block, sizes, within, total)
8718 }
8719 Stmt::Switch { cases, default, .. } => {
8720 for c in cases {
8721 let id = *next;
8722 *next += 1;
8723 for x in &c.body {
8724 if matches!(x, Stmt::Block(_)) {
8725 scoped(x, next, def_block, sizes, within, total);
8726 } else {
8727 scope_pass_b(x, id, next, def_block, sizes, within, total);
8728 }
8729 }
8730 }
8731 if let Some(d) = default {
8732 scoped(d, next, def_block, sizes, within, total);
8733 }
8734 }
8735 Stmt::Try {
8736 body,
8737 catches,
8738 finally,
8739 } => {
8740 scoped(body, next, def_block, sizes, within, total);
8741 for c in catches {
8742 scoped(&c.body, next, def_block, sizes, within, total);
8743 }
8744 if let Some(f) = finally {
8745 scoped(f, next, def_block, sizes, within, total);
8746 }
8747 }
8748 Stmt::Synchronized { body, .. } => {
8749 scoped(body, next, def_block, sizes, within, total)
8750 }
8751 Stmt::Labeled { body, .. } => {
8752 scope_pass_b(body, cur, next, def_block, sizes, within, total)
8753 }
8754 _ => {}
8755 }
8756 }
8757 }
8758}
8759
8760pub fn strip_enum_const_inits(s: &mut Stmt, class: &crate::PoolClass) {
8769 let const_fields: jdc_core::FxHashSet<&str> = class
8770 .static_fields
8771 .iter()
8772 .filter(|f| f.access & crate::access::ACC_ENUM != 0)
8773 .map(|f| f.name.as_str())
8774 .collect();
8775 if const_fields.is_empty() {
8776 return;
8777 }
8778 if let Stmt::Block(stmts) = s {
8779 stmts.retain(|st| {
8780 !matches!(
8781 st,
8782 Stmt::ExprStmt(Expr::Assign { target, value, .. })
8783 if matches!(&**target,
8784 Expr::Field { cls, name, is_static: true, .. }
8785 if cls.as_ref() == class.name
8786 && const_fields.contains(&**name))
8787 && matches!(&**value, Expr::New { cls: ncls, .. } if ncls.as_ref() == class.name)
8788 )
8789 });
8790 }
8791}
8792
8793fn contains_this_access(e: &Expr) -> bool {
8802 let mut hit = false;
8803 visit_exprs(e, &mut |x| {
8804 if hit {
8805 return;
8806 }
8807 match x {
8808 Expr::This => hit = true,
8809 Expr::Field { owner, is_static: false, .. } => {
8810 if owner.is_none() || matches!(&**owner.as_ref().unwrap(), Expr::This) {
8811 hit = true;
8812 }
8813 }
8814 Expr::Method { owner, is_static: false, .. } => {
8815 if owner.is_none() || matches!(&**owner.as_ref().unwrap(), Expr::This) {
8816 hit = true;
8817 }
8818 }
8819 _ => {}
8820 }
8821 });
8822 hit
8823}
8824
8825fn var_occurs_in(stmts: &[Stmt], var: u32) -> bool {
8840 stmts.iter().any(|st| {
8841 let mut hit = false;
8842 visit_stmt_exprs_ro(st, &mut |e| {
8843 if !hit {
8844 visit_exprs(e, &mut |x| {
8845 if let Expr::Local { var: v, .. } = x {
8846 if *v == var {
8847 hit = true;
8848 }
8849 }
8850 });
8851 }
8852 });
8853 hit
8854 })
8855}
8856
8857pub struct CtorHelper {
8867 pub name: String,
8868 pub ret: JavaType,
8869 pub params: Vec<(JavaType, String)>,
8870 pub body: Stmt,
8871 pub vt: VarTable,
8872}
8873
8874fn ctor_helpers() -> &'static std::sync::Mutex<std::collections::HashMap<String, Vec<CtorHelper>>> {
8875 static H: std::sync::OnceLock<std::sync::Mutex<std::collections::HashMap<String, Vec<CtorHelper>>>> =
8876 std::sync::OnceLock::new();
8877 H.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::default()))
8878}
8879
8880pub struct CtorRelay {
8885 pub formals: Vec<JavaType>,
8887 pub is_super: bool,
8888}
8889
8890type RelayMap = std::collections::HashMap<String, Vec<(String, CtorRelay)>>;
8891
8892fn ctor_relays() -> &'static std::sync::Mutex<RelayMap> {
8893 static H: std::sync::OnceLock<std::sync::Mutex<RelayMap>> = std::sync::OnceLock::new();
8894 H.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::default()))
8895}
8896
8897pub fn take_ctor_relays(class: &str) -> Vec<(String, CtorRelay)> {
8900 let mut m = ctor_relays().lock().unwrap_or_else(|e| e.into_inner());
8901 let mut v = m.remove(class).unwrap_or_default();
8902 v.sort_by(|a, b| a.0.cmp(&b.0));
8903 v
8904}
8905
8906fn array_init(args: Vec<Expr>) -> Expr {
8907 Expr::NewArray {
8908 elem: TypeRef::J(JavaType::Object(std::sync::Arc::from("java/lang/Object"))),
8909 dims: Vec::new(),
8910 trailing_dims: 0,
8911 init: Some(args),
8912 }
8913}
8914
8915fn helper_name(class: &str, desc: &str) -> String {
8918 let mut h: u64 = 0xcbf29ce484222325;
8919 for b in class.as_bytes().iter().chain(b"::").chain(desc.as_bytes()) {
8920 h ^= *b as u64;
8921 h = h.wrapping_mul(0x100000001b3);
8922 }
8923 format!("resolve${:x}", (h >> 16) & 0xffff_ffff)
8924}
8925
8926pub fn take_ctor_helpers(class: &str) -> Vec<CtorHelper> {
8929 let mut m = ctor_helpers().lock().unwrap_or_else(|e| e.into_inner());
8930 let mut v = m.remove(class).unwrap_or_default();
8931 v.sort_by(|a, b| a.name.cmp(&b.name));
8932 v
8933}
8934
8935fn strip_delegations_deep(stmts: &mut Vec<Stmt>) {
8936 stmts.retain(|s| !is_bare_ctor_call(s));
8937 for s in stmts.iter_mut() {
8938 strip_delegation_one(s);
8939 }
8940}
8941
8942fn strip_delegation_one(st: &mut Stmt) {
8943 if is_bare_ctor_call(st) {
8944 *st = Stmt::Block(Vec::new());
8945 return;
8946 }
8947 match st {
8948 Stmt::Block(v) => strip_delegations_deep(v),
8949 Stmt::If { then_stmt, else_stmt, .. } => {
8950 strip_delegation_one(then_stmt);
8951 if let Some(e) = else_stmt {
8952 strip_delegation_one(e);
8953 }
8954 }
8955 Stmt::While { body, .. }
8956 | Stmt::DoWhile { body, .. }
8957 | Stmt::ForEach { body, .. }
8958 | Stmt::Synchronized { body, .. }
8959 | Stmt::Labeled { body, .. } => strip_delegation_one(body),
8960 Stmt::For { init, body, .. } => {
8961 strip_delegations_deep(init);
8962 strip_delegation_one(body);
8963 }
8964 Stmt::Switch { cases, default, .. } => {
8965 for c in cases.iter_mut() {
8966 strip_delegations_deep(&mut c.body);
8967 }
8968 if let Some(d) = default {
8969 strip_delegation_one(d);
8970 }
8971 }
8972 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
8973 strip_delegation_one(body);
8974 for c in catches.iter_mut() {
8975 strip_delegation_one(&mut c.body);
8976 }
8977 if let Some(f) = finally {
8978 strip_delegation_one(f);
8979 }
8980 }
8981 _ => {}
8982 }
8983}
8984
8985#[allow(clippy::ptr_arg)]
8986fn rewrite_bare_returns(stmts: &mut Vec<Stmt>, val: &Expr) {
8987 for s in stmts.iter_mut() {
8988 rewrite_bare_returns_one(s, val);
8989 }
8990}
8991
8992fn rewrite_bare_returns_one(st: &mut Stmt, val: &Expr) {
8993 match st {
8994 Stmt::Return(None) => *st = Stmt::Return(Some(val.clone())),
8995 Stmt::Block(v) => rewrite_bare_returns(v, val),
8996 Stmt::If { then_stmt, else_stmt, .. } => {
8997 rewrite_bare_returns_one(then_stmt, val);
8998 if let Some(e) = else_stmt {
8999 rewrite_bare_returns_one(e, val);
9000 }
9001 }
9002 Stmt::While { body, .. }
9003 | Stmt::DoWhile { body, .. }
9004 | Stmt::ForEach { body, .. }
9005 | Stmt::Synchronized { body, .. }
9006 | Stmt::Labeled { body, .. } => rewrite_bare_returns_one(body, val),
9007 Stmt::For { init, body, .. } => {
9008 rewrite_bare_returns(init, val);
9009 rewrite_bare_returns_one(body, val);
9010 }
9011 Stmt::Switch { cases, default, .. } => {
9012 for c in cases.iter_mut() {
9013 rewrite_bare_returns(&mut c.body, val);
9014 }
9015 if let Some(d) = default {
9016 rewrite_bare_returns_one(d, val);
9017 }
9018 }
9019 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
9020 rewrite_bare_returns_one(body, val);
9021 for c in catches.iter_mut() {
9022 rewrite_bare_returns_one(&mut c.body, val);
9023 }
9024 if let Some(f) = finally {
9025 rewrite_bare_returns_one(f, val);
9026 }
9027 }
9028 _ => {}
9029 }
9030}
9031
9032fn collect_def_vars(stmts: &[Stmt], out: &mut jdc_core::FxHashSet<u32>) {
9033 for st in stmts {
9034 match st {
9035 Stmt::LocalDef { var, .. } => {
9036 out.insert(*var);
9037 }
9038 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
9039 if let Expr::Local { var, .. } = &**target {
9040 out.insert(*var);
9041 }
9042 }
9043 Stmt::Block(v) => collect_def_vars(v, out),
9044 Stmt::If { then_stmt, else_stmt, .. } => {
9045 collect_def_vars(std::slice::from_ref(then_stmt.as_ref()), out);
9046 if let Some(e) = else_stmt {
9047 collect_def_vars(std::slice::from_ref(e.as_ref()), out);
9048 }
9049 }
9050 Stmt::While { body, .. }
9051 | Stmt::DoWhile { body, .. }
9052 | Stmt::Synchronized { body, .. }
9053 | Stmt::Labeled { body, .. } => {
9054 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
9055 }
9056 Stmt::ForEach { var, body, .. } => {
9057 out.insert(*var);
9058 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
9059 }
9060 Stmt::For { init, body, .. } => {
9061 collect_def_vars(init, out);
9062 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
9063 }
9064 Stmt::Switch { cases, default, .. } => {
9065 for c in cases {
9066 collect_def_vars(&c.body, out);
9067 }
9068 if let Some(d) = default {
9069 collect_def_vars(std::slice::from_ref(d.as_ref()), out);
9070 }
9071 }
9072 Stmt::Try { body, catches, finally }
9073 | Stmt::TryWithResources { body, catches, finally, .. } => {
9074 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
9075 for c in catches {
9076 out.insert(c.var);
9077 collect_def_vars(std::slice::from_ref(c.body.as_ref()), out);
9078 }
9079 if let Some(f) = finally {
9080 collect_def_vars(std::slice::from_ref(f.as_ref()), out);
9081 }
9082 }
9083 _ => {}
9084 }
9085 }
9086}
9087
9088fn extraction_shape_ok(stmts: &[Stmt]) -> bool {
9091 stmts.iter().all(|st| match st {
9092 Stmt::Block(v) => extraction_shape_ok(v),
9093 Stmt::LocalDef { .. }
9094 | Stmt::ExprStmt(_)
9095 | Stmt::Return(_)
9096 | Stmt::Throw(_)
9097 | Stmt::Assert { .. }
9098 | Stmt::Break(_)
9099 | Stmt::Continue(_) => true,
9100 Stmt::If { then_stmt, else_stmt, .. } => {
9101 ok_one(then_stmt)
9102 && else_stmt.as_deref().map(ok_one).unwrap_or(true)
9103 }
9104 Stmt::While { body, .. }
9105 | Stmt::DoWhile { body, .. }
9106 | Stmt::ForEach { body, .. }
9107 | Stmt::Synchronized { body, .. } => ok_one(body),
9108 Stmt::Labeled { body, .. } => ok_one(body),
9109 Stmt::For { init, body, .. } => extraction_shape_ok(init) && ok_one(body),
9110 Stmt::Switch { cases, default, .. } => {
9111 cases.iter().all(|c| extraction_shape_ok(&c.body))
9112 && default.as_deref().map(ok_one).unwrap_or(true)
9113 }
9114 Stmt::Try { body, catches, finally }
9115 | Stmt::TryWithResources { body, catches, finally, .. } => {
9116 ok_one(body)
9117 && catches.iter().all(|c| ok_one(&c.body))
9118 && finally.as_deref().map(ok_one).unwrap_or(true)
9119 }
9120 _ => false,
9123 })
9124}
9125
9126fn ok_one(st: &Stmt) -> bool {
9127 extraction_shape_ok(std::slice::from_ref(st))
9128}
9129
9130fn static_safe(stmts: &[Stmt], this_id: Option<u32>) -> bool {
9131 let mut ok = true;
9132 for st in stmts {
9133 visit_stmt_exprs_ro(st, &mut |e| {
9134 visit_exprs(e, &mut |x| {
9135 match x {
9136 Expr::This => ok = false,
9137 Expr::Local { var, .. } => {
9143 if Some(*var) == this_id {
9144 ok = false;
9145 }
9146 }
9147 Expr::Field { owner: None, is_static: false, .. } => ok = false,
9149 Expr::Method { owner: None, is_static: false, .. } => ok = false,
9150 _ => {}
9151 }
9152 });
9153 });
9154 }
9155 ok
9156}
9157
9158pub fn strip_clinit_hotfix_guard(body: &mut Stmt) {
9169 if let Stmt::Block(vs) = body {
9173 flatten_top_blocks(vs);
9174 }
9175 let Stmt::Block(stmts) = body else { return };
9176 let mut gi = 0usize;
9179 while gi < stmts.len() && matches!(&stmts[gi], Stmt::LocalDef { init: None, .. }) {
9180 gi += 1;
9181 }
9182 let Some(first) = stmts.get(gi) else { return };
9183 let Stmt::If { then_stmt, else_stmt, .. } = first else {
9184 return;
9185 };
9186 if else_stmt.is_some() {
9187 return;
9188 }
9189 let mut has_ret = false;
9190 let mut has_proxy = false;
9191 let c = then_stmt.as_ref();
9192 walk_all(c, &mut |st| {
9193 if matches!(st, Stmt::Return(None)) {
9194 has_ret = true;
9195 }
9196 });
9197 let mut cc = c.clone();
9198 walk_stmt_exprs(&mut cc, &mut |e| {
9199 deep_rewrite(e, &mut |x| {
9200 if let Expr::Method { cls, .. } = x {
9201 if cls.contains("PatchProxy") {
9202 has_proxy = true;
9203 }
9204 }
9205 });
9206 });
9207 if has_ret && has_proxy {
9208 stmts.remove(gi);
9209 }
9210}
9211
9212pub(crate) fn first_leaf_stmt(s: &Stmt) -> &Stmt {
9233 match s {
9234 Stmt::Block(v) if !v.is_empty() => first_leaf_stmt(&v[0]),
9235 other => other,
9236 }
9237}
9238
9239pub fn strip_ctor_hotfix_guards(body: &mut Stmt) {
9258 fn marker_hit(e: &Expr) -> bool {
9259 let has = |n: &str| {
9260 let l = n.to_ascii_lowercase();
9261 l.contains("hotfix")
9262 || l.contains("instantrun")
9263 || l.contains("robust")
9264 || l.contains("titan")
9265 || l.contains("patchproxy")
9266 || l.contains("changequickredirect")
9267 || l.contains("constructorcode")
9268 };
9269 let mut hit = false;
9270 visit_exprs(e, &mut |x| {
9271 match x {
9272 Expr::Method { cls, .. } | Expr::New { cls, .. } | Expr::Field { cls, .. } => {
9273 if has(cls) {
9274 hit = true;
9275 }
9276 }
9277 _ => {}
9278 }
9279 if let Expr::Field { ty, .. } = x {
9280 if let TypeRef::J(JavaType::Object(n)) = ty {
9281 if has(n) {
9282 hit = true;
9283 }
9284 }
9285 }
9286 });
9287 hit
9288 }
9289 fn is_guard(st: &Stmt) -> bool {
9293 let mut cur = st;
9294 loop {
9295 match cur {
9296 Stmt::Block(v) if v.len() == 1 => cur = &v[0],
9297 Stmt::If { .. } => break,
9298 _ => return false,
9299 }
9300 }
9301 let mut has_del = false;
9302 walk_all(cur, &mut |s2| {
9303 if is_bare_ctor_call(s2) {
9304 has_del = true;
9305 }
9306 });
9307 if !has_del {
9308 return false;
9309 }
9310 let mut has_marker = false;
9311 visit_stmt_exprs_ro(cur, &mut |e| {
9312 if marker_hit(e) {
9313 has_marker = true;
9314 }
9315 });
9316 has_marker
9317 }
9318 fn strip_in(stmts: &mut Vec<Stmt>) {
9329 stmts.retain(|st| !is_guard(st));
9330 for st in stmts.iter_mut() {
9331 if let Stmt::Block(v) = st {
9332 strip_in(v);
9333 }
9334 }
9335 }
9336 let Stmt::Block(stmts) = body else { return };
9337 strip_in(stmts);
9338}
9339
9340pub fn hoist_branch_delegations(body: &mut Stmt, vt: &VarTable) {
9353 let Stmt::Block(stmts) = body else { return };
9354 if stmts.first().is_some_and(is_bare_ctor_call) {
9355 return; }
9357 let mut dels: Vec<Expr> = Vec::new();
9358 for st in stmts.iter() {
9359 visit_stmt_exprs_ro(st, &mut |e| {
9360 if is_delegation_expr(e) {
9361 dels.push(e.clone());
9362 }
9363 });
9364 }
9365 if dels.is_empty() {
9366 return;
9367 }
9368 if !dels.iter().all(|d| *d == dels[0]) {
9369 return;
9370 }
9371 let mut bad = false;
9372 visit_exprs(&dels[0], &mut |x| {
9373 if let Expr::Local { var, .. } = x {
9374 if (*var as usize) >= vt.vars.len() || !vt.vars[*var as usize].is_param {
9375 bad = true;
9376 }
9377 }
9378 });
9379 if bad {
9380 return;
9381 }
9382 let del = dels[0].clone();
9383 strip_delegations_deep(stmts);
9384 stmts.insert(0, Stmt::ExprStmt(del));
9385}
9386
9387pub fn extract_branched_delegation_helper(
9421 body: &mut Stmt,
9422 vt: &VarTable,
9423 pool: &DexPool,
9424 class: &crate::PoolClass,
9425 desc_str: &str,
9426) {
9427 if class.is_enum() {
9428 return;
9429 }
9430 if class.nesting.enclosing_class.is_some() && !crate::ctx::nested_is_static(pool, class) {
9435 return;
9436 }
9437 let Stmt::Block(stmts) = body else { return };
9438 let mut dels: Vec<Expr> = Vec::new();
9439 for st in stmts.iter() {
9440 visit_stmt_exprs_ro(st, &mut |e| {
9441 if is_delegation_expr(e) {
9442 dels.push(e.clone());
9443 }
9444 });
9445 }
9446 if dels.is_empty() {
9447 return;
9448 }
9449 if dels.len() == 1 && stmts.first().is_some_and(is_bare_ctor_call) {
9450 return; }
9452 let template = dels[0].clone();
9453 let Expr::Method { args: t_args, .. } = &template else {
9454 return;
9455 };
9456 enum Mode {
9460 Single(u32),
9461 Position(usize),
9462 Full,
9469 }
9470 let full_gate = (|| -> Option<()> {
9477 let Expr::Method {
9478 args: a0,
9479 desc: d0,
9480 is_super: s0,
9481 cls: c0,
9482 ..
9483 } = &dels[0]
9484 else {
9485 return None;
9486 };
9487 if a0.is_empty() {
9488 return None;
9489 }
9490 for d in &dels[1..] {
9491 match d {
9492 Expr::Method {
9493 args,
9494 desc,
9495 is_super,
9496 cls,
9497 ..
9498 } if args.len() == a0.len()
9499 && **desc == **d0
9500 && is_super == s0
9501 && cls == c0 => {}
9502 _ => return None,
9503 }
9504 }
9505 if class
9506 .all_methods()
9507 .any(|m| &*m.name == "<init>" && &*m.desc == "([Ljava/lang/Object;)V")
9508 {
9509 return None;
9510 }
9511 Some(())
9512 })()
9513 .is_some();
9514 let mode = if dels.iter().all(|d| *d == dels[0]) {
9515 let mut carrier: Option<u32> = None;
9516 let mut reads = 0usize;
9517 let mut multi = false;
9518 for a in t_args.iter() {
9519 let c = a.clone();
9520 visit_exprs(&c, &mut |x| {
9521 if let Expr::Local { var, .. } = x {
9522 if (*var as usize) < vt.vars.len() && !vt.vars[*var as usize].is_param {
9523 reads += 1;
9524 match carrier {
9525 Some(v) if v == *var => {}
9526 Some(_) => multi = true,
9527 None => carrier = Some(*var),
9528 }
9529 }
9530 }
9531 });
9532 }
9533 match carrier {
9534 Some(cv) if !multi && reads == 1 => Mode::Single(cv),
9535 _ if full_gate => Mode::Full,
9536 _ => return,
9537 }
9538 } else {
9539 let positioned: Option<Mode> = (|| {
9540 let arity = t_args.len();
9541 if dels
9545 .iter()
9546 .any(|d| !matches!(d, Expr::Method { args, .. } if args.len() == arity))
9547 {
9548 return None;
9549 }
9550 let mut diff: Vec<usize> = Vec::new();
9551 for j in 0..arity {
9552 if dels.iter().any(|d| match d {
9553 Expr::Method { args, .. } => args.get(j) != t_args.get(j),
9554 _ => true,
9555 }) {
9556 diff.push(j);
9557 }
9558 }
9559 if diff.len() != 1 {
9560 return None;
9561 }
9562 let k = diff[0];
9563 let mut base0 = dels[0].clone();
9567 if let Expr::Method { args, .. } = &mut base0 {
9568 args[k] = Expr::Const(ConstVal::Null);
9569 }
9570 for d in dels.iter() {
9571 let mut c = d.clone();
9572 if let Expr::Method { args, .. } = &mut c {
9573 args[k] = Expr::Const(ConstVal::Null);
9574 }
9575 if c != base0 {
9576 return None;
9577 }
9578 }
9579 let mut carrier_ty: Option<JavaType> = None;
9582 for d in dels.iter() {
9583 let Expr::Method { args, .. } = d else {
9584 return None;
9585 };
9586 let Expr::Local { var, .. } = &args[k] else {
9587 return None;
9588 };
9589 if (*var as usize) >= vt.vars.len() || vt.vars[*var as usize].is_param {
9590 return None;
9591 }
9592 let ty = vt.vars[*var as usize].ty.erased();
9593 match &carrier_ty {
9594 Some(t) if *t == ty => {}
9595 Some(_) => return None,
9596 None => carrier_ty = Some(ty),
9597 }
9598 for (j, a) in args.iter().enumerate() {
9604 if j == k {
9605 continue;
9606 }
9607 let mut bad = false;
9608 visit_exprs(a, &mut |x| {
9609 if let Expr::Local { var: v2, .. } = x {
9610 if (*v2 as usize) >= vt.vars.len() || !vt.vars[*v2 as usize].is_param {
9611 bad = true;
9612 }
9613 }
9614 });
9615 if bad {
9616 return None;
9617 }
9618 }
9619 }
9620 Some(Mode::Position(k))
9621 })();
9622 match positioned {
9623 Some(m) => m,
9624 None if full_gate => Mode::Full,
9625 None => return,
9626 }
9627 };
9628 let l = stmts.iter().rposition(is_bare_ctor_call);
9629 if l == Some(0) {
9630 return;
9631 }
9632 let (mut extracted, mut tail): (Vec<Stmt>, Vec<Stmt>) = match l {
9633 Some(l) => (stmts[..l].to_vec(), stmts[l + 1..].to_vec()),
9634 None => (stmts.clone(), Vec::new()),
9635 };
9636 if !extraction_shape_ok(&extracted) {
9637 return;
9638 }
9639 let mut site_tails: Vec<Vec<Stmt>> = Vec::new();
9646 {
9647 let mut probe = extracted.clone();
9648 site_tails_walk(&mut probe, &mut site_tails, false);
9649 }
9650 let hoist = !site_tails.is_empty() && {
9651 let n0 = normalize_tail(&site_tails[0]);
9652 site_tails.iter().all(|t| normalize_tail(t) == n0)
9653 && (l.is_none() || normalize_tail(&tail) == n0)
9654 };
9655 if hoist {
9656 site_tails_walk(&mut extracted, &mut Vec::new(), true);
9657 if l.is_none() {
9658 tail = normalize_tail(&site_tails[0]);
9659 }
9660 }
9661 let primary_del: Expr = match l {
9664 Some(l) => match &stmts[l] {
9665 Stmt::ExprStmt(e) => e.clone(),
9666 _ => return,
9667 },
9668 None => dels[0].clone(),
9669 };
9670 let ret = match mode {
9671 Mode::Single(cv) => vt.var(cv).ty.erased(),
9672 Mode::Position(k) => match &primary_del {
9673 Expr::Method { args, .. } => match args.get(k) {
9674 Some(Expr::Local { var, .. }) => vt.var(*var).ty.erased(),
9675 _ => return,
9676 },
9677 _ => return,
9678 },
9679 Mode::Full => JavaType::Array(Box::new(JavaType::Object(std::sync::Arc::from(
9680 "java/lang/Object",
9681 )))),
9682 };
9683 match mode {
9684 Mode::Single(cv) => {
9685 strip_delegations_deep(&mut extracted);
9686 let cv_local = Expr::Local {
9687 var: cv,
9688 ty: vt.var(cv).ty.clone(),
9689 };
9690 rewrite_bare_returns(&mut extracted, &cv_local);
9691 if !always_returns(&extracted) {
9692 extracted.push(Stmt::Return(Some(cv_local)));
9693 }
9694 }
9695 Mode::Position(k) => {
9696 fill_returns_by_position(&mut extracted, &|args: &[Expr]| args.get(k).cloned());
9700 let fallback = match &primary_del {
9701 Expr::Method { args, .. } => args[k].clone(),
9702 _ => return,
9703 };
9704 rewrite_bare_returns(&mut extracted, &fallback);
9705 if !always_returns(&extracted) {
9706 extracted.push(Stmt::Return(Some(fallback)));
9707 }
9708 }
9709 Mode::Full => {
9710 fill_returns_by_position(&mut extracted, &|args: &[Expr]| {
9717 Some(array_init(args.to_vec()))
9718 });
9719 if l.is_none() {
9720 if !always_returns(&extracted) {
9727 let cut = (1..=extracted.len())
9728 .find(|&i| always_returns(&extracted[..i]));
9729 match cut {
9730 Some(i) => extracted.truncate(i),
9731 None => return,
9732 }
9733 }
9734 }
9735 let fallback_args = match &primary_del {
9736 Expr::Method { args, .. } => args.clone(),
9737 _ => return,
9738 };
9739 let fallback = array_init(fallback_args);
9740 rewrite_bare_returns(&mut extracted, &fallback);
9741 if !always_returns(&extracted) {
9742 extracted.push(Stmt::Return(Some(fallback)));
9743 }
9744 }
9745 }
9746 let this_id = vt
9747 .vars
9748 .iter()
9749 .find(|v| v.is_param && v.name == "this")
9750 .map(|v| v.id);
9751 if !static_safe(&extracted, this_id) {
9752 return;
9753 }
9754 let mut defs: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
9755 collect_def_vars(&extracted, &mut defs);
9756 if defs.iter().any(|v| var_occurs_in(&tail, *v)) {
9757 return;
9758 }
9759 let params: Vec<(u32, JavaType, String)> = vt
9760 .vars
9761 .iter()
9762 .filter(|v| v.is_param && v.name != "this")
9763 .map(|v| (v.id, v.ty.erased(), v.name.clone()))
9764 .collect();
9765 let name = helper_name(&class.name, desc_str);
9766 let call = Expr::Method {
9767 owner: None,
9768 cls: std::sync::Arc::from(class.name.as_str()),
9769 name: std::sync::Arc::from(name.as_str()),
9770 desc: std::sync::Arc::new(jdc_core::types::MethodDescriptor {
9771 ret: ret.clone(),
9772 args: params.iter().map(|p| p.1.clone()).collect(),
9773 }),
9774 args: params
9775 .iter()
9776 .map(|(id, ty, _)| Expr::Local {
9777 var: *id,
9778 ty: TypeRef::J(ty.clone()),
9779 })
9780 .collect(),
9781 is_static: true,
9782 is_interface: false,
9783 is_special: false,
9784 is_super: false,
9785 is_dynamic: false,
9786 type_args: Vec::new(),
9787 };
9788 let mut new_del = primary_del.clone();
9789 match mode {
9790 Mode::Single(cv) => {
9791 deep_rewrite(&mut new_del, &mut |x| {
9792 if let Expr::Local { var, .. } = x {
9793 if *var == cv {
9794 *x = call.clone();
9795 }
9796 }
9797 });
9798 }
9799 Mode::Position(k) => {
9800 let Expr::Method { args, .. } = &mut new_del else {
9801 return;
9802 };
9803 if k >= args.len() {
9804 return;
9805 }
9806 args[k] = call.clone();
9807 }
9808 Mode::Full => {
9809 new_del = Expr::Method {
9813 owner: None,
9814 cls: std::sync::Arc::from(class.name.as_str()),
9815 name: "<init>".into(),
9816 desc: std::sync::Arc::new(jdc_core::types::MethodDescriptor {
9817 args: vec![JavaType::Array(Box::new(JavaType::Object(
9818 std::sync::Arc::from("java/lang/Object"),
9819 )))],
9820 ret: JavaType::Void,
9821 }),
9822 args: vec![call.clone()],
9823 is_static: false,
9824 is_interface: false,
9825 is_special: true,
9826 is_super: false,
9827 is_dynamic: false,
9828 type_args: Vec::new(),
9829 };
9830 }
9831 }
9832 {
9833 let mut m = ctor_helpers().lock().unwrap_or_else(|e| e.into_inner());
9834 let v = m.entry(class.name.clone()).or_default();
9835 if !v.iter().any(|h| h.name == name) {
9836 let mut hbody = Stmt::Block(extracted);
9840 invert_empty_thens(&mut hbody);
9841 v.push(CtorHelper {
9842 name,
9843 ret: ret.clone(),
9844 params: params.into_iter().map(|(_, ty, nm)| (ty, nm)).collect(),
9845 body: hbody,
9846 vt: vt.clone(),
9847 });
9848 if matches!(mode, Mode::Full) {
9849 let Expr::Method {
9850 desc: d0,
9851 is_super: s0,
9852 ..
9853 } = &primary_del
9854 else {
9855 return;
9856 };
9857 let mut rm = ctor_relays().lock().unwrap_or_else(|e| e.into_inner());
9858 let rv = rm.entry(class.name.clone()).or_default();
9859 if !rv.iter().any(|(d, _)| d == desc_str) {
9860 rv.push((
9861 desc_str.to_string(),
9862 CtorRelay {
9863 formals: d0.args.clone(),
9864 is_super: *s0,
9865 },
9866 ));
9867 }
9868 }
9869 } else {
9870 return; }
9872 }
9873 *stmts = std::iter::once(Stmt::ExprStmt(new_del)).chain(tail).collect();
9874}
9875
9876fn fill_returns_by_position(
9883 stmts: &mut Vec<Stmt>,
9884 pick: &dyn Fn(&[Expr]) -> Option<Expr>,
9885) {
9886 let mut i = 0;
9887 while i < stmts.len() {
9888 if is_bare_ctor_call(&stmts[i]) {
9889 let karg = match &stmts[i] {
9890 Stmt::ExprStmt(Expr::Method { args, .. }) => pick(args),
9891 _ => None,
9892 };
9893 if let Some(v) = karg {
9894 let mut j = i + 1;
9895 let mut found = None;
9896 while j < stmts.len() {
9897 if matches!(stmts[j], Stmt::Return(None)) {
9898 found = Some(j);
9899 break;
9900 }
9901 if is_bare_ctor_call(&stmts[j]) {
9902 break; }
9904 j += 1;
9905 }
9906 match found {
9907 Some(j) => {
9908 stmts[j] = Stmt::Return(Some(v));
9909 stmts.remove(i);
9910 continue;
9911 }
9912 None => {
9913 stmts[i] = Stmt::Return(Some(v));
9914 stmts.truncate(i + 1);
9915 break;
9916 }
9917 }
9918 }
9919 }
9920 fill_returns_one(&mut stmts[i], pick);
9921 i += 1;
9922 }
9923}
9924
9925fn fill_returns_one(st: &mut Stmt, pick: &dyn Fn(&[Expr]) -> Option<Expr>) {
9926 match st {
9927 Stmt::Block(v) => fill_returns_by_position(v, pick),
9928 Stmt::If { then_stmt, else_stmt, .. } => {
9929 fill_returns_stmt(then_stmt, pick);
9930 if let Some(e) = else_stmt {
9931 fill_returns_stmt(e, pick);
9932 }
9933 }
9934 Stmt::While { body, .. }
9935 | Stmt::DoWhile { body, .. }
9936 | Stmt::ForEach { body, .. }
9937 | Stmt::Synchronized { body, .. }
9938 | Stmt::Labeled { body, .. } => fill_returns_stmt(body, pick),
9939 Stmt::For { init, body, .. } => {
9940 fill_returns_by_position(init, pick);
9941 fill_returns_stmt(body, pick);
9942 }
9943 Stmt::Switch { cases, default, .. } => {
9944 for c in cases.iter_mut() {
9945 fill_returns_by_position(&mut c.body, pick);
9946 }
9947 if let Some(d) = default {
9948 fill_returns_stmt(d, pick);
9949 }
9950 }
9951 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
9952 fill_returns_stmt(body, pick);
9953 for c in catches.iter_mut() {
9954 fill_returns_stmt(&mut c.body, pick);
9955 }
9956 if let Some(f) = finally {
9957 fill_returns_stmt(f, pick);
9958 }
9959 }
9960 _ => {}
9961 }
9962}
9963
9964fn fill_returns_stmt(st: &mut Stmt, pick: &dyn Fn(&[Expr]) -> Option<Expr>) {
9965 if is_bare_ctor_call(st) {
9966 if let Stmt::ExprStmt(Expr::Method { args, .. }) = st {
9969 if let Some(v) = pick(args) {
9970 *st = Stmt::Return(Some(v));
9971 return;
9972 }
9973 }
9974 }
9975 fill_returns_one(st, pick);
9976}
9977
9978fn normalize_tail(t: &[Stmt]) -> Vec<Stmt> {
9979 let mut v = t.to_vec();
9980 while matches!(v.last(), Some(Stmt::Return(None))) {
9981 v.pop();
9982 }
9983 v
9984}
9985
9986fn site_tails_walk(stmts: &mut Vec<Stmt>, out: &mut Vec<Vec<Stmt>>, carve: bool) {
9991 let mut i = 0;
9992 while i < stmts.len() {
9993 if is_bare_ctor_call(&stmts[i]) {
9994 let mut j = i + 1;
9995 let mut end = stmts.len();
9996 while j < stmts.len() {
9997 if matches!(stmts[j], Stmt::Return(None)) {
9998 end = j;
9999 break;
10000 }
10001 if is_bare_ctor_call(&stmts[j]) {
10002 end = j;
10003 break;
10004 }
10005 j += 1;
10006 }
10007 out.push(stmts[i + 1..end].to_vec());
10008 if carve {
10009 stmts.splice(i + 1..end, []);
10010 }
10011 i += 1;
10012 continue;
10013 }
10014 site_tails_one(&mut stmts[i], out, carve);
10015 i += 1;
10016 }
10017}
10018
10019fn site_tails_one(st: &mut Stmt, out: &mut Vec<Vec<Stmt>>, carve: bool) {
10020 if is_bare_ctor_call(st) {
10021 out.push(Vec::new());
10022 return;
10023 }
10024 match st {
10025 Stmt::Block(v) => site_tails_walk(v, out, carve),
10026 Stmt::If { then_stmt, else_stmt, .. } => {
10027 site_tails_one(then_stmt, out, carve);
10028 if let Some(e) = else_stmt {
10029 site_tails_one(e, out, carve);
10030 }
10031 }
10032 Stmt::While { body, .. }
10033 | Stmt::DoWhile { body, .. }
10034 | Stmt::ForEach { body, .. }
10035 | Stmt::Synchronized { body, .. }
10036 | Stmt::Labeled { body, .. } => site_tails_one(body, out, carve),
10037 Stmt::For { init, body, .. } => {
10038 site_tails_walk(init, out, carve);
10039 site_tails_one(body, out, carve);
10040 }
10041 Stmt::Switch { cases, default, .. } => {
10042 for c in cases.iter_mut() {
10043 site_tails_walk(&mut c.body, out, carve);
10044 }
10045 if let Some(d) = default {
10046 site_tails_one(d, out, carve);
10047 }
10048 }
10049 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
10050 site_tails_one(body, out, carve);
10051 for c in catches.iter_mut() {
10052 site_tails_one(&mut c.body, out, carve);
10053 }
10054 if let Some(f) = finally {
10055 site_tails_one(f, out, carve);
10056 }
10057 }
10058 _ => {}
10059 }
10060}
10061
10062fn always_returns(stmts: &[Stmt]) -> bool {
10068 let Some(last) = stmts
10069 .iter()
10070 .rev()
10071 .find(|s| !matches!(s, Stmt::Block(v) if v.is_empty()))
10072 else {
10073 return false;
10074 };
10075 match last {
10076 Stmt::Return(Some(_)) | Stmt::Throw(_) => true,
10077 Stmt::Block(v) => always_returns(v),
10078 Stmt::If { then_stmt, else_stmt, .. } => match else_stmt {
10079 Some(e) => always_returns_one(then_stmt) && always_returns_one(e),
10080 None => false,
10081 },
10082 Stmt::Labeled { body, .. } | Stmt::Synchronized { body, .. } => {
10083 always_returns_one(body)
10084 }
10085 Stmt::Switch { cases, default, .. } => {
10086 default.as_ref().is_some_and(|d| always_returns_one(d))
10087 && cases.iter().all(|c| always_returns(&c.body))
10088 }
10089 Stmt::Try { body, catches, finally }
10090 | Stmt::TryWithResources { body, catches, finally, .. } => {
10091 finally.as_ref().is_some_and(|f| always_returns_one(f))
10092 || (always_returns_one(body)
10093 && catches.iter().all(|c| always_returns_one(&c.body)))
10094 }
10095 _ => false,
10096 }
10097}
10098
10099fn always_returns_one(st: &Stmt) -> bool {
10100 always_returns(std::slice::from_ref(st))
10101}
10102
10103fn inline_ctor_arg_defs(
10104 call: &mut Stmt,
10105 prefix: &[&Stmt],
10106 vt: &VarTable,
10107 consumed: &mut Vec<(u32, usize)>,
10108) -> bool {
10109 let Stmt::ExprStmt(cexpr) = call else {
10110 return false;
10111 };
10112 fn subst(e: &mut Expr, v: u32, val: &Option<Expr>) -> bool {
10115 let mut ok = true;
10116 deep_rewrite(e, &mut |x| {
10117 if let Expr::Local { var, .. } = x {
10118 if *var == v {
10119 match val {
10120 Some(e2) => *x = e2.clone(),
10121 None => ok = false,
10122 }
10123 }
10124 }
10125 });
10126 ok
10127 }
10128 let mut seen: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
10129 for _ in 0..32 {
10130 let mut need: Option<u32> = None;
10131 visit_exprs(cexpr, &mut |x| {
10132 if need.is_none() {
10133 if let Expr::Local { var, .. } = x {
10134 if (*var as usize) < vt.vars.len() && !vt.vars[*var as usize].is_param {
10135 need = Some(*var);
10136 }
10137 }
10138 }
10139 });
10140 let Some(v) = need else { break };
10141 if !seen.insert(v) {
10142 return false; }
10144 let mut val: Option<Expr> = None;
10156 let mut ok = true;
10157 for (si, st) in prefix.iter().enumerate() {
10158 match st {
10159 Stmt::LocalDef { var, init: None, .. } if *var == v => {}
10163 Stmt::LocalDef { var, init, .. } if *var == v => {
10164 let Some(e) = init else { ok = false; break };
10165 let mut e2 = (*e).clone();
10166 if !subst(&mut e2, v, &val) {
10167 ok = false;
10168 break;
10169 }
10170 val = Some(e2);
10171 consumed.push((v, si));
10172 }
10173 Stmt::ExprStmt(Expr::Assign { target, op, .. })
10174 if matches!(&**target, Expr::Local { var: tv, .. } if *tv == v) =>
10175 {
10176 if !matches!(op, AssignOp::Plain) {
10177 ok = false; break;
10179 }
10180 let Stmt::ExprStmt(Expr::Assign { value, .. }) = st else {
10181 unreachable!()
10182 };
10183 let mut e2 = value.as_ref().clone();
10184 if !subst(&mut e2, v, &val) {
10185 ok = false;
10186 break;
10187 }
10188 val = Some(e2);
10189 consumed.push((v, si));
10190 }
10191 _ => {
10192 let mut touched = false;
10193 visit_all_exprs(st, &mut |x| {
10194 if let Expr::Local { var, .. } = x {
10195 if *var == v {
10196 touched = true;
10197 }
10198 }
10199 });
10200 if touched {
10201 ok = false;
10202 break;
10203 }
10204 }
10205 }
10206 }
10207 if !ok {
10208 return false;
10209 }
10210 let Some(de) = val else { return false };
10211 if contains_this_access(&de) {
10212 return false;
10213 }
10214 let mut de_nodes = 0usize;
10216 visit_exprs(&de, &mut |_| de_nodes += 1);
10217 if de_nodes > 256 {
10218 return false;
10219 }
10220 deep_rewrite(cexpr, &mut |x| {
10221 if let Expr::Local { var, .. } = x {
10222 if *var == v {
10223 *x = de.clone();
10224 }
10225 }
10226 });
10227 let mut nodes = 0usize;
10232 visit_exprs(cexpr, &mut |_| nodes += 1);
10233 if nodes > 2_048 {
10234 return false;
10235 }
10236 }
10237 true
10238}
10239
10240fn fold_sb_chain(stmts: &mut Vec<Stmt>, pos: usize) -> usize {
10253 let mut sbv: Option<u32> = None;
10255 let mut def_i: Option<usize> = None;
10256 let mut def_init: Option<Expr> = None;
10257 for (i, st) in stmts[..pos].iter().enumerate() {
10258 let cand: Option<(u32, &Expr)> = match st {
10259 Stmt::LocalDef {
10260 var,
10261 init: Some(e),
10262 ..
10263 } => Some((*var, e)),
10264 Stmt::ExprStmt(Expr::Assign {
10265 target,
10266 value,
10267 op: AssignOp::Plain,
10268 ..
10269 }) => match &**target {
10270 Expr::Local { var, .. } => Some((*var, value.as_ref())),
10271 _ => None,
10272 },
10273 _ => None,
10274 };
10275 if let Some((v, init)) = cand {
10276 if let Expr::New { cls, .. } = init {
10277 if cls.as_ref() == "java/lang/StringBuilder" {
10278 sbv = Some(v);
10279 def_i = Some(i);
10280 def_init = Some(init.clone());
10281 break;
10282 }
10283 }
10284 }
10285 }
10286 let (Some(sbv), Some(def_i), Some(init)) = (sbv, def_i, def_init) else {
10287 return pos;
10288 };
10289 if contains_this_access(&init) {
10290 return pos;
10291 }
10292 let mut appends: Vec<usize> = Vec::new();
10294 for (i, st) in stmts[..pos].iter().enumerate() {
10295 if let Stmt::ExprStmt(Expr::Method {
10296 owner: Some(o),
10297 cls,
10298 name,
10299 ..
10300 }) = st
10301 {
10302 if matches!(&**o, Expr::Local { var, .. } if *var == sbv)
10303 && cls.as_ref() == "java/lang/StringBuilder"
10304 && &**name == "append"
10305 {
10306 appends.push(i);
10307 }
10308 }
10309 }
10310 let mut post_read = false;
10312 for st in stmts.iter().skip(pos + 1) {
10313 visit_all_exprs(st, &mut |e| {
10314 if let Expr::Local { var, .. } = e {
10315 if *var == sbv {
10316 post_read = true;
10317 }
10318 }
10319 });
10320 }
10321 if post_read {
10322 return pos;
10323 }
10324 for (i, st) in stmts[..pos].iter().enumerate() {
10327 if i == def_i || appends.contains(&i) {
10328 if let Stmt::ExprStmt(Expr::Method { args, .. }) = st {
10330 if args.iter().any(contains_this_access) {
10331 return pos;
10332 }
10333 }
10334 continue;
10335 }
10336 let mut leak = false;
10337 visit_all_exprs(st, &mut |e| {
10338 if let Expr::Local { var, .. } = e {
10339 if *var == sbv {
10340 leak = true;
10341 }
10342 }
10343 });
10344 if leak {
10345 return pos;
10346 }
10347 }
10348 let mut local_reads = 0usize;
10350 let mut tostring_reads = 0usize;
10351 visit_all_exprs(&stmts[pos], &mut |e| match e {
10352 Expr::Local { var, .. } if *var == sbv => local_reads += 1,
10353 Expr::Method {
10354 owner: Some(o),
10355 name,
10356 args,
10357 ..
10358 } if args.is_empty()
10359 && &**name == "toString"
10360 && matches!(&**o, Expr::Local { var, .. } if *var == sbv) =>
10361 {
10362 tostring_reads += 1;
10363 }
10364 _ => {}
10365 });
10366 if local_reads != 1 || tostring_reads != 1 {
10367 return pos;
10368 }
10369 let mut chain = init;
10371 for ai in &appends {
10372 if let Stmt::ExprStmt(Expr::Method {
10373 owner: _,
10374 cls,
10375 name,
10376 desc,
10377 args,
10378 is_static,
10379 is_interface,
10380 is_special,
10381 is_super,
10382 is_dynamic,
10383 type_args,
10384 ..
10385 }) = &stmts[*ai]
10386 {
10387 chain = Expr::Method {
10388 owner: Some(Box::new(chain)),
10389 cls: cls.clone(),
10390 name: name.clone(),
10391 desc: desc.clone(),
10392 args: args.clone(),
10393 is_static: *is_static,
10394 is_interface: *is_interface,
10395 is_special: *is_special,
10396 is_super: *is_super,
10397 is_dynamic: *is_dynamic,
10398 type_args: type_args.clone(),
10399 };
10400 }
10401 }
10402 let mut replaced = false;
10403 walk_stmt_exprs(&mut stmts[pos], &mut |e| {
10404 deep_rewrite(e, &mut |x| {
10405 if let Expr::Method {
10406 owner: Some(o),
10407 name,
10408 args,
10409 ..
10410 } = x
10411 {
10412 if args.is_empty()
10413 && &**name == "toString"
10414 && matches!(&**o, Expr::Local { var, .. } if *var == sbv)
10415 {
10416 **o = chain.clone();
10417 replaced = true;
10418 }
10419 }
10420 });
10421 });
10422 if !replaced {
10423 return pos; }
10425 let mut removed: Vec<usize> = appends.clone();
10427 removed.push(def_i);
10428 removed.sort_unstable();
10429 removed.dedup();
10430 for i in removed.iter().rev() {
10431 stmts.remove(*i);
10432 }
10433 pos - removed.len()
10434}
10435
10436pub fn fix_ctor_super_first(body: &mut Stmt, vt: &VarTable) {
10437 let Stmt::Block(stmts) = body else { return };
10438 if stmts.is_empty() || is_bare_ctor_call(stmts.first().unwrap()) {
10439 return;
10440 }
10441 if let Some(pos0) = stmts.iter().position(is_bare_ctor_call) {
10443 let pos = fold_sb_chain(stmts, pos0);
10444 let mut call = stmts[pos].clone();
10445 let prefix: Vec<&Stmt> = stmts[..pos].iter().collect();
10446 let mut consumed: Vec<(u32, usize)> = Vec::new();
10447 if !inline_ctor_arg_defs(&mut call, &prefix, vt, &mut consumed) {
10448 return;
10449 }
10450 stmts.remove(pos);
10451 let mut drop_idx: Vec<usize> = consumed
10458 .iter()
10459 .filter(|(v, _)| !var_occurs_in(&stmts[pos..], *v))
10460 .map(|(_, k)| *k)
10461 .collect();
10462 drop_idx.sort_unstable();
10463 drop_idx.dedup();
10464 for k in drop_idx.iter().rev() {
10465 stmts.remove(*k);
10466 }
10467 stmts.insert(0, call);
10468 return;
10469 }
10470 let mut found: Option<(usize, usize)> = None;
10480 for (i, st) in stmts.iter().enumerate() {
10481 if let Stmt::Block(inner) = st {
10482 if let Some(p) = inner.iter().position(is_bare_ctor_call) {
10483 if inner[..p]
10484 .iter()
10485 .all(|s| matches!(s, Stmt::LocalDef { .. } | Stmt::ExprStmt(_)))
10486 {
10487 found = Some((i, p));
10488 break;
10489 }
10490 }
10491 }
10492 }
10493 if let Some((i, p)) = found {
10494 let mut call = match &stmts[i] {
10495 Stmt::Block(inner) => inner[p].clone(),
10496 _ => unreachable!(),
10497 };
10498 let mut prefix: Vec<&Stmt> = stmts[..i].iter().collect();
10499 if let Stmt::Block(inner) = &stmts[i] {
10500 prefix.extend(inner[..p].iter());
10501 }
10502 let mut consumed: Vec<(u32, usize)> = Vec::new();
10503 if !inline_ctor_arg_defs(&mut call, &prefix, vt, &mut consumed) {
10504 return;
10505 }
10506 let mut ks: Vec<usize> = Vec::new();
10513 {
10514 let inner_tail: &[Stmt] = match &stmts[i] {
10515 Stmt::Block(inner) if p < inner.len() => &inner[p + 1..],
10516 _ => &[],
10517 };
10518 for (v, k) in &consumed {
10519 if !var_occurs_in(inner_tail, *v) && !var_occurs_in(&stmts[i + 1..], *v) {
10520 ks.push(*k);
10521 }
10522 }
10523 }
10524 ks.sort_unstable();
10525 ks.dedup();
10526 if let Stmt::Block(inner) = &mut stmts[i] {
10527 inner.remove(p);
10528 for k in ks.iter().rev().filter(|k| **k >= i) {
10529 inner.remove(k - i);
10530 }
10531 }
10532 for k in ks.iter().rev().filter(|k| **k < i) {
10533 stmts.remove(*k);
10534 }
10535 stmts.insert(0, call);
10536 }
10537}
10538
10539fn is_delegation_expr(e: &Expr) -> bool {
10548 matches!(
10549 e,
10550 Expr::Method { name, is_special: true, owner, .. }
10551 if &**name == "<init>"
10552 && (owner.is_none() || matches!(owner.as_deref(), Some(Expr::This)))
10553 )
10554}
10555
10556pub(crate) fn is_bare_ctor_call(s: &Stmt) -> bool {
10557 matches!(s, Stmt::ExprStmt(e) if is_delegation_expr(e))
10558}
10559
10560pub(crate) fn local_is_written(body: &Stmt, var: u32) -> bool {
10564 let mut hit = false;
10565 visit_stmt_exprs_ro(body, &mut |e| {
10566 if hit {
10567 return;
10568 }
10569 visit_exprs(e, &mut |x| {
10570 if hit {
10571 return;
10572 }
10573 match x {
10574 Expr::Assign { target, .. }
10575 if matches!(&**target, Expr::Local { var: v, .. } if *v == var) =>
10576 {
10577 hit = true;
10578 }
10579 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. }
10580 if matches!(&**e, Expr::Local { var: v, .. } if *v == var) =>
10581 {
10582 hit = true;
10583 }
10584 _ => {}
10585 }
10586 });
10587 });
10588 hit
10589}
10590
10591pub(crate) fn rewrite_inner_ctor_outer_param(
10601 body: &mut Stmt,
10602 param0: u32,
10603 outer_display: &str,
10604 outer_ty: &TypeRef,
10605 eligible: &[String],
10606) {
10607 walk_stmt_exprs(body, &mut |e| {
10611 if let Expr::Method { name, cls, args, .. } = e {
10612 if &**name == "<init>"
10613 && eligible.iter().any(|c| c.as_str() == cls.as_ref())
10614 && args.first().is_some_and(|a| matches!(a, Expr::Local { var: v, .. } if *v == param0))
10615 {
10616 args.remove(0);
10617 }
10618 }
10619 });
10620 let text = format!("{outer_display}.this");
10622 let ty = outer_ty.clone();
10623 walk_stmt_exprs(body, &mut |e| {
10624 deep_rewrite_reads(e, &mut |x| {
10625 if let Expr::Local { var: v, .. } = x {
10626 if *v == param0 {
10627 *x = Expr::RawT(text.clone(), ty.clone());
10628 }
10629 }
10630 });
10631 });
10632}
10633
10634fn stmts_count_writes(stmts: &[Stmt], var: u32) -> usize {
10682 let mut n = 0usize;
10683 for s in stmts {
10684 visit_stmt_exprs_ro(s, &mut |e| {
10685 visit_exprs(e, &mut |x| match x {
10686 Expr::Assign { target, .. }
10687 if matches!(&**target, Expr::Local { var: v, .. } if *v == var) =>
10688 {
10689 n += 1;
10690 }
10691 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. }
10692 if matches!(&**e, Expr::Local { var: v, .. } if *v == var) =>
10693 {
10694 n += 1;
10695 }
10696 _ => {}
10697 });
10698 });
10699 }
10700 n
10701}
10702
10703fn prefix_written_vars(stmts: &[Stmt]) -> jdc_core::FxHashSet<u32> {
10707 let mut set = jdc_core::FxHashSet::default();
10708 for s in stmts {
10709 let mut c = s.clone();
10710 walk_stmt_exprs(&mut c, &mut |e| {
10711 deep_rewrite(e, &mut |x| match x {
10712 Expr::Assign { target, .. } => {
10713 if let Expr::Local { var, .. } = &**target {
10714 set.insert(*var);
10715 }
10716 }
10717 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => {
10718 if let Expr::Local { var, .. } = &**e {
10719 set.insert(*var);
10720 }
10721 }
10722 _ => {}
10723 });
10724 });
10725 if let Stmt::LocalDef { var, .. } = s {
10726 set.insert(*var);
10727 }
10728 }
10729 set
10730}
10731
10732fn pre_this_safe(e: &Expr, prefix_vars: &jdc_core::FxHashSet<u32>) -> bool {
10736 match e {
10737 Expr::Local { var, .. } => !prefix_vars.contains(var),
10738 Expr::Const(_) => true,
10739 Expr::Cast { e, .. } => pre_this_safe(e, prefix_vars),
10740 Expr::Bin { l, r, .. } => {
10741 pre_this_safe(l, prefix_vars) && pre_this_safe(r, prefix_vars)
10742 }
10743 Expr::Cond { c, t, f } => {
10744 pre_this_safe(c, prefix_vars)
10745 && pre_this_safe(t, prefix_vars)
10746 && pre_this_safe(f, prefix_vars)
10747 }
10748 Expr::Field { owner, is_static, .. } => {
10749 *is_static
10750 && owner
10751 .as_ref()
10752 .is_none_or(|o| pre_this_safe(o, prefix_vars))
10753 }
10754 Expr::Method {
10755 owner,
10756 args,
10757 is_static,
10758 ..
10759 } => {
10760 args.iter().all(|a| pre_this_safe(a, prefix_vars))
10761 && match owner {
10762 Some(o) => pre_this_safe(o, prefix_vars),
10763 None => *is_static,
10764 }
10765 }
10766 Expr::New { args, .. } => args.iter().all(|a| pre_this_safe(a, prefix_vars)),
10774 Expr::NewArray { dims, init, .. } => {
10775 dims.iter().all(|d| pre_this_safe(d, prefix_vars))
10776 && init
10777 .as_ref()
10778 .map(|l| l.iter().all(|x| pre_this_safe(x, prefix_vars)))
10779 .unwrap_or(true)
10780 }
10781 Expr::Un { e, .. } | Expr::InstanceOf { e, .. } => pre_this_safe(e, prefix_vars),
10783 Expr::ArrayIndex { array, index } => {
10784 pre_this_safe(array, prefix_vars) && pre_this_safe(index, prefix_vars)
10785 }
10786 _ => false,
10787 }
10788}
10789
10790fn single_assign_to_v(s: &Stmt, v: u32) -> Option<Expr> {
10794 match s {
10795 Stmt::ExprStmt(Expr::Assign {
10796 target,
10797 op: AssignOp::Plain,
10798 value,
10799 }) if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v) => {
10800 Some((**value).clone())
10801 }
10802 Stmt::Block(xs) if xs.len() == 1 => single_assign_to_v(&xs[0], v),
10803 _ => None,
10804 }
10805}
10806
10807pub(crate) fn flatten_top_blocks(stmts: &mut Vec<Stmt>) {
10829 fn rec(s: Stmt, out: &mut Vec<Stmt>) {
10830 match s {
10831 Stmt::Block(inner) => {
10832 for x in inner {
10833 rec(x, out);
10834 }
10835 }
10836 other => out.push(other),
10837 }
10838 }
10839 if !stmts.iter().any(|s| matches!(s, Stmt::Block(_))) {
10840 return;
10841 }
10842 let mut out: Vec<Stmt> = Vec::with_capacity(stmts.len());
10843 for s in std::mem::take(stmts) {
10844 rec(s, &mut out);
10845 }
10846 *stmts = out;
10847}
10848
10849fn fold_default_arg_bridge(stmts: &mut Vec<Stmt>) -> bool {
10850 flatten_top_blocks(stmts);
10851 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
10852 return false;
10853 };
10854 if pos == 0 {
10855 return false;
10856 }
10857 let Stmt::ExprStmt(Expr::Method { args, .. }) = &stmts[pos] else {
10858 return false;
10859 };
10860 let mut arg_vars: Vec<u32> = Vec::new();
10861 let mut reads = 0usize;
10862 for a in args {
10863 let mut c = a.clone();
10864 deep_rewrite_reads(&mut c, &mut |x| {
10865 if let Expr::Local { var, .. } = x {
10866 reads += 1;
10867 if !arg_vars.contains(var) {
10868 arg_vars.push(*var);
10869 }
10870 }
10871 });
10872 }
10873 if arg_vars.is_empty() || reads != arg_vars.len() {
10874 return false; }
10876 for &v in &arg_vars {
10879 if stmts_read_var(&stmts[pos + 1..], v) != 0
10880 || stmts_count_writes(&stmts[pos + 1..], v) != 0
10881 {
10882 return false;
10883 }
10884 }
10885 let prefix_vars = prefix_written_vars(&stmts[..pos]);
10886 let mut repl: Vec<(u32, Expr)> = Vec::new();
10887 let mut drop: Vec<usize> = Vec::new();
10888 for &v in &arg_vars {
10889 let mut base: Option<(usize, Expr)> = None;
10891 let mut decl: Option<usize> = None;
10892 let mut condf: Option<(usize, Expr, Option<Expr>, Option<Expr>)> = None;
10893 let mut ok = true;
10894 for (i, s) in stmts[..pos].iter().enumerate() {
10895 match s {
10896 Stmt::LocalDef { var, init, .. } if *var == v => match init {
10897 None => {
10898 if decl.is_some() {
10899 ok = false;
10900 break;
10901 }
10902 decl = Some(i);
10903 }
10904 Some(e) => {
10905 if base.is_some() {
10906 ok = false;
10907 break;
10908 }
10909 base = Some((i, e.clone()));
10910 }
10911 },
10912 Stmt::ExprStmt(Expr::Assign {
10913 target,
10914 op: AssignOp::Plain,
10915 value,
10916 }) if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v) => {
10917 if base.is_some() {
10918 ok = false;
10919 break;
10920 }
10921 base = Some((i, (**value).clone()));
10922 }
10923 Stmt::If {
10924 cond,
10925 then_stmt,
10926 else_stmt,
10927 } => {
10928 let t = single_assign_to_v(then_stmt, v);
10929 let f = else_stmt.as_ref().and_then(|b| single_assign_to_v(b, v));
10930 let then_touches = t.is_some()
10931 || stmts_read_var(std::slice::from_ref(&**then_stmt), v) != 0
10932 || stmts_count_writes(std::slice::from_ref(&**then_stmt), v) != 0;
10933 let else_touches = else_stmt.as_ref().is_some_and(|b| {
10934 f.is_some()
10935 || stmts_read_var(std::slice::from_ref(b), v) != 0
10936 || stmts_count_writes(std::slice::from_ref(b), v) != 0
10937 });
10938 if !then_touches && !else_touches {
10939 continue; }
10941 if condf.is_some()
10946 || base.is_none()
10947 || (then_touches && t.is_none())
10948 || (else_touches && f.is_none())
10949 {
10950 ok = false;
10951 break;
10952 }
10953 let cond_stmt = Stmt::ExprStmt(cond.clone());
10954 if stmts_read_var(std::slice::from_ref(&cond_stmt), v) != 0 {
10955 ok = false;
10956 break;
10957 }
10958 condf = Some((i, cond.clone(), t, f));
10959 }
10960 _ => {}
10961 }
10962 }
10963 if !ok {
10964 continue;
10965 }
10966 let Some((bi, be)) = base else { continue };
10967 let want_writes = 1
10970 + condf
10971 .as_ref()
10972 .map(|(_, _, t, f)| t.is_some() as usize + f.is_some() as usize)
10973 .unwrap_or(0);
10974 if stmts_count_writes(&stmts[..pos], v) != want_writes
10975 || stmts_read_var(&stmts[..pos], v) != 0
10976 {
10977 continue;
10978 }
10979 let rep = match condf {
10980 Some((ci, c, t, f)) => {
10981 drop.push(ci);
10982 match (t, f) {
10983 (Some(t), Some(f)) => Expr::Cond {
10984 c: Box::new(c),
10985 t: Box::new(t),
10986 f: Box::new(f),
10987 },
10988 (Some(t), None) => Expr::Cond {
10989 c: Box::new(c),
10990 t: Box::new(t),
10991 f: Box::new(be.clone()),
10992 },
10993 (None, Some(f)) => Expr::Cond {
10994 c: Box::new(c),
10995 t: Box::new(be.clone()),
10996 f: Box::new(f),
10997 },
10998 (None, None) => unreachable!("condf requires a branch assign"),
10999 }
11000 }
11001 None => be.clone(),
11002 };
11003 if !pre_this_safe(&rep, &prefix_vars) {
11004 continue;
11005 }
11006 drop.push(bi);
11007 if let Some(d) = decl {
11008 drop.push(d);
11009 }
11010 repl.push((v, rep));
11011 }
11012 if repl.is_empty() {
11013 return false;
11014 }
11015 if let Stmt::ExprStmt(Expr::Method { args, .. }) = &mut stmts[pos] {
11016 for a in args.iter_mut() {
11017 deep_rewrite_reads(a, &mut |x| {
11018 if let Expr::Local { var, .. } = x {
11019 if let Some((_, e)) = repl.iter().find(|(vv, _)| vv == var) {
11020 *x = e.clone();
11021 }
11022 }
11023 });
11024 }
11025 }
11026 drop.sort_unstable();
11027 drop.dedup();
11028 for i in drop.into_iter().rev() {
11029 stmts.remove(i);
11030 }
11031 true
11032}
11033
11034pub fn rewrite_kotlin_facades(body: &mut Stmt, pool: &crate::DexPool) {
11042 let map = pool.kotlin_facade_map();
11043 if map.is_empty() {
11044 return;
11045 }
11046 walk_stmt_exprs(body, &mut |e| {
11047 match e {
11048 Expr::Method {
11049 cls,
11050 name,
11051 desc,
11052 is_static,
11053 ..
11054 } if *is_static => {
11055 if let Some((base, cover)) = map.get(cls.as_ref()) {
11056 let hit = match cover {
11057 None => true,
11058 Some(c) => c.0.contains(&(name.to_string(), desc.to_string())),
11059 };
11060 if hit {
11061 *cls = std::sync::Arc::from(base.as_str());
11062 }
11063 }
11064 }
11065 Expr::Field {
11066 cls,
11067 name,
11068 is_static,
11069 ..
11070 } if *is_static => {
11071 if let Some((base, cover)) = map.get(cls.as_ref()) {
11072 let hit = match cover {
11073 None => true,
11074 Some(c) => c.1.contains(name.as_ref()),
11075 };
11076 if hit {
11077 *cls = std::sync::Arc::from(base.as_str());
11078 }
11079 }
11080 }
11081 _ => {}
11082 }
11083 });
11084}
11085
11086pub fn fold_enum_default_arg_bridge(body: &mut Stmt) {
11094 let Stmt::Block(stmts) = body else { return };
11095 fold_default_arg_bridge(stmts);
11096}
11097
11098pub fn fix_ctor_delegation_arg_defs(body: &mut Stmt) {
11099 let Stmt::Block(stmts) = body else { return };
11100 fold_default_arg_bridge(stmts);
11103 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
11107 return;
11108 };
11109 if pos == 0 {
11110 return;
11111 }
11112 let mut arg_vars: Vec<u32> = Vec::new();
11115 let mut total_reads = 0usize;
11116 {
11117 let Stmt::ExprStmt(Expr::Method { args, .. }) = &stmts[pos] else {
11118 return;
11119 };
11120 for a in args {
11121 let mut c = a.clone();
11122 deep_rewrite_reads(&mut c, &mut |x| {
11123 if let Expr::Local { var: v, .. } = x {
11124 total_reads += 1;
11125 if !arg_vars.contains(v) {
11126 arg_vars.push(*v);
11127 }
11128 }
11129 });
11130 }
11131 }
11132 if arg_vars.is_empty() || total_reads != arg_vars.len() {
11133 return;
11134 }
11135 let prefix_defs: Vec<u32> = stmts[..pos]
11138 .iter()
11139 .filter_map(|s| match s {
11140 Stmt::LocalDef { var, init: Some(_), .. } => Some(*var),
11141 _ => None,
11142 })
11143 .collect();
11144 let mut plan: Vec<(u32, usize, Option<usize>)> = Vec::new();
11149 for v in arg_vars {
11150 let defs: Vec<usize> = stmts[..pos]
11151 .iter()
11152 .enumerate()
11153 .filter(|(i, _)| {
11154 matches!(&stmts[*i], Stmt::LocalDef { var, init: Some(_), .. } if *var == v)
11155 })
11156 .map(|(i, _)| i)
11157 .collect();
11158 if defs.len() > 1 {
11159 return; }
11161 let Some(&q) = defs.first() else {
11162 continue; };
11164 let init = match &stmts[q] {
11165 Stmt::LocalDef { init: Some(e), .. } => e.clone(),
11166 _ => return,
11167 };
11168 if stmts_read_var(&stmts[q + 1..pos], v) != 0 {
11169 return; }
11171 let mut init_vars: Vec<u32> = Vec::new();
11174 deep_rewrite_reads(&mut { init.clone() }, &mut |x| {
11175 if let Expr::Local { var: vv, .. } = x {
11176 if !init_vars.contains(vv) {
11177 init_vars.push(*vv);
11178 }
11179 }
11180 });
11181 if init_vars.iter().any(|iv| prefix_defs.contains(iv)) {
11182 return;
11183 }
11184 match stmts[pos + 1..]
11185 .iter()
11186 .position(|s| {
11187 matches!(s, Stmt::ExprStmt(Expr::Assign { target, .. })
11188 if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v))
11189 }) {
11190 Some(w) => {
11191 if stmts_read_var(&stmts[pos + 1..pos + 1 + w], v) != 0 {
11193 return;
11194 }
11195 plan.push((v, q, Some(pos + 1 + w)));
11196 }
11197 None => {
11198 if stmts_read_var(&stmts[pos + 1..], v) != 0 {
11200 return;
11201 }
11202 plan.push((v, q, None));
11203 }
11204 }
11205 }
11206 if plan.is_empty() {
11207 return;
11208 }
11209 let values: Vec<(u32, Expr)> = plan
11211 .iter()
11212 .map(|(_v, q, _)| match &stmts[*q] {
11213 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e.clone()),
11214 _ => unreachable!("plan entries carry a LocalDef with init"),
11215 })
11216 .collect();
11217 if let Stmt::ExprStmt(Expr::Method { args, .. }) = &mut stmts[pos] {
11218 for a in args {
11219 deep_rewrite_reads(a, &mut |x| {
11220 if let Expr::Local { var: v, .. } = x {
11221 if let Some((_, e)) = values.iter().find(|(vv, _)| vv == v) {
11222 *x = e.clone();
11223 }
11224 }
11225 });
11226 }
11227 }
11228 for (v, _, w) in &plan {
11232 if let Some(w) = w {
11233 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = &mut stmts[*w] {
11234 if matches!(&**target, Expr::Local { var: vv, .. } if vv == v) {
11235 let taken = std::mem::replace(&mut **value, Expr::Const(ConstVal::Null));
11236 stmts[*w] = Stmt::LocalDef {
11237 var: *v,
11238 init: Some(taken),
11239 is_final: false,
11240 force_type: false,
11241 };
11242 }
11243 }
11244 }
11245 }
11246 let mut order: Vec<usize> = plan.iter().map(|(_, q, _)| *q).collect();
11247 order.sort_unstable();
11248 order.reverse();
11249 for q in order {
11250 stmts.remove(q);
11251 }
11252}
11253
11254pub fn fix_bool_returns(vt: &VarTable, body: &mut Stmt) {
11265 walk_mut_deep(body, &mut |st| {
11266 if let Stmt::Return(Some(e)) = st {
11267 let numeric = match &*e {
11268 Expr::Local { var, ty } => {
11269 let t = if (*var as usize) < vt.vars.len() {
11270 vt.var(*var).ty.erased()
11271 } else {
11272 ty.erased()
11273 };
11274 matches!(
11275 t,
11276 JavaType::Int
11277 | JavaType::Long
11278 | JavaType::Short
11279 | JavaType::Byte
11280 | JavaType::Char
11281 )
11282 }
11283 other => matches!(
11284 other.type_ref().erased(),
11285 JavaType::Int | JavaType::Long | JavaType::Short | JavaType::Byte
11286 | JavaType::Char
11287 ),
11288 };
11289 if numeric {
11290 let taken = e.clone();
11291 *e = Expr::Bin {
11292 op: BinOp::Ne,
11293 l: Box::new(taken),
11294 r: Box::new(Expr::Const(ConstVal::Int(0))),
11295 ty: Some(TypeRef::J(JavaType::Boolean)),
11296 };
11297 }
11298 }
11299 });
11300}
11301
11302pub fn fix_int_returns(vt: &VarTable, body: &mut Stmt) {
11303 walk_mut_deep(body, &mut |st| {
11304 if let Stmt::Return(Some(e)) = st {
11305 let is_bool = match e {
11306 Expr::Local { var, .. } => {
11307 (*var as usize) < vt.vars.len()
11308 && vt.vars[*var as usize].ty.erased() == JavaType::Boolean
11309 }
11310 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
11311 _ => false,
11312 };
11313 if is_bool {
11314 *e = Expr::Cond {
11315 c: Box::new(e.clone()),
11316 t: Box::new(Expr::Const(ConstVal::Int(1))),
11317 f: Box::new(Expr::Const(ConstVal::Int(0))),
11318 };
11319 }
11320 }
11321 });
11322}
11323
11324pub fn split_generations(vt: &mut VarTable, body: &mut Stmt, pool: &DexPool) {
11340 let mut counter: u32 = 0;
11341 let mut gen: std::collections::HashMap<u32, u32> = std::collections::HashMap::default();
11342 split_walk_stmt(body, vt, &mut gen, &mut counter, pool);
11343}
11344
11345fn split_needed(vt: &VarTable, target: u32, value: &Expr, pool: &DexPool) -> bool {
11347 let n = vt.vars.len();
11348 if target as usize >= n {
11349 return false;
11350 }
11351 let declared = vt.vars[target as usize].ty.erased();
11352 let vt_is_ref = |t: &JavaType| matches!(t, JavaType::Object(_) | JavaType::Array(_));
11353 fn side_bool(e: &Expr, vt: &VarTable) -> bool {
11362 match e {
11363 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
11364 matches!(vt.var(*var).ty.erased(), JavaType::Boolean)
11365 }
11366 Expr::Const(ConstVal::Int(0 | 1)) => true,
11367 Expr::Bin { op: BinOp::Or | BinOp::And | BinOp::Xor, l, r, .. } => {
11368 side_bool(l, vt) && side_bool(r, vt)
11369 }
11370 other => matches!(other.type_ref().erased(), JavaType::Boolean),
11371 }
11372 }
11373 let value_ty = match value {
11374 Expr::Local { var, ty } if (*var as usize) < vt.vars.len() => vt.var(*var).ty.erased(),
11375 Expr::Bin {
11376 op: BinOp::Or | BinOp::And | BinOp::Xor,
11377 l,
11378 r,
11379 ..
11380 } if side_bool(l, vt) && side_bool(r, vt) => JavaType::Boolean,
11381 other => other.type_ref().erased(),
11382 };
11383 if vt_is_ref(&declared) && vt_is_ref(&value_ty) {
11384 let obj = JavaType::Object("java/lang/Object".into());
11394 if declared == value_ty || declared == obj || value_ty == obj {
11395 return false;
11396 }
11397 if let (JavaType::Object(v), JavaType::Object(d)) = (&value_ty, &declared) {
11410 if matches!(value, Expr::New { .. })
11411 && (pool.get(v).is_none() || pool.get(d).is_none())
11412 {
11413 return false;
11414 }
11415 }
11416 true
11417 } else {
11418 if vt_is_ref(&declared) != vt_is_ref(&value_ty) {
11422 return true;
11423 }
11424 let d_bool = matches!(declared, JavaType::Boolean);
11432 let v_bool = matches!(value_ty, JavaType::Boolean);
11433 let zero_one = matches!(value, Expr::Const(ConstVal::Int(0 | 1)));
11434 d_bool != v_bool && !zero_one
11435 }
11436}
11437
11438fn rewrite_gen_reads(e: &mut Expr, gen: &std::collections::HashMap<u32, u32>, vt: &VarTable) {
11439 deep_rewrite(e, &mut |x| {
11440 if let Expr::Local { var, ty } = x {
11441 if let Some(&to) = gen.get(var) {
11442 if to != *var {
11443 *var = to;
11444 *ty = vt.var(to).ty.clone();
11445 }
11446 }
11447 }
11448 });
11449}
11450
11451fn do_split(
11452 vt: &mut VarTable,
11453 target: &mut Expr,
11454 old: u32,
11455 value_ty: &JavaType,
11456 gen: &mut std::collections::HashMap<u32, u32>,
11457 counter: &mut u32,
11458) {
11459 *counter += 1;
11460 let info = &vt.vars[old as usize];
11461 let id = vt.vars.len() as u32;
11462 let name = format!("{}_g{}", info.name, counter);
11463 let slot = info.slot;
11464 let is_param = false;
11472 vt.vars.push(jdc_core::var::VarInfo {
11473 id,
11474 slot,
11475 name,
11476 ty: TypeRef::J(value_ty.clone()),
11477 is_param,
11478 range_start: 0,
11479 range_end: u16::MAX,
11480 synthetic_name: true,
11481 });
11482 while vt.by_slot.len() <= slot as usize {
11483 vt.by_slot.push(Vec::new());
11484 }
11485 vt.by_slot[slot as usize].push((0, u16::MAX, id));
11486 gen.insert(old, id);
11487 if let Expr::Local { var, ty, .. } = target {
11488 *var = id;
11489 *ty = TypeRef::J(value_ty.clone());
11490 }
11491}
11492
11493fn rename_gen_back(s: &mut Stmt, from: u32, to: u32, vt: &VarTable) {
11505 if from == to || (to as usize) >= vt.vars.len() {
11506 return;
11507 }
11508 let to_ty = vt.vars[to as usize].ty.clone();
11509 walk_mut_deep(s, &mut |st| {
11510 if let Stmt::LocalDef { var, .. } = st {
11511 if *var == from {
11512 *var = to;
11513 }
11514 }
11515 });
11516 rewrite_exprs(s, &mut |e| {
11517 deep_rewrite(e, &mut |x| {
11518 if let Expr::Local { var, ty } = x {
11519 if *var == from {
11520 *var = to;
11521 *ty = to_ty.clone();
11522 }
11523 }
11524 });
11525 });
11526}
11527
11528fn drop_gen_slot(vt: &mut VarTable, dead: u32) {
11532 if (dead as usize) >= vt.vars.len() {
11533 return;
11534 }
11535 let slot = vt.vars[dead as usize].slot as usize;
11536 if let Some(seg) = vt.by_slot.get_mut(slot) {
11537 seg.retain(|&(_, _, id)| id != dead);
11538 }
11539}
11540
11541fn undo_type_safe(vt: &VarTable, k: u32, v: u32) -> bool {
11550 if (k as usize) >= vt.vars.len() || (v as usize) >= vt.vars.len() {
11551 return false;
11552 }
11553 let kt = vt.vars[k as usize].ty.erased();
11554 let vt_ty = vt.vars[v as usize].ty.erased();
11555 if kt == vt_ty {
11556 return true;
11557 }
11558 let is_prim = |t: &JavaType| {
11559 matches!(
11560 t,
11561 JavaType::Int
11562 | JavaType::Long
11563 | JavaType::Short
11564 | JavaType::Byte
11565 | JavaType::Char
11566 | JavaType::Float
11567 | JavaType::Double
11568 | JavaType::Boolean
11569 )
11570 };
11571 is_prim(&kt) && is_prim(&vt_ty)
11572}
11573
11574fn split_walk_stmt(
11575 st: &mut Stmt,
11576 vt: &mut VarTable,
11577 gen: &mut std::collections::HashMap<u32, u32>,
11578 counter: &mut u32,
11579 pool: &DexPool,
11580) {
11581 match st {
11582 Stmt::Block(v) => {
11583 for x in v.iter_mut() {
11584 split_walk_stmt(x, vt, gen, counter, pool);
11585 }
11586 }
11587 Stmt::ExprStmt(e) => {
11588 rewrite_gen_reads(e, gen, vt);
11589 if let Expr::Assign {
11590 target,
11591 value,
11592 op: AssignOp::Plain,
11593 ..
11594 } = e
11595 {
11596 if let Expr::Local { var, .. } = &**target {
11597 let old = *var;
11598 if split_needed(vt, old, value, pool) {
11599 let value_ty = value.type_ref().erased();
11600 do_split(vt, target, old, &value_ty, gen, counter);
11601 }
11602 }
11603 }
11604 }
11605 Stmt::LocalDef { var, init, .. } => {
11606 if let Some(e) = init {
11607 rewrite_gen_reads(e, gen, vt);
11608 if split_needed(vt, *var, e, pool) {
11609 let value_ty = e.type_ref().erased();
11613 let old = *var;
11614 let mut fake = Expr::Local {
11615 var: old,
11616 ty: vt.vars[old as usize].ty.clone(),
11617 };
11618 do_split(vt, &mut fake, old, &value_ty, gen, counter);
11619 if let Expr::Local { var: nv, .. } = fake {
11620 *var = nv;
11621 }
11622 }
11623 }
11624 }
11625 Stmt::If {
11626 cond,
11627 then_stmt,
11628 else_stmt,
11629 ..
11630 } => {
11631 rewrite_gen_reads(cond, gen, vt);
11632 let then_floor = vt.vars.len() as u32;
11633 let mut g_then = gen.clone();
11634 split_walk_stmt(then_stmt, vt, &mut g_then, counter, pool);
11635 let else_floor = vt.vars.len() as u32;
11636 let mut g_else = gen.clone();
11637 if let Some(e) = else_stmt {
11638 split_walk_stmt(e, vt, &mut g_else, counter, pool);
11639 }
11640 let mut undo_then: Vec<(u32, u32)> = g_then
11664 .iter()
11665 .filter(|(&k, &v)| {
11666 v >= then_floor
11667 && v < else_floor
11668 && g_else.get(&k).copied() == gen.get(&k).copied()
11669 && undo_type_safe(vt, k, v)
11670 })
11671 .map(|(&k, &v)| (v, k))
11672 .collect();
11673 if else_floor > then_floor && !undo_then.is_empty() {
11674 let vars: Vec<u32> = undo_then.iter().map(|&(v, _)| v).collect();
11675 let mut hits = vec![false; vars.len()];
11676 mark_read_vars(std::slice::from_ref(&**then_stmt), &vars, &mut hits);
11677 let mut i = 0usize;
11678 undo_then.retain(|_| {
11679 let keep = !hits[i];
11680 i += 1;
11681 keep
11682 });
11683 }
11684 undo_then.sort_unstable_by_key(|&(v, _)| std::cmp::Reverse(v));
11685 for &(v, k) in &undo_then {
11686 rename_gen_back(then_stmt, v, k, vt);
11687 drop_gen_slot(vt, v);
11688 }
11689 if let Some(e) = else_stmt.as_deref_mut() {
11690 let mut undo_else: Vec<(u32, u32)> = g_else
11691 .iter()
11692 .filter(|(&k, &v)| {
11693 v >= else_floor
11694 && g_then.get(&k).copied() == gen.get(&k).copied()
11695 && undo_type_safe(vt, k, v)
11696 })
11697 .map(|(&k, &v)| (v, k))
11698 .collect();
11699 if (vt.vars.len() as u32) > else_floor && !undo_else.is_empty() {
11700 let vars: Vec<u32> = undo_else.iter().map(|&(v, _)| v).collect();
11701 let mut hits = vec![false; vars.len()];
11702 mark_read_vars(std::slice::from_ref(&*e), &vars, &mut hits);
11703 let mut i = 0usize;
11704 undo_else.retain(|_| {
11705 let keep = !hits[i];
11706 i += 1;
11707 keep
11708 });
11709 }
11710 undo_else.sort_unstable_by_key(|&(v, _)| std::cmp::Reverse(v));
11711 for &(v, k) in &undo_else {
11712 rename_gen_back(e, v, k, vt);
11713 drop_gen_slot(vt, v);
11714 }
11715 }
11716 gen.retain(|k, v| g_then.get(k) == Some(v) && g_else.get(k) == Some(v));
11719 }
11720 Stmt::While { cond, body, .. } | Stmt::DoWhile { cond, body, .. } => {
11721 let entry = gen.clone();
11725 for _ in 0..3 {
11726 rewrite_gen_reads(cond, gen, vt);
11727 split_walk_stmt(body, vt, gen, counter, pool);
11728 }
11729 *gen = entry;
11732 }
11733 Stmt::For { init, body, .. } => {
11734 for x in init.iter_mut() {
11735 split_walk_stmt(x, vt, gen, counter, pool);
11736 }
11737 let entry = gen.clone();
11738 for _ in 0..3 {
11739 split_walk_stmt(body, vt, gen, counter, pool);
11740 }
11741 *gen = entry;
11742 }
11743 Stmt::ForEach { body, .. } => {
11744 let entry = gen.clone();
11745 for _ in 0..3 {
11746 split_walk_stmt(body, vt, gen, counter, pool);
11747 }
11748 *gen = entry;
11749 }
11750 Stmt::Switch { cases, default, .. } => {
11751 let entry = gen.clone();
11752 for c in cases.iter_mut() {
11753 let mut g_case = entry.clone();
11754 for x in c.body.iter_mut() {
11755 split_walk_stmt(x, vt, &mut g_case, counter, pool);
11756 }
11757 gen.retain(|k, v| g_case.get(k) == Some(v));
11758 }
11759 if let Some(d) = default {
11760 let mut g_def = entry.clone();
11761 split_walk_stmt(d, vt, &mut g_def, counter, pool);
11762 gen.retain(|k, v| g_def.get(k) == Some(v));
11763 }
11764 }
11765 Stmt::Try {
11766 body,
11767 catches,
11768 finally,
11769 } => {
11770 let entry = gen.clone();
11771 let mut g_try = entry.clone();
11772 split_walk_stmt(body, vt, &mut g_try, counter, pool);
11773 let mut g_all = g_try.clone();
11774 for c in catches.iter_mut() {
11775 let mut g_c = entry.clone();
11778 split_walk_stmt(&mut c.body, vt, &mut g_c, counter, pool);
11779 g_all.retain(|k, v| g_c.get(k) == Some(v));
11780 }
11781 *gen = g_all;
11782 if let Some(f) = finally {
11783 split_walk_stmt(f, vt, gen, counter, pool);
11784 }
11785 }
11786 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => {
11787 split_walk_stmt(body, vt, gen, counter, pool);
11788 }
11789 Stmt::Return(Some(e)) | Stmt::Throw(e) => {
11790 rewrite_gen_reads(e, gen, vt);
11791 }
11792 _ => {}
11793 }
11794}
11795
11796fn fw_super(c: &str) -> Option<&'static str> {
11803 crate::fwdb::super_of(c)
11804}
11805
11806fn exc_subtype(sub: &str, sup: &str, pool: &DexPool) -> bool {
11812 if sub == sup {
11813 return true;
11814 }
11815 if pool.is_subtype(sub, sup) {
11816 return true;
11817 }
11818 let mut cur = sub;
11819 for _ in 0..24 {
11820 let Some(next) = fw_super(cur) else { break };
11821 if next == sup {
11822 return true;
11823 }
11824 cur = next;
11825 }
11826 false
11827}
11828
11829pub fn dedupe_multicatch(s: &mut Stmt, pool: &DexPool) {
11835 dedupe_multicatch_walk(s, pool);
11836}
11837
11838fn dedupe_multicatch_walk(s: &mut Stmt, pool: &DexPool) {
11839 match s {
11840 Stmt::Try {
11841 body,
11842 catches,
11843 finally,
11844 }
11845 | Stmt::TryWithResources {
11846 body,
11847 catches,
11848 finally,
11849 ..
11850 } => {
11851 dedupe_multicatch_walk(body, pool);
11852 for c in catches.iter_mut() {
11853 if c.exc.len() > 1 {
11854 let mut keep: Vec<std::sync::Arc<str>> = Vec::with_capacity(c.exc.len());
11855 for e in c.exc.iter() {
11856 let shadowed = c.exc.iter().any(|o| {
11857 o.as_ref() != e.as_ref() && exc_subtype(e, o, pool)
11858 });
11859 if !shadowed && !keep.iter().any(|k| k.as_ref() == e.as_ref()) {
11860 keep.push(e.clone());
11861 }
11862 }
11863 c.exc = keep;
11864 }
11865 dedupe_multicatch_walk(&mut c.body, pool);
11866 }
11867 if let Some(f) = finally {
11868 dedupe_multicatch_walk(f, pool);
11869 }
11870 }
11871 Stmt::Block(v) => {
11872 for x in v.iter_mut() {
11873 dedupe_multicatch_walk(x, pool);
11874 }
11875 }
11876 Stmt::If {
11877 then_stmt,
11878 else_stmt,
11879 ..
11880 } => {
11881 dedupe_multicatch_walk(then_stmt, pool);
11882 if let Some(e) = else_stmt {
11883 dedupe_multicatch_walk(e, pool);
11884 }
11885 }
11886 Stmt::While { body, .. }
11887 | Stmt::DoWhile { body, .. }
11888 | Stmt::For { body, .. }
11889 | Stmt::ForEach { body, .. }
11890 | Stmt::Labeled { body, .. }
11891 | Stmt::Synchronized { body, .. } => dedupe_multicatch_walk(body, pool),
11892 Stmt::Switch { cases, default, .. } => {
11893 for c in cases.iter_mut() {
11894 for x in c.body.iter_mut() {
11895 dedupe_multicatch_walk(x, pool);
11896 }
11897 }
11898 if let Some(d) = default {
11899 dedupe_multicatch_walk(d, pool);
11900 }
11901 }
11902 _ => {}
11903 }
11904}
11905
11906pub fn resolve_dangling_gotos(s: &mut Stmt) {
11918 let mut labels: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
11919 walk_all(s, &mut |st| {
11922 if let Stmt::Label(id) = st {
11923 labels.insert(*id);
11924 }
11925 });
11926 resolve_gotos_walk(s, &labels, 0);
11927}
11928
11929fn resolve_gotos_walk(s: &mut Stmt, labels: &jdc_core::FxHashSet<u32>, loop_depth: u32) {
11930 let depth = match s {
11931 Stmt::While { .. } | Stmt::DoWhile { .. } | Stmt::For { .. } | Stmt::ForEach { .. } => {
11932 loop_depth + 1
11933 }
11934 _ => loop_depth,
11935 };
11936 match s {
11937 Stmt::Goto(id) => {
11938 if labels.contains(id) {
11939 return; }
11941 if depth > 0 {
11942 *s = Stmt::Break(None);
11943 } else {
11944 *s = Stmt::Comment(format!(
11945 "$DDC: unresolved jump to block {} (irreducible control flow)",
11946 id
11947 ));
11948 }
11949 }
11950 Stmt::Block(v) => {
11951 let mut i = 0usize;
11952 while i < v.len() {
11953 let dangling_here =
11958 depth == 0 && matches!(&v[i], Stmt::Goto(id) if !labels.contains(id));
11959 resolve_gotos_walk(&mut v[i], labels, depth);
11960 if dangling_here {
11961 v.truncate(i + 1);
11962 break;
11963 }
11964 i += 1;
11965 }
11966 }
11967 Stmt::If { then_stmt, else_stmt, .. } => {
11968 resolve_gotos_walk(then_stmt, labels, depth);
11969 if let Some(e) = else_stmt {
11970 resolve_gotos_walk(e, labels, depth);
11971 }
11972 }
11973 Stmt::While { body, .. }
11974 | Stmt::DoWhile { body, .. }
11975 | Stmt::For { body, .. }
11976 | Stmt::ForEach { body, .. }
11977 | Stmt::Labeled { body, .. }
11978 | Stmt::Synchronized { body, .. } => resolve_gotos_walk(body, labels, depth),
11979 Stmt::Try {
11980 body,
11981 catches,
11982 finally,
11983 } => {
11984 resolve_gotos_walk(body, labels, depth);
11985 for c in catches.iter_mut() {
11986 resolve_gotos_walk(&mut c.body, labels, depth);
11987 }
11988 if let Some(f) = finally {
11989 resolve_gotos_walk(f, labels, depth);
11990 }
11991 }
11992 Stmt::Switch { cases, default, .. } => {
11993 for c in cases.iter_mut() {
11994 for x in c.body.iter_mut() {
11995 resolve_gotos_walk(x, labels, depth);
11996 }
11997 }
11998 if let Some(d) = default {
11999 resolve_gotos_walk(d, labels, depth);
12000 }
12001 }
12002 _ => {}
12003 }
12004}
12005
12006fn mark_read_vars(stmts: &[Stmt], vars: &[u32], hits: &mut [bool]) {
12018 let mut remaining = vars.len();
12019 for s in stmts {
12020 if remaining == 0 {
12021 break;
12022 }
12023 visit_stmt_exprs_ro(s, &mut |e| {
12024 if remaining == 0 {
12025 return;
12026 }
12027 visit_exprs_reads(e, &mut |x| {
12028 if remaining == 0 {
12029 return;
12030 }
12031 if let Expr::Local { var, .. } = x {
12032 for (i, v) in vars.iter().enumerate() {
12033 if *v == *var && !hits[i] {
12034 hits[i] = true;
12035 remaining -= 1;
12036 }
12037 }
12038 }
12039 });
12040 });
12041 }
12042}
12043
12044fn stmts_read_var(stmts: &[Stmt], v: u32) -> usize {
12045 let mut n = 0usize;
12046 for s in stmts {
12047 visit_stmt_exprs_ro(s, &mut |e| {
12048 visit_exprs_reads(e, &mut |x| {
12049 if let Expr::Local { var: vv, .. } = x {
12050 if *vv == v {
12051 n += 1;
12052 }
12053 }
12054 });
12055 });
12056 }
12057 n
12058}
12059
12060pub fn fix_ctor_conditional_super(body: &mut Stmt) {
12061 if !matches!(body, Stmt::Block(_)) {
12064 let inner = std::mem::replace(body, Stmt::Block(Vec::new()));
12065 *body = Stmt::Block(vec![inner]);
12066 }
12067 let Stmt::Block(stmts) = body else { return };
12068 while stmts.len() == 1 {
12072 let Stmt::Block(inner) = &stmts[0] else { break };
12073 *stmts = inner.clone();
12074 }
12075 if stmts.is_empty() {
12076 return;
12077 }
12078 if is_bare_ctor_call(stmts.first().unwrap()) {
12079 dedupe_ctor_delegations(body);
12080 return;
12081 }
12082 if try_linear_super_inline(stmts) {
12083 return;
12084 }
12085 try_merge_branched_super(stmts);
12086}
12087
12088pub fn strip_secondary_delegations(body: &mut Stmt) {
12112 let Stmt::Block(stmts) = body else { return };
12113 if !stmts.first().is_some_and(is_bare_ctor_call) {
12114 return;
12115 }
12116 fn strip_in(s: &mut Stmt) {
12117 if is_bare_ctor_call(s) {
12118 *s = Stmt::Block(Vec::new());
12119 return;
12120 }
12121 match s {
12122 Stmt::Block(v) => v.iter_mut().for_each(strip_in),
12123 Stmt::If {
12124 then_stmt,
12125 else_stmt,
12126 ..
12127 } => {
12128 strip_in(then_stmt);
12129 if let Some(e) = else_stmt {
12130 strip_in(e);
12131 }
12132 }
12133 Stmt::While { body, .. }
12134 | Stmt::DoWhile { body, .. }
12135 | Stmt::ForEach { body, .. }
12136 | Stmt::Synchronized { body, .. }
12137 | Stmt::Labeled { body, .. } => strip_in(body),
12138 Stmt::For { init, body, .. } => {
12139 init.iter_mut().for_each(strip_in);
12140 strip_in(body);
12141 }
12142 Stmt::Switch { cases, default, .. } => {
12143 for c in cases.iter_mut() {
12144 c.body.iter_mut().for_each(strip_in);
12145 }
12146 if let Some(d) = default {
12147 strip_in(d);
12148 }
12149 }
12150 Stmt::Try {
12151 body,
12152 catches,
12153 finally,
12154 }
12155 | Stmt::TryWithResources {
12156 body,
12157 catches,
12158 finally,
12159 ..
12160 } => {
12161 strip_in(body);
12162 for c in catches.iter_mut() {
12163 strip_in(&mut c.body);
12164 }
12165 if let Some(f) = finally {
12166 strip_in(f);
12167 }
12168 }
12169 _ => {}
12170 }
12171 }
12172 for st in stmts.iter_mut().skip(1) {
12173 strip_in(st);
12174 }
12175}
12176
12177pub fn dedupe_ctor_delegations(body: &mut Stmt) {
12178 let Stmt::Block(stmts) = body else { return };
12179 let Some(d0) = stmts.first().and_then(|s| match s {
12180 Stmt::ExprStmt(e) if is_delegation_expr(e) => Some(e.clone()),
12181 _ => None,
12182 }) else {
12183 return;
12184 };
12185 fn rec(s: &mut Stmt, d0: &Expr) {
12186 match s {
12187 Stmt::Block(v) => {
12188 v.retain(|x| {
12189 !matches!(x, Stmt::ExprStmt(e) if is_delegation_expr(e) && same_delegation(e, d0))
12190 });
12191 for x in v.iter_mut() {
12192 rec(x, d0);
12193 }
12194 }
12195 Stmt::If { then_stmt, else_stmt, .. } => {
12196 rec(then_stmt, d0);
12197 if let Some(e) = else_stmt {
12198 rec(e, d0);
12199 }
12200 }
12201 _ => {}
12202 }
12203 }
12204 {
12208 let d0r = &d0;
12209 let mut idx = 0usize;
12210 stmts.retain(|s| {
12211 idx += 1;
12212 idx == 1
12213 || !(matches!(s, Stmt::ExprStmt(e)
12214 if is_delegation_expr(e) && same_delegation(e, d0r)))
12215 });
12216 }
12217 for s in stmts.iter_mut().skip(1) {
12218 rec(s, &d0);
12219 }
12220}
12221
12222fn same_delegation(a: &Expr, b: &Expr) -> bool {
12226 match (a, b) {
12227 (
12228 Expr::Method { name: n1, cls: c1, is_super: s1, args: a1, .. },
12229 Expr::Method { name: n2, cls: c2, is_super: s2, args: a2, .. },
12230 ) => {
12231 &**n1 == "<init>"
12232 && &**n2 == "<init>"
12233 && c1 == c2
12234 && s1 == s2
12235 && (a1 == a2 || (a1.is_empty() && a2.is_empty()))
12236 }
12237 _ => false,
12238 }
12239}
12240
12241fn ctor_call_expr(s: &Stmt) -> Option<&Expr> {
12243 match s {
12244 Stmt::ExprStmt(e) if is_delegation_expr(e) => Some(e),
12245 _ => None,
12246 }
12247}
12248
12249fn same_ctor_kind(a: &Expr, b: &Expr) -> bool {
12251 match (a, b) {
12252 (
12253 Expr::Method { cls: c1, is_super: s1, owner: o1, .. },
12254 Expr::Method { cls: c2, is_super: s2, owner: o2, .. },
12255 ) => c1 == c2 && s1 == s2 && o1 == o2,
12256 _ => false,
12257 }
12258}
12259
12260fn flat_list(s: &Stmt) -> Vec<&Stmt> {
12265 fn rec<'a>(s: &'a Stmt, out: &mut Vec<&'a Stmt>) {
12266 match s {
12267 Stmt::Block(v) => {
12268 for x in v {
12269 rec(x, out);
12270 }
12271 }
12272 other => out.push(other),
12273 }
12274 }
12275 let mut out = Vec::new();
12276 rec(s, &mut out);
12277 out
12278}
12279
12280fn strip_lost_alloc_owners(e: &mut Expr) {
12286 deep_rewrite(e, &mut |x| {
12287 if let Expr::Method { name, is_special: true, owner: Some(o), .. } = x {
12288 if &**name == "<init>" && !matches!(**o, Expr::This) {
12289 **o = Expr::Const(jdc_core::ir::expr::ConstVal::Int(0));
12290 }
12291 }
12292 });
12293}
12294
12295fn count_locals_expr(e: &Expr) -> jdc_core::FxHashMap<u32, usize> {
12298 let mut m = jdc_core::FxHashMap::default();
12299 count_expr_ro(e, &mut m);
12300 m
12301}
12302
12303pub(crate) fn count_locals_stmts(ss: &[Stmt]) -> jdc_core::FxHashMap<u32, usize> {
12304 let mut m = jdc_core::FxHashMap::default();
12305 for s in ss {
12306 visit_stmt_exprs_ro(s, &mut |e| count_expr_ro(e, &mut m));
12307 }
12308 m
12309}
12310
12311fn cnt(m: &jdc_core::FxHashMap<u32, usize>, v: u32) -> usize {
12312 m.get(&v).copied().unwrap_or(0)
12313}
12314
12315fn inline_locals(
12319 e: &Expr,
12320 map: &std::collections::HashMap<u32, Expr>,
12321 depth: u32,
12322) -> Option<Expr> {
12323 if depth > 4 {
12324 return None;
12325 }
12326 let mut out = e.clone();
12327 let mut fail = false;
12328 deep_rewrite(&mut out, &mut |x| {
12329 if let Expr::Local { var, .. } = x {
12330 if let Some(def) = map.get(var) {
12331 match inline_locals(def, map, depth + 1) {
12332 Some(r) => *x = r,
12333 None => fail = true,
12334 }
12335 }
12336 }
12337 });
12338 if fail {
12339 None
12340 } else {
12341 Some(out)
12342 }
12343}
12344
12345fn def_of(s: &Stmt) -> Option<(u32, &Expr)> {
12347 match s {
12348 Stmt::LocalDef { var, init: Some(e), .. } => Some((*var, e)),
12349 Stmt::ExprStmt(Expr::Assign {
12350 target,
12351 value,
12352 op: jdc_core::ir::expr::AssignOp::Plain,
12353 ..
12354 }) => match &**target {
12355 Expr::Local { var, .. } => Some((*var, value)),
12356 _ => None,
12357 },
12358 _ => None,
12359 }
12360}
12361
12362fn try_linear_super_inline(stmts: &mut Vec<Stmt>) -> bool {
12364 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
12365 return false;
12366 };
12367 if pos == 0 {
12368 return false; }
12370 if !stmts[..pos]
12372 .iter()
12373 .all(|s| matches!(s, Stmt::LocalDef { .. } | Stmt::ExprStmt(_)))
12374 {
12375 return false;
12376 }
12377 let Some(call) = ctor_call_expr(&stmts[pos]) else {
12378 return false;
12379 };
12380 let Expr::Method { args, .. } = call else {
12381 return false;
12382 };
12383 let mut map: std::collections::HashMap<u32, Expr> =
12385 std::collections::HashMap::new();
12386 let mut def_idx: jdc_core::FxHashMap<u32, usize> =
12387 jdc_core::FxHashMap::default();
12388 for (i, s) in stmts[..pos].iter().enumerate() {
12389 if let Some((v, e)) = def_of(s) {
12390 map.insert(v, e.clone());
12391 def_idx.insert(v, i);
12392 }
12393 }
12394 let mut needed: Vec<u32> = Vec::new();
12396 for a in args {
12397 let mut probe = a.clone();
12398 strip_lost_alloc_owners(&mut probe);
12399 deep_rewrite(&mut probe, &mut |x| {
12400 if let Expr::Local { var, .. } = x {
12401 if map.contains_key(var) && !needed.contains(var) {
12402 needed.push(*var);
12403 }
12404 }
12405 });
12406 }
12407 if needed.is_empty() {
12408 return false; }
12410 let mut consumed: Vec<usize> = Vec::new();
12413 let use_counts = count_locals_stmts(stmts);
12414 for &v in &needed {
12415 let def = &map[&v];
12416 let uses = cnt(&use_counts, v);
12417 if jdc_core::ir::build::has_side_effects(def) && uses != 1 {
12418 return false;
12419 }
12420 if uses == 1 {
12421 consumed.push(def_idx[&v]);
12422 }
12423 }
12424 let mut new_args: Vec<Expr> = Vec::with_capacity(args.len());
12426 for a in args {
12427 match inline_locals(a, &map, 0) {
12428 Some(r) => new_args.push(r),
12429 None => return false,
12430 }
12431 }
12432 let mut call_stmt = stmts.remove(pos);
12435 if let Stmt::ExprStmt(Expr::Method { args: slot, .. }) = &mut call_stmt {
12436 *slot = new_args;
12437 }
12438 let mut rest: Vec<Stmt> = Vec::with_capacity(stmts.len() + 1);
12439 rest.push(call_stmt);
12440 for (i, s) in stmts.drain(..).enumerate() {
12441 let orig = if i >= pos { i + 1 } else { i };
12444 if !consumed.contains(&orig) {
12445 rest.push(s);
12446 }
12447 }
12448 *stmts = rest;
12449 true
12450}
12451
12452fn try_merge_branched_super(stmts: &mut Vec<Stmt>) -> bool {
12454 let if_positions: Vec<usize> = stmts
12458 .iter()
12459 .enumerate()
12460 .filter(|(_, s)| matches!(s, Stmt::If { .. }))
12461 .map(|(i, _)| i)
12462 .collect();
12463 for if_pos in if_positions {
12464 let gate = match &stmts[if_pos] {
12470 Stmt::If { then_stmt, else_stmt, .. } => {
12471 contains_delegation(then_stmt)
12472 || else_stmt.as_ref().is_some_and(|e| contains_delegation(e))
12473 }
12474 _ => false,
12475 };
12476 if gate && merge_at(stmts, if_pos) {
12477 return true;
12478 }
12479 }
12480 false
12481}
12482
12483fn contains_delegation(s: &Stmt) -> bool {
12486 match s {
12487 Stmt::ExprStmt(e) => is_delegation_expr(e),
12488 Stmt::Block(v) => v.iter().any(contains_delegation),
12489 Stmt::If { then_stmt, else_stmt, .. } => {
12490 contains_delegation(then_stmt)
12491 || else_stmt.as_ref().is_some_and(|e| contains_delegation(e))
12492 }
12493 _ => false,
12494 }
12495}
12496
12497
12498
12499fn merge_at(stmts: &mut Vec<Stmt>, if_pos: usize) -> bool {
12512 enum Br {
12517 Call(usize, Vec<(u32, Expr)>, Vec<usize>),
12519 Clean,
12520 Dirty,
12521 }
12522 fn split_branch(list: &[&Stmt]) -> Br {
12523 let call_pos = list.iter().position(|s| ctor_call_expr(s).is_some());
12524 let Some(c) = call_pos else {
12525 return if list.iter().any(|s| contains_delegation(s)) {
12528 Br::Dirty
12529 } else {
12530 Br::Clean
12531 };
12532 };
12533 if list.iter().skip(c + 1).any(|s| ctor_call_expr(s).is_some()) {
12534 return Br::Dirty; }
12536 let mut defs: Vec<(u32, Expr)> = Vec::new();
12537 let mut def_idxs: Vec<usize> = Vec::new();
12538 for (i, s) in list.iter().enumerate().take(c) {
12539 match def_of(s) {
12540 Some((v, e)) => {
12541 defs.push((v, e.clone()));
12542 def_idxs.push(i);
12543 }
12544 None => match s {
12545 Stmt::ExprStmt(_) | Stmt::LocalDef { .. } => {}
12548 _ => return Br::Dirty, },
12550 }
12551 }
12552 Br::Call(c, defs, def_idxs)
12553 }
12554 fn cond_def_of(s: &Stmt, scope: &[Stmt]) -> Option<(u32, Expr)> {
12556 let Stmt::If { cond, then_stmt, else_stmt: Some(e), .. } = s else {
12557 return None;
12558 };
12559 fn one<'x>(b: &'x Stmt, scope: &[Stmt]) -> Option<(u32, &'x Expr)> {
12560 let l = flat_list(b);
12561 if l.len() == 1 {
12562 return def_of(l[0]);
12563 }
12564 if l.len() == 2 {
12573 let (w, ex) = def_of(l[0])?;
12574 let Stmt::ExprStmt(Expr::Assign {
12575 target,
12576 op: AssignOp::Plain,
12577 value,
12578 }) = l[1]
12579 else {
12580 return None;
12581 };
12582 let Expr::Local { var: tv, .. } = &**target else {
12583 return None;
12584 };
12585 let Expr::Local { var: rv, .. } = &**value else {
12586 return None;
12587 };
12588 if *rv != w || *tv == w {
12589 return None;
12590 }
12591 let mut ldef_writes = 0usize;
12595 for st in scope {
12596 walk_all(st, &mut |x| {
12597 if let Stmt::LocalDef {
12598 var,
12599 init: Some(_),
12600 ..
12601 } = x
12602 {
12603 if *var == w {
12604 ldef_writes += 1;
12605 }
12606 }
12607 });
12608 }
12609 if stmts_read_var(scope, w) != 1
12610 || stmts_count_writes(scope, w) + ldef_writes != 1
12611 {
12612 return None;
12613 }
12614 return Some((*tv, ex));
12615 }
12616 None
12617 }
12618 let (v1, e1) = one(then_stmt, scope)?;
12619 let (v2, e2) = one(e, scope)?;
12620 if v1 != v2 {
12621 return None;
12622 }
12623 Some((
12624 v1,
12625 Expr::Cond {
12626 c: Box::new(cond.clone()),
12627 t: Box::new(e1.clone()),
12628 f: Box::new(e2.clone()),
12629 },
12630 ))
12631 }
12632
12633 let mut pmap: std::collections::HashMap<u32, Expr> =
12635 std::collections::HashMap::new();
12636 let mut pidx: jdc_core::FxHashMap<u32, usize> =
12637 jdc_core::FxHashMap::default();
12638 for (i, s) in stmts[..if_pos].iter().enumerate() {
12639 match s {
12640 Stmt::LocalDef { .. } | Stmt::ExprStmt(_) => {
12641 if let Some((v, e)) = def_of(s) {
12642 pmap.insert(v, e.clone());
12643 pidx.insert(v, i);
12644 }
12645 }
12646 Stmt::If { .. } => match cond_def_of(s, stmts) {
12647 Some((v, e)) => {
12648 pmap.insert(v, e);
12649 pidx.insert(v, i);
12650 }
12651 None => return false,
12652 },
12653 _ => return false,
12654 }
12655 }
12656 let (cond, then_v, else_v, had_else) = match &stmts[if_pos] {
12657 Stmt::If { cond, then_stmt, else_stmt, .. } => (
12658 cond.clone(),
12659 then_stmt.as_ref().clone(),
12660 else_stmt.as_ref().map_or_else(|| Stmt::Block(Vec::new()), |e| e.as_ref().clone()),
12661 else_stmt.is_some(),
12662 ),
12663 _ => return false,
12664 };
12665 fn normalize_branch(b: &mut Stmt) {
12671 let slot: &mut Stmt = match b {
12676 Stmt::Block(v) if v.len() == 1 => &mut v[0],
12677 other => other,
12678 };
12679 if !matches!(slot, Stmt::If { .. }) || !contains_delegation(slot) {
12680 return;
12681 }
12682 let taken = std::mem::replace(slot, Stmt::Block(Vec::new()));
12683 let mut tmp = vec![taken];
12684 if merge_at(&mut tmp, 0) {
12685 *slot = Stmt::Block(tmp);
12686 } else {
12687 *slot = tmp.pop().unwrap();
12688 }
12689 }
12690 let mut then_v = then_v;
12691 let mut else_v = else_v;
12692 normalize_branch(&mut then_v);
12693 normalize_branch(&mut else_v);
12694 let then_list = flat_list(&then_v);
12695 let else_list = flat_list(&else_v);
12696
12697 enum Side {
12700 Call {
12701 at: usize,
12702 defs: Vec<(u32, Expr)>,
12703 def_idxs: Vec<usize>,
12704 },
12705 Clean,
12706 }
12707 let (t_side, e_side, two_sided) = match (split_branch(&then_list), split_branch(&else_list)) {
12708 (Br::Call(tc, td, ti), Br::Call(ec, ed, ei)) => (
12709 Side::Call { at: tc, defs: td, def_idxs: ti },
12710 Side::Call { at: ec, defs: ed, def_idxs: ei },
12711 true,
12712 ),
12713 (Br::Call(tc, td, ti), Br::Clean) => (
12714 Side::Call { at: tc, defs: td, def_idxs: ti },
12715 Side::Clean,
12716 false,
12717 ),
12718 (Br::Clean, Br::Call(ec, ed, ei)) => (
12719 Side::Clean,
12720 Side::Call { at: ec, defs: ed, def_idxs: ei },
12721 false,
12722 ),
12723 _ => return false,
12724 };
12725 let (t_call, e_call) = match (&t_side, &e_side) {
12727 (Side::Call { at: ta, .. }, Side::Call { at: ea, .. }) => (
12728 ctor_call_expr(then_list[*ta]),
12729 ctor_call_expr(else_list[*ea]),
12730 ),
12731 (Side::Call { at: ta, .. }, Side::Clean) => {
12732 (ctor_call_expr(then_list[*ta]), None)
12733 }
12734 (Side::Clean, Side::Call { at: ea, .. }) => {
12735 (None, ctor_call_expr(else_list[*ea]))
12736 }
12737 _ => return false,
12738 };
12739 let (call_list, call_at) = match &t_side {
12741 Side::Call { at, .. } => (&then_list, *at),
12742 Side::Clean => match &e_side {
12743 Side::Call { at, .. } => (&else_list, *at),
12744 Side::Clean => return false,
12745 },
12746 };
12747 let (base_args, other_args) = if two_sided {
12748 let (Some(tc), Some(ec)) = (t_call, e_call) else {
12749 return false;
12750 };
12751 if !same_ctor_kind(tc, ec) {
12752 return false;
12753 }
12754 let (Expr::Method { args: a1, .. }, Expr::Method { args: a2, .. }) = (tc, ec) else {
12755 return false;
12756 };
12757 if a1.len() != a2.len() {
12758 return false;
12759 }
12760 (a1, Some(a2))
12761 } else {
12762 let c = t_call.or(e_call);
12763 let Some(c) = c else { return false };
12764 let Expr::Method { args, .. } = c else {
12765 return false;
12766 };
12767 (args, None)
12768 };
12769
12770 let mut tmap: std::collections::HashMap<u32, Expr> = pmap.clone();
12772 let mut emap: std::collections::HashMap<u32, Expr> = pmap.clone();
12773 if let Side::Call { defs, .. } = &t_side {
12774 for (v, e) in defs {
12775 tmap.insert(*v, e.clone());
12776 }
12777 }
12778 if let Side::Call { defs, .. } = &e_side {
12779 for (v, e) in defs {
12780 emap.insert(*v, e.clone());
12781 }
12782 }
12783 let Some(cond_i) = inline_locals(&cond, &pmap, 0) else {
12784 return false;
12785 };
12786 let mut then_args: Vec<Expr> = Vec::with_capacity(base_args.len());
12788 let mut else_args: Vec<Expr> = Vec::new();
12789 if two_sided {
12790 for a in base_args {
12791 match inline_locals(a, &tmap, 0) {
12792 Some(r) => then_args.push(r),
12793 None => return false,
12794 }
12795 }
12796 for a in other_args.unwrap() {
12797 match inline_locals(a, &emap, 0) {
12798 Some(r) => else_args.push(r),
12799 None => return false,
12800 }
12801 }
12802 } else {
12803 let from_then = matches!(&t_side, Side::Call { .. });
12806 for a in base_args {
12807 let r = if from_then {
12808 inline_locals(a, &tmap, 0)
12809 } else {
12810 inline_locals(a, &emap, 0)
12811 };
12812 match r {
12813 Some(r) => then_args.push(r),
12814 None => return false,
12815 }
12816 }
12817 }
12818 let primary_args = base_args;
12819 let mut merged: Vec<Expr> = Vec::with_capacity(then_args.len());
12821 let mut differ = 0usize;
12822 for (j, t) in then_args.iter().enumerate() {
12823 if two_sided {
12824 let e = &else_args[j];
12825 if t == e {
12826 merged.push(t.clone());
12827 } else {
12828 differ += 1;
12829 merged.push(Expr::Cond {
12830 c: Box::new(cond_i.clone()),
12831 t: Box::new(t.clone()),
12832 f: Box::new(e.clone()),
12833 });
12834 }
12835 } else {
12836 merged.push(t.clone());
12837 }
12838 }
12839
12840 let vars_in = |e: &Expr| -> Vec<u32> {
12842 let mut vs = Vec::new();
12843 let mut probe = e.clone();
12844 strip_lost_alloc_owners(&mut probe);
12845 deep_rewrite(&mut probe, &mut |x| {
12846 if let Expr::Local { var, .. } = x {
12847 if !vs.contains(var) {
12848 vs.push(*var);
12849 }
12850 }
12851 });
12852 vs
12853 };
12854 let branch_remainder = |list: &[&Stmt], side: &Side| -> Vec<Stmt> {
12862 match side {
12863 Side::Clean => list.iter().map(|s| (*s).clone()).collect(),
12864 Side::Call { at, def_idxs, .. } => {
12865 list.iter()
12866 .enumerate()
12867 .filter(|(i, _)| *i != *at && !def_idxs.contains(i))
12868 .map(|(_, s)| (*s).clone())
12869 .collect()
12870 }
12871 }
12872 };
12873 let tail_then_v = branch_remainder(&then_list, &t_side);
12874 let tail_else_v = branch_remainder(&else_list, &e_side);
12875 let post = &stmts[if_pos + 1..];
12876 let mut inlined: Vec<u32> = vars_in(&cond);
12877 for a in primary_args.iter().chain(else_args.iter()) {
12878 for v in vars_in(a) {
12879 if !inlined.contains(&v) {
12880 inlined.push(v);
12881 }
12882 }
12883 }
12884 inlined.retain(|v| pmap.contains_key(v) || tmap.contains_key(v) || emap.contains_key(v));
12885 for &v in &inlined {
12887 let in_branch = match (&t_side, &e_side) {
12888 (Side::Call { defs: td, .. }, Side::Call { defs: ed, .. }) => {
12889 td.iter().any(|(d, _)| *d == v) || ed.iter().any(|(d, _)| *d == v)
12890 }
12891 (Side::Call { defs, .. }, Side::Clean)
12892 | (Side::Clean, Side::Call { defs, .. }) => {
12893 defs.iter().any(|(d, _)| *d == v)
12894 }
12895 _ => false,
12896 };
12897 if pmap.contains_key(&v) && in_branch {
12898 return false;
12899 }
12900 }
12901 type Counts = jdc_core::FxHashMap<u32, usize>;
12902 type GuardCounts = (
12903 Counts,
12904 Vec<Counts>,
12905 Vec<Counts>,
12906 Counts,
12907 Counts,
12908 Counts,
12909 Vec<(u32, Counts)>,
12910 );
12911 let (cond_counts, a1_counts, a2_counts, tail1_counts, tail2_counts, post_counts, def_counts): GuardCounts =
12912 if inlined.is_empty() {
12913 Default::default()
12914 } else {
12915 let all_defs: Vec<(u32, &Expr)> = pmap
12916 .iter()
12917 .map(|(k, v)| (*k, v))
12918 .chain(match &t_side {
12919 Side::Call { defs, .. } => defs.iter().map(|(k, v)| (*k, v)).collect(),
12920 Side::Clean => Vec::new(),
12921 })
12922 .chain(match &e_side {
12923 Side::Call { defs, .. } => defs.iter().map(|(k, v)| (*k, v)).collect(),
12924 Side::Clean => Vec::new(),
12925 })
12926 .collect();
12927 (
12928 count_locals_expr(&cond),
12929 primary_args.iter().map(count_locals_expr).collect(),
12930 else_args.iter().map(count_locals_expr).collect(),
12931 count_locals_stmts(&tail_then_v),
12932 count_locals_stmts(&tail_else_v),
12933 count_locals_stmts(post),
12934 all_defs.into_iter().map(|(v, e)| (v, count_locals_expr(e))).collect(),
12935 )
12936 };
12937 for &v in &inlined {
12938 let in_t = matches!(&t_side, Side::Call { defs, .. } if defs.iter().any(|(d, _)| *d == v));
12939 let in_e = matches!(&e_side, Side::Call { defs, .. } if defs.iter().any(|(d, _)| *d == v));
12940 let effectful = [
12941 in_t.then(|| tmap.get(&v)).flatten(),
12942 in_e.then(|| emap.get(&v)).flatten(),
12943 pmap.get(&v),
12944 ]
12945 .into_iter()
12946 .flatten()
12947 .any(jdc_core::ir::build::has_side_effects);
12948 if !effectful {
12949 continue;
12950 }
12951 let u_cond = cnt(&cond_counts, v);
12952 let u_post = cnt(&post_counts, v);
12953 let u_t1 = cnt(&tail1_counts, v);
12954 let u_t2 = cnt(&tail2_counts, v);
12955 let mut u_odef = 0usize;
12956 for (dv, dc) in &def_counts {
12957 if *dv != v && inlined.contains(dv) {
12958 u_odef += cnt(dc, v);
12959 }
12960 }
12961 let mut emit = u_cond * (1 + differ);
12962 let mut uses_args = 0usize;
12963 for j in 0..primary_args.len() {
12964 let c1 = cnt(&a1_counts[j], v);
12965 let c2 = if two_sided && j < a2_counts.len() {
12966 cnt(&a2_counts[j], v)
12967 } else {
12968 0
12969 };
12970 uses_args += c1 + c2;
12971 emit += if two_sided && then_args[j] != else_args[j] {
12972 c1 + c2
12973 } else {
12974 c1.max(c2)
12975 };
12976 }
12977 let ok = if in_t || in_e {
12978 u_cond == 0
12979 && u_post == 0
12980 && u_t1 == 0
12981 && u_t2 == 0
12982 && u_odef == 0
12983 && a1_counts.iter().map(|c| cnt(c, v)).sum::<usize>() <= 1
12984 && a2_counts.iter().map(|c| cnt(c, v)).sum::<usize>() <= 1
12985 && emit <= 2
12986 && (in_t || a1_counts.iter().all(|c| cnt(c, v) == 0))
12987 && (in_e || a2_counts.iter().all(|c| cnt(c, v) == 0))
12988 } else {
12989 u_post == 0
12990 && u_t1 == 0
12991 && u_t2 == 0
12992 && u_odef == 0
12993 && ((u_cond == 1 && uses_args == 0 && emit <= 2)
12994 || (u_cond == 0 && emit <= 1))
12995 };
12996 if !ok {
12997 return false;
12998 }
12999 }
13000
13001 let mut call_stmt = call_list[call_at].clone();
13003 if let Stmt::ExprStmt(Expr::Method { args: slot, .. }) = &mut call_stmt {
13004 *slot = merged;
13005 }
13006 let residual = |v: u32| -> usize {
13007 cnt(&tail1_counts, v) + cnt(&tail2_counts, v) + cnt(&post_counts, v)
13008 };
13009 let mut kept_prelude: Vec<Stmt> = Vec::new();
13010 for (i, s) in stmts[..if_pos].iter().enumerate() {
13011 let consumed = match s {
13012 Stmt::LocalDef { var, .. } => Some(*var),
13013 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
13014 Expr::Local { var, .. } => Some(*var),
13015 _ => None,
13016 },
13017 Stmt::If { .. } => cond_def_of(s, stmts).map(|(v, _)| v),
13018 _ => None,
13019 };
13020 if let Some(v) = consumed {
13021 if inlined.contains(&v) && pidx.get(&v) == Some(&i) && residual(v) == 0 {
13022 continue;
13023 }
13024 }
13025 kept_prelude.push(s.clone());
13026 }
13027 let build_keep = |list: &[&Stmt], side: &Side| -> Vec<Stmt> {
13028 match side {
13029 Side::Clean => list.iter().map(|s| (*s).clone()).collect(),
13030 Side::Call { at, .. } => {
13031 let mut out = Vec::new();
13032 for (i, s) in list.iter().enumerate() {
13033 if i == *at {
13034 continue;
13035 }
13036 if i < *at {
13037 if let Some((v, _)) = def_of(s) {
13038 if inlined.contains(&v) && residual(v) == 0 {
13039 continue;
13040 }
13041 }
13042 }
13043 out.push((*s).clone());
13044 }
13045 out
13046 }
13047 }
13048 };
13049 let then_keep = build_keep(&then_list, &t_side);
13050 let else_keep = build_keep(&else_list, &e_side);
13051 let else_part = if else_keep.is_empty() && !had_else {
13052 None
13053 } else {
13054 Some(Box::new(Stmt::Block(else_keep)))
13055 };
13056 let new_if = Stmt::If {
13057 cond: cond_i,
13058 then_stmt: Box::new(Stmt::Block(then_keep)),
13059 else_stmt: else_part,
13060 };
13061 let if_empty = match &new_if {
13062 Stmt::If { then_stmt, else_stmt, .. } => {
13063 let be = |s: &Stmt| matches!(s, Stmt::Block(v) if v.is_empty());
13064 be(then_stmt) && else_stmt.as_ref().is_none_or(|e| be(e))
13065 }
13066 _ => false,
13067 };
13068 let mut out: Vec<Stmt> = Vec::with_capacity(stmts.len());
13069 out.push(call_stmt);
13070 out.extend(kept_prelude);
13071 if !if_empty {
13072 out.push(new_if);
13073 }
13074 out.extend(stmts[if_pos + 1..].to_vec());
13075 *stmts = out;
13076 true
13077}
13078
13079
13080pub fn strip_enum_ctor_super(body: &mut Stmt) {
13081 fn is_enum_super(s: &Stmt) -> bool {
13082 matches!(
13083 s,
13084 Stmt::ExprStmt(Expr::Method { name, is_super: true, .. }) if &**name == "<init>"
13085 )
13086 }
13087 fn strip_deep(stmts: &mut Vec<Stmt>) {
13097 stmts.retain(|s| !is_enum_super(s));
13098 for st in stmts.iter_mut() {
13099 strip_one(st);
13100 }
13101 }
13102 fn strip_one(st: &mut Stmt) {
13103 match st {
13104 Stmt::Block(v) => strip_deep(v),
13105 Stmt::If { then_stmt, else_stmt, .. } => {
13106 strip_one(then_stmt);
13107 if let Some(e) = else_stmt {
13108 strip_one(e);
13109 }
13110 }
13111 Stmt::While { body, .. }
13112 | Stmt::DoWhile { body, .. }
13113 | Stmt::ForEach { body, .. }
13114 | Stmt::Synchronized { body, .. }
13115 | Stmt::Labeled { body, .. } => strip_one(body),
13116 Stmt::For { init, body, .. } => {
13117 strip_deep(init);
13118 strip_one(body);
13119 }
13120 Stmt::Switch { cases, default, .. } => {
13121 for c in cases.iter_mut() {
13122 strip_deep(&mut c.body);
13123 }
13124 if let Some(d) = default {
13125 strip_one(d);
13126 }
13127 }
13128 Stmt::Try { body, catches, finally }
13129 | Stmt::TryWithResources { body, catches, finally, .. } => {
13130 strip_one(body);
13131 for c in catches.iter_mut() {
13132 strip_one(&mut c.body);
13133 }
13134 if let Some(f) = finally {
13135 strip_one(f);
13136 }
13137 }
13138 _ => {}
13139 }
13140 }
13141 let Stmt::Block(stmts) = body else {
13142 return;
13143 };
13144 strip_deep(stmts);
13145}
13146
13147pub fn ensure_declared(body: &mut Stmt, vt: &VarTable) {
13148 let n = vt.vars.len().max(1);
13152
13153 let mut seen: HashSet<u32> = HashSet::default();
13156 demote_dup_decls(body, &mut seen, vt);
13157
13158 let mut def_block: Vec<u32> = vec![u32::MAX; n];
13166 let mut sizes: Vec<u32> = vec![0];
13167 let mut next_id: u32 = 1;
13168 scope_pass_a(body, 0, &mut next_id, &mut def_block, &mut sizes);
13169 sizes[0] = next_id;
13170 let mut within: Vec<u32> = vec![0; n];
13171 let mut total: Vec<u32> = vec![0; n];
13172 let mut next_b: u32 = 1;
13173 scope_pass_b(body, 0, &mut next_b, &def_block, &sizes, &mut within, &mut total);
13174 let mut hoist: HashSet<u32> = HashSet::default();
13175 for v in 0..n {
13176 if def_block[v] != u32::MAX && def_block[v] != 0 && within[v] < total[v] {
13177 hoist.insert(v as u32);
13178 }
13179 }
13180 if !hoist.is_empty() {
13181 let mut seed: HashSet<u32> = hoist.iter().copied().collect();
13182 demote_dup_decls(body, &mut seed, vt);
13183 }
13184
13185 let mut declared: Vec<bool> = vec![false; n];
13186 let mut is_param: Vec<bool> = vec![false; n];
13187 for v in &vt.vars {
13188 if v.is_param && (v.id as usize) < n {
13189 is_param[v.id as usize] = true;
13190 }
13191 }
13192 let mut is_catch: Vec<bool> = vec![false; n];
13198 walk_all(body, &mut |st| {
13199 if let Stmt::Try { catches, .. } = st {
13200 for c in catches {
13201 if (c.var as usize) < n {
13202 is_catch[c.var as usize] = true;
13203 }
13204 }
13205 }
13206 });
13207 walk_all(body, &mut |st| {
13208 if let Stmt::LocalDef { var, .. } = st {
13209 if (*var as usize) < n {
13210 declared[*var as usize] = true;
13211 }
13212 }
13213 });
13214 let mut used: HashSet<u32> = HashSet::default();
13215 stmt_collect_vars(body, &mut used, true);
13219
13220 let mut needs_decl: Vec<u32> = used
13223 .into_iter()
13224 .filter(|v| {
13225 let i = *v as usize;
13226 i >= n
13227 || ((!declared[i] && !is_param[i] && !is_catch[i]) || hoist.contains(v))
13228 })
13229 .collect();
13230 needs_decl.sort_unstable();
13231 needs_decl.dedup();
13232
13233 if !needs_decl.is_empty() {
13234 let mut decls: Vec<Stmt> = needs_decl
13235 .into_iter()
13236 .map(|v| Stmt::LocalDef {
13237 var: v,
13238 init: None,
13239 is_final: false,
13240 force_type: true,
13241 })
13242 .collect();
13243 decls.reverse();
13244 match body {
13245 Stmt::Block(v) => {
13246 for d in decls {
13247 v.insert(0, d);
13248 }
13249 }
13250 other => {
13251 let inner = std::mem::replace(other, Stmt::Block(vec![]));
13252 *other = Stmt::Block({
13253 let mut vs = decls;
13254 vs.push(inner);
13255 vs
13256 });
13257 }
13258 }
13259 }
13260}
13261
13262fn demote_dup_decls(s: &mut Stmt, seen: &mut HashSet<u32>, vt: &VarTable) {
13263 match s {
13264 Stmt::Block(v) => {
13267 for x in v.iter_mut() {
13268 demote_dup_decls(x, seen, vt);
13269 }
13270 }
13271 Stmt::LocalDef { var, init, .. } => {
13272 if seen.contains(var) {
13273 let ty = vt.var(*var).ty.clone();
13274 if let Some(e) = init.take() {
13275 *s = Stmt::ExprStmt(Expr::Assign {
13276 target: Box::new(Expr::Local { var: *var, ty }),
13277 op: AssignOp::Plain,
13278 value: Box::new(e),
13279 });
13280 } else {
13281 *s = Stmt::Block(vec![]);
13282 }
13283 } else {
13284 seen.insert(*var);
13285 }
13286 }
13287 other => {
13288 walk_mut(other, &mut |x| demote_dup_decls(x, seen, vt));
13289 }
13290 }
13291}
13292
13293#[allow(dead_code)]
13294fn unused(_: &Vec<CaseGroup>, _: &Catch) {}
13295
13296pub fn apply_local_names(vt: &mut VarTable, body: &Stmt) {
13310 use std::collections::{HashMap, HashSet};
13311
13312 let mut by_call: HashMap<u32, Vec<String>> = HashMap::new();
13315 let mut by_intrinsics: HashMap<u32, String> = HashMap::new();
13316
13317 visit_all_exprs(body, &mut |e| {
13318 if let Expr::Method { cls, name, args, .. } = e {
13319 if (cls.as_ref() == "kotlin/jvm/internal/Intrinsics"
13320 || cls.as_ref() == "kotlin/jvm/internal/IntrinsicsKt")
13321 && matches!(name.as_ref(), "checkNotNullParameter" | "checkParameterIsNotNull")
13322 && args.len() == 2
13323 {
13324 if let (Expr::Local { var, .. }, Expr::Const(ConstVal::Str(s))) = (&args[0], &args[1])
13325 {
13326 by_intrinsics
13327 .entry(*var)
13328 .or_insert_with(|| sanitize_name(s).unwrap_or_default());
13329 }
13330 }
13331 }
13332 });
13333
13334 walk_all(body, &mut |st| {
13335 let (var, value) = match st {
13336 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e),
13337 Stmt::ExprStmt(Expr::Assign { target, value, .. })
13338 if matches!(&**target, Expr::Local { .. }) =>
13339 {
13340 let Expr::Local { var, .. } = &**target else { return };
13341 (*var, value.as_ref())
13342 }
13343 _ => return,
13344 };
13345 if let Some(n) = defining_call_name(value) {
13346 by_call.entry(var).or_default().push(n);
13347 }
13348 });
13349
13350 let sanit = |n: &str| crate::classdec::java_ident(n).into_owned();
13361 let mut taken: HashSet<String> = HashSet::default();
13362 for v in vt.vars.iter() {
13367 if v.synthetic_name {
13372 taken.insert(sanit(&v.name));
13373 }
13374 }
13375 for v in vt.vars.iter_mut() {
13376 if v.synthetic_name {
13377 continue;
13378 }
13379 if !taken.insert(sanit(&v.name)) {
13380 let base = sanit(&v.name);
13381 for i in 2.. {
13382 let cand = format!("{base}{i}");
13383 if taken.insert(cand.clone()) {
13384 v.name = cand;
13385 break;
13386 }
13387 }
13388 }
13389 }
13390 let claim = |taken: &mut HashSet<String>, want: &str| -> String {
13391 let w = crate::classdec::java_ident(want).into_owned();
13392 if taken.insert(w.clone()) {
13393 return w;
13394 }
13395 for i in 2.. {
13396 let cand = format!("{w}{i}");
13397 if taken.insert(cand.clone()) {
13398 return cand;
13399 }
13400 }
13401 w
13402 };
13403
13404 for info in vt.vars.iter_mut() {
13405 if !info.synthetic_name {
13406 continue;
13407 }
13408 if let Some(n) = by_intrinsics.get(&info.id) {
13411 if !n.is_empty() {
13412 info.name = claim(&mut taken, n);
13413 continue;
13414 }
13415 }
13416 if let Some(names) = by_call.get(&info.id) {
13418 let unique: HashSet<&String> = names.iter().collect();
13419 if unique.len() == 1 {
13420 let n = unique.into_iter().next().unwrap();
13421 if !is_java_keyword(n) {
13422 info.name = claim(&mut taken, n);
13423 continue;
13424 }
13425 }
13426 }
13427 let want = type_alias(&info.ty).map(str::to_string).or_else(|| simple_type_name(&info.ty));
13430 if let Some(n) = want {
13431 info.name = claim(&mut taken, &n);
13432 }
13433 }
13434
13435 let mut seen: HashSet<String> = HashSet::default();
13440 for v in vt.vars.iter_mut() {
13441 if !seen.insert(sanit(&v.name)) {
13442 let base = sanit(&v.name);
13443 for i in 2.. {
13444 let cand = format!("{base}{i}");
13445 if seen.insert(cand.clone()) {
13446 v.name = cand;
13447 break;
13448 }
13449 }
13450 }
13451 }
13452}
13453
13454fn simple_type_name(ty: &TypeRef) -> Option<String> {
13456 let TypeRef::J(JavaType::Object(n)) = ty else {
13457 return None;
13458 };
13459 let simple = n.rsplit('/').next().unwrap_or(n);
13460 let simple = simple.rsplit('$').next().unwrap_or(simple);
13461 let mut chars = simple.chars();
13464 let mut out = String::with_capacity(simple.len());
13465 let mut lowered = false;
13466 for c in chars.by_ref() {
13467 if c.is_ascii_alphabetic() && !lowered {
13468 out.push(c.to_ascii_lowercase());
13469 lowered = true;
13470 } else {
13471 out.push(c);
13472 }
13473 if lowered {
13474 break;
13475 }
13476 }
13477 out.extend(chars);
13478 sanitize_name(&out)
13479}
13480
13481fn defining_call_name(e: &Expr) -> Option<String> {
13485 match e {
13486 Expr::Method { name, .. } => {
13487 let base = name
13488 .strip_prefix("get")
13489 .or_else(|| name.strip_prefix("is"))
13490 .filter(|rest| rest.chars().next().is_some_and(|c| c.is_ascii_uppercase()))
13491 .unwrap_or(name);
13492 let first = base.chars().next()?;
13493 let mut out = String::with_capacity(base.len());
13494 out.push(first.to_ascii_lowercase());
13495 out.extend(base.chars().skip(1));
13496 sanitize_name(&out)
13497 }
13498 Expr::New { cls, .. } => {
13499 let simple = cls.rsplit('/').next().unwrap_or(cls);
13500 let simple = simple.rsplit('$').next().unwrap_or(simple);
13501 let first = simple.chars().next()?;
13502 let mut out = String::with_capacity(simple.len());
13503 out.push(first.to_ascii_lowercase());
13504 out.extend(simple.chars().skip(1));
13505 sanitize_name(&out)
13506 }
13507 _ => None,
13508 }
13509}
13510
13511fn sanitize_name(s: &str) -> Option<String> {
13513 let mut out = String::with_capacity(s.len());
13514 for c in s.chars() {
13515 if c.is_ascii_alphanumeric() || c == '_' || c == '$' {
13516 out.push(c);
13517 } else {
13518 out.push('_');
13519 }
13520 }
13521 if out.len() < 2
13522 || out.chars().next().is_some_and(|c| c.is_ascii_digit())
13523 || is_java_keyword(&out)
13524 {
13525 return None;
13526 }
13527 Some(out)
13528}
13529
13530fn type_alias(ty: &TypeRef) -> Option<&'static str> {
13533 let TypeRef::J(JavaType::Object(n)) = ty else {
13534 return None;
13535 };
13536 Some(match n.as_ref() {
13537 "java/lang/String" | "kotlin/String" => "str",
13538 "java/lang/Class" => "cls",
13539 "java/lang/Throwable" => "th",
13540 "java/lang/Object" | "kotlin/Any" => "obj",
13541 "java/util/Iterator" | "kotlin/collections/Iterator" => "it",
13542 "java/lang/Boolean" => "bool",
13543 "java/lang/Integer" => "num",
13544 "java/lang/Character" => "ch",
13545 "java/lang/Byte" => "b",
13546 "java/lang/Short" => "sh",
13547 "java/lang/Float" => "f",
13548 "java/lang/Double" => "d",
13549 "java/lang/Long" => "i",
13550 "java/lang/StringBuilder" => "sb",
13551 "java/util/ArrayList" => "list",
13552 "java/util/HashMap" => "map",
13553 "android/content/Context" => "context",
13554 "android/content/Intent" => "intent",
13555 "android/os/Bundle" => "bundle",
13556 "android/view/View" => "view",
13557 "android/graphics/Bitmap" => "bitmap",
13558 "android/view/ViewGroup" => "viewGroup",
13559 _ => return None,
13560 })
13561}
13562
13563pub(crate) fn visit_all_exprs<F: FnMut(&Expr)>(s: &Stmt, f: &mut F) {
13565 walk_all(s, &mut |st| {
13566 let exprs: Vec<&Expr> = match st {
13567 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
13568 let mut v: Vec<&Expr> = vec![value];
13569 if !matches!(&**target, Expr::Local { .. }) {
13570 v.push(target);
13571 }
13572 v
13573 }
13574 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
13575 vec![e]
13576 }
13577 Stmt::Return(Some(e)) => vec![e],
13578 Stmt::LocalDef { init: Some(e), .. } => vec![e],
13579 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
13580 vec![cond]
13581 }
13582 _ => Vec::new(),
13583 };
13584 for e in exprs {
13585 visit_exprs(e, f);
13586 }
13587 });
13588}
13589
13590pub fn platform_constants(body: &mut Stmt) {
13595 if !crate::platform::installed() {
13596 return;
13597 }
13598 rewrite_exprs(body, &mut |e| {
13599 if let Expr::Method { cls, name, desc, args, .. } = e {
13600 for a in args.iter_mut() {
13601 let (value, is_long) = match a {
13602 Expr::Const(ConstVal::Int(v)) => (*v as i64, false),
13603 Expr::Const(ConstVal::Long(v)) => (*v, true),
13604 _ => continue,
13605 };
13606 if let Some(named) =
13607 crate::platform::named_constant(cls, name, &desc_to_string(desc), 0, value)
13608 {
13609 let _ = is_long;
13610 *a = Expr::Raw(named);
13611 }
13612 }
13613 }
13614 });
13615}
13616
13617fn desc_to_string(d: &jdc_core::types::MethodDescriptor) -> String {
13619 let mut out = String::from("(");
13620 for a in &d.args {
13621 type_to_desc(a, &mut out);
13622 }
13623 out.push(')');
13624 type_to_desc(&d.ret, &mut out);
13625 out
13626}
13627
13628fn type_to_desc(t: &jdc_core::types::JavaType, out: &mut String) {
13629 match t {
13630 JavaType::Void => out.push('V'),
13631 JavaType::Boolean => out.push('Z'),
13632 JavaType::Byte => out.push('B'),
13633 JavaType::Char => out.push('C'),
13634 JavaType::Short => out.push('S'),
13635 JavaType::Int => out.push('I'),
13636 JavaType::Float => out.push('F'),
13637 JavaType::Long => out.push('J'),
13638 JavaType::Double => out.push('D'),
13639 JavaType::Object(n) => {
13640 out.push('L');
13641 out.push_str(n);
13642 out.push(';');
13643 }
13644 JavaType::Array(inner) => {
13645 out.push('[');
13646 type_to_desc(inner, out);
13647 }
13648 }
13649}
13650
13651pub fn remove_kotlin_checks(s: &mut Stmt) {
13657 walk_mut_deep(s, &mut |st| {
13658 let Stmt::Block(v) = st else { return };
13659 v.retain(|x| !is_kotlin_check(x));
13660 });
13661}
13662
13663fn is_kotlin_check(s: &Stmt) -> bool {
13664 let Stmt::ExprStmt(e) = s else { return false };
13665 let Expr::Method { cls, name, .. } = e else { return false };
13666 (cls.as_ref() == "kotlin/jvm/internal/Intrinsics" || cls.as_ref() == "kotlin/jvm/internal/IntrinsicsKt__Jdk7Kt")
13667 && matches!(
13668 name.as_ref(),
13669 "checkNotNullParameter"
13670 | "checkParameterIsNotNull"
13671 | "checkExpressionValueIsNotNull"
13672 | "checkNotNullExpressionValue"
13673 | "checkReturnedValueIsNotNull"
13674 | "checkFieldIsNotNull"
13675 | "checkNotNull"
13676 )
13677}
13678
13679fn inline_ref_ok(pool: &DexPool, host: &str, target: &str, member: Option<(&str, bool)>) -> bool {
13699 let hpkg = match host.rfind('/') {
13700 Some(i) => &host[..i],
13701 None => "",
13702 };
13703 let mut cur = target;
13704 loop {
13705 let tpkg = match cur.rfind('/') {
13706 Some(i) => &cur[..i],
13707 None => "",
13708 };
13709 if tpkg != hpkg {
13710 if let Some(pc) = pool.get(cur) {
13711 if pc.access & crate::access::ACC_PUBLIC == 0 {
13712 return false;
13713 }
13714 if cur == target {
13715 if let Some((mname, is_method)) = member {
13716 let ok = if is_method {
13717 pc.all_methods().any(|m| {
13718 &*m.name == mname
13719 && m.access & crate::access::ACC_PUBLIC != 0
13720 })
13721 } else {
13722 pc.static_fields
13723 .iter()
13724 .chain(pc.instance_fields.iter())
13725 .any(|f| {
13726 f.name == mname
13727 && f.access & crate::access::ACC_PUBLIC != 0
13728 })
13729 };
13730 if !ok
13735 && (if is_method {
13736 pc.all_methods().any(|m| &*m.name == mname)
13737 } else {
13738 pc.static_fields
13739 .iter()
13740 .chain(pc.instance_fields.iter())
13741 .any(|f| f.name == mname)
13742 })
13743 {
13744 return false;
13745 }
13746 }
13747 }
13748 }
13749 }
13750 match cur.rfind('$') {
13751 Some(i) if i > 0 => cur = &cur[..i],
13752 _ => break,
13753 }
13754 }
13755 true
13756}
13757
13758pub fn inline_accessors(s: &mut Stmt, pool: &DexPool, host: &str) {
13759 rewrite_exprs(s, &mut |e| {
13760 let Expr::Method { cls, name, desc, args, is_static, .. } = e else { return };
13761 if !*is_static || args.is_empty() {
13762 return;
13763 }
13764 let Some(target) = pool.get(cls) else { return };
13765 let desc_s = desc_to_string(desc);
13766 let Some(m) = target.find_method(name, &desc_s) else { return };
13767 if !(m.is_static() && m.access & access::ACC_SYNTHETIC != 0) {
13768 return;
13769 }
13770 let Some(dex) = pool.dex(m.dex_idx) else { return };
13771 let code = match pool.accessor_code(m.dex_idx, m.code_off) {
13776 Some(bytes) => ddc_dex::CodeItem::parse(&bytes, 0),
13777 None => dex.code_at(m.code_off),
13778 };
13779 let Some(code) = code else { return };
13780 let insns: Vec<&Insn> =
13781 code.insns.iter().filter(|i| !matches!(i.kind, InsnKind::Nop)).collect();
13782 let core: Vec<&Insn> = insns
13784 .iter()
13785 .copied()
13786 .filter(|i| !is_const_only_trace(&i.kind, &insns))
13787 .collect();
13788 match accessor_shape(&dex, &core, &code) {
13789 Some(Shape::Identity(arg_i)) => {
13790 if let Some(a) = args.get(arg_i) {
13791 *e = a.clone();
13792 }
13793 }
13794 Some(Shape::FieldRead { arg, cls: field_cls, field, field_ty }) => {
13795 if !inline_ref_ok(pool, host, &field_cls, Some((&field, false))) {
13796 return;
13797 }
13798 if let Some(owner) = args.get(arg).cloned().map(Box::new) {
13799 let ty = TypeRef::J(desc_type(&field_ty));
13800 let name = jdc_core::rename::field_display(
13806 &field_cls,
13807 &field,
13808 &field_ty,
13809 )
13810 .map(std::sync::Arc::from)
13811 .unwrap_or_else(|| field.into());
13812 *e = Expr::Field {
13813 owner: Some(owner),
13814 cls: field_cls.into(),
13815 name,
13816 ty,
13817 is_static: false,
13818 };
13819 }
13820 }
13821 Some(Shape::Forward { cls: tcls, name: tname, desc: tdesc, instance }) => {
13822 if !inline_ref_ok(pool, host, &tcls, Some((&tname, true))) {
13823 return;
13824 }
13825 let disp = jdc_core::rename::field_display(
13829 &tcls,
13830 &tname,
13831 &tdesc.to_string(),
13832 )
13833 .map(std::sync::Arc::from)
13834 .unwrap_or_else(|| tname.into());
13835 *cls = tcls.into();
13836 *name = disp;
13837 *desc = std::sync::Arc::new(tdesc);
13838 if instance && !args.is_empty() {
13839 let recv = args.remove(0);
13841 if let Expr::Method { owner, .. } = e {
13842 *owner = Some(Box::new(recv));
13843 }
13844 }
13845 }
13846 None => {}
13847 }
13848 });
13849}
13850
13851enum Shape {
13853 Identity(usize),
13855 FieldRead { arg: usize, cls: String, field: String, field_ty: String },
13857 Forward { cls: String, name: String, desc: MethodDescriptor, instance: bool },
13860}
13861
13862fn field_of(dex: &ddc_dex::DexFile, field_idx: u32) -> Option<(String, String, String)> {
13864 let f = dex.field(field_idx);
13865 Some((
13866 dex.class_name(f.class_idx),
13869 dex.string(f.name_idx).to_string(),
13870 dex.type_name(f.type_idx).to_string(),
13872 ))
13873}
13874
13875fn param_index(code: &ddc_dex::CodeItem, reg: u16) -> Option<usize> {
13878 let rs = code.registers_size as usize;
13879 let ins = code.ins_size as usize;
13880 let r = reg as usize;
13881 let first = rs.checked_sub(ins)?;
13882 (r >= first && r < rs).then(|| r - first)
13883}
13884
13885fn accessor_shape(
13886 dex: &ddc_dex::DexFile,
13887 core: &[&Insn],
13888 code: &ddc_dex::CodeItem,
13889) -> Option<Shape> {
13890 let reads = |rest: &[&Insn], r: u16| {
13900 rest.iter()
13901 .any(|i| crate::lift::src_regs(&i.kind).contains(&r))
13902 };
13903 let core: &[&Insn] = match core.split_first() {
13904 Some((first, rest))
13905 if matches!(first.kind, InsnKind::Const { dst, .. } if !reads(rest, dst)) =>
13906 {
13907 rest
13908 }
13909 _ => core,
13910 };
13911 let core: &[&Insn] = match core.split_last() {
13912 Some((last, rest))
13913 if matches!(last.kind, InsnKind::Const { dst, .. } if !reads(rest, dst)) =>
13914 {
13915 rest
13916 }
13917 _ => core,
13918 };
13919 match core {
13920 [Insn { kind: InsnKind::Return { src }, .. }] => {
13922 let idx = param_index(code, *src)?;
13923 Some(Shape::Identity(idx))
13924 }
13925 [
13927 Insn { kind: InsnKind::IGet { dst, obj, field_idx }, .. },
13928 Insn { kind: InsnKind::Return { src }, .. },
13929 ] if dst == src => {
13930 let arg = param_index(code, *obj)?;
13931 let (cls, field, field_ty) = field_of(dex, *field_idx)?;
13932 Some(Shape::FieldRead { arg, cls, field, field_ty })
13933 }
13934 [Insn { kind: InsnKind::Invoke { method_idx, regs, kind, .. }, .. }, rest @ ..] if rest.len() <= 2 => {
13936 if !regs.iter().all(|r| param_index(code, *r).is_some()) || regs.is_empty() {
13938 return None;
13939 }
13940 if rest.len() == 2 {
13943 let ok = matches!(rest[0].kind, InsnKind::MoveResult { .. })
13944 && matches!(rest[1].kind, InsnKind::Return { .. } | InsnKind::ReturnVoid);
13945 if !ok {
13946 return None;
13947 }
13948 }
13949 let mid = dex.method(*method_idx);
13950 let cls = dex.class_name(mid.class_idx);
13953 let name = dex.string(mid.name_idx).to_string();
13954 let mut s = String::from("(");
13955 for t in dex.proto_params(mid.proto_idx) {
13956 s.push_str(dex.type_name(*t));
13957 }
13958 s.push(')');
13959 s.push_str(dex.type_name(dex.proto(mid.proto_idx).return_type_idx));
13960 let desc = parse_method_descriptor(&s)?;
13961 Some(Shape::Forward { cls, name, desc, instance: !matches!(kind, InvokeKind::Static) })
13962 }
13963 _ => None,
13964 }
13965}
13966
13967
13968fn is_const_only_trace(kind: &InsnKind, insns: &[&Insn]) -> bool {
13974 let InsnKind::Invoke { kind, regs, .. } = kind else { return false };
13975 if !matches!(kind, InvokeKind::Static) || regs.is_empty() {
13976 return false;
13977 }
13978 regs.iter().all(|r| const_loaded(*r, insns, 0))
13979}
13980
13981fn const_loaded(r: u16, insns: &[&Insn], depth: u8) -> bool {
13984 if depth > 3 {
13985 return false;
13986 }
13987 insns.iter().any(|i| match &i.kind {
13988 InsnKind::Const { dst, .. } => *dst == r,
13989 InsnKind::Move { dst, src } => *dst == r && const_loaded(*src, insns, depth + 1),
13990 _ => false,
13991 })
13992}