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_ref() == 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 final_dead_assigns(body: &mut Stmt) {
2540 let mut reads: Vec<usize> = Vec::new();
2542 count_reads(body, &mut reads);
2543 let is_dead = |st: &Stmt| -> bool {
2544 let Stmt::ExprStmt(Expr::Assign {
2545 target,
2546 value,
2547 op: jdc_core::ir::expr::AssignOp::Plain,
2548 ..
2549 }) = st
2550 else {
2551 return false;
2552 };
2553 let Expr::Local { var, .. } = &**target else {
2554 return false;
2555 };
2556 reads.get(*var as usize).copied().unwrap_or(0) == 0 && !has_side_effects(value)
2557 };
2558 walk_mut_deep(body, &mut |st| {
2562 if is_dead(st) {
2563 *st = Stmt::Block(vec![]);
2564 }
2565 });
2566 walk_mut_deep(body, &mut |st| {
2567 if let Stmt::Block(v) = st {
2568 v.retain(|x| !is_dead(x));
2569 }
2570 });
2571}
2572
2573pub fn drop_pure_value_stmts(body: &mut Stmt) {
2578 fn is_pure(s: &Stmt) -> bool {
2581 let Stmt::ExprStmt(e) = s else {
2582 return false;
2583 };
2584 !has_side_effects(e)
2585 }
2586 walk_mut_deep(body, &mut |st| {
2587 if is_pure(st) {
2590 *st = Stmt::Block(vec![]);
2591 return;
2592 }
2593 match st {
2594 Stmt::Switch { cases, .. } => {
2595 for c in cases.iter_mut() {
2596 c.body.retain(|x| !is_pure(x));
2597 }
2598 }
2599 Stmt::For { init, .. } => init.retain(|x| !is_pure(x)),
2600 _ => {}
2601 }
2602 });
2603}
2604
2605pub fn drop_dead_raw_news(body: &mut Stmt) {
2606 fn is_orphan(s: &Stmt) -> bool {
2607 matches!(s, Stmt::ExprStmt(Expr::New { raw: true, .. }))
2608 }
2609 walk_mut_deep(body, &mut |st| match st {
2610 Stmt::Block(v) => v.retain(|x| !is_orphan(x)),
2611 Stmt::Switch { cases, .. } => {
2612 for c in cases.iter_mut() {
2613 c.body.retain(|x| !is_orphan(x));
2614 }
2615 }
2616 Stmt::For { init, .. } => init.retain(|x| !is_orphan(x)),
2617 Stmt::TryWithResources { resources, .. } => resources.retain(|x| !is_orphan(x)),
2618 _ => {}
2619 });
2620}
2621
2622pub fn fold_synchronized(s: &mut Stmt) {
2626 fold_sync_walk(s);
2627}
2628
2629
2630fn fold_sync_walk(s: &mut Stmt) {
2631 walk_mut_deep(s, &mut |st| {
2632 let Stmt::Block(v) = st else { return };
2633 let mut i = 0;
2634 while i < v.len() {
2635 #[allow(unused_assignments)]
2639 let mut advance = true;
2640 if i + 1 < v.len() {
2641 if let Some((repl, consumed)) = try_sync_at(v, i) {
2642 advance = !matches!(repl.first(), Some(Stmt::Synchronized { .. }));
2643 let _ = v.drain(i..i + consumed);
2644 let n = repl.len();
2645 v.splice(i..i, repl);
2646 if advance {
2647 i += n;
2648 }
2649 continue;
2650 }
2651 }
2652 if let Some((repl, consumed)) = try_sync_in_try(v, i) {
2653 let _ = v.drain(i..i + consumed);
2654 let n = repl.len();
2655 v.splice(i..i, repl);
2656 i += n;
2657 continue;
2658 }
2659 if let Some((repl, consumed)) = try_sync_deep(v, i) {
2660 let _ = v.drain(i..i + consumed);
2661 let n = repl.len();
2662 v.splice(i..i, repl);
2663 i += n;
2664 continue;
2665 }
2666 if let Some((repl, consumed)) = try_sync_absorb_after(v, i) {
2667 let _ = v.drain(i..i + consumed);
2668 let n = repl.len();
2669 v.splice(i..i, repl);
2670 i += n;
2671 continue;
2672 }
2673 i += 1;
2674 }
2675 });
2676}
2677
2678fn is_pure_read_expr(e: &Expr) -> bool {
2682 let mut pure = true;
2683 visit_exprs(e, &mut |x| match x {
2684 Expr::Const(_)
2685 | Expr::Local { .. }
2686 | Expr::This
2687 | Expr::Field { .. } => {}
2688 _ => pure = false,
2689 });
2690 pure
2691}
2692
2693fn resolve_lock(e: &Expr, defs: &jdc_core::FxHashMap<u32, Expr>, depth: u32) -> Expr {
2698 if depth >= 4 {
2699 return e.clone();
2700 }
2701 if let Expr::Local { var, .. } = e {
2702 if let Some(d) = defs.get(var) {
2703 return resolve_lock(d, defs, depth + 1);
2704 }
2705 }
2706 e.clone()
2707}
2708
2709fn lock_same(a: &Expr, b: &Expr) -> bool {
2710 match (a, b) {
2711 (Expr::This, Expr::This) => true,
2712 (Expr::Local { var: x, .. }, Expr::Local { var: y, .. }) => x == y,
2713 (
2714 Expr::Field {
2715 owner: oa,
2716 cls: ca,
2717 name: na,
2718 ..
2719 },
2720 Expr::Field {
2721 owner: ob,
2722 cls: cb,
2723 name: nb,
2724 ..
2725 },
2726 ) => {
2727 ca == cb
2728 && na == nb
2729 && match (oa, ob) {
2730 (None, None) => true,
2731 (Some(ra), Some(rb)) => lock_same(ra, rb),
2732 _ => false,
2733 }
2734 }
2735 (Expr::Const(x), Expr::Const(y)) => x == y,
2736 _ => false,
2737 }
2738}
2739
2740fn lock_equiv(a: &Expr, b: &Expr, defs: &jdc_core::FxHashMap<u32, Expr>) -> bool {
2741 lock_same(&resolve_lock(a, defs, 0), &resolve_lock(b, defs, 0))
2742}
2743
2744fn flatten_leaves<'a>(s: &'a Stmt, out: &mut Vec<&'a Stmt>) {
2745 match s {
2746 Stmt::Block(v) => {
2747 for x in v {
2748 flatten_leaves(x, out);
2749 }
2750 }
2751 _ => out.push(s),
2752 }
2753}
2754
2755fn strip_lock_exits(s: &mut Stmt, lock: &Expr, defs: &jdc_core::FxHashMap<u32, Expr>) {
2759 walk_mut_deep(s, &mut |st| {
2760 let Stmt::Block(v) = st else { return };
2761 v.retain(|x| !matches!(x, Stmt::MonitorExit(e) if lock_equiv(e, lock, defs)));
2762 });
2763}
2764
2765fn try_sync_at(v: &[Stmt], i: usize) -> Option<(Vec<Stmt>, usize)> {
2766 let mut defs: jdc_core::FxHashMap<u32, Expr> = jdc_core::FxHashMap::default();
2768 for s in &v[..i] {
2769 if let Stmt::LocalDef { var, init: Some(e), .. } = s {
2770 defs.insert(*var, e.clone());
2771 }
2772 }
2773 let (pre, enter_e, mut j, mut absorbed): (Vec<Stmt>, &Expr, usize, Vec<Stmt>) = match &v[i] {
2777 Stmt::MonitorEnter(e) => (Vec::new(), e, i + 1, Vec::new()),
2778 Stmt::Block(b) => {
2779 let split = b
2780 .iter()
2781 .position(|x| matches!(x, Stmt::MonitorEnter(_)))?;
2782 let e = match &b[split] {
2783 Stmt::MonitorEnter(e) => e,
2784 _ => unreachable!(),
2785 };
2786 let mut absorbed: Vec<Stmt> = Vec::new();
2787 let mut k = split + 1;
2788 while let Some(Stmt::LocalDef { var, init: Some(ex), .. }) = b.get(k) {
2789 if !is_pure_read_expr(ex) {
2790 break;
2791 }
2792 defs.insert(*var, ex.clone());
2793 absorbed.push(b[k].clone());
2794 k += 1;
2795 }
2796 if k != b.len() {
2799 return None;
2800 }
2801 let pre: Vec<Stmt> = b[..split].to_vec();
2802 for s in &pre {
2803 if let Stmt::LocalDef { var, init: Some(e2), .. } = s {
2804 defs.insert(*var, e2.clone());
2805 }
2806 }
2807 (pre, e, i + 1, absorbed)
2808 }
2809 _ => return None,
2810 };
2811 let mut absorbed_vars: Vec<u32> = Vec::new();
2815 while let Some(Stmt::LocalDef { var, init: Some(e), .. }) = v.get(j) {
2816 if !is_pure_read_expr(e) {
2817 break;
2818 }
2819 defs.insert(*var, e.clone());
2820 absorbed_vars.push(*var);
2821 absorbed.push(v[j].clone());
2822 j += 1;
2823 }
2824 let Stmt::Try {
2825 body,
2826 catches,
2827 finally,
2828 } = v.get(j)?
2829 else {
2830 return None;
2831 };
2832 if finally.is_some() || catches.len() != 1 {
2833 return None;
2834 }
2835 let c = &catches[0];
2836 if !c.exc.is_empty() {
2837 return None;
2838 }
2839 let mut flat: Vec<&Stmt> = Vec::new();
2842 flatten_leaves(c.body.as_ref(), &mut flat);
2843 if flat.len() < 2 {
2844 return None;
2845 }
2846 let Stmt::MonitorExit(exit_e) = flat[0] else {
2847 return None;
2848 };
2849 if !lock_equiv(enter_e, exit_e, &defs) {
2850 return None;
2851 }
2852 if !matches!(flat[flat.len() - 1], Stmt::Throw(_)) {
2853 return None;
2854 }
2855 for mid in &flat[1..flat.len() - 1] {
2856 if !matches!(mid, Stmt::MonitorEnter(_) | Stmt::MonitorExit(_)) {
2857 return None;
2858 }
2859 }
2860 if !absorbed_vars.is_empty() {
2863 let outside = count_locals_stmts(&v[j + 1..]);
2864 if absorbed_vars
2865 .iter()
2866 .any(|av| outside.get(av).copied().unwrap_or(0) > 0)
2867 {
2868 return None;
2869 }
2870 }
2871 let mut body_stmt = (**body).clone();
2872 strip_lock_exits(&mut body_stmt, enter_e, &defs);
2873 absorbed.push(body_stmt);
2874 let mut consumed = j - i + 1;
2875 if let Some(Stmt::MonitorExit(e)) = v.get(j + 1) {
2878 if lock_equiv(e, enter_e, &defs) {
2879 consumed += 1;
2880 }
2881 }
2882 let mut repl = pre;
2883 repl.push(Stmt::Synchronized {
2884 lock: enter_e.clone(),
2885 body: Box::new(Stmt::Block(absorbed)),
2886 });
2887 Some((repl, consumed))
2888}
2889
2890fn try_sync_in_try(v: &[Stmt], i: usize) -> Option<(Vec<Stmt>, usize)> {
2896 let Stmt::Try {
2897 body,
2898 catches,
2899 finally,
2900 } = &v[i]
2901 else {
2902 return None;
2903 };
2904 if finally.is_some() || catches.len() != 1 {
2905 return None;
2906 }
2907 let c = &catches[0];
2908 if !c.exc.is_empty() {
2909 return None;
2910 }
2911 let mut flat: Vec<&Stmt> = Vec::new();
2912 flatten_leaves(c.body.as_ref(), &mut flat);
2913 if flat.len() < 2 {
2914 return None;
2915 }
2916 let Stmt::MonitorExit(_) = flat[0] else {
2917 return None;
2918 };
2919 if !matches!(flat[flat.len() - 1], Stmt::Throw(_)) {
2920 return None;
2921 }
2922 for mid in &flat[1..flat.len() - 1] {
2923 if !matches!(mid, Stmt::MonitorEnter(_) | Stmt::MonitorExit(_)) {
2924 return None;
2925 }
2926 }
2927 let Stmt::Block(bv) = body.as_ref() else {
2931 return None;
2932 };
2933 let mut defs: jdc_core::FxHashMap<u32, Expr> = jdc_core::FxHashMap::default();
2934 for s in &v[..i] {
2935 if let Stmt::LocalDef { var, init: Some(e), .. } = s {
2936 defs.insert(*var, e.clone());
2937 }
2938 }
2939 let mut pre: Vec<Stmt> = Vec::new();
2940 let mut enter_e: Option<Expr> = None;
2941 let mut absorbed: Vec<Stmt> = Vec::new();
2942 let mut absorbed_vars: Vec<u32> = Vec::new();
2943 let mut rest_start: Option<usize> = None;
2944 for (p, el) in bv.iter().enumerate() {
2945 match el {
2946 Stmt::MonitorEnter(e) => {
2947 enter_e = Some(e.clone());
2948 rest_start = Some(p + 1);
2949 break;
2950 }
2951 Stmt::Block(inner) => {
2952 let split = inner
2953 .iter()
2954 .position(|x| matches!(x, Stmt::MonitorEnter(_)));
2955 if let Some(sp) = split {
2956 let mut k = sp + 1;
2957 let mut ok = true;
2958 let mut tail: Vec<Stmt> = Vec::new();
2959 while let Some(Stmt::LocalDef { var: _, init: Some(ex), .. }) = inner.get(k) {
2960 if !is_pure_read_expr(ex) {
2961 ok = false;
2962 break;
2963 }
2964 tail.push(inner[k].clone());
2965 k += 1;
2966 }
2967 if ok && k == inner.len() {
2968 for s in &inner[..sp] {
2969 if let Stmt::LocalDef { var, init: Some(ex), .. } = s {
2970 defs.insert(*var, ex.clone());
2971 }
2972 }
2973 pre.extend(inner[..sp].iter().cloned());
2974 enter_e = Some(match &inner[sp] {
2975 Stmt::MonitorEnter(e) => e.clone(),
2976 _ => unreachable!(),
2977 });
2978 absorbed = tail;
2979 rest_start = Some(p + 1);
2980 break;
2981 }
2982 }
2983 for s in inner.iter() {
2985 if let Stmt::LocalDef { var, init: Some(ex), .. } = s {
2986 defs.insert(*var, ex.clone());
2987 }
2988 }
2989 pre.push(el.clone());
2990 }
2991 other => {
2992 if let Stmt::LocalDef { var, init: Some(ex), .. } = other {
2993 defs.insert(*var, ex.clone());
2994 }
2995 pre.push(other.clone());
2996 }
2997 }
2998 }
2999 let enter_e = enter_e?;
3000 let rs = rest_start?;
3001 let mut q = rs;
3003 while let Some(Stmt::LocalDef { var, init: Some(ex), .. }) = bv.get(q) {
3004 if !is_pure_read_expr(ex) {
3005 break;
3006 }
3007 defs.insert(*var, ex.clone());
3008 absorbed_vars.push(*var);
3009 absorbed.push(bv[q].clone());
3010 q += 1;
3011 }
3012 let rest: Vec<Stmt> = bv[q..].to_vec();
3013 if rest.is_empty() {
3014 return None;
3015 }
3016 if pre
3019 .iter()
3020 .any(|s| !matches!(s, Stmt::LocalDef { init: Some(e), .. } if is_pure_read_expr(e)))
3021 {
3022 return None;
3023 }
3024 let Stmt::MonitorExit(exit_e) = flat[0] else {
3026 return None;
3027 };
3028 if !lock_equiv(&enter_e, exit_e, &defs) {
3029 return None;
3030 }
3031 let outside_vars: Vec<u32> = pre
3033 .iter()
3034 .chain(absorbed.iter())
3035 .filter_map(|s| match s {
3036 Stmt::LocalDef { var, .. } => Some(*var),
3037 _ => None,
3038 })
3039 .collect();
3040 if !outside_vars.is_empty() {
3041 let outside = count_locals_stmts(&v[i + 1..]);
3042 if outside_vars
3043 .iter()
3044 .any(|av| outside.get(av).copied().unwrap_or(0) > 0)
3045 {
3046 return None;
3047 }
3048 }
3049 let mut body_stmts = pre;
3050 body_stmts.extend(absorbed);
3051 body_stmts.extend(rest);
3052 let mut sync_body = Stmt::Block(body_stmts);
3053 strip_lock_exits(&mut sync_body, &enter_e, &defs);
3054 let mut consumed = 1;
3055 if let Some(Stmt::MonitorExit(e)) = v.get(i + 1) {
3056 if lock_equiv(e, &enter_e, &defs) {
3057 consumed += 1;
3058 }
3059 }
3060 Some((
3061 vec![Stmt::Synchronized {
3062 lock: enter_e,
3063 body: Box::new(sync_body),
3064 }],
3065 consumed,
3066 ))
3067}
3068
3069fn try_sync_absorb_after(v: &[Stmt], i: usize) -> Option<(Vec<Stmt>, usize)> {
3079 let Stmt::Synchronized { lock, body } = &v[i] else {
3080 return None;
3081 };
3082 let Stmt::Try {
3083 body: tbody,
3084 catches,
3085 finally,
3086 } = v.get(i + 1)?
3087 else {
3088 return None;
3089 };
3090 if finally.is_some() {
3091 return None;
3092 }
3093 let defs = defs_probe(v, i, body);
3099 let mut absorbed_mid: Vec<Stmt> = Vec::new();
3100 let mut rest_after: Option<Stmt> = None;
3101 let mut found = false;
3102 let mut scan = i + 2;
3103 'scan: while scan < v.len() && scan < i + 14 {
3104 match &v[scan] {
3105 Stmt::MonitorExit(e) => {
3106 if !lock_equiv(e, lock, &defs) {
3107 return None;
3108 }
3109 found = true;
3110 scan += 1;
3111 break;
3112 }
3113 Stmt::Block(bv) => {
3114 for (k, s) in bv.iter().enumerate() {
3115 match s {
3116 Stmt::MonitorExit(e) => {
3117 if !lock_equiv(e, lock, &defs) {
3118 return None;
3119 }
3120 absorbed_mid.push(Stmt::Block(bv[..k].to_vec()));
3121 let rest = &bv[k + 1..];
3122 rest_after = (!rest.is_empty())
3123 .then(|| Stmt::Block(rest.to_vec()));
3124 found = true;
3125 scan += 1;
3126 break 'scan;
3127 }
3128 Stmt::ExprStmt(e) if mid_safe(e) || is_local_commit(e) => {}
3129 _ => break 'scan,
3130 }
3131 }
3132 absorbed_mid.push(v[scan].clone());
3133 scan += 1;
3134 }
3135 Stmt::ExprStmt(e) if mid_safe(e) || is_local_commit(e) => {
3136 absorbed_mid.push(v[scan].clone());
3137 scan += 1;
3138 }
3139 _ => break,
3140 }
3141 }
3142 if !found {
3143 return None;
3144 }
3145 let consumed = scan - i;
3146 let mut t = (**tbody).clone();
3149 let defs = defs_probe(v, i, body);
3150 strip_lock_exits(&mut t, lock, &defs);
3151 let mut cs = Vec::with_capacity(catches.len());
3152 for c in catches.iter() {
3153 let mut cb = (*c.body).clone();
3154 strip_lock_exits(&mut cb, lock, &defs);
3155 let bare_rethrow = c.exc.is_empty() && {
3159 let mut leaves: Vec<&Stmt> = Vec::new();
3160 flatten_leaves(&cb, &mut leaves);
3161 matches!(leaves.as_slice(), [Stmt::Throw(Expr::Local { var, .. })] if *var == c.var)
3162 };
3163 if bare_rethrow {
3164 continue;
3165 }
3166 let mut c2 = c.clone();
3167 *c2.body = cb;
3168 cs.push(c2);
3169 }
3170 let mut new_body = (**body).clone();
3171 let adopted = Stmt::Try {
3172 body: Box::new(t),
3173 catches: cs,
3174 finally: None,
3175 };
3176 match &mut new_body {
3177 Stmt::Block(bv) => bv.push(adopted),
3178 other => {
3179 let inner = std::mem::replace(other, Stmt::Block(vec![]));
3180 *other = Stmt::Block(vec![inner, adopted]);
3181 }
3182 }
3183 if let Stmt::Block(bv) = &mut new_body {
3184 bv.extend(absorbed_mid);
3185 }
3186 let sync = Stmt::Synchronized {
3187 lock: lock.clone(),
3188 body: Box::new(new_body),
3189 };
3190 let mut repl = vec![sync];
3191 if let Some(r) = rest_after {
3192 repl.push(r);
3193 }
3194 Some((repl, consumed))
3195}
3196
3197fn mid_safe(e: &Expr) -> bool {
3201 !has_side_effects(e)
3202}
3203
3204fn is_local_commit(e: &Expr) -> bool {
3206 match e {
3207 Expr::Assign { target, op: AssignOp::Plain, .. } => {
3208 matches!(&**target, Expr::Local { .. })
3209 }
3210 _ => false,
3211 }
3212}
3213
3214fn defs_probe<'a>(
3217 v: &'a [Stmt],
3218 i: usize,
3219 body: &'a Stmt,
3220) -> jdc_core::FxHashMap<u32, Expr> {
3221 let mut defs: jdc_core::FxHashMap<u32, Expr> = jdc_core::FxHashMap::default();
3222 for s in &v[..i] {
3223 if let Stmt::LocalDef { var, init: Some(e), .. } = s {
3224 defs.insert(*var, e.clone());
3225 }
3226 }
3227 if let Stmt::Block(bv) = body {
3228 for s in bv {
3229 if let Stmt::LocalDef { var, init: Some(e), .. } = s {
3230 defs.insert(*var, e.clone());
3231 }
3232 }
3233 }
3234 defs
3235}
3236
3237
3238fn try_sync_deep(v: &[Stmt], i: usize) -> Option<(Vec<Stmt>, usize)> {
3247 let Stmt::Try {
3248 body,
3249 catches,
3250 finally,
3251 } = &v[i]
3252 else {
3253 return None;
3254 };
3255 if finally.is_some() || catches.len() != 1 {
3256 return None;
3257 }
3258 let c = &catches[0];
3259 if !c.exc.is_empty() {
3260 return None;
3261 }
3262 let mut flat: Vec<&Stmt> = Vec::new();
3263 flatten_leaves(c.body.as_ref(), &mut flat);
3264 if flat.len() < 2 {
3265 return None;
3266 }
3267 let catch_exit = match flat[0] {
3268 Stmt::MonitorExit(e) => e.clone(),
3269 _ => return None,
3270 };
3271 if !matches!(flat[flat.len() - 1], Stmt::Throw(_)) {
3272 return None;
3273 }
3274 for mid in &flat[1..flat.len() - 1] {
3275 if !matches!(mid, Stmt::MonitorEnter(_) | Stmt::MonitorExit(_)) {
3276 return None;
3277 }
3278 }
3279 let mut defs0: jdc_core::FxHashMap<u32, Expr> = jdc_core::FxHashMap::default();
3280 for s in &v[..i] {
3281 if let Stmt::LocalDef { var, init: Some(e), .. } = s {
3282 defs0.insert(*var, e.clone());
3283 }
3284 }
3285 let mut body_mut = (**body).clone();
3286 let folded = match &mut body_mut {
3287 Stmt::Block(b) => try_fold_spine(b, &catch_exit, &defs0),
3288 Stmt::If {
3289 then_stmt,
3290 else_stmt,
3291 ..
3292 } => {
3293 let mut hit = false;
3294 if let Stmt::Block(b) = &mut **then_stmt {
3295 hit |= try_fold_spine(b, &catch_exit, &defs0);
3296 }
3297 if !hit {
3298 if let Some(els) = else_stmt {
3299 if let Stmt::Block(b) = &mut **els {
3300 hit |= try_fold_spine(b, &catch_exit, &defs0);
3301 }
3302 }
3303 }
3304 hit
3305 }
3306 _ => false,
3307 };
3308 if !folded {
3309 return None;
3310 }
3311 let throw = flat[flat.len() - 1].clone();
3313 let mut catches_out = catches.clone();
3314 *catches_out[0].body = Stmt::Block(vec![throw]);
3315 let mut consumed = 1;
3316 if let Some(Stmt::MonitorExit(e)) = v.get(i + 1) {
3317 if lock_equiv(e, &catch_exit, &defs0) {
3318 consumed += 1;
3319 }
3320 }
3321 Some((
3322 vec![Stmt::Try {
3323 body: Box::new(body_mut),
3324 catches: catches_out,
3325 finally: None,
3326 }],
3327 consumed,
3328 ))
3329}
3330
3331fn try_fold_spine(
3337 seq: &mut Vec<Stmt>,
3338 catch_exit: &Expr,
3339 defs_in: &jdc_core::FxHashMap<u32, Expr>,
3340) -> bool {
3341 for p in (0..seq.len()).rev() {
3342 if let Stmt::MonitorEnter(e0) = &seq[p] {
3344 let mut defs = defs_in.clone();
3345 for s in &seq[..p] {
3346 if let Stmt::LocalDef { var, init: Some(ex), .. } = s {
3347 defs.insert(*var, ex.clone());
3348 }
3349 }
3350 for s in &seq[p + 1..] {
3353 match s {
3354 Stmt::LocalDef { var, init: Some(ex), .. } if is_pure_read_expr(ex) => {
3355 defs.insert(*var, ex.clone());
3356 }
3357 _ => break,
3358 }
3359 }
3360 if !lock_equiv(e0, catch_exit, &defs) {
3361 return false;
3362 }
3363 let enter_e = e0.clone();
3364 let rest: Vec<Stmt> = seq.split_off(p + 1);
3365 let mut body = Stmt::Block(rest);
3366 strip_lock_exits(&mut body, &enter_e, &defs);
3367 seq[p] = Stmt::Synchronized {
3368 lock: enter_e,
3369 body: Box::new(body),
3370 };
3371 return true;
3372 }
3373 let is_last = p + 1 == seq.len();
3376 let mut trial = seq[p].clone();
3377 let hit = match &mut trial {
3378 Stmt::Block(b) => try_fold_spine(b, catch_exit, defs_in),
3379 Stmt::If {
3380 then_stmt,
3381 else_stmt,
3382 ..
3383 } => {
3384 let mut hit = false;
3385 if let Stmt::Block(b) = &mut **then_stmt {
3386 hit |= try_fold_spine(b, catch_exit, defs_in);
3387 }
3388 if !hit {
3389 if let Some(els) = else_stmt {
3390 if let Stmt::Block(b) = &mut **els {
3391 hit |= try_fold_spine(b, catch_exit, defs_in);
3392 }
3393 }
3394 }
3395 hit
3396 }
3397 _ => false,
3398 };
3399 if hit {
3400 if is_last {
3401 seq[p] = trial;
3402 return true;
3403 }
3404 return false;
3405 }
3406 }
3407 false
3408}
3409
3410pub fn forward_single_use(s: &mut Stmt, _vt: &VarTable) {
3415 let mut analysis = VarAnalysis {
3419 assigns: Vec::new(),
3420 reads: Vec::new(),
3421 values: Vec::new(),
3422 };
3423 analyze_vars(s, &mut analysis);
3424 let mut assigns = analysis.assigns;
3425 let mut reads = analysis.reads;
3426 let n_vars = assigns.len().max(_vt.vars.len()).max(1);
3429 assigns.resize(n_vars, 0);
3430 reads.resize(n_vars, 0);
3431 let mut values = analysis.values;
3432 values.resize(n_vars, None);
3433
3434 let mut cand = vec![false; n_vars];
3439 let mut edges: Vec<Vec<u32>> = vec![Vec::new(); n_vars];
3440 for (v, cand_v) in cand.iter_mut().enumerate() {
3441 if assigns.get(v).copied().unwrap_or(0) != 1 || reads.get(v).copied().unwrap_or(0) != 1 {
3442 continue;
3443 }
3444 if let Some(val) = values.get(v).and_then(|o| o.as_ref()) {
3445 let mut refs = HashSet::default();
3446 collect_vars(val, &mut refs);
3447 if refs.iter().any(|r| assigns[*r as usize] > 1) {
3448 continue;
3449 }
3450 edges[v] = refs.into_iter().collect();
3454 }
3455 *cand_v = true;
3456 }
3457
3458 let mut color = vec![0u8; n_vars]; let mut bad = vec![false; n_vars];
3476 for root in 0..n_vars {
3477 if color[root] != 0 || edges[root].is_empty() {
3478 continue;
3479 }
3480 color[root] = 1;
3481 let mut stack: Vec<(usize, usize)> = vec![(root, 0)];
3482 while let Some(top) = stack.last_mut() {
3483 let v = top.0;
3484 let i = top.1;
3485 top.1 += 1;
3486 if i < edges[v].len() {
3487 let r = edges[v][i] as usize;
3488 if color[r] == 1 {
3489 for &(pn, _) in &stack {
3490 bad[pn] = true;
3491 }
3492 } else if color[r] == 0 {
3493 color[r] = 1;
3494 stack.push((r, 0));
3495 } else if bad[r] {
3496 for &(pn, _) in &stack {
3497 bad[pn] = true;
3498 }
3499 }
3500 } else {
3501 color[v] = 2;
3502 stack.pop();
3503 }
3504 }
3505 }
3506 let single: Vec<bool> = (0..n_vars).map(|v| cand[v] && !bad[v]).collect();
3507 if !single.iter().any(|&b| b) {
3508 return;
3509 }
3510 let mut pure: Vec<bool> = (0..n_vars)
3511 .map(|v| {
3512 single[v]
3513 && values
3514 .get(v)
3515 .and_then(|o| o.as_ref())
3516 .map(|e| !has_side_effects(e))
3517 .unwrap_or(false)
3518 })
3519 .collect();
3520 let mut impure: Vec<bool> = (0..n_vars).map(|v| single[v] && !pure[v]).collect();
3521
3522 {
3535 type FieldKey = (std::sync::Arc<str>, std::sync::Arc<str>, bool);
3536 let mut field_writes: jdc_core::FxHashMap<FieldKey, Vec<usize>> =
3537 jdc_core::FxHashMap::default();
3538 let mut def_pos: Vec<usize> = vec![usize::MAX; n_vars];
3539 let mut use_pos: Vec<usize> = vec![usize::MAX; n_vars];
3540 let mut pos = 0usize;
3541 walk_all(s, &mut |st| {
3542 let here = pos;
3543 pos += 1;
3544 for e in stmt_read_exprs(st) {
3545 visit_exprs(e, &mut |x| {
3546 if let Expr::Local { var, .. } = x {
3547 let v = *var as usize;
3548 if v < use_pos.len() && use_pos[v] == usize::MAX {
3549 use_pos[v] = here;
3550 }
3551 }
3552 });
3553 }
3554 match st {
3555 Stmt::LocalDef { var, .. } => {
3556 let v = *var as usize;
3557 if v < def_pos.len() {
3558 def_pos[v] = here;
3559 }
3560 }
3561 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
3562 Expr::Local { var, .. } => {
3563 let v = *var as usize;
3564 if v < def_pos.len() {
3565 def_pos[v] = here;
3566 }
3567 }
3568 Expr::Field { cls, name, is_static, .. } => {
3569 field_writes
3570 .entry((cls.clone(), name.clone(), *is_static))
3571 .or_default()
3572 .push(here);
3573 }
3574 _ => {}
3575 },
3576 Stmt::ExprStmt(Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. }) => {
3577 if let Expr::Field { cls, name, is_static, .. } = &**e {
3578 field_writes
3579 .entry((cls.clone(), name.clone(), *is_static))
3580 .or_default()
3581 .push(here);
3582 }
3583 }
3584 _ => {}
3585 }
3586 });
3587 let mut hazard = vec![false; n_vars];
3588 for v in 0..n_vars {
3589 if !pure[v] {
3590 continue;
3591 }
3592 let Some(Some(val)) = values.get(v).map(|o| o.as_ref()) else {
3593 continue;
3594 };
3595 let (d, u) = (def_pos[v], use_pos[v]);
3596 if d == usize::MAX || u == usize::MAX || u <= d {
3597 continue;
3598 }
3599 visit_exprs(val, &mut |x| {
3600 if let Expr::Field { cls, name, is_static, .. } = x {
3601 if let Some(ws) = field_writes.get(&(cls.clone(), name.clone(), *is_static)) {
3602 if ws.iter().any(|w| *w > d && *w < u) {
3603 hazard[v] = true;
3604 }
3605 }
3606 }
3607 });
3608 }
3609 for v in 0..n_vars {
3610 if hazard[v] {
3611 pure[v] = false;
3612 impure[v] = single[v];
3613 }
3614 }
3615 }
3616
3617 if pure.iter().any(|&b| b) {
3619 let vals: Vec<Option<Expr>> = (0..n_vars)
3620 .map(|v| {
3621 if pure[v] {
3622 values.get(v).cloned().flatten()
3623 } else {
3624 None
3625 }
3626 })
3627 .collect();
3628 rewrite_exprs(s, &mut |e| {
3629 deep_rewrite_reads(e, &mut |x| {
3630 if let Expr::Local { var, .. } = x {
3631 if let Some(Some(v)) = vals.get(*var as usize) {
3632 *x = v.clone();
3633 }
3634 }
3635 });
3636 });
3637 drop_defs(s, &pure);
3638 }
3639
3640 if impure.iter().any(|&b| b) {
3643 forward_adjacent_impure(s, &values, &impure);
3644 }
3645}
3646
3647fn stmt_read_exprs(st: &Stmt) -> Vec<&Expr> {
3653 match st {
3654 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
3655 let mut v: Vec<&Expr> = vec![value];
3656 if !matches!(&**target, Expr::Local { .. }) {
3657 v.push(target);
3658 }
3659 v
3660 }
3661 Stmt::ExprStmt(e @ (Expr::PreIncDec { .. } | Expr::PostIncDec { .. })) => {
3664 let inner = match e {
3665 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => e,
3666 _ => unreachable!(),
3667 };
3668 if matches!(**inner, Expr::Local { .. }) {
3669 Vec::new()
3670 } else {
3671 vec![e]
3672 }
3673 }
3674 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
3675 vec![e]
3676 }
3677 Stmt::Return(Some(e)) => vec![e],
3678 Stmt::LocalDef { init: Some(e), .. } => vec![e],
3679 Stmt::If { cond, .. } => vec![cond],
3680 Stmt::While { cond, .. } => vec![cond],
3681 Stmt::DoWhile { cond, .. } => vec![cond],
3682 Stmt::Switch { selector, .. } => vec![selector],
3688 Stmt::ForEach { iterable, .. } => vec![iterable],
3689 Stmt::Synchronized { lock, .. } => vec![lock],
3690 Stmt::For { cond, update, .. } => {
3691 let mut v: Vec<&Expr> = Vec::with_capacity(update.len() + 1);
3692 if let Some(c) = cond {
3693 v.push(c);
3694 }
3695 v.extend(update.iter());
3696 v
3697 }
3698 Stmt::Assert { cond, msg } => {
3699 let mut v: Vec<&Expr> = vec![cond];
3700 if let Some(m) = msg {
3701 v.push(m);
3702 }
3703 v
3704 }
3705 _ => Vec::new(),
3706 }
3707}
3708
3709fn count_reads(s: &Stmt, out: &mut Vec<usize>) {
3711 walk_all(s, &mut |st| {
3712 for e in stmt_read_exprs(st) {
3713 visit_exprs(e, &mut |x| {
3714 if let Expr::Local { var, .. } = x {
3715 grow_to(out, *var);
3716 out[*var as usize] += 1;
3717 }
3718 });
3719 }
3720 });
3721}
3722
3723
3724fn drop_defs(s: &mut Stmt, vars: &[bool]) {
3725 walk_mut_deep(s, &mut |st| {
3726 if let Stmt::Block(v) = st {
3727 v.retain(|x| match x {
3728 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
3729 Expr::Local { var, .. } => !vars.get(*var as usize).copied().unwrap_or(false),
3730 _ => true,
3731 },
3732 Stmt::LocalDef { var, .. } => !vars.get(*var as usize).copied().unwrap_or(false),
3733 _ => true,
3734 });
3735 }
3736 });
3737}
3738
3739fn forward_adjacent_impure(s: &mut Stmt, values: &[Option<Expr>], impure: &[bool]) {
3741 if let Stmt::Block(v) = s {
3742 let mut i = 0;
3743 while i + 1 < v.len() {
3744 let def_var = match &v[i] {
3745 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
3746 Expr::Local { var, .. } => Some(*var),
3747 _ => None,
3748 },
3749 Stmt::LocalDef { var, .. } => Some(*var),
3750 _ => None,
3751 };
3752 if let Some(var) = def_var {
3753 if impure.get(var as usize).copied().unwrap_or(false) {
3754 let mut reads_here = vec![0usize; values.len().max(1)];
3756 count_reads(&v[i + 1], &mut reads_here);
3757 if reads_here.get(var as usize).copied().unwrap_or(0) == 1 {
3758 let val = match &v[i] {
3762 Stmt::ExprStmt(Expr::Assign { value, .. }) => (**value).clone(),
3763 Stmt::LocalDef { init: Some(e), .. } => (*e).clone(),
3764 _ => {
3765 i += 1;
3766 continue;
3767 }
3768 };
3769 rewrite_exprs(&mut v[i + 1], &mut |e| {
3770 deep_rewrite_reads(e, &mut |x| {
3771 if let Expr::Local { var: v2, .. } = x {
3772 if *v2 == var {
3773 *x = val.clone();
3774 }
3775 }
3776 });
3777 });
3778 v.remove(i);
3779 i = i.saturating_sub(1);
3782 continue;
3783 }
3784 }
3785 }
3786 i += 1;
3787 }
3788 }
3789 walk_mut(s, &mut |x| forward_adjacent_impure(x, values, impure));
3790}
3791
3792pub fn infer_types(vt: &mut VarTable, body: &mut Stmt, ret: &JavaType, env: &MethodEnv) {
3798 let n = vt.vars.len();
3799 let mut evidence: Vec<Vec<(JavaType, bool)>> = vec![Vec::new(); n];
3807 let ev = |evidence: &mut Vec<Vec<(JavaType, bool)>>, var: u32, t: JavaType, strong: bool| {
3808 if (var as usize) < evidence.len() {
3809 evidence[var as usize].push((t, strong));
3810 }
3811 };
3812
3813 walk_all(body, &mut |st| match st {
3814 Stmt::LocalDef { var, init, .. } => {
3815 if let Some(e) = init {
3816 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
3817 let strong = match e {
3825 Expr::Const(_) => false,
3826 Expr::Local { var: src, .. } => {
3827 let src_ty = vt.vars.get(*src as usize).map(|v| v.ty.erased());
3828 let tgt_ty = vt.vars.get(*var as usize).map(|v| v.ty.erased());
3829 match (src_ty, tgt_ty) {
3830 (Some(s), Some(g)) => {
3831 (s.is_numeric() && g.is_numeric())
3832 || (s.is_reference() && g.is_reference())
3833 }
3834 _ => false,
3835 }
3836 }
3837 _ => true,
3838 };
3839 ev(&mut evidence, *var, e.type_ref().erased(), strong);
3840 }
3841 }
3842 Stmt::ExprStmt(e) => {
3843 if let Expr::Assign { target, value, .. } = e {
3850 if let Expr::Field { owner: Some(o), cls, is_static: false, .. } = &**target {
3857 if cls.is_empty() {
3858 } else if let Expr::Local { var, .. } = &**o {
3860 ev(
3861 &mut evidence,
3862 *var,
3863 JavaType::Object(cls.clone()),
3864 true,
3865 );
3866 }
3867 }
3868 if let Expr::Local { var, .. } = &**target {
3869 let strong = match &**value {
3870 Expr::Const(_) => false,
3871 Expr::Local { var: src, .. } => {
3872 let src_ty = vt.vars.get(*src as usize).map(|v| v.ty.erased());
3879 let tgt_ty = vt.vars.get(*var as usize).map(|v| v.ty.erased());
3880 match (src_ty, tgt_ty) {
3881 (Some(s), Some(g)) => {
3882 (s.is_numeric() && g.is_numeric())
3883 || (s.is_reference() && g.is_reference())
3884 }
3885 _ => false,
3886 }
3887 }
3888 _ => true,
3889 };
3890 ev(&mut evidence, *var, value.type_ref().erased(), strong);
3891 }
3892 }
3893 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
3894 }
3895 Stmt::Throw(e) => {
3896 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
3897 if let Expr::Local { var, .. } = e {
3898 ev(
3899 &mut evidence,
3900 *var,
3901 JavaType::Object("java/lang/Throwable".into()),
3902 true,
3903 );
3904 }
3905 }
3906 Stmt::Return(Some(e)) => {
3907 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
3908 if let Expr::Local { var, .. } = e {
3909 ev(&mut evidence, *var, ret.clone(), true);
3910 }
3911 }
3912 Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
3913 if let Expr::Local { var, .. } = e {
3914 ev(
3915 &mut evidence,
3916 *var,
3917 JavaType::Object("java/lang/Object".into()),
3918 true,
3919 );
3920 }
3921 }
3922 Stmt::ForEach {
3923 var,
3924 iterable,
3925 is_array,
3926 ..
3927 } => {
3928 if let Expr::Local { var: v0, .. } = iterable {
3929 if *is_array {
3930 ev(&mut evidence, *v0, JavaType::Array(Box::new(JavaType::Int)), true);
3931 } else {
3932 ev(
3933 &mut evidence,
3934 *v0,
3935 JavaType::Object("java/lang/Object".into()),
3936 true,
3937 );
3938 }
3939 }
3940 let _ = var;
3941 }
3942 _ => {}
3943 });
3944 let _ = env;
3945
3946 let mut derefed: Vec<bool> = vec![false; vt.vars.len()];
3956 mark_derefs(body, &mut derefed);
3957 let mut arr_derefed: Vec<bool> = vec![false; vt.vars.len()];
3962 visit_all_exprs(body, &mut |x| {
3963 if let Expr::ArrayIndex { array, .. } = x {
3964 if let Expr::Local { var, .. } = &**array {
3965 if (*var as usize) < arr_derefed.len() {
3966 arr_derefed[*var as usize] = true;
3967 }
3968 }
3969 }
3970 if let Expr::Bin { op, l, r, .. } = x {
3978 if matches!(op, BinOp::Eq | BinOp::Ne)
3979 && (matches!(&**r, Expr::Const(ConstVal::Null))
3980 || matches!(&**l, Expr::Const(ConstVal::Null)))
3981 {
3982 for side in [l, r] {
3983 if let Expr::Local { var, .. } = &**side {
3984 ev(
3985 &mut evidence,
3986 *var,
3987 JavaType::Object("java/lang/Object".into()),
3988 true,
3989 );
3990 }
3991 }
3992 }
3993 }
3994 });
3995 const FINAL_JDK: &[&str] = &[
3996 "java/lang/Boolean",
3997 "java/lang/String",
3998 "java/lang/Integer",
3999 "java/lang/Long",
4000 "java/lang/Short",
4001 "java/lang/Byte",
4002 "java/lang/Character",
4003 "java/lang/Float",
4004 "java/lang/Double",
4005 ];
4006 for (i, evs) in evidence.iter().enumerate() {
4007 let info = &vt.vars[i];
4008 if info.is_param {
4009 continue;
4010 }
4011 let strong: Vec<JavaType> = evs
4012 .iter()
4013 .filter(|(_, s)| *s)
4014 .map(|(t, _)| t.clone())
4015 .collect();
4016 let all: Vec<JavaType> = evs.iter().map(|(t, _)| t.clone()).collect();
4017 if info.ty.erased().is_reference() {
4018 if strong.is_empty() {
4024 let refs: std::collections::HashSet<&str> = all
4025 .iter()
4026 .filter_map(|t| match t {
4027 JavaType::Object(n) if n.as_ref() != "java/lang/Object" => {
4028 Some(n.as_ref())
4029 }
4030 _ => None,
4031 })
4032 .collect();
4033 let mixed = all.iter().any(|t| t.is_numeric());
4034 if refs.len() >= 2 || (mixed && !refs.is_empty()) || (mixed && all.iter().any(|t| matches!(t, JavaType::Array(_)))) {
4035 vt.vars[i].ty =
4036 TypeRef::J(JavaType::Object("java/lang/Object".into()));
4037 continue;
4038 }
4039 }
4040 if arr_derefed[i] {
4043 let arr = strong
4044 .iter()
4045 .chain(all.iter())
4046 .find(|t| matches!(t, JavaType::Array(_)))
4047 .cloned();
4048 if let Some(t) = arr {
4049 vt.vars[i].ty = TypeRef::J(t);
4050 continue;
4051 }
4052 }
4053 let deref_pick = || {
4057 if !derefed[i] {
4058 return None;
4059 }
4060 strong
4061 .iter()
4062 .find(|t| {
4063 matches!(t, JavaType::Object(n)
4064 if !n.is_empty()
4065 && n.as_ref() != "java/lang/Object"
4066 && !FINAL_JDK.contains(&n.as_ref()))
4067 })
4068 .cloned()
4069 };
4070 if let Some(t) = deref_pick()
4071 .or_else(|| pick_object(&strong))
4072 .or_else(|| pick_object(&all))
4073 {
4074 vt.vars[i].ty = TypeRef::J(t);
4075 }
4076 } else if let Some(t) = pick_object(&strong) {
4077 vt.vars[i].ty = TypeRef::J(t);
4081 } else if strong.contains(&JavaType::Boolean) {
4082 vt.vars[i].ty = TypeRef::J(JavaType::Boolean);
4086 } else if !all.is_empty() && all.iter().all(|t| t.is_numeric()) {
4087 if let Some(t) = pick_numeric(&all) {
4088 vt.vars[i].ty = TypeRef::J(t);
4089 }
4090 } else {
4091 let refs = all.iter().filter(|t| t.is_reference()).count();
4095 let nums = all.iter().filter(|t| t.is_numeric()).count();
4096 if refs > 0 && nums > 0 {
4097 vt.vars[i].ty =
4098 TypeRef::J(JavaType::Object("java/lang/Object".into()));
4099 } else if let Some(t) = pick_object(&strong).or_else(|| pick_object(&all)) {
4100 vt.vars[i].ty = TypeRef::J(t);
4101 }
4102 }
4103 }
4104
4105 let types: Vec<&TypeRef> = vt.vars.iter().map(|v| &v.ty).collect();
4110 rewrite_exprs(body, &mut |e| {
4111 deep_rewrite(e, &mut |x| {
4112 if let Expr::Local { var, ty } = x {
4113 if let Some(want) = types.get(*var as usize) {
4114 if ty != *want {
4115 *ty = (*want).clone();
4116 }
4117 }
4118 }
4119 });
4120 });
4121
4122 coerce_num_consts(body, &types);
4125}
4126
4127fn coerce_num_consts(body: &mut Stmt, types: &[&TypeRef]) {
4128 walk_mut_deep(body, &mut |st| {
4129 let (var, val): (u32, &mut Expr) = match st {
4130 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &mut **target {
4131 Expr::Local { var, .. } => (*var, value),
4132 _ => return,
4133 },
4134 Stmt::LocalDef {
4135 var, init: Some(e), ..
4136 } => (*var, e),
4137 _ => return,
4138 };
4139 let want = match types.get(var as usize) {
4140 Some(TypeRef::J(t)) => t.clone(),
4141 _ => return,
4142 };
4143 let rewritten = match &*val {
4144 Expr::Const(ConstVal::Long(l)) => match &want {
4145 JavaType::Double => Some(Expr::Const(ConstVal::Double(*l as f64))),
4146 JavaType::Float => Some(Expr::Const(ConstVal::Float(*l as f32))),
4147 _ => None,
4148 },
4149 Expr::Const(ConstVal::Int(i)) => match &want {
4150 JavaType::Double => Some(Expr::Const(ConstVal::Double(*i as f64))),
4151 JavaType::Float => Some(Expr::Const(ConstVal::Float(*i as f32))),
4152 _ => None,
4153 },
4154 _ => None,
4155 };
4156 if let Some(e) = rewritten {
4157 *val = e;
4158 }
4159 });
4160}
4161
4162fn pick_object(evs: &[JavaType]) -> Option<JavaType> {
4163 evs.iter()
4164 .find(|t| {
4165 t.is_reference()
4166 && !matches!(t, JavaType::Object(n) if n.is_empty() || n.as_ref() == "java/lang/Object")
4167 })
4168 .cloned()
4169}
4170
4171fn pick_numeric(evs: &[JavaType]) -> Option<JavaType> {
4172 if evs.is_empty() {
4173 return None;
4174 }
4175 let mut t = evs[0].clone();
4176 for x in &evs[1..] {
4177 t = join_numeric(&t, x);
4178 }
4179 Some(t)
4180}
4181
4182fn join_numeric(a: &JavaType, b: &JavaType) -> JavaType {
4183 if a == b {
4184 return a.clone();
4185 }
4186 match (a, b) {
4187 (JavaType::Double, _) | (_, JavaType::Double) => JavaType::Double,
4188 (JavaType::Float, _) | (_, JavaType::Float) => JavaType::Float,
4189 (JavaType::Long, _) | (_, JavaType::Long) => JavaType::Long,
4190 (JavaType::Int, x) if x.is_integral() => JavaType::Int,
4191 (x, JavaType::Int) if x.is_integral() => JavaType::Int,
4192 (x, y) if x.is_integral() && y.is_integral() => JavaType::Int,
4193 _ => JavaType::Int,
4194 }
4195}
4196
4197fn expr_evidence<F: FnMut(u32, JavaType)>(e: &Expr, f: &mut F) {
4199 visit_exprs(e, &mut |x| match x {
4200 Expr::Method {
4201 owner,
4202 args,
4203 desc,
4204 is_static,
4205 cls,
4206 ..
4207 } => {
4208 if !*is_static {
4209 if let Some(o) = owner {
4210 if let Expr::Local { var, .. } = &**o {
4211 f(*var, JavaType::Object(cls.clone()));
4212 }
4213 }
4214 }
4215 for (i, a) in args.iter().enumerate() {
4216 if let Expr::Local { var, .. } = a {
4217 if let Some(t) = desc.args.get(i) {
4218 f(*var, t.clone());
4219 }
4220 }
4221 }
4222 }
4223 Expr::Field {
4224 owner,
4225 cls,
4226 ty,
4227 is_static,
4228 ..
4229 } => {
4230 if !*is_static && !cls.is_empty() {
4231 if let Some(o) = owner {
4232 if let Expr::Local { var, .. } = &**o {
4233 f(*var, JavaType::Object(cls.clone()));
4243 }
4244 }
4245 }
4246 let _ = ty;
4247 }
4248 Expr::Bin { op, l, r, .. }
4253 if matches!(op, BinOp::Eq | BinOp::Ne)
4254 && (matches!(&**r, Expr::Const(ConstVal::Null))
4255 || matches!(&**l, Expr::Const(ConstVal::Null))) =>
4256 {
4257 for side in [l, r] {
4258 if let Expr::Local { var, .. } = &**side {
4259 f(*var, JavaType::Object("java/lang/Object".into()));
4260 }
4261 }
4262 }
4263 Expr::ArrayIndex { array, .. } => {
4264 if let Expr::Local { var, ty, .. } = &**array {
4265 let t = ty.erased();
4270 if matches!(t, JavaType::Array(_)) {
4271 f(*var, t);
4272 } else {
4273 f(*var, JavaType::Array(Box::new(JavaType::Int)));
4274 }
4275 }
4276 }
4277 Expr::Assign { .. } => {
4278 }
4281 Expr::Cast { e: inner, .. } => {
4282 let _ = inner;
4288 }
4289 Expr::InstanceOf { e: inner, .. } => {
4290 let _ = inner;
4291 }
4292 _ => {}
4293 });
4294}
4295
4296pub fn mark_field_owner_concrete(vt: &mut VarTable, body: &mut Stmt) {
4308 let mut owners: HashSet<u32> = HashSet::default();
4309 rewrite_exprs(body, &mut |e| {
4310 visit_exprs(e, &mut |x| {
4311 if let Expr::Field {
4312 owner: Some(o),
4313 is_static: false,
4314 ..
4315 } = x
4316 {
4317 if let Expr::Local { var, .. } = &**o {
4318 owners.insert(*var);
4319 }
4320 }
4321 });
4322 });
4323 for var in owners {
4324 let i = var as usize;
4325 if i >= vt.vars.len() {
4326 continue;
4327 }
4328 if let JavaType::Object(cls) = vt.vars[i].ty.erased() {
4329 if let Some(last) = cls.rsplit('$').next() {
4330 if !last.is_empty() && last.chars().all(|c| c.is_ascii_digit()) {
4331 vt.force_concrete_vars.insert(var);
4332 }
4333 }
4334 }
4335 }
4336 let concrete: Vec<u32> = vt
4345 .vars
4346 .iter()
4347 .filter(|v| {
4348 if let JavaType::Object(cls) = v.ty.erased() {
4349 cls.rsplit('$')
4350 .next()
4351 .is_some_and(|l| !l.is_empty() && l.chars().all(|c| c.is_ascii_digit()))
4352 } else {
4353 false
4354 }
4355 })
4356 .map(|v| v.id)
4357 .collect();
4358 for var in concrete {
4359 vt.force_concrete_vars.insert(var);
4360 }
4361}
4362
4363pub fn insert_object_narrowing_casts(
4375 vt: &VarTable,
4376 body: &mut Stmt,
4377 pool: &DexPool,
4378 ret: &JavaType,
4379) {
4380 let value_ty = |e: &Expr| -> JavaType {
4387 match e {
4388 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4389 other => other.type_ref().erased(),
4390 }
4391 };
4392 let is_top_object_ty = |v: &JavaType| -> bool {
4393 matches!(v, JavaType::Object(c) if c.as_ref() == "java/lang/Object")
4394 };
4395 let specific_ref = |ty: &TypeRef| -> Option<TypeRef> {
4400 match ty {
4401 TypeRef::J(JavaType::Object(c)) if c.as_ref() != "java/lang/Object" => {
4402 Some(ty.clone())
4403 }
4404 TypeRef::J(JavaType::Array(_)) => Some(ty.clone()),
4405 _ => match ty.erased() {
4406 JavaType::Object(c) if c.as_ref() != "java/lang/Object" => Some(ty.clone()),
4407 JavaType::Array(_) => Some(ty.clone()),
4408 _ => None,
4409 },
4410 }
4411 };
4412 let non_cast_shape = |e: &Expr| -> bool {
4413 !matches!(e, Expr::Const(_) | Expr::Cast { .. } | Expr::InstanceOf { .. })
4414 };
4415 let needs_downcast = |vty: &JavaType, tt: &JavaType| -> bool {
4425 let (JavaType::Object(vn), JavaType::Object(tn)) = (vty, tt) else {
4426 return false;
4427 };
4428 if vn.as_ref() == "java/lang/Object" || tn.as_ref() == "java/lang/Object" {
4429 return false;
4430 }
4431 vn != tn && !pool.is_subtype(vn, tn)
4432 };
4433 let wants_cast = |e: &Expr, vty: &JavaType, tt: &JavaType| -> bool {
4435 non_cast_shape(e) && (is_top_object_ty(vty) || needs_downcast(vty, tt))
4436 };
4437 walk_mut_deep(body, &mut |st| match st {
4438 Stmt::ExprStmt(Expr::Assign {
4439 target,
4440 value,
4441 op: AssignOp::Plain,
4442 ..
4443 }) => {
4444 let tgt_erased: JavaType;
4448 let tgt_ref: Option<&TypeRef>;
4449 match &**target {
4450 Expr::Local { var, .. } => {
4451 let ty = &vt.var(*var).ty;
4452 tgt_erased = ty.erased();
4453 tgt_ref = Some(ty);
4454 }
4455 Expr::Field { ty, .. } => {
4456 tgt_erased = ty.erased();
4457 tgt_ref = Some(ty);
4458 }
4459 Expr::ArrayIndex { array, .. } => {
4463 let aty = match &**array {
4464 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4465 other => other.type_ref().erased(),
4466 };
4467 match aty {
4468 JavaType::Array(inner) => {
4469 tgt_erased = *inner;
4470 tgt_ref = None;
4471 }
4472 _ => return,
4473 }
4474 }
4475 _ => return,
4476 }
4477 let specific = match tgt_ref {
4481 Some(tr) => specific_ref(tr),
4482 None => specific_ref(&TypeRef::J(tgt_erased.clone())),
4483 };
4484 let Some(t) = specific else {
4485 return;
4486 };
4487 let vty = value_ty(value);
4488 if wants_cast(value, &vty, &tgt_erased) {
4489 let v = std::mem::replace(value, Box::new(Expr::This));
4490 **value = Expr::Cast { ty: t, e: v };
4491 }
4492 }
4493 Stmt::LocalDef { var, init: Some(value), .. } => {
4494 if let Some(t) = specific_ref(&vt.var(*var).ty) {
4495 let tt = vt.var(*var).ty.erased();
4496 let vty = value_ty(value);
4497 if wants_cast(value, &vty, &tt) {
4498 let v = std::mem::replace(value, Expr::This);
4499 *value = Expr::Cast {
4500 ty: t,
4501 e: Box::new(v),
4502 };
4503 }
4504 }
4505 }
4506 Stmt::Return(Some(value)) => {
4507 if let Some(t) = specific_ref(&TypeRef::J(ret.clone())) {
4508 let vty = value_ty(value);
4509 if wants_cast(value, &vty, ret) {
4510 let v = std::mem::replace(value, Expr::This);
4511 *value = Expr::Cast { ty: t, e: Box::new(v) };
4512 }
4513 }
4514 }
4515 _ => {}
4516 });
4517 walk_stmt_exprs(body, &mut |e| {
4524 deep_rewrite(e, &mut |x| {
4525 let (args, arg_tys): (&mut Vec<Expr>, &[JavaType]) = match x {
4526 Expr::Method { desc, args, is_dynamic: false, .. } => {
4527 (args, desc.args.as_slice())
4528 }
4529 Expr::New { arg_tys, args, .. } => (args, arg_tys.as_slice()),
4533 _ => return,
4534 };
4535 for (i, a) in args.iter_mut().enumerate() {
4536 let Some(formal) = arg_tys.get(i) else {
4537 continue;
4538 };
4539 let Some(t) = specific_ref(&TypeRef::J(formal.clone())) else {
4540 continue;
4541 };
4542 let aty = value_ty(a);
4543 if !wants_cast(a, &aty, formal) {
4544 continue;
4545 }
4546 let v = std::mem::replace(a, Expr::Const(ConstVal::Null));
4547 *a = Expr::Cast { ty: t, e: Box::new(v) };
4548 }
4549 });
4550 });
4551}
4552
4553pub fn fix_primitive_assign_casts(vt: &VarTable, body: &mut Stmt, ret_ty: &JavaType) {
4562 fn numlike(t: &JavaType) -> bool {
4563 matches!(
4564 t,
4565 JavaType::Byte
4566 | JavaType::Short
4567 | JavaType::Int
4568 | JavaType::Char
4569 | JavaType::Long
4570 | JavaType::Float
4571 | JavaType::Double
4572 )
4573 }
4574 fn val_ty(e: &Expr, vt: &VarTable) -> JavaType {
4575 match e {
4576 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
4577 vt.var(*var).ty.erased()
4578 }
4579 other => other.type_ref().erased(),
4580 }
4581 }
4582 fn wrap(ty: &JavaType, value: &mut Expr) {
4583 if matches!(value, Expr::Cast { .. }) {
4584 return;
4585 }
4586 if val_ty_static(value) == *ty {
4587 return;
4588 }
4589 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
4590 *value = Expr::Cast {
4591 ty: TypeRef::J(ty.clone()),
4592 e: Box::new(taken),
4593 };
4594 }
4595 fn val_ty_static(e: &Expr) -> JavaType {
4596 e.type_ref().erased()
4597 }
4598 fn coerce(tt: &JavaType, value: &mut Expr, vt: &VarTable) {
4606 match (&*value, tt) {
4611 (Expr::Const(ConstVal::Int(b)), JavaType::Float) => {
4612 *value = Expr::Const(ConstVal::Float(f32::from_bits(*b as u32)));
4613 return;
4614 }
4615 (Expr::Const(ConstVal::Long(b)), JavaType::Double) => {
4616 *value = Expr::Const(ConstVal::Double(f64::from_bits(*b as u64)));
4617 return;
4618 }
4619 (Expr::Const(ConstVal::Float(f)), JavaType::Int) => {
4620 *value = Expr::Const(ConstVal::Int(f.to_bits() as i32));
4621 return;
4622 }
4623 (Expr::Const(ConstVal::Double(d)), JavaType::Long) => {
4624 *value = Expr::Const(ConstVal::Long(d.to_bits() as i64));
4625 return;
4626 }
4627 _ => {}
4628 }
4629 let vt_val = val_ty(value, vt);
4630 let tt_bool = matches!(tt, JavaType::Boolean);
4631 let val_bool = matches!(vt_val, JavaType::Boolean);
4632 if tt_bool && !val_bool && numlike(&vt_val) {
4633 if matches!(
4634 value,
4635 Expr::Const(_)
4636 | Expr::Bin { op: BinOp::Eq | BinOp::Ne, .. }
4637 | Expr::Un { op: UnOp::Not, .. }
4638 ) {
4639 return;
4644 }
4645 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
4646 *value = Expr::Bin {
4647 op: BinOp::Ne,
4648 l: Box::new(taken),
4649 r: Box::new(Expr::Const(ConstVal::Int(0))),
4650 ty: Some(TypeRef::J(JavaType::Boolean)),
4651 };
4652 } else if !tt_bool && val_bool && numlike(tt) {
4653 if matches!(value, Expr::Cast { .. }) {
4654 return;
4655 }
4656 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
4657 *value = Expr::Cast {
4658 ty: TypeRef::J(tt.clone()),
4659 e: Box::new(Expr::Cond {
4660 c: Box::new(taken),
4661 t: Box::new(Expr::Const(ConstVal::Int(1))),
4662 f: Box::new(Expr::Const(ConstVal::Int(0))),
4663 }),
4664 };
4665 } else if numlike(tt) && numlike(&vt_val) {
4666 wrap(tt, value);
4667 }
4668 }
4669 walk_mut_deep(body, &mut |st| match st {
4670 Stmt::ExprStmt(Expr::Assign {
4671 target,
4672 op: AssignOp::Plain,
4673 value,
4674 }) => {
4675 let tt = match &**target {
4676 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
4677 vt.var(*var).ty.erased()
4678 }
4679 Expr::Field { ty, .. } => ty.erased(),
4680 Expr::ArrayIndex { array, .. } => match array.type_ref().erased() {
4684 JavaType::Array(inner) => *inner,
4685 _ => return,
4686 },
4687 _ => return,
4688 };
4689 coerce(&tt, value, vt);
4690 }
4691 Stmt::LocalDef {
4692 var,
4693 init: Some(value),
4694 ..
4695 } => {
4696 if (*var as usize) >= vt.vars.len() {
4697 return;
4698 }
4699 let tt = vt.var(*var).ty.erased();
4700 coerce(&tt, value, vt);
4701 }
4702 Stmt::Return(Some(e)) => {
4703 if !numlike(ret_ty) || !numlike(&val_ty(e, vt)) {
4704 return;
4705 }
4706 wrap(ret_ty, e);
4707 }
4708 _ => {}
4709 });
4710}
4711
4712pub fn fix_shadowed_super_fields(
4736 body: &mut Stmt,
4737 vt: &VarTable,
4738 pool: &DexPool,
4739 class_name: &str,
4740) {
4741 fn display(pool: &DexPool, cls: &str, name: &str) -> Option<String> {
4745 let pc = pool.get(cls)?;
4746 let f = pc
4747 .static_fields
4748 .iter()
4749 .chain(pc.instance_fields.iter())
4750 .find(|f| f.name.as_ref() == name)?;
4751 Some(
4752 jdc_core::rename::field_display(cls, name, &f.desc)
4753 .map(|d| d.to_string())
4754 .unwrap_or_else(|| name.to_string()),
4755 )
4756 }
4757 fn owner_ct(
4758 o: &Option<Box<Expr>>,
4759 vt: &VarTable,
4760 class_name: &str,
4761 ) -> Option<std::sync::Arc<str>> {
4762 match o.as_deref() {
4763 None | Some(Expr::This) => Some(std::sync::Arc::from(class_name)),
4764 Some(Expr::Local { var, .. }) => match vt.var(*var).ty.erased() {
4765 JavaType::Object(n) => Some(n),
4766 _ => None,
4767 },
4768 Some(other) => match other.type_ref().erased() {
4769 JavaType::Object(n) => Some(n),
4770 _ => None,
4771 },
4772 }
4773 }
4774 walk_stmt_exprs(body, &mut |e| {
4775 deep_rewrite(e, &mut |x| {
4776 let Expr::Field {
4777 owner,
4778 cls,
4779 name,
4780 is_static: false,
4781 ..
4782 } = x
4783 else {
4784 return;
4785 };
4786 let Some(ot) = owner_ct(owner, vt, class_name) else {
4787 return;
4788 };
4789 if cls.as_ref() == ot.as_ref() {
4790 return;
4791 }
4792 let Some(ref_display) = display(pool, cls, name) else {
4793 return;
4794 };
4795 let mut cur = ot.to_string();
4799 let mut shadowed = false;
4800 let mut found = false;
4801 for _ in 0..64 {
4802 if display(pool, &cur, name).as_deref() == Some(ref_display.as_str()) {
4803 shadowed = true;
4804 }
4805 let Some(pc) = pool.get(&cur) else {
4806 return;
4807 };
4808 let Some(sup) = pc.super_name.clone() else {
4809 return;
4810 };
4811 if sup.as_str() == cls.as_ref() {
4812 found = true;
4813 break;
4814 }
4815 cur = sup;
4816 }
4817 if !found || !shadowed {
4818 return;
4819 }
4820 let ty = TypeRef::J(JavaType::Object(cls.clone()));
4821 match owner {
4822 Some(o) => {
4823 if let Expr::Cast { ty: ct, .. } = &mut **o {
4824 *ct = ty;
4825 } else {
4826 let taken = std::mem::replace(&mut **o, Expr::This);
4827 **o = Expr::Cast {
4828 ty,
4829 e: Box::new(taken),
4830 };
4831 }
4832 }
4833 None => {
4834 *owner = Some(Box::new(Expr::Cast {
4835 ty,
4836 e: Box::new(Expr::This),
4837 }))
4838 }
4839 }
4840 });
4841 });
4842}
4843
4844pub fn fix_field_owner_downcasts(body: &mut Stmt, vt: &VarTable, pool: &DexPool) {
4863 fn owner_internal(o: &Expr, vt: &VarTable) -> Option<std::sync::Arc<str>> {
4864 let t = match o {
4865 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4866 other => other.type_ref().erased(),
4867 };
4868 match t {
4869 JavaType::Object(n) => Some(n),
4870 _ => None,
4871 }
4872 }
4873 fn declares_up(pool: &DexPool, class: &str, name: &str) -> bool {
4874 let mut cur = Some(class.to_string());
4875 let mut hops = 0;
4876 while let Some(c) = cur {
4877 hops += 1;
4878 if hops > 64 {
4879 return false;
4880 }
4881 let Some(pc) = pool.get(&c) else { return false };
4882 if pc
4883 .instance_fields
4884 .iter()
4885 .chain(pc.static_fields.iter())
4886 .any(|f| f.name.as_ref() == name)
4887 {
4888 return true;
4889 }
4890 cur = pc.super_name.clone();
4891 }
4892 false
4893 }
4894 walk_stmt_exprs(body, &mut |e| {
4895 deep_rewrite(e, &mut |x| {
4896 if let Expr::Field {
4897 owner: Some(o),
4898 cls,
4899 name,
4900 is_static: false,
4901 ..
4902 } = x
4903 {
4904 let Some(ot) = owner_internal(o, vt) else {
4905 return;
4906 };
4907 if ot.as_ref() == cls.as_ref() || !pool.is_subtype(cls, &ot) {
4908 return;
4909 }
4910 if declares_up(pool, &ot, name) || !declares_up(pool, cls, name) {
4911 return;
4912 }
4913 let ty = TypeRef::J(JavaType::Object(cls.clone()));
4914 if let Expr::Cast { ty: ct, .. } = &mut **o {
4915 *ct = ty;
4916 } else {
4917 let taken = std::mem::replace(&mut **o, Expr::This);
4918 **o = Expr::Cast { ty, e: Box::new(taken) };
4919 }
4920 }
4921 let (tgt_ty, value) = match x {
4923 Expr::Assign { target, op: AssignOp::Plain, value } => {
4924 let t = match &**target {
4925 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4926 Expr::Field { ty, .. } => ty.erased(),
4927 _ => return,
4928 };
4929 (t, value)
4930 }
4931 _ => return,
4932 };
4933 let JavaType::Object(sn) = tgt_ty else { return };
4934 if let Expr::Cast { ty, .. } = &mut **value {
4935 if let TypeRef::J(JavaType::Object(tn)) = ty {
4936 if tn.as_ref() != sn.as_ref() && pool.is_subtype(&sn, tn) {
4937 *ty = TypeRef::J(JavaType::Object(sn.clone()));
4938 }
4939 }
4940 }
4941 });
4942 });
4943 walk_mut_deep(body, &mut |st| {
4945 if let Stmt::LocalDef { var, init: Some(value), .. } = st {
4946 let tt = if (*var as usize) < vt.vars.len() {
4947 vt.vars[*var as usize].ty.erased()
4948 } else {
4949 return;
4950 };
4951 let JavaType::Object(sn) = tt else { return };
4952 if let Expr::Cast { ty, .. } = value {
4953 if let TypeRef::J(JavaType::Object(tn)) = ty {
4954 if tn.as_ref() != sn.as_ref() && pool.is_subtype(&sn, tn) {
4955 *ty = TypeRef::J(JavaType::Object(sn.clone()));
4956 }
4957 }
4958 }
4959 }
4960 });
4961}
4962
4963pub fn fix_incomparable_equality(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
4970 fn side_ty(e: &Expr, vt: &VarTable) -> JavaType {
4971 match e {
4972 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
4973 vt.var(*var).ty.erased()
4974 }
4975 other => other.type_ref().erased(),
4976 }
4977 }
4978 let incomparable = |a: &JavaType, b: &JavaType| -> bool {
4979 use JavaType::{Array, Object};
4980 match (a, b) {
4981 (Object(x), Object(y)) => {
4982 if x == y || x.as_ref() == "java/lang/Object" || y.as_ref() == "java/lang/Object"
4983 {
4984 return false;
4985 }
4986 let xi = pool.get(x).map(|c| c.is_interface()).unwrap_or(true);
4987 let yi = pool.get(y).map(|c| c.is_interface()).unwrap_or(true);
4988 if xi || yi {
4992 return false;
4993 }
4994 !pool.is_subtype(x, y) && !pool.is_subtype(y, x)
4995 }
4996 (Array(_), Object(y)) | (Object(y), Array(_)) => {
4997 y.as_ref() != "java/lang/Object"
4998 && y.as_ref() != "java/lang/Cloneable"
4999 && y.as_ref() != "java/io/Serializable"
5000 && !pool.get(y).map(|c| c.is_interface()).unwrap_or(false)
5001 }
5002 _ => false,
5003 }
5004 };
5005 let is_ref = |t: &JavaType| matches!(t, JavaType::Object(_) | JavaType::Array(_));
5006 walk_stmt_exprs(body, &mut |e| {
5007 deep_rewrite(e, &mut |x| {
5008 if let Expr::Bin {
5009 op: BinOp::Eq | BinOp::Ne,
5010 l,
5011 r,
5012 ..
5013 } = x
5014 {
5015 let lt = side_ty(l, vt);
5016 let rt = side_ty(r, vt);
5017 if is_ref(<) && matches!(&**r, Expr::Const(ConstVal::Int(0))) {
5023 **r = Expr::Const(ConstVal::Null);
5024 return;
5025 }
5026 if is_ref(&rt) && matches!(&**l, Expr::Const(ConstVal::Int(0))) {
5027 **l = Expr::Const(ConstVal::Null);
5028 return;
5029 }
5030 if incomparable(<, &rt) {
5031 let taken = std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
5032 **l = Expr::Cast {
5033 ty: TypeRef::J(JavaType::Object("java/lang/Object".into())),
5034 e: taken,
5035 };
5036 }
5037 }
5038 });
5039 });
5040}
5041
5042pub fn fix_primitive_arg_bridges(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
5056 fn val_ty(e: &Expr, vt: &VarTable) -> JavaType {
5057 match e {
5058 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
5059 other => other.type_ref().erased(),
5060 }
5061 }
5062 #[allow(clippy::type_complexity)]
5063 let is_bool = |t: &JavaType| matches!(t, JavaType::Boolean);
5064 let is_num = |t: &JavaType| {
5065 matches!(
5066 t,
5067 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
5068 | JavaType::Long | JavaType::Float | JavaType::Double
5069 )
5070 };
5071 fn bridge(actual: &JavaType, formal: &JavaType, arg: &Expr) -> Option<Expr> {
5076 match (arg, formal) {
5086 (Expr::Const(ConstVal::Int(b)), JavaType::Float) => {
5087 return Some(Expr::Const(ConstVal::Float(f32::from_bits(*b as u32))));
5088 }
5089 (Expr::Const(ConstVal::Long(b)), JavaType::Double) => {
5090 return Some(Expr::Const(ConstVal::Double(f64::from_bits(*b as u64))));
5091 }
5092 (Expr::Const(ConstVal::Float(f)), JavaType::Int) => {
5093 return Some(Expr::Const(ConstVal::Int(f.to_bits() as i32)));
5094 }
5095 (Expr::Const(ConstVal::Double(d)), JavaType::Long) => {
5096 return Some(Expr::Const(ConstVal::Long(d.to_bits() as i64)));
5097 }
5098 _ => {}
5099 }
5100 if matches!(actual, JavaType::Boolean)
5101 && matches!(
5102 formal,
5103 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
5104 | JavaType::Long | JavaType::Float | JavaType::Double
5105 )
5106 {
5107 Some(Expr::Cast {
5108 ty: TypeRef::J(formal.clone()),
5109 e: Box::new(Expr::Cond {
5110 c: Box::new(arg.clone()),
5111 t: Box::new(Expr::Const(ConstVal::Int(1))),
5112 f: Box::new(Expr::Const(ConstVal::Int(0))),
5113 }),
5114 })
5115 } else if matches!(formal, JavaType::Boolean)
5116 && matches!(
5117 actual,
5118 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
5119 | JavaType::Long | JavaType::Float | JavaType::Double
5120 )
5121 {
5122 Some(Expr::Bin {
5123 op: BinOp::Ne,
5124 l: Box::new(arg.clone()),
5125 r: Box::new(Expr::Const(ConstVal::Int(0))),
5126 ty: Some(TypeRef::J(JavaType::Boolean)),
5127 })
5128 } else if matches!(
5129 actual,
5130 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
5131 | JavaType::Long | JavaType::Float | JavaType::Double
5132 ) && matches!(
5133 formal,
5134 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
5135 | JavaType::Long | JavaType::Float | JavaType::Double
5136 ) && !implicit_widening(actual, formal)
5137 {
5138 Some(Expr::Cast {
5147 ty: TypeRef::J(formal.clone()),
5148 e: Box::new(arg.clone()),
5149 })
5150 } else {
5151 None
5152 }
5153 }
5154 fn fw_resolvable(n: &str) -> bool {
5170 crate::fwdb::exists(n)
5171 }
5172 fn null_arg_cast(a: &mut Expr, f: &JavaType, pool: &DexPool) -> bool {
5173 if !matches!(&*a, Expr::Const(ConstVal::Null)) {
5174 return false;
5175 }
5176 if let JavaType::Object(n) = f {
5177 let resolvable = pool.get(n).is_some() || fw_resolvable(n);
5183 if n.as_ref() != "java/lang/Object" && resolvable {
5184 *a = Expr::Cast {
5185 ty: TypeRef::J(f.clone()),
5186 e: Box::new(Expr::Const(ConstVal::Null)),
5187 };
5188 return true;
5189 }
5190 } else if let JavaType::Array(_) = f {
5191 let mut e: &JavaType = f;
5198 while let JavaType::Array(inner) = e {
5199 e = &**inner;
5200 }
5201 let resolvable = match e {
5202 JavaType::Object(n) => pool.get(n).is_some() || fw_resolvable(n),
5203 _ => true, };
5205 if resolvable {
5206 *a = Expr::Cast {
5207 ty: TypeRef::J(f.clone()),
5208 e: Box::new(Expr::Const(ConstVal::Null)),
5209 };
5210 return true;
5211 }
5212 }
5213 false
5216 }
5217 fn implicit_widening(a: &JavaType, f: &JavaType) -> bool {
5220 use JavaType::*;
5221 match (a, f) {
5222 (Byte, Short) | (Byte, Int) | (Byte, Long) | (Byte, Float) | (Byte, Double) => true,
5223 (Short, Int) | (Short, Long) | (Short, Float) | (Short, Double) => true,
5224 (Char, Int) | (Char, Long) | (Char, Float) | (Char, Double) => true,
5225 (Int, Long) | (Int, Float) | (Int, Double) => true,
5226 (Long, Float) | (Long, Double) => true,
5227 (Float, Double) => true,
5228 (x, y) => x == y,
5229 }
5230 }
5231 let overload_competes = |cls: &str, name: &str, args: &[JavaType]| -> bool {
5240 if let Some(c) = pool.get(cls) {
5241 return c.all_methods().any(|m| {
5242 &*m.name == name
5243 && m.access & (crate::access::ACC_BRIDGE | crate::access::ACC_SYNTHETIC) == 0
5249 && m.parsed_desc()
5250 .is_some_and(|d| d.args.len() == args.len() && d.args != args)
5251 });
5252 }
5253 let _ = args;
5262 false
5263 };
5264 let ctor_formals = |cls: &str, n: usize| -> Option<Vec<JavaType>> {
5267 let c = pool.get(cls)?;
5268 let mut out: Option<Vec<JavaType>> = None;
5269 for m in c.all_methods() {
5270 if &*m.name != "<init>" {
5271 continue;
5272 }
5273 let Some(d) = m.parsed_desc() else { continue };
5274 if d.args.len() != n {
5275 continue;
5276 }
5277 match &out {
5278 None => out = Some(d.args.clone()),
5279 Some(prev) if *prev != d.args => return None,
5280 Some(_) => {}
5281 }
5282 }
5283 out
5284 };
5285 walk_stmt_exprs(body, &mut |e| {
5286 deep_rewrite(e, &mut |x| match x {
5287 Expr::Method {
5288 cls,
5289 name,
5290 desc,
5291 args,
5292 is_static,
5293 ..
5294 } => {
5295 if args.len() != desc.args.len() {
5296 return;
5297 }
5298 let _ = is_static;
5299 let mut competes: Option<bool> = None;
5300 for (a, f) in args.iter_mut().zip(desc.args.iter()) {
5301 if null_arg_cast(a, f, pool) {
5302 continue;
5303 }
5304 if matches!(a, Expr::Cast { .. }) {
5305 continue;
5306 }
5307 let subtype_arg = match (&val_ty(a, vt), f) {
5318 (JavaType::Object(an), JavaType::Object(fn_)) => {
5319 (an != fn_ && pool.is_subtype(an, fn_))
5324 || (fn_.as_ref() == "java/lang/Object"
5325 && an.as_ref() != "java/lang/Object")
5326 }
5327 (JavaType::Array(_), JavaType::Object(fn_))
5328 if fn_.as_ref() == "java/lang/Object" =>
5329 {
5330 true
5331 }
5332 _ => false,
5333 };
5334 let null_obj =
5348 matches!(a, Expr::Const(ConstVal::Null)) && matches!(f, JavaType::Object(_));
5349 let framework_owner = pool.get(cls.as_ref()).is_none();
5350 let fw_null_ambiguous = framework_owner
5351 && matches!(
5352 (cls.as_ref(), name.as_ref()),
5353 ("java/lang/StringBuilder", "append")
5354 | ("java/lang/StringBuffer", "append")
5355 | ("java/io/PrintStream", "println")
5356 | ("java/io/PrintStream", "print")
5357 | ("java/io/PrintWriter", "println")
5358 | ("java/io/PrintWriter", "print")
5359 | ("java/lang/String", "valueOf")
5360 );
5361 let fw_subtype_pinnable = framework_owner
5372 && subtype_arg
5373 && !matches!(f, JavaType::Object(fn_) if fn_.as_ref() == "java/lang/Object")
5374 && matches!(
5375 (cls.as_ref(), name.as_ref()),
5376 ("android/content/Intent", "putExtra")
5377 | ("java/util/concurrent/ScheduledExecutorService", "schedule")
5378 | ("java/util/concurrent/ExecutorService", "submit")
5379 | ("java/util/concurrent/ExecutorService", "invokeAll")
5380 );
5381 if fw_subtype_pinnable
5382 || (subtype_arg
5383 && *competes.get_or_insert_with(|| {
5384 overload_competes(cls, name, &desc.args)
5385 }))
5386 || (null_obj
5387 && (fw_null_ambiguous
5388 || *competes.get_or_insert_with(|| {
5389 overload_competes(cls, name, &desc.args)
5390 })))
5391 {
5392 let old =
5393 std::mem::replace(a, Expr::Const(ConstVal::Null));
5394 *a = Expr::Cast {
5395 ty: TypeRef::J(f.clone()),
5396 e: Box::new(old),
5397 };
5398 continue;
5399 }
5400 if !is_bool(&val_ty(a, vt)) && !is_num(&val_ty(a, vt)) {
5401 continue;
5402 }
5403 if !is_bool(f) && !is_num(f) {
5404 continue;
5405 }
5406 if let Some(b) = bridge(&val_ty(a, vt), f, a) {
5407 *a = b;
5408 }
5409 }
5410 }
5411 Expr::New { cls, args, arg_tys, .. } => {
5412 let formals_owned = if arg_tys.len() == args.len() && !arg_tys.is_empty() {
5413 Some(arg_tys.clone())
5414 } else {
5415 ctor_formals(cls, args.len())
5416 };
5417 if let Some(formals) = formals_owned {
5418 for (a, f) in args.iter_mut().zip(formals.iter()) {
5419 if null_arg_cast(a, f, pool) {
5420 continue;
5421 }
5422 if matches!(a, Expr::Cast { .. }) {
5423 continue;
5424 }
5425 if !is_bool(f) && !is_num(f) {
5426 continue;
5427 }
5428 if let Some(b) = bridge(&val_ty(a, vt), f, a) {
5429 *a = b;
5430 }
5431 }
5432 }
5433 }
5434 _ => {}
5435 });
5436 });
5437}
5438
5439pub fn deshadow_locals(vt: &mut VarTable, pool: &crate::DexPool, class_name: &str, body: &Stmt) {
5451 let segs = pool.root_pkg_segs();
5452 let obscured = crate::classdec::obscured_simples_snapshot();
5456 let self_pkg = class_name.rfind('/').map(|i| &class_name[..i]).unwrap_or("");
5463 let mut same_pkg_simples: jdc_core::FxHashSet<String> = jdc_core::FxHashSet::default();
5464 visit_all_exprs(body, &mut |x| {
5465 let cls = match x {
5466 Expr::Field { cls, is_static: true, .. } => Some(cls.as_ref()),
5467 Expr::Method { cls, is_static: true, .. } => Some(cls.as_ref()),
5468 _ => None,
5469 };
5470 if let Some(c) = cls {
5471 let pkg = c.rfind('/').map(|i| &c[..i]).unwrap_or("");
5472 if pkg == self_pkg {
5473 let simple = &c[c.rfind('/').map(|i| i + 1).unwrap_or(0)..];
5474 let head = simple.split('$').next().unwrap_or(simple);
5475 if !head.is_empty() {
5476 same_pkg_simples.insert(head.to_string());
5477 }
5478 }
5479 }
5480 });
5481 let hit = |name: &str| {
5482 segs.contains(name) || obscured.contains(name) || same_pkg_simples.contains(name)
5483 };
5484 if (segs.is_empty() && obscured.is_empty() && same_pkg_simples.is_empty())
5485 || !vt.vars.iter().any(|v| hit(&v.name))
5486 {
5487 return;
5488 }
5489 let mut taken: jdc_core::FxHashSet<String> =
5490 vt.vars.iter().map(|v| v.name.clone()).collect();
5491 for v in vt.vars.iter_mut() {
5492 if !hit(&v.name) {
5493 continue;
5494 }
5495 let mut k = 0u32;
5496 let cand = loop {
5497 k += 1;
5498 let c = if k == 1 {
5499 format!("{}x", v.name)
5500 } else {
5501 format!("{}x{}", v.name, k)
5502 };
5503 if !taken.contains(&c) {
5504 break c;
5505 }
5506 };
5507 taken.insert(cand.clone());
5508 v.name = cand;
5509 }
5510}
5511
5512pub fn fix_ctor_this_aliases(body: &mut Stmt, vt: &VarTable) {
5538 let Some(this_var) = vt
5539 .vars
5540 .iter()
5541 .find(|v| v.is_param && v.name == "this")
5542 .map(|v| v.id)
5543 else {
5544 return;
5545 };
5546 let mut analysis = VarAnalysis {
5547 assigns: Vec::new(),
5548 reads: Vec::new(),
5549 values: Vec::new(),
5550 };
5551 analyze_vars(body, &mut analysis);
5552 enum Src {
5563 This,
5564 Loc(u32),
5565 Other,
5566 }
5567 let mut sources: jdc_core::FxHashMap<u32, Vec<Src>> = jdc_core::FxHashMap::default();
5568 walk_all(body, &mut |st| {
5569 let (tgt, val): (u32, &Expr) = match st {
5570 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
5571 Expr::Local { var, .. } => (*var, &**value),
5572 _ => return,
5573 },
5574 Stmt::LocalDef { var, init: Some(value), .. } => (*var, value),
5575 _ => return,
5576 };
5577 let s = match val {
5578 Expr::This => Src::This,
5579 Expr::Local { var: src, .. } if *src == this_var => Src::This,
5580 Expr::Local { var: src, .. } => Src::Loc(*src),
5581 _ => Src::Other,
5582 };
5583 sources.entry(tgt).or_default().push(s);
5584 });
5585 let n = vt
5586 .vars
5587 .len()
5588 .max(analysis.assigns.len())
5589 .max(sources.keys().map(|k| *k as usize + 1).max().unwrap_or(0));
5590 let mut alias = vec![false; n];
5591 loop {
5592 let mut changed = false;
5593 for (v, srcs) in &sources {
5594 let vi = *v as usize;
5595 if vi >= n || alias[vi] || srcs.is_empty() {
5596 continue;
5597 }
5598 let ok = srcs.iter().all(|s| match s {
5599 Src::This => true,
5600 Src::Loc(w) => (*w as usize) < n && alias[*w as usize],
5601 Src::Other => false,
5602 });
5603 if ok {
5604 alias[vi] = true;
5605 changed = true;
5606 }
5607 }
5608 if !changed {
5609 break;
5610 }
5611 }
5612 if !alias.iter().any(|&b| b) {
5613 return;
5614 }
5615 walk_stmt_exprs(body, &mut |e| {
5616 deep_rewrite_reads(e, &mut |x| {
5617 if let Expr::Local { var, .. } = x {
5618 if (*var as usize) < n && alias[*var as usize] {
5619 *x = Expr::This;
5620 }
5621 }
5622 });
5623 });
5624}
5625
5626pub fn fix_this0_owners(body: &mut Stmt, class_name: &str) {
5627 walk_stmt_exprs(body, &mut |e| {
5628 deep_rewrite(e, &mut |x| {
5629 if let Expr::Field { owner: Some(_), cls, name, .. } = x {
5630 if name.as_ref() == "this$0" && cls.as_ref() == class_name {
5631 if let Expr::Field { owner, .. } = x {
5632 *owner = None;
5633 }
5634 }
5635 }
5636 });
5637 });
5638}
5639
5640pub fn rescue_primitive_receivers(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
5641 fn prim(t: &JavaType) -> bool {
5642 match t {
5643 JavaType::Int
5644 | JavaType::Long
5645 | JavaType::Short
5646 | JavaType::Byte
5647 | JavaType::Char
5648 | JavaType::Float
5649 | JavaType::Double
5650 | JavaType::Boolean => true,
5651 JavaType::Object(n) => matches!(
5655 n.as_ref(),
5656 "java/lang/Boolean"
5657 | "java/lang/String"
5658 | "java/lang/Integer"
5659 | "java/lang/Long"
5660 | "java/lang/Short"
5661 | "java/lang/Byte"
5662 | "java/lang/Character"
5663 | "java/lang/Float"
5664 | "java/lang/Double"
5665 ),
5666 _ => false,
5667 }
5668 }
5669 walk_stmt_exprs(body, &mut |e| {
5670 deep_rewrite(e, &mut |x| {
5671 let (recv_var, recv_ty) = {
5673 let o: &Expr = match &*x {
5674 Expr::Method { owner: Some(o), .. } => o,
5675 Expr::Field { owner: Some(o), is_static: false, .. } => o,
5676 _ => return,
5677 };
5678 let Expr::Local { var, ty } = o else {
5679 return;
5680 };
5681 let vt_ty = if (*var as usize) < vt.vars.len() {
5682 vt.var(*var).ty.erased()
5683 } else {
5684 ty.erased()
5685 };
5686 if !prim(&vt_ty) {
5687 return;
5688 }
5689 (*var, vt_ty)
5690 };
5691 let cls: std::sync::Arc<str> = match &*x {
5692 Expr::Method { cls, .. } | Expr::Field { cls, .. } => cls.clone(),
5693 _ => return,
5694 };
5695 if let JavaType::Object(n) = &recv_ty {
5704 if n.as_ref() == cls.as_ref() || pool.is_subtype(n.as_ref(), cls.as_ref()) {
5705 return;
5706 }
5707 }
5708 let mut cands: Vec<u32> = Vec::new();
5710 for v in &vt.vars {
5711 if let JavaType::Object(n) = v.ty.erased() {
5712 if n.as_ref() == cls.as_ref()
5713 || pool.is_subtype(n.as_ref(), cls.as_ref())
5714 {
5715 cands.push(v.id);
5716 }
5717 }
5718 }
5719 let id = if cands.len() == 1 {
5720 cands[0]
5721 } else if cands.len() > 1 {
5722 let slot = if (recv_var as usize) < vt.vars.len() {
5727 Some(vt.var(recv_var).slot)
5728 } else {
5729 None
5730 };
5731 let mut hits: Vec<u32> = match slot {
5732 Some(sl) => cands
5733 .iter()
5734 .copied()
5735 .filter(|&c| {
5736 c != recv_var
5737 && (c as usize) < vt.vars.len()
5738 && vt.var(c).slot == sl
5739 })
5740 .collect(),
5741 None => Vec::new(),
5742 };
5743 if hits.is_empty() {
5744 hits = cands
5747 .iter()
5748 .copied()
5749 .filter(|&c| c != recv_var)
5750 .collect();
5751 }
5752 hits.sort_unstable();
5753 hits.dedup();
5754 if hits.is_empty() {
5755 return;
5756 }
5757 *hits.last().unwrap()
5758 } else {
5759 if cands.is_empty() {
5766 return;
5767 }
5768 let slot = if (recv_var as usize) < vt.vars.len() {
5769 vt.var(recv_var).slot
5770 } else {
5771 return;
5772 };
5773 let mut hits: Vec<u32> = cands
5774 .into_iter()
5775 .filter(|&c| {
5776 c != recv_var
5777 && (c as usize) < vt.vars.len()
5778 && vt.var(c).slot == slot
5779 })
5780 .collect();
5781 hits.sort_unstable();
5782 hits.dedup();
5783 if hits.len() != 1 {
5784 return;
5785 }
5786 hits[0]
5787 };
5788 let o: &mut Box<Expr> = match x {
5790 Expr::Method { owner: Some(o), .. } => o,
5791 Expr::Field { owner: Some(o), is_static: false, .. } => o,
5792 _ => return,
5793 };
5794 if let Expr::Local { var, ty } = &mut **o {
5795 if *var == recv_var {
5796 *var = id;
5797 *ty = vt.vars[id as usize].ty.clone();
5798 }
5799 }
5800 });
5801 });
5802
5803}
5804
5805pub fn rescue_arg_return_swaps(
5811 body: &mut Stmt,
5812 vt: &VarTable,
5813 pool: &crate::DexPool,
5814 ret_ty: &JavaType,
5815) {
5816 let mut top_ids: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
5824 for v in &vt.vars {
5825 if v.is_param {
5826 top_ids.insert(v.id);
5827 }
5828 }
5829 if let Stmt::Block(stmts) = body {
5830 for st in stmts.iter() {
5831 if let Stmt::LocalDef { var, .. } = st {
5832 top_ids.insert(*var);
5833 }
5834 }
5835 }
5836 let ret_wants_ref = match ret_ty {
5837 JavaType::Object(n) => n.as_ref() != "java/lang/Object",
5838 JavaType::Array(_) => true,
5839 _ => false,
5840 };
5841 let try_swap = |a: &mut Expr, want: &JavaType| {
5842 let Expr::Local { var, ty } = &mut *a else {
5843 return;
5844 };
5845 if (*var as usize) >= vt.vars.len() {
5846 return;
5847 }
5848 let avt = vt.var(*var).ty.erased();
5849 let avt_ref = matches!(avt, JavaType::Object(_) | JavaType::Array(_));
5850 let want_arr = matches!(want, JavaType::Array(_));
5851 if avt_ref && !(avt == JavaType::Object("java/lang/Object".into())) {
5854 return;
5855 }
5856 if avt_ref && !matches!(want, JavaType::Object(_)) {
5857 return;
5858 }
5859 let slot = vt.var(*var).slot;
5860 let mut hits: Vec<u32> = Vec::new();
5861 for v in &vt.vars {
5862 if v.slot != slot || !top_ids.contains(&v.id) || v.id == *var {
5863 continue;
5864 }
5865 let t = v.ty.erased();
5866 let ok = match (&t, want) {
5867 (JavaType::Object(n), JavaType::Object(w)) => {
5868 n.as_ref() == w.as_ref()
5869 || pool.is_subtype(n.as_ref(), w.as_ref())
5870 }
5871 (JavaType::Array(_), JavaType::Array(_)) => want_arr,
5872 _ => false,
5873 };
5874 if ok {
5875 hits.push(v.id);
5876 }
5877 }
5878 hits.sort_unstable();
5879 hits.dedup();
5880 if hits.is_empty() {
5881 return;
5882 }
5883 let id = *hits.last().unwrap();
5889 *var = id;
5890 *ty = vt.vars[id as usize].ty.clone();
5891 };
5892 walk_stmt_exprs(body, &mut |e| {
5893 deep_rewrite(e, &mut |x| {
5894 match x {
5895 Expr::Method { desc, args, is_dynamic: false, .. } => {
5896 for (i, a) in args.iter_mut().enumerate() {
5897 if let Some(f) = desc.args.get(i) {
5898 if matches!(f, JavaType::Object(n) if n.as_ref() != "java/lang/Object")
5899 || matches!(f, JavaType::Array(_))
5900 {
5901 try_swap(a, f);
5902 }
5903 }
5904 }
5905 }
5906 Expr::New { arg_tys, args, .. } => {
5907 for (i, a) in args.iter_mut().enumerate() {
5908 if let Some(f) = arg_tys.get(i) {
5909 if matches!(f, JavaType::Object(n) if n.as_ref() != "java/lang/Object")
5910 || matches!(f, JavaType::Array(_))
5911 {
5912 try_swap(a, f);
5913 }
5914 }
5915 }
5916 }
5917 _ => {}
5918 }
5919 });
5920 });
5921 if ret_wants_ref {
5922 walk_mut_deep(body, &mut |st| {
5923 if let Stmt::Return(Some(e)) = st {
5924 try_swap(e, ret_ty);
5925 }
5926 });
5927 }
5928}
5929
5930pub fn fix_ref_null_assigns(vt: &VarTable, body: &mut Stmt) {
5945 let is_ref_var = |var: u32| -> bool {
5946 (var as usize) < vt.vars.len()
5947 && matches!(
5948 vt.vars[var as usize].ty.erased(),
5949 JavaType::Object(_) | JavaType::Array(_)
5950 )
5951 };
5952 walk_mut_deep(body, &mut |st| match st {
5953 Stmt::ExprStmt(Expr::Assign {
5954 target,
5955 value,
5956 op: AssignOp::Plain,
5957 ..
5958 }) => {
5959 if let Expr::Local { var, .. } = &**target {
5960 if is_ref_var(*var) && matches!(&**value, Expr::Const(ConstVal::Int(0))) {
5961 **value = Expr::Const(ConstVal::Null);
5962 }
5963 }
5964 }
5965 Stmt::LocalDef {
5966 var,
5967 init: Some(e),
5968 ..
5969 } => {
5970 if is_ref_var(*var) && matches!(e, Expr::Const(ConstVal::Int(0))) {
5971 *e = Expr::Const(ConstVal::Null);
5972 }
5973 }
5974 _ => {}
5975 });
5976}
5977
5978pub fn fix_ref_array_null_consts(body: &mut Stmt) {
5979 walk_stmt_exprs(body, &mut |e| {
5980 deep_rewrite(e, &mut |x| {
5981 if let Expr::NewArray { elem, init: Some(list), .. } = x {
5982 if matches!(elem.erased(), JavaType::Object(_) | JavaType::Array(_)) {
5983 for slot in list.iter_mut() {
5984 if matches!(&*slot, Expr::Const(ConstVal::Int(0))) {
5985 *slot = Expr::Const(ConstVal::Null);
5986 }
5987 }
5988 }
5989 }
5990 });
5991 });
5992}
5993
5994pub fn fix_null_sentinels(body: &mut Stmt, vt: &VarTable, ret: &JavaType) {
6033 #[derive(Clone, Copy, PartialEq)]
6034 enum WK {
6035 Zero,
6036 Copy(u32),
6037 Other,
6038 }
6039 fn is_ref_jt(t: &JavaType) -> bool {
6040 matches!(t, JavaType::Object(_) | JavaType::Array(_))
6041 }
6042 fn cls_write(e: &Expr) -> WK {
6043 match e {
6044 Expr::Const(ConstVal::Int(0)) => WK::Zero,
6045 Expr::Local { var, .. } => WK::Copy(*var),
6046 _ => WK::Other,
6047 }
6048 }
6049 fn expr_writes(e: &Expr, writes: &mut jdc_core::FxHashMap<u32, Vec<WK>>) {
6050 visit_exprs(e, &mut |x| match x {
6051 Expr::Assign { target, op, value } => {
6052 if let Expr::Local { var, .. } = &**target {
6053 let k = if matches!(op, AssignOp::Plain) {
6054 cls_write(value)
6055 } else {
6056 WK::Other
6057 };
6058 writes.entry(*var).or_default().push(k);
6059 }
6060 }
6061 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
6062 if let Expr::Local { var, .. } = &**inner {
6063 writes.entry(*var).or_default().push(WK::Other);
6064 }
6065 }
6066 _ => {}
6067 });
6068 }
6069 fn collect_writes(
6070 s: &Stmt,
6071 writes: &mut jdc_core::FxHashMap<u32, Vec<WK>>,
6072 has_zero: &mut bool,
6073 ) {
6074 fn note(e: &Expr, has_zero: &mut bool, writes: &mut jdc_core::FxHashMap<u32, Vec<WK>>) {
6075 visit_exprs(e, &mut |x| {
6076 if let Expr::Const(ConstVal::Int(0)) = x {
6077 *has_zero = true;
6078 }
6079 });
6080 expr_writes(e, writes);
6081 }
6082 match s {
6083 Stmt::Block(v) => {
6084 for x in v {
6085 collect_writes(x, writes, has_zero);
6086 }
6087 }
6088 Stmt::LocalDef { var, init, .. } => {
6089 if let Some(e) = init {
6090 if matches!(e, Expr::Const(ConstVal::Int(0))) {
6091 *has_zero = true;
6092 }
6093 writes.entry(*var).or_default().push(cls_write(e));
6094 }
6095 }
6096 Stmt::ExprStmt(e) => note(e, has_zero, writes),
6097 Stmt::Return(e) => {
6098 if let Some(x) = e {
6099 note(x, has_zero, writes);
6100 }
6101 }
6102 Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
6103 note(e, has_zero, writes);
6104 }
6105 Stmt::TernaryValue { e } => note(e, has_zero, writes),
6106 Stmt::If { cond, then_stmt, else_stmt } => {
6107 note(cond, has_zero, writes);
6108 collect_writes(then_stmt, writes, has_zero);
6109 if let Some(b) = else_stmt {
6110 collect_writes(b, writes, has_zero);
6111 }
6112 }
6113 Stmt::While { cond, body } | Stmt::DoWhile { body, cond } => {
6114 note(cond, has_zero, writes);
6115 collect_writes(body, writes, has_zero);
6116 }
6117 Stmt::For { init, cond, update, body } => {
6118 for x in init {
6119 collect_writes(x, writes, has_zero);
6120 }
6121 if let Some(c) = cond {
6122 note(c, has_zero, writes);
6123 }
6124 for u in update {
6125 note(u, has_zero, writes);
6126 }
6127 collect_writes(body, writes, has_zero);
6128 }
6129 Stmt::ForEach { var, iterable, body, .. } => {
6130 writes.entry(*var).or_default().push(WK::Other);
6131 note(iterable, has_zero, writes);
6132 collect_writes(body, writes, has_zero);
6133 }
6134 Stmt::Switch { selector, cases, default, .. } => {
6135 note(selector, has_zero, writes);
6136 for c in cases {
6137 if let Some(g) = &c.guard {
6138 note(g, has_zero, writes);
6139 }
6140 for x in &c.body {
6141 collect_writes(x, writes, has_zero);
6142 }
6143 }
6144 if let Some(d) = default {
6145 collect_writes(d, writes, has_zero);
6146 }
6147 }
6148 Stmt::Try { body, catches, finally } => {
6149 collect_writes(body, writes, has_zero);
6150 for c in catches {
6151 writes.entry(c.var).or_default().push(WK::Other);
6152 collect_writes(&c.body, writes, has_zero);
6153 }
6154 if let Some(f) = finally {
6155 collect_writes(f, writes, has_zero);
6156 }
6157 }
6158 Stmt::TryWithResources { resources, body, catches, finally, .. } => {
6159 for r in resources {
6160 collect_writes(r, writes, has_zero);
6161 }
6162 collect_writes(body, writes, has_zero);
6163 for c in catches {
6164 writes.entry(c.var).or_default().push(WK::Other);
6165 collect_writes(&c.body, writes, has_zero);
6166 }
6167 if let Some(f) = finally {
6168 collect_writes(f, writes, has_zero);
6169 }
6170 }
6171 Stmt::Assert { cond, msg } => {
6172 note(cond, has_zero, writes);
6173 if let Some(m) = msg {
6174 note(m, has_zero, writes);
6175 }
6176 }
6177 Stmt::Synchronized { lock, body } => {
6178 note(lock, has_zero, writes);
6179 collect_writes(body, writes, has_zero);
6180 }
6181 Stmt::Labeled { body, .. } => collect_writes(body, writes, has_zero),
6182 _ => {}
6183 }
6184 }
6185
6186 let mut writes: jdc_core::FxHashMap<u32, Vec<WK>> =
6187 jdc_core::FxHashMap::default();
6188 let mut has_zero = false;
6189 collect_writes(body, &mut writes, &mut has_zero);
6190
6191 let mut eligible: HashSet<u32> = HashSet::default();
6193 let int_typed = |v: u32| {
6194 (v as usize) < vt.vars.len() && vt.vars[v as usize].ty.erased() == JavaType::Int
6195 };
6196 loop {
6197 let mut changed = false;
6198 for (&v, ws) in writes.iter() {
6199 if eligible.contains(&v) || ws.is_empty() || !int_typed(v) {
6200 continue;
6201 }
6202 if ws.iter().all(|k| match k {
6203 WK::Zero => true,
6204 WK::Copy(u) => *u != v && eligible.contains(u),
6205 WK::Other => false,
6206 }) {
6207 eligible.insert(v);
6208 changed = true;
6209 }
6210 }
6211 if !changed {
6212 break;
6213 }
6214 }
6215 if eligible.is_empty() && !has_zero {
6216 return; }
6218
6219 #[derive(Clone, Copy, PartialEq)]
6220 enum Mode {
6221 Scan,
6222 Rewrite,
6223 }
6224 struct St<'a> {
6225 mode: Mode,
6226 vt: &'a VarTable,
6227 eligible: &'a HashSet<u32>,
6228 ref_read: HashSet<u32>,
6229 dead: HashSet<u32>,
6230 sentinels: HashSet<u32>,
6231 n_lit: usize,
6232 n_var: usize,
6233 }
6234 fn arg_ctx(t: &JavaType) -> Option<JavaType> {
6238 match t {
6239 JavaType::Object(n) if n.as_ref() == "java/lang/Object" => None,
6240 t if is_ref_jt(t) => Some(t.clone()),
6241 _ => None,
6242 }
6243 }
6244 fn walk_expr(e: &mut Expr, ctx: Option<JavaType>, st: &mut St) {
6245 walk_expr_c(e, ctx, false, st);
6246 }
6247 fn walk_expr_c(e: &mut Expr, ctx: Option<JavaType>, cast_arg: bool, st: &mut St) {
6253 let slot_ref = ctx.as_ref().is_some_and(is_ref_jt);
6254 fn mk_null(ctx: &Option<JavaType>, cast_arg: bool) -> Expr {
6255 match (cast_arg, ctx) {
6256 (true, Some(t)) => Expr::Cast {
6257 ty: TypeRef::J(t.clone()),
6258 e: Box::new(Expr::Const(ConstVal::Null)),
6259 },
6260 _ => Expr::Const(ConstVal::Null),
6261 }
6262 }
6263 match e {
6264 Expr::Const(ConstVal::Int(0)) if slot_ref => {
6265 if st.mode == Mode::Rewrite {
6266 *e = mk_null(&ctx, cast_arg);
6267 st.n_lit += 1;
6268 }
6269 return;
6270 }
6271 Expr::Local { var, .. } => {
6272 if st.eligible.contains(var) {
6273 if slot_ref {
6274 st.ref_read.insert(*var);
6275 } else {
6276 st.dead.insert(*var);
6277 }
6278 }
6279 if st.mode == Mode::Rewrite && slot_ref && st.sentinels.contains(var) {
6280 *e = mk_null(&ctx, cast_arg);
6281 st.n_var += 1;
6282 }
6283 return;
6284 }
6285 _ => {}
6286 }
6287 match e {
6288 Expr::Method { owner, desc, args, .. } => {
6289 if let Some(o) = owner {
6290 walk_expr(o, None, st);
6291 }
6292 for (i, a) in args.iter_mut().enumerate() {
6293 let c = desc.args.get(i).and_then(arg_ctx);
6294 walk_expr_c(a, c, true, st);
6295 }
6296 }
6297 Expr::Invokedynamic { desc, args, .. } => {
6298 for (i, a) in args.iter_mut().enumerate() {
6299 let c = desc.args.get(i).and_then(arg_ctx);
6300 walk_expr_c(a, c, true, st);
6301 }
6302 }
6303 Expr::New { args, arg_tys, .. } => {
6304 for (i, a) in args.iter_mut().enumerate() {
6305 let c = arg_tys.get(i).and_then(arg_ctx);
6306 walk_expr_c(a, c, true, st);
6307 }
6308 }
6309 Expr::AnonNew { args, .. } => {
6310 for a in args.iter_mut() {
6311 walk_expr(a, None, st);
6312 }
6313 }
6314 Expr::Assign { target, value, .. } => {
6315 if let Expr::Local { var: u, .. } = &**target {
6319 if st.eligible.contains(u) {
6320 return;
6321 }
6322 }
6323 let vctx = match &**target {
6324 Expr::Field { ty, .. } => {
6325 let t = ty.erased();
6326 is_ref_jt(&t).then_some(t)
6327 }
6328 Expr::ArrayIndex { array, .. } => match array.type_ref().erased() {
6329 JavaType::Array(inner) if is_ref_jt(&inner) => Some(*inner),
6330 _ => None,
6331 },
6332 _ => None,
6333 };
6334 walk_expr(target, None, st);
6335 walk_expr(value, vctx, st);
6336 }
6337 Expr::Cast { ty, e: inner } => {
6338 let t = ty.erased();
6339 let c = is_ref_jt(&t).then_some(t);
6340 walk_expr(inner, c, st);
6341 }
6342 Expr::InstanceOf { e: inner, .. } => {
6343 walk_expr(inner, Some(JavaType::Object("java/lang/Object".into())), st);
6344 }
6345 Expr::Cond { c, t, f } => {
6346 walk_expr(c, None, st);
6347 let tc = ctx.clone();
6348 walk_expr(t, tc, st);
6349 walk_expr(f, ctx, st);
6350 }
6351 Expr::NewArray { elem, dims, init, .. } => {
6352 for d in dims.iter_mut() {
6353 walk_expr(d, None, st);
6354 }
6355 if let Some(list) = init {
6356 let et = elem.erased();
6357 let c = is_ref_jt(&et).then_some(et);
6358 for slot in list.iter_mut() {
6359 walk_expr(slot, c.clone(), st);
6360 }
6361 }
6362 }
6363 Expr::NewMultiArray { dims, .. } => {
6364 for d in dims.iter_mut() {
6365 walk_expr(d, None, st);
6366 }
6367 }
6368 Expr::ArrayIndex { array, index } => {
6369 walk_expr(array, None, st);
6370 walk_expr(index, None, st);
6371 }
6372 Expr::Field { owner, .. } => {
6373 if let Some(o) = owner {
6374 walk_expr(o, None, st);
6375 }
6376 }
6377 Expr::Bin { l, r, .. } => {
6378 walk_expr(l, None, st);
6379 walk_expr(r, None, st);
6380 }
6381 Expr::Un { e: inner, .. } => walk_expr(inner, None, st),
6382 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
6383 walk_expr(inner, None, st);
6384 }
6385 Expr::StringConcat(parts) => {
6386 for p in parts.iter_mut() {
6387 if let ConcatPart::Str(x) = p {
6388 walk_expr(x, None, st);
6389 }
6390 }
6391 }
6392 Expr::Lambda(l) => {
6393 for c in l.captures.iter_mut() {
6394 walk_expr(c, None, st);
6395 }
6396 }
6397 _ => {}
6398 }
6399 }
6400 fn walk_stmt(s: &mut Stmt, st: &mut St, ret: &JavaType) {
6401 match s {
6402 Stmt::Block(v) => {
6403 for x in v.iter_mut() {
6404 walk_stmt(x, st, ret);
6405 }
6406 }
6407 Stmt::ExprStmt(e) => walk_expr(e, None, st),
6408 Stmt::LocalDef { var, init, .. } => {
6409 if let Some(e) = init {
6410 let ctx = if (*var as usize) < st.vt.vars.len() {
6411 let t = st.vt.vars[*var as usize].ty.erased();
6412 is_ref_jt(&t).then_some(t)
6413 } else {
6414 None
6415 };
6416 walk_expr(e, ctx, st);
6417 }
6418 }
6419 Stmt::Return(e) => {
6420 if let Some(x) = e {
6421 let c = is_ref_jt(ret).then(|| ret.clone());
6422 walk_expr(x, c, st);
6423 }
6424 }
6425 Stmt::Throw(e) => {
6426 walk_expr(e, Some(JavaType::Object("java/lang/Throwable".into())), st);
6427 }
6428 Stmt::If { cond, then_stmt, else_stmt } => {
6429 walk_expr(cond, None, st);
6430 walk_stmt(then_stmt, st, ret);
6431 if let Some(b) = else_stmt {
6432 walk_stmt(b, st, ret);
6433 }
6434 }
6435 Stmt::While { cond, body } => {
6436 walk_expr(cond, None, st);
6437 walk_stmt(body, st, ret);
6438 }
6439 Stmt::DoWhile { body, cond } => {
6440 walk_stmt(body, st, ret);
6441 walk_expr(cond, None, st);
6442 }
6443 Stmt::For { init, cond, update, body } => {
6444 for x in init.iter_mut() {
6445 walk_stmt(x, st, ret);
6446 }
6447 if let Some(c) = cond {
6448 walk_expr(c, None, st);
6449 }
6450 for u in update.iter_mut() {
6451 walk_expr(u, None, st);
6452 }
6453 walk_stmt(body, st, ret);
6454 }
6455 Stmt::ForEach { iterable, body, .. } => {
6456 walk_expr(iterable, None, st);
6457 walk_stmt(body, st, ret);
6458 }
6459 Stmt::Switch { selector, cases, default, .. } => {
6460 walk_expr(selector, None, st);
6461 for c in cases.iter_mut() {
6462 if let Some(g) = &mut c.guard {
6463 walk_expr(g, None, st);
6464 }
6465 for x in c.body.iter_mut() {
6466 walk_stmt(x, st, ret);
6467 }
6468 }
6469 if let Some(d) = default {
6470 walk_stmt(d, st, ret);
6471 }
6472 }
6473 Stmt::Try { body, catches, finally } => {
6474 walk_stmt(body, st, ret);
6475 for c in catches.iter_mut() {
6476 walk_stmt(&mut c.body, st, ret);
6477 }
6478 if let Some(f) = finally {
6479 walk_stmt(f, st, ret);
6480 }
6481 }
6482 Stmt::TryWithResources { resources, body, catches, finally, .. } => {
6483 for r in resources.iter_mut() {
6484 walk_stmt(r, st, ret);
6485 }
6486 walk_stmt(body, st, ret);
6487 for c in catches.iter_mut() {
6488 walk_stmt(&mut c.body, st, ret);
6489 }
6490 if let Some(f) = finally {
6491 walk_stmt(f, st, ret);
6492 }
6493 }
6494 Stmt::Assert { cond, msg } => {
6495 walk_expr(cond, None, st);
6496 if let Some(m) = msg {
6497 walk_expr(m, None, st);
6498 }
6499 }
6500 Stmt::Synchronized { lock, body } => {
6501 walk_expr(lock, Some(JavaType::Object("java/lang/Object".into())), st);
6502 walk_stmt(body, st, ret);
6503 }
6504 Stmt::TernaryValue { e } => walk_expr(e, None, st),
6505 Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
6506 walk_expr(e, Some(JavaType::Object("java/lang/Object".into())), st);
6507 }
6508 Stmt::Labeled { body, .. } => walk_stmt(body, st, ret),
6509 _ => {}
6510 }
6511 }
6512
6513 let mut st = St {
6514 mode: Mode::Scan,
6515 vt,
6516 eligible: &eligible,
6517 ref_read: HashSet::default(),
6518 dead: HashSet::default(),
6519 sentinels: HashSet::default(),
6520 n_lit: 0,
6521 n_var: 0,
6522 };
6523 if !eligible.is_empty() {
6524 walk_stmt(body, &mut st, ret);
6525 st.sentinels = eligible
6526 .iter()
6527 .copied()
6528 .filter(|v| st.ref_read.contains(v) && !st.dead.contains(v))
6529 .collect();
6530 }
6531 if st.sentinels.is_empty() && !has_zero {
6532 return;
6533 }
6534 st.mode = Mode::Rewrite;
6535 walk_stmt(body, &mut st, ret);
6536 if std::env::var_os("DDC_SENT").is_some() {
6537 eprintln!(
6538 "[sent] elig={} confirmed={} lit={} var={}",
6539 eligible.len(),
6540 st.sentinels.len(),
6541 st.n_lit,
6542 st.n_var
6543 );
6544 }
6545}
6546
6547pub fn fix_int_operand_bridges(body: &mut Stmt, vt: &VarTable) {
6555 fn side_bool(e: &Expr, vt: &VarTable) -> bool {
6556 match e {
6557 Expr::Local { var, .. } => {
6558 matches!(vt.var(*var).ty.erased(), JavaType::Boolean)
6559 }
6560 Expr::Bin {
6570 op: BinOp::And | BinOp::Or | BinOp::Xor,
6571 l,
6572 r,
6573 ..
6574 } => side_bool(l, vt) && side_bool(r, vt),
6575 other => matches!(other.type_ref().erased(), JavaType::Boolean),
6576 }
6577 }
6578 fn side_int(e: &Expr, vt: &VarTable) -> bool {
6579 if side_bool(e, vt) {
6583 return false;
6584 }
6585 matches!(
6586 match e {
6587 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
6588 other => other.type_ref().erased(),
6589 },
6590 JavaType::Int | JavaType::Short | JavaType::Byte | JavaType::Char | JavaType::Long
6591 )
6592 }
6593 fn wrap(e: Box<Expr>) -> Box<Expr> {
6594 Box::new(Expr::Cond {
6595 c: e,
6596 t: Box::new(Expr::Const(ConstVal::Int(1))),
6597 f: Box::new(Expr::Const(ConstVal::Int(0))),
6598 })
6599 }
6600 walk_stmt_exprs(body, &mut |e| {
6601 deep_rewrite(e, &mut |x| {
6602 if let Expr::Assign { target, value, op: AssignOp::Plain } = x {
6609 let tgt_ty: Option<JavaType> = match &**target {
6610 Expr::Field { ty, .. } => Some(ty.erased()),
6611 Expr::ArrayIndex { array, .. } => {
6612 match array.type_ref().erased() {
6613 JavaType::Array(el) => Some(el.as_ref().clone()),
6614 _ => None,
6615 }
6616 }
6617 Expr::Local { var, .. } => {
6622 if (*var as usize) < vt.vars.len() {
6623 Some(vt.var(*var).ty.erased())
6624 } else {
6625 None
6626 }
6627 }
6628 _ => None,
6629 };
6630 if let Some(t) = tgt_ty {
6631 let tgt_int = matches!(
6632 t,
6633 JavaType::Int
6634 | JavaType::Short
6635 | JavaType::Byte
6636 | JavaType::Char
6637 | JavaType::Long
6638 );
6639 if tgt_int && side_bool(value, vt) && !side_int(value, vt) {
6640 let taken = std::mem::replace(
6641 value,
6642 Box::new(Expr::Const(ConstVal::Null)),
6643 );
6644 let mut wrapped = wrap(taken);
6645 if !matches!(t, JavaType::Int | JavaType::Long) {
6646 wrapped = Box::new(Expr::Cast {
6647 ty: TypeRef::J(t.clone()),
6648 e: wrapped,
6649 });
6650 }
6651 *value = wrapped;
6652 } else if matches!(t, JavaType::Boolean)
6653 && side_int(value, vt)
6654 && !side_bool(value, vt)
6655 {
6656 let taken = std::mem::replace(
6657 value,
6658 Box::new(Expr::Const(ConstVal::Null)),
6659 );
6660 **value = Expr::Bin {
6661 op: BinOp::Ne,
6662 l: taken,
6663 r: Box::new(Expr::Const(ConstVal::Int(0))),
6664 ty: Some(TypeRef::J(JavaType::Boolean)),
6665 };
6666 }
6667 }
6668 }
6669 if let Expr::Cast { ty, e } = x {
6675 let t = ty.erased();
6676 let t_num = matches!(
6677 t,
6678 JavaType::Int
6679 | JavaType::Short
6680 | JavaType::Byte
6681 | JavaType::Char
6682 | JavaType::Long
6683 | JavaType::Float
6684 | JavaType::Double
6685 );
6686 if t_num && side_bool(e, vt) && !side_int(e, vt) {
6687 let taken = std::mem::replace(e, Box::new(Expr::Const(ConstVal::Null)));
6688 **e = Expr::Cond {
6689 c: taken,
6690 t: Box::new(Expr::Const(ConstVal::Int(1))),
6691 f: Box::new(Expr::Const(ConstVal::Int(0))),
6692 };
6693 } else if matches!(t, JavaType::Boolean)
6694 && side_int(e, vt)
6695 && !side_bool(e, vt)
6696 {
6697 let taken = std::mem::replace(e, Box::new(Expr::Const(ConstVal::Null)));
6698 *x = Expr::Bin {
6699 op: BinOp::Ne,
6700 l: taken,
6701 r: Box::new(Expr::Const(ConstVal::Int(0))),
6702 ty: Some(TypeRef::J(JavaType::Boolean)),
6703 };
6704 }
6705 }
6706 if let Expr::Bin { op, l, r, .. } = x {
6715 if matches!(op, BinOp::Eq | BinOp::Ne) {
6716 if matches!(&**r, Expr::Const(ConstVal::Null))
6717 && side_int(l, vt)
6718 && !side_bool(l, vt)
6719 {
6720 **r = Expr::Const(ConstVal::Int(0));
6721 } else if matches!(&**l, Expr::Const(ConstVal::Null))
6722 && side_int(r, vt)
6723 && !side_bool(r, vt)
6724 {
6725 **l = Expr::Const(ConstVal::Int(0));
6726 } else if matches!(&**r, Expr::Const(ConstVal::Null))
6727 && side_bool(l, vt)
6728 {
6729 let taken =
6730 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
6731 *x = if matches!(op, BinOp::Eq) {
6732 Expr::Un { op: UnOp::Not, e: taken }
6733 } else {
6734 *taken
6735 };
6736 } else if matches!(&**l, Expr::Const(ConstVal::Null))
6737 && side_bool(r, vt)
6738 {
6739 let taken =
6740 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
6741 *x = if matches!(op, BinOp::Eq) {
6742 Expr::Un { op: UnOp::Not, e: taken }
6743 } else {
6744 *taken
6745 };
6746 }
6747 }
6748 }
6749 if let Expr::ArrayIndex { index, .. } = x {
6753 if side_bool(index, vt) && !side_int(index, vt) {
6754 let taken =
6755 std::mem::replace(index, Box::new(Expr::Const(ConstVal::Null)));
6756 *index = wrap(taken);
6757 }
6758 }
6759 if let Expr::NewArray { dims, .. } = x {
6765 for d in dims.iter_mut() {
6766 if side_bool(d, vt) && !side_int(d, vt) {
6767 let taken =
6768 std::mem::replace(d, Expr::Const(ConstVal::Null));
6769 *d = *wrap(Box::new(taken));
6770 }
6771 }
6772 }
6773 if let Expr::Bin { op, l, r, .. } = x {
6774 let int_kind = matches!(
6775 op,
6776 BinOp::Or
6777 | BinOp::And
6778 | BinOp::Xor
6779 | BinOp::Add
6780 | BinOp::Sub
6781 | BinOp::Mul
6782 | BinOp::Div
6783 | BinOp::Rem
6784 | BinOp::Shl
6785 | BinOp::Shr
6786 | BinOp::Ushr
6787 | BinOp::Eq
6788 | BinOp::Ne
6789 | BinOp::Lt
6790 | BinOp::Ge
6791 | BinOp::Gt
6792 | BinOp::Le
6793 );
6794 if !int_kind {
6795 return;
6796 }
6797 if matches!(
6810 op,
6811 BinOp::Or
6812 | BinOp::And
6813 | BinOp::Xor
6814 | BinOp::Add
6815 | BinOp::Sub
6816 | BinOp::Mul
6817 | BinOp::Div
6818 | BinOp::Rem
6819 | BinOp::Shl
6820 | BinOp::Shr
6821 | BinOp::Ushr
6822 ) {
6823 if matches!(&**r, Expr::Const(ConstVal::Null))
6824 && (side_bool(l, vt) || side_int(l, vt))
6825 {
6826 **r = Expr::Const(ConstVal::Int(0));
6827 } else if matches!(&**l, Expr::Const(ConstVal::Null))
6828 && (side_bool(r, vt) || side_int(r, vt))
6829 {
6830 **l = Expr::Const(ConstVal::Int(0));
6831 }
6832 }
6833 let arith = matches!(
6843 op,
6844 BinOp::Add
6845 | BinOp::Sub
6846 | BinOp::Mul
6847 | BinOp::Div
6848 | BinOp::Rem
6849 | BinOp::Shl
6850 | BinOp::Shr
6851 | BinOp::Ushr
6852 );
6853 let ordered = matches!(op, BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le);
6854 if arith || ordered {
6855 let side_str = |e: &Expr| {
6856 matches!(
6857 match e {
6858 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
6859 other => other.type_ref().erased(),
6860 },
6861 JavaType::Object(ref s) if s.as_ref() == "java/lang/String"
6862 )
6863 };
6864 let concat = arith && (side_str(l) || side_str(r));
6865 if !concat {
6866 if side_bool(l, vt) {
6867 let taken =
6868 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
6869 *l = wrap(taken);
6870 }
6871 if side_bool(r, vt) {
6872 let taken =
6873 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
6874 *r = wrap(taken);
6875 }
6876 }
6877 return;
6878 }
6879 if side_bool(l, vt) && side_int(r, vt) && !side_bool(r, vt) {
6882 let taken = std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
6883 *l = wrap(taken);
6884 } else if side_bool(r, vt) && side_int(l, vt) && !side_bool(l, vt) {
6885 let taken = std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
6886 *r = wrap(taken);
6887 }
6888 }
6889 });
6890 });
6891}
6892
6893pub fn fold_value_diamonds(body: &mut Stmt, vt: &VarTable) {
6931 let Stmt::Block(stmts) = body else { return };
6932 fold_diamonds_in(stmts, vt);
6933}
6934
6935fn diamond_types_ok(a: &Expr, b: &Expr) -> bool {
6936 fn num(t: &JavaType) -> bool {
6937 matches!(
6938 t,
6939 JavaType::Byte
6940 | JavaType::Short
6941 | JavaType::Int
6942 | JavaType::Char
6943 | JavaType::Long
6944 | JavaType::Float
6945 | JavaType::Double
6946 )
6947 }
6948 if let Some(other) = if matches!(a, Expr::Const(ConstVal::Null)) {
6956 Some(b)
6957 } else if matches!(b, Expr::Const(ConstVal::Null)) {
6958 Some(a)
6959 } else {
6960 None
6961 } {
6962 return !num(&other.type_ref().erased());
6963 }
6964 let ta = a.type_ref().erased();
6965 let tb = b.type_ref().erased();
6966 ta == tb || (num(&ta) && num(&tb))
6967}
6968
6969fn single_local_assign(st: &Stmt) -> Option<(u32, Expr, Expr)> {
6977 let mut inner = st;
6978 while let Stmt::Block(v) = inner {
6979 if v.len() != 1 {
6980 return None;
6981 }
6982 inner = &v[0];
6983 }
6984 match inner {
6985 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain }) => {
6986 match &**target {
6987 Expr::Local { var, .. } => Some((*var, *target.clone(), *value.clone())),
6988 _ => None,
6989 }
6990 }
6991 _ => None,
6992 }
6993}
6994
6995fn arm_assign_inlined(st: &Stmt) -> Option<(u32, Expr, Expr)> {
7004 if let Some(r) = single_local_assign(st) {
7005 return Some(r);
7006 }
7007 let mut inner = st;
7008 while let Stmt::Block(v) = inner {
7009 if v.len() == 1 {
7010 inner = &v[0];
7011 } else {
7012 break;
7013 }
7014 }
7015 let Stmt::Block(v) = inner else { return None };
7016 if v.len() != 2 {
7017 return None;
7018 }
7019 let Stmt::LocalDef {
7020 var: x,
7021 init: Some(init),
7022 ..
7023 } = &v[0]
7024 else {
7025 return None;
7026 };
7027 let (var, tgt, val) = single_local_assign(&v[1])?;
7028 let mut count = 0usize;
7029 let mut c = val.clone();
7030 deep_rewrite(&mut c, &mut |e| {
7031 if let Expr::Local { var: lv, .. } = e {
7032 if lv == x {
7033 count += 1;
7034 }
7035 }
7036 });
7037 if count != 1 {
7038 return None;
7039 }
7040 let mut out = val.clone();
7041 deep_rewrite(&mut out, &mut |e| {
7042 if let Expr::Local { var: lv, .. } = e {
7043 if lv == x {
7044 *e = init.clone();
7045 }
7046 }
7047 });
7048 Some((var, tgt, out))
7049}
7050
7051fn mk_ternary_assign(tgt: Expr, c: Expr, a: Expr, b: Expr) -> Stmt {
7052 Stmt::ExprStmt(Expr::Assign {
7053 target: Box::new(tgt),
7054 op: AssignOp::Plain,
7055 value: Box::new(Expr::Cond {
7056 c: Box::new(c),
7057 t: Box::new(a),
7058 f: Box::new(b),
7059 }),
7060 })
7061}
7062
7063fn same_render_var(v1: u32, v2: u32, vt: &VarTable) -> bool {
7068 let (a, b) = (vt.vars.get(v1 as usize), vt.vars.get(v2 as usize));
7069 match (a, b) {
7070 (Some(x), Some(y)) => x.name == y.name && x.ty.erased() == y.ty.erased(),
7071 _ => false,
7072 }
7073}
7074
7075fn canon_stmts_eq(a: &[Stmt], b: &[Stmt], vt: &VarTable) -> bool {
7082 if a.len() != b.len() {
7083 return false;
7084 }
7085 let mut map: std::collections::HashMap<String, u32> = std::collections::HashMap::default();
7086 let canon = |s: &Stmt, map: &mut std::collections::HashMap<String, u32>| -> Stmt {
7087 let mut c = s.clone();
7088 walk_stmt_exprs(&mut c, &mut |e| {
7089 deep_rewrite(e, &mut |x| {
7090 if let Expr::Local { var, .. } = x {
7091 let key = match vt.vars.get(*var as usize) {
7092 Some(vi) => format!("{}#{:?}", vi.name, vi.ty.erased()),
7093 None => format!("v{}", var),
7094 };
7095 let id = *map.entry(key).or_insert(*var);
7096 *x = Expr::Local {
7097 var: id,
7098 ty: vt.var(id).ty.clone(),
7099 };
7100 }
7101 });
7102 });
7103 c
7104 };
7105 a.iter()
7106 .zip(b.iter())
7107 .all(|(x, y)| canon(x, &mut map) == canon(y, &mut map))
7108}
7109
7110fn fold_diamonds_in(stmts: &mut Vec<Stmt>, vt: &VarTable) {
7111 let mut i = 0usize;
7112 while i < stmts.len() {
7113 match &mut stmts[i] {
7114 Stmt::Block(v) => fold_diamonds_in(v, vt),
7115 Stmt::If { then_stmt, else_stmt, .. } => {
7116 if let Stmt::Block(v) = &mut **then_stmt {
7117 fold_diamonds_in(v, vt);
7118 }
7119 if let Some(e) = else_stmt {
7120 if let Stmt::Block(v) = &mut **e {
7121 fold_diamonds_in(v, vt);
7122 }
7123 }
7124 }
7125 _ => {}
7126 }
7127 let shape1 = if let Stmt::If { cond, then_stmt, else_stmt: Some(el), .. } = &stmts[i] {
7129 match (arm_assign_inlined(then_stmt), arm_assign_inlined(el)) {
7130 (Some((v1, tgt, a)), Some((v2, _, b)))
7131 if v1 == v2 && diamond_types_ok(&a, &b) =>
7132 {
7133 Some(mk_ternary_assign(tgt, cond.clone(), a, b))
7134 }
7135 _ => None,
7136 }
7137 } else {
7138 None
7139 };
7140 if let Some(f) = shape1 {
7141 stmts[i] = f;
7142 i += 1;
7143 continue;
7144 }
7145 let shape3 = if let Stmt::If {
7158 cond,
7159 then_stmt,
7160 else_stmt: Some(el),
7161 ..
7162 } = &stmts[i]
7163 {
7164 let arm = |st: &Stmt| -> Option<((u32, Expr, Expr), Vec<Stmt>)> {
7165 let Stmt::Block(v) = st else { return None };
7166 if v.len() < 2 {
7167 return None;
7168 }
7169 let first = single_local_assign(&v[0])?;
7170 Some((first, v[1..].to_vec()))
7171 };
7172 match (arm(then_stmt), arm(el)) {
7173 (Some(((v1, tgt1, a), mut rest_t)), Some(((v2, _t2, b), rest_e)))
7174 if !rest_t.is_empty()
7175 && is_bare_ctor_call(first_leaf_stmt(&rest_t[0]))
7176 && stmts_count_writes(&rest_t, v1) == 0
7177 && stmts_count_writes(&rest_t, v2) == 0
7178 && diamond_types_ok(&a, &b) =>
7179 {
7180 if v1 == v2 || same_render_var(v1, v2, vt) {
7181 if canon_stmts_eq(&rest_t, &rest_e, vt) {
7183 Some((mk_ternary_assign(tgt1, cond.clone(), a, b), rest_t))
7184 } else {
7185 None
7186 }
7187 } else if stmts_read_var(&rest_t, v1) == 1
7188 && stmts_read_var(&rest_e, v2) == 1
7189 && stmts_read_var(&rest_t, v2) == 0
7190 && stmts_read_var(&rest_e, v1) == 0
7191 {
7192 let mut unified = rest_e.clone();
7200 for st in unified.iter_mut() {
7201 let mut c2 = st.clone();
7202 walk_stmt_exprs(&mut c2, &mut |e| {
7203 deep_rewrite(e, &mut |x| {
7204 if let Expr::Local { var, .. } = x {
7205 if *var == v2 {
7206 *x = Expr::Local {
7207 var: v1,
7208 ty: vt.var(v1).ty.clone(),
7209 };
7210 }
7211 }
7212 });
7213 });
7214 *st = c2;
7215 }
7216 if unified != rest_t {
7217 None
7218 } else {
7219 let tern = Expr::Cond {
7220 c: Box::new(cond.clone()),
7221 t: Box::new(a.clone()),
7222 f: Box::new(b.clone()),
7223 };
7224 for st in rest_t.iter_mut() {
7225 let mut c2 = st.clone();
7226 walk_stmt_exprs(&mut c2, &mut |e| {
7227 deep_rewrite(e, &mut |x| {
7228 if let Expr::Local { var, .. } = x {
7229 if *var == v1 {
7230 *x = tern.clone();
7231 }
7232 }
7233 });
7234 });
7235 *st = c2;
7236 }
7237 let first = rest_t.remove(0);
7241 Some((first, rest_t))
7242 }
7243 } else {
7244 None
7245 }
7246 }
7247 _ => None,
7248 }
7249 } else {
7250 None
7251 };
7252 if let Some((folded, rest)) = shape3 {
7253 stmts[i] = folded;
7254 stmts.splice(i + 1..i + 1, rest);
7255 i += 1;
7256 continue;
7257 }
7258 if i + 1 < stmts.len() {
7268 let shape2 = {
7288 let s_i = single_local_assign(&stmts[i]);
7289 if let Stmt::If {
7290 cond,
7291 then_stmt,
7292 else_stmt,
7293 ..
7294 } = &stmts[i + 1]
7295 {
7296 let then_a = single_local_assign(then_stmt);
7297 let else_a = else_stmt.as_deref().and_then(single_local_assign);
7298 let then_empty =
7299 matches!(&**then_stmt, Stmt::Block(v) if v.is_empty());
7300 let else_empty = else_stmt
7301 .as_deref()
7302 .map(|e| matches!(e, Stmt::Block(v) if v.is_empty()))
7303 .unwrap_or(true);
7304 let try_fold = |over: Option<(u32, Expr, Expr)>,
7305 keep_over: bool|
7306 -> Option<Stmt> {
7307 let (v2, tgt2, a) = over?;
7308 let (v1, _t1, b) = s_i.as_ref()?;
7309 if !(*v1 == v2 || same_render_var(*v1, v2, vt)) {
7310 return None;
7311 }
7312 let keep = if *v1 == v2 {
7313 b.clone()
7314 } else {
7315 Expr::Local {
7316 var: *v1,
7317 ty: vt.var(*v1).ty.clone(),
7318 }
7319 };
7320 if !diamond_types_ok(&a, &keep) {
7321 return None;
7322 }
7323 if keep_over {
7324 Some(mk_ternary_assign(tgt2, cond.clone(), keep, a))
7325 } else {
7326 Some(mk_ternary_assign(tgt2, cond.clone(), a, keep))
7327 }
7328 };
7329 if then_a.is_some() && else_empty {
7330 try_fold(then_a, false)
7331 } else if else_a.is_some() && then_empty {
7332 try_fold(else_a, true)
7333 } else {
7334 None
7335 }
7336 } else {
7337 None
7338 }
7339 };
7340 let mut lead_fold: Option<(Stmt, Stmt)> = None;
7349 if shape2.is_none() && i + 1 < stmts.len() {
7350 let s_i2 = single_local_assign(&stmts[i]);
7351 let leading = |st: &Stmt| -> Option<((u32, Expr, Expr), Vec<Stmt>)> {
7352 let Stmt::Block(v) = st else { return None };
7353 if v.len() < 2 {
7354 return None;
7355 }
7356 let first = single_local_assign(&v[0])?;
7357 Some((first, v[1..].to_vec()))
7358 };
7359 if let (Some((v1, _t1, b)), Stmt::If { cond, then_stmt, else_stmt, .. }) =
7360 (&s_i2, &stmts[i + 1])
7361 {
7362 let else_absent = else_stmt.is_none()
7363 || else_stmt
7364 .as_deref()
7365 .is_some_and(|e| matches!(e, Stmt::Block(v) if v.is_empty()));
7366 let then_absent = matches!(&**then_stmt, Stmt::Block(v) if v.is_empty());
7367 let try_lead = |over: Option<((u32, Expr, Expr), Vec<Stmt>)>,
7368 keep_over: bool|
7369 -> Option<(Stmt, Stmt)> {
7370 let ((v2, tgt2, a), rest) = over?;
7371 if !(*v1 == v2 || same_render_var(*v1, v2, vt)) {
7372 return None;
7373 }
7374 if stmts_count_writes(&rest, *v1) != 0
7375 || stmts_count_writes(&rest, v2) != 0
7376 {
7377 return None;
7378 }
7379 let keep = if *v1 == v2 {
7380 b.clone()
7381 } else {
7382 Expr::Local {
7383 var: *v1,
7384 ty: vt.var(*v1).ty.clone(),
7385 }
7386 };
7387 if !diamond_types_ok(&a, &keep) {
7388 return None;
7389 }
7390 let t = if keep_over {
7391 mk_ternary_assign(tgt2, cond.clone(), keep, a)
7392 } else {
7393 mk_ternary_assign(tgt2, cond.clone(), a, keep)
7394 };
7395 let mut new_if = stmts[i + 1].clone();
7396 if let Stmt::If { then_stmt, else_stmt, .. } = &mut new_if {
7397 if keep_over {
7398 *else_stmt = Some(Box::new(Stmt::Block(rest)));
7399 } else {
7400 **then_stmt = Stmt::Block(rest);
7401 }
7402 }
7403 Some((t, new_if))
7404 };
7405 if else_absent {
7406 lead_fold = try_lead(leading(then_stmt), false);
7407 } else if then_absent {
7408 if let Some(e) = else_stmt {
7409 lead_fold = try_lead(leading(e), true);
7410 }
7411 }
7412 }
7413 }
7414 if let Some((folded, new_if)) = lead_fold {
7415 stmts[i] = folded;
7416 stmts[i + 1] = new_if;
7417 continue;
7418 }
7419 if let Some(f) = shape2 {
7420 stmts[i] = f;
7421 stmts.remove(i + 1);
7422 continue; }
7424 }
7425 i += 1;
7426 }
7427}
7428
7429pub fn fold_bool_value_diamonds(body: &mut Stmt, vt: &VarTable) {
7430 fn is_bool_var(v: u32, vt: &VarTable) -> bool {
7431 (v as usize) < vt.vars.len()
7432 && matches!(vt.vars[v as usize].ty.erased(), JavaType::Boolean)
7433 }
7434 fn bool_const(e: &Expr) -> Option<bool> {
7435 fn cint(e: &Expr) -> Option<i32> {
7436 match e {
7437 Expr::Const(ConstVal::Int(n)) => Some(*n),
7438 _ => None,
7439 }
7440 }
7441 match e {
7442 Expr::Const(ConstVal::Int(0)) => Some(false),
7443 Expr::Const(ConstVal::Int(1)) => Some(true),
7444 Expr::Bin { op: op @ (BinOp::Eq | BinOp::Ne), l, r, .. } => {
7448 let (a, b) = (cint(l)?, cint(r)?);
7449 let eq = a == b;
7450 Some(if matches!(op, BinOp::Eq) { eq } else { !eq })
7451 }
7452 _ => None,
7453 }
7454 }
7455 fn reads_var(e: &Expr, v: u32) -> bool {
7456 let mut hit = false;
7457 visit_exprs(e, &mut |x| {
7458 if let Expr::Local { var, .. } = x {
7459 if *var == v {
7460 hit = true;
7461 }
7462 }
7463 });
7464 hit
7465 }
7466 fn bool_valued(e: &Expr, vt: &VarTable) -> bool {
7470 fn side(e: &Expr, vt: &VarTable) -> bool {
7471 bool_const(e).is_some() || bool_valued(e, vt)
7472 }
7473 match e {
7474 Expr::Local { var, .. } => is_bool_var(*var, vt),
7475 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
7476 Expr::InstanceOf { .. } => true,
7477 Expr::Un { op: UnOp::Not, e: x, .. } => bool_valued(x, vt) || bool_const(x).is_some(),
7478 Expr::Bin { op, l, r, .. } => match op {
7479 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le => true,
7480 BinOp::LogAnd | BinOp::LogOr => {
7483 side(l, vt) && side(r, vt)
7484 }
7485 _ => false,
7486 },
7487 Expr::Cond { t, f, .. } => side(t, vt) && side(f, vt),
7488 _ => false,
7489 }
7490 }
7491 fn lone_assign(
7500 b: &Stmt,
7501 v: u32,
7502 vt: &VarTable,
7503 an: &VarAnalysis,
7504 consumed: &mut HashSet<u32>,
7505 ) -> Option<Expr> {
7506 let l = flat_list(b);
7507 if l.len() == 2 {
7508 let (u, e): (u32, &Expr) = match l[0] {
7509 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e),
7510 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
7511 match &**target {
7512 Expr::Local { var, .. } => (*var, &**value),
7513 _ => return None,
7514 }
7515 }
7516 _ => return None,
7517 };
7518 if u == v || consumed.contains(&u) {
7519 return None;
7520 }
7521 if an.assigns.get(u as usize).copied().unwrap_or(0) != 1
7528 || an.reads.get(u as usize).copied().unwrap_or(0) != 1
7529 {
7530 return None;
7531 }
7532 let (tv, rv) = match l[1] {
7533 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
7534 match (&**target, &**value) {
7535 (Expr::Local { var: t, .. }, Expr::Local { var: r, .. }) => (*t, *r),
7536 _ => return None,
7537 }
7538 }
7539 Stmt::LocalDef { var, init: Some(Expr::Local { var: r, .. }), .. } => (*var, *r),
7540 _ => return None,
7541 };
7542 if tv != v || rv != u {
7543 return None;
7544 }
7545 if reads_var(e, v) || !(bool_valued(e, vt) || bool_const(e).is_some()) {
7546 return None;
7547 }
7548 consumed.insert(u);
7549 return Some(e.clone());
7550 }
7551 if l.len() != 1 {
7552 return None;
7553 }
7554 let (target, value) = match l[0] {
7555 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => (target, value),
7556 Stmt::LocalDef { var, init: Some(value), .. } if *var == v => {
7557 if reads_var(value, v) || !(bool_valued(value, vt) || bool_const(value).is_some()) {
7558 return None;
7559 }
7560 return Some(value.clone());
7561 }
7562 _ => return None,
7563 };
7564 let Expr::Local { var: tv, .. } = &**target else { return None };
7565 if *tv != v || reads_var(value, v) {
7566 return None;
7567 }
7568 if !(bool_valued(value, vt) || bool_const(value).is_some()) {
7569 return None;
7570 }
7571 Some((**value).clone())
7572 }
7573 fn not(e: Expr) -> Expr {
7574 Expr::Un { op: UnOp::Not, e: Box::new(e) }
7575 }
7576 fn simplify_bool(e: Expr) -> Expr {
7581 match e {
7582 Expr::Un { op: UnOp::Not, e: inner, .. } => match *inner {
7583 Expr::Bin { op: BinOp::Ne, l, r, ty } => {
7588 simplify_bool(Expr::Bin { op: BinOp::Eq, l, r, ty })
7589 }
7590 Expr::Bin { op: BinOp::Eq, l, r, ty } => {
7591 simplify_bool(Expr::Bin { op: BinOp::Ne, l, r, ty })
7592 }
7593 other => Expr::Un { op: UnOp::Not, e: Box::new(simplify_bool(other)) },
7594 },
7595 Expr::Bin { op: op @ (BinOp::Eq | BinOp::Ne), l, r, ty } => {
7596 fn cond01(e: &Expr) -> Option<Expr> {
7598 if let Expr::Cond { c, t, f } = e {
7599 if matches!(&**t, Expr::Const(ConstVal::Int(1)))
7600 && matches!(&**f, Expr::Const(ConstVal::Int(0)))
7601 {
7602 return Some((**c).clone());
7603 }
7604 }
7605 None
7606 }
7607 let (lc, rc) = (cond01(&l), cond01(&r));
7608 match (lc, rc) {
7609 (Some(c), None) => match (&*r, op) {
7610 (Expr::Const(ConstVal::Int(1)), BinOp::Eq) => c,
7611 (Expr::Const(ConstVal::Int(1)), BinOp::Ne) => not(c),
7612 (Expr::Const(ConstVal::Int(0)), BinOp::Eq) => not(c),
7613 (Expr::Const(ConstVal::Int(0)), BinOp::Ne) => c,
7614 _ => Expr::Bin { op, l: Box::new(c), r, ty },
7615 },
7616 (None, Some(c)) => match (&*l, op) {
7617 (Expr::Const(ConstVal::Int(1)), BinOp::Eq) => c,
7618 (Expr::Const(ConstVal::Int(1)), BinOp::Ne) => not(c),
7619 (Expr::Const(ConstVal::Int(0)), BinOp::Eq) => not(c),
7620 (Expr::Const(ConstVal::Int(0)), BinOp::Ne) => c,
7621 _ => Expr::Bin { op, l, r: Box::new(c), ty },
7622 },
7623 _ => Expr::Bin { op, l, r, ty },
7624 }
7625 }
7626 other => other,
7627 }
7628 }
7629 fn bin(op: BinOp, l: Expr, r: Expr) -> Expr {
7630 Expr::Bin { op, l: Box::new(l), r: Box::new(r), ty: None }
7631 }
7632
7633 for _round in 0..4 {
7634 let mut changed = false;
7635 let mut analysis = VarAnalysis {
7636 assigns: Vec::new(),
7637 reads: Vec::new(),
7638 values: Vec::new(),
7639 };
7640 analyze_vars(body, &mut analysis);
7641 let mut consumed: HashSet<u32> = HashSet::default();
7642 walk_mut_deep(body, &mut |st| {
7643 let Stmt::Block(list) = st else { return };
7644 let mut i = 0usize;
7645 while i + 1 < list.len() {
7646 let folded = try_at(list, i, vt, &is_bool_var, &bool_const, &reads_var, &lone_assign, &analysis, &mut consumed);
7647 match folded {
7648 Some(expr) => {
7649 let is_def = matches!(&list[i], Stmt::LocalDef { .. });
7650 let var = match &list[i] {
7651 Stmt::LocalDef { var, .. } => *var,
7652 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
7653 match &**target { Expr::Local { var, .. } => *var, _ => unreachable!() }
7654 }
7655 _ => unreachable!(),
7656 };
7657 let (is_final, force_type) = match &list[i] {
7658 Stmt::LocalDef { is_final, force_type, .. } => (*is_final, *force_type),
7659 _ => (false, false),
7660 };
7661 list[i] = if is_def {
7662 Stmt::LocalDef { var, init: Some(expr), is_final, force_type }
7663 } else {
7664 Stmt::ExprStmt(Expr::Assign {
7665 target: Box::new(Expr::Local {
7666 var,
7667 ty: TypeRef::J(JavaType::Boolean),
7668 }),
7669 op: AssignOp::Plain,
7670 value: Box::new(expr),
7671 })
7672 };
7673 list.remove(i + 1);
7674 changed = true;
7675 }
7678 None => i += 1,
7679 }
7680 }
7681 });
7682 if !changed {
7683 break;
7684 }
7685 }
7686
7687 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
7688 fn try_at(
7689 list: &[Stmt],
7690 i: usize,
7691 vt: &VarTable,
7692 is_bool_var: &dyn Fn(u32, &VarTable) -> bool,
7693 bool_const: &dyn Fn(&Expr) -> Option<bool>,
7694 reads_var: &dyn Fn(&Expr, u32) -> bool,
7695 lone_assign: &dyn Fn(&Stmt, u32, &VarTable, &VarAnalysis, &mut HashSet<u32>) -> Option<Expr>,
7696 an: &VarAnalysis,
7697 consumed: &mut HashSet<u32>,
7698 ) -> Option<Expr> {
7699 let (v, base) = match &list[i] {
7701 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
7702 match &**target {
7703 Expr::Local { var, .. } => {
7704 let b = bool_const(value)?;
7707 if !is_bool_var(*var, vt) { return None; }
7708 (*var, b)
7709 }
7710 _ => return None,
7711 }
7712 }
7713 Stmt::LocalDef { var, init: Some(e), .. } => {
7714 let b = bool_const(e)?;
7715 if !is_bool_var(*var, vt) { return None; }
7716 (*var, b)
7717 }
7718 _ => return None,
7719 };
7720 let Stmt::If { cond, then_stmt, else_stmt } = &list[i + 1] else {
7721 return None;
7722 };
7723 if reads_var(cond, v) {
7724 return None;
7725 }
7726 let mut touches_v = false;
7727 visit_exprs(cond, &mut |x| {
7728 if let Expr::Assign { target, .. } = x {
7729 if let Expr::Local { var, .. } = &**target {
7730 if *var == v { touches_v = true; }
7731 }
7732 }
7733 });
7734 if touches_v {
7735 return None;
7736 }
7737 let (cond_n, then_arm, else_arm): (Expr, &Stmt, Option<&Stmt>) =
7740 if flat_list(then_stmt).is_empty() && else_stmt.is_some() {
7741 (not(cond.clone()), else_stmt.as_ref().unwrap(), None)
7742 } else {
7743 (cond.clone(), then_stmt.as_ref(), else_stmt.as_deref())
7744 };
7745 let e1 = lone_assign(then_arm, v, vt, an, consumed)?;
7746 let e2 = match else_arm {
7747 None => None,
7748 Some(b) => Some(lone_assign(b, v, vt, an, consumed)?),
7749 };
7750 let e1c = bool_const(&e1);
7751 let c = simplify_bool(cond_n);
7752 Some(match e2 {
7753 None => {
7754 if !base {
7755 match e1c {
7756 Some(true) => c,
7757 Some(false) => return None, None => bin(BinOp::LogAnd, c, e1),
7759 }
7760 } else {
7761 match e1c {
7762 Some(false) => not(c),
7763 Some(true) => return None,
7764 None => bin(BinOp::LogOr, not(c), e1),
7765 }
7766 }
7767 }
7768 Some(e2) => {
7769 if e1 == e2 {
7771 return None; }
7773 Expr::Cond { c: Box::new(c), t: Box::new(e1), f: Box::new(e2) }
7774 }
7775 })
7776 }
7777}
7778
7779pub fn idiom_compounds(body: &mut Stmt, vt: &VarTable) {
7780 walk_mut_deep(body, &mut |st| {
7781 let Stmt::ExprStmt(e) = st else { return };
7782 let Expr::Assign { target, op: AssignOp::Plain, value } = &mut *e else {
7783 return;
7784 };
7785 enum Tgt {
7788 Loc(u32),
7789 Fld,
7790 }
7791 let tgt = match &**target {
7792 Expr::Local { var, .. } => Tgt::Loc(*var),
7793 Expr::Field { owner, .. }
7794 if owner.is_none()
7795 || matches!(&**owner.as_ref().unwrap(), Expr::This) =>
7796 {
7797 Tgt::Fld
7798 }
7799 _ => return,
7800 };
7801 let Expr::Bin { op, l, r, .. } = &mut **value else {
7802 return;
7803 };
7804 match (&tgt, &**l) {
7806 (Tgt::Loc(v), Expr::Local { var, .. }) if *var == *v => {}
7807 (Tgt::Fld, Expr::Field { cls: c2, name: n2, .. }) => {
7808 let Expr::Field { cls: c1, name: n1, .. } = &**target else {
7809 return;
7810 };
7811 if c1 != c2 || n1 != n2 {
7812 return;
7813 }
7814 }
7815 _ => return,
7816 }
7817 let one = match &**r {
7819 Expr::Const(ConstVal::Int(1)) => Some(1i64),
7820 Expr::Const(ConstVal::Int(-1)) => Some(-1),
7821 Expr::Const(ConstVal::Long(1)) => Some(1),
7822 Expr::Const(ConstVal::Long(-1)) => Some(-1),
7823 _ => None,
7824 };
7825 if *op == BinOp::Add || *op == BinOp::Sub {
7826 if let Some(mut d) = one {
7827 if *op == BinOp::Sub {
7828 d = -d;
7829 }
7830 let tgt_ty = match &tgt {
7831 Tgt::Loc(v) => vt.var(*v).ty.erased(),
7832 Tgt::Fld => {
7833 let Expr::Field { ty, .. } = &**target else {
7834 return;
7835 };
7836 ty.erased()
7837 }
7838 };
7839 let numeric = matches!(
7840 tgt_ty,
7841 JavaType::Int
7842 | JavaType::Long
7843 | JavaType::Short
7844 | JavaType::Byte
7845 | JavaType::Char
7846 | JavaType::Float
7847 | JavaType::Double
7848 );
7849 if numeric {
7850 let wide = matches!(tgt_ty, JavaType::Long);
7851 let inner = match &tgt {
7852 Tgt::Loc(v) => Expr::Local {
7853 var: *v,
7854 ty: vt.var(*v).ty.clone(),
7855 },
7856 Tgt::Fld => (**target).clone(),
7857 };
7858 *e = Expr::PostIncDec {
7859 e: Box::new(inner),
7860 delta: d,
7861 wide,
7862 };
7863 return;
7864 }
7865 }
7866 }
7867 let cop = match op {
7868 BinOp::Add => AssignOp::Add,
7869 BinOp::Sub => AssignOp::Sub,
7870 BinOp::Mul => AssignOp::Mul,
7871 BinOp::Div => AssignOp::Div,
7872 BinOp::Rem => AssignOp::Rem,
7873 BinOp::Shl => AssignOp::Shl,
7874 BinOp::Shr => AssignOp::Shr,
7875 BinOp::Ushr => AssignOp::Ushr,
7876 BinOp::And => AssignOp::And,
7877 BinOp::Or => AssignOp::Or,
7878 BinOp::Xor => AssignOp::Xor,
7879 _ => return,
7880 };
7881 let rhs = std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
7882 let new_target = match &tgt {
7883 Tgt::Loc(v) => Box::new(Expr::Local {
7884 var: *v,
7885 ty: vt.var(*v).ty.clone(),
7886 }),
7887 Tgt::Fld => target.clone(),
7888 };
7889 *e = Expr::Assign {
7890 target: new_target,
7891 op: cop,
7892 value: rhs,
7893 };
7894 });
7895}
7896
7897pub fn fix_bool_xor(body: &mut Stmt, vt: &VarTable, ret_bool: bool) {
7898 fn is_one(e: &Expr) -> bool {
7899 matches!(e, Expr::Const(ConstVal::Int(1)))
7900 }
7901 fn bty(e: &Expr, vt: &VarTable) -> bool {
7902 match e {
7903 Expr::Local { var, .. } => {
7904 matches!(vt.var(*var).ty.erased(), JavaType::Boolean)
7905 }
7906 Expr::Bin {
7915 op: BinOp::And | BinOp::Or | BinOp::Xor,
7916 l,
7917 r,
7918 ..
7919 } => bty(l, vt) && bty(r, vt),
7920 other => matches!(other.type_ref().erased(), JavaType::Boolean),
7921 }
7922 }
7923 fn inv_of_xor(x: &Expr, vt: &VarTable) -> Option<Expr> {
7928 if let Expr::Bin { op: BinOp::Xor, l, r, .. } = x {
7929 let (int_side, one_side) = if is_one(r) {
7930 (l, r)
7931 } else if is_one(l) {
7932 (r, l)
7933 } else {
7934 return None;
7935 };
7936 let _ = one_side;
7937 if !bty(int_side, vt) {
7938 return Some(Expr::Bin {
7939 op: BinOp::Eq,
7940 l: int_side.clone(),
7941 r: Box::new(Expr::Const(ConstVal::Int(0))),
7942 ty: Some(TypeRef::J(JavaType::Boolean)),
7943 });
7944 }
7945 }
7946 None
7947 }
7948 let tgt_bool = |target: &Expr, vt: &VarTable| -> bool {
7949 match target {
7950 Expr::Local { var, .. } => matches!(vt.var(*var).ty.erased(), JavaType::Boolean),
7951 Expr::Field { ty, .. } => matches!(ty.erased(), JavaType::Boolean),
7952 _ => false,
7953 }
7954 };
7955 walk_mut_deep(body, &mut |st| match st {
7956 Stmt::Return(Some(e)) if ret_bool => {
7957 if let Some(r) = inv_of_xor(e, vt) {
7958 *e = r;
7959 }
7960 }
7961 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain, .. }) => {
7962 if tgt_bool(target, vt) {
7963 if let Some(r) = inv_of_xor(value, vt) {
7964 **value = r;
7965 }
7966 }
7967 }
7968 Stmt::LocalDef { var, init: Some(value), .. }
7969 if matches!(vt.var(*var).ty.erased(), JavaType::Boolean) =>
7970 {
7971 if let Some(r) = inv_of_xor(value, vt) {
7972 *value = r;
7973 }
7974 }
7975 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
7976 if let Some(r) = inv_of_xor(cond, vt) {
7977 *cond = r;
7978 }
7979 }
7980 _ => {}
7981 });
7982 fn ity(e: &Expr, vt: &VarTable) -> bool {
7983 matches!(
7984 match e {
7985 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
7986 other => other.type_ref().erased(),
7987 },
7988 JavaType::Int | JavaType::Short | JavaType::Byte | JavaType::Char
7989 )
7990 }
7991 fn mixed_rewrite(e: &mut Expr, vt: &VarTable) {
8000 deep_rewrite(e, &mut |x| {
8001 if let Expr::Bin { op, l, r, ty } = x {
8002 match op {
8003 BinOp::Xor => {
8004 if is_one(r) && bty(l, vt) {
8005 let taken =
8006 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
8007 *x = Expr::Un { op: UnOp::Not, e: taken };
8008 } else if is_one(l) && bty(r, vt) {
8009 let taken =
8010 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
8011 *x = Expr::Un { op: UnOp::Not, e: taken };
8012 }
8013 }
8014 BinOp::Or | BinOp::And => {
8015 if bty(l, vt) && ity(r, vt) {
8016 let taken =
8017 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
8018 **r = Expr::Bin {
8019 op: BinOp::Ne,
8020 l: taken,
8021 r: Box::new(Expr::Const(ConstVal::Int(0))),
8022 ty: Some(TypeRef::J(JavaType::Boolean)),
8023 };
8024 *ty = Some(TypeRef::J(JavaType::Boolean));
8025 } else if ity(l, vt) && bty(r, vt) {
8026 let taken =
8027 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
8028 **l = Expr::Bin {
8029 op: BinOp::Ne,
8030 l: taken,
8031 r: Box::new(Expr::Const(ConstVal::Int(0))),
8032 ty: Some(TypeRef::J(JavaType::Boolean)),
8033 };
8034 *ty = Some(TypeRef::J(JavaType::Boolean));
8035 }
8036 }
8037 _ => {}
8038 }
8039 }
8040 });
8041 }
8042 walk_mut_deep(body, &mut |st| match st {
8045 Stmt::Return(Some(e)) if ret_bool => mixed_rewrite(e, vt),
8046 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain, .. }) => {
8047 if tgt_bool(target, vt) {
8048 mixed_rewrite(value, vt);
8049 }
8050 }
8051 Stmt::LocalDef { var, init: Some(e), .. }
8052 if matches!(vt.var(*var).ty.erased(), JavaType::Boolean) =>
8053 {
8054 mixed_rewrite(e, vt);
8055 }
8056 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
8057 mixed_rewrite(cond, vt);
8058 }
8059 _ => {}
8060 });
8061 walk_stmt_exprs(body, &mut |e| {
8064 deep_rewrite(e, &mut |x| {
8065 if let Expr::Cond { c, .. } = x {
8066 mixed_rewrite(c, vt);
8067 }
8068 });
8069 });
8070}
8071
8072pub fn booleanize(vt: &mut VarTable, body: &mut Stmt, ret_bool: bool) -> usize {
8078 let n0 = vt.vars.len();
8085 let mut derefed = vec![false; n0];
8086 mark_derefs(body, &mut derefed);
8087 let mut total = 0usize;
8088 for _ in 0..4 {
8089 let n = booleanize_round(vt, body, ret_bool, &derefed);
8090 if n == 0 {
8091 break;
8092 }
8093 total += n;
8094 }
8095 total
8096}
8097
8098fn mark_deref_expr(e: &Expr, derefed: &mut [bool]) {
8102 match e {
8103 Expr::Field { owner: Some(o), is_static: false, .. }
8104 | Expr::Method { owner: Some(o), .. } => {
8105 if let Expr::Local { var, .. } = &**o {
8106 if (*var as usize) < derefed.len() {
8107 derefed[*var as usize] = true;
8108 }
8109 }
8110 }
8111 Expr::ArrayIndex { array, .. } => {
8112 if let Expr::Local { var, .. } = &**array {
8113 if (*var as usize) < derefed.len() {
8114 derefed[*var as usize] = true;
8115 }
8116 }
8117 }
8118 _ => {}
8119 }
8120 for_each_child(e, &mut |c| mark_deref_expr(c, derefed));
8121}
8122
8123fn mark_derefs(body: &Stmt, derefed: &mut [bool]) {
8124 walk_all(body, &mut |st| {
8125 match st {
8126 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
8127 mark_deref_expr(e, derefed);
8128 }
8129 Stmt::Return(Some(e)) => mark_deref_expr(e, derefed),
8130 Stmt::LocalDef { init: Some(e), .. } => mark_deref_expr(e, derefed),
8131 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
8132 mark_deref_expr(cond, derefed);
8133 }
8134 Stmt::For { init, .. } => {
8135 for x in init {
8136 mark_derefs(x, derefed);
8137 }
8138 }
8139 Stmt::Switch { selector, .. } => mark_deref_expr(selector, derefed),
8140 Stmt::ForEach { iterable, .. } => mark_deref_expr(iterable, derefed),
8141 _ => {}
8142 }
8143 });
8144}
8145
8146fn booleanize_round(
8147 vt: &mut VarTable,
8148 body: &mut Stmt,
8149 ret_bool: bool,
8150 derefed: &[bool],
8151) -> usize {
8152 let n = vt.vars.len();
8153 let mut in_cond = vec![false; n];
8154
8155 walk_all(body, &mut |st| {
8158 let cond: Option<&Expr> = match st {
8159 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
8160 Some(cond)
8161 }
8162 _ => None,
8163 };
8164 if let Some(c) = cond {
8165 if let Expr::Bin {
8166 op: BinOp::Eq | BinOp::Ne,
8167 l,
8168 r,
8169 ..
8170 } = c
8171 {
8172 for (x, other) in [(l, r), (r, l)] {
8173 if let (Expr::Local { var, .. }, Expr::Const(ConstVal::Int(0))) =
8174 (&**x, &**other)
8175 {
8176 if (*var as usize) < n {
8177 in_cond[*var as usize] = true;
8178 }
8179 }
8180 }
8181 }
8182 }
8183 if ret_bool {
8188 if let Stmt::Return(Some(Expr::Local { var, .. })) = st {
8189 if (*var as usize) < n {
8190 in_cond[*var as usize] = true;
8191 }
8192 }
8193 }
8194 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = st {
8201 if let Expr::Field { ty, .. } = &**target {
8202 if ty.erased() == JavaType::Boolean {
8203 if let Expr::Local { var, .. } = &**value {
8204 if (*var as usize) < n {
8205 in_cond[*var as usize] = true;
8206 }
8207 }
8208 }
8209 }
8210 }
8211 let val: Option<&Expr> = match st {
8212 Stmt::ExprStmt(Expr::Assign { value, .. }) => Some(value),
8213 Stmt::ExprStmt(e) => Some(e),
8216 Stmt::LocalDef { init: Some(e), .. } => Some(e),
8217 Stmt::Return(Some(e)) => Some(e),
8218 _ => None,
8219 };
8220 if let Some(v) = val {
8221 visit_exprs(v, &mut |x| {
8222 if let Expr::Method { desc, args, .. } = x {
8228 for (i, a) in args.iter().enumerate() {
8229 if desc.args.get(i) == Some(&JavaType::Boolean) {
8230 if let Expr::Local { var, .. } = a {
8231 if (*var as usize) < n {
8232 in_cond[*var as usize] = true;
8233 }
8234 }
8235 }
8236 }
8237 }
8238 if let Expr::Bin { op, l, r, .. } = x {
8239 if matches!(
8240 op,
8241 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
8242 ) {
8243 for side in [l, r] {
8244 if let Expr::Local { var, .. } = &**side {
8245 if (*var as usize) < n {
8246 in_cond[*var as usize] = true;
8247 }
8248 }
8249 }
8250 }
8251 if matches!(op, BinOp::Or | BinOp::And) {
8257 let l_bool = l.type_ref().erased() == JavaType::Boolean;
8258 let r_bool = r.type_ref().erased() == JavaType::Boolean;
8259 if l_bool != r_bool {
8260 let side = if l_bool { r } else { l };
8261 if let Expr::Local { var, .. } = &**side {
8262 if (*var as usize) < n {
8263 in_cond[*var as usize] = true;
8264 }
8265 }
8266 }
8267 }
8268 }
8269 });
8270 }
8271 });
8272
8273 let mut assigned_any = vec![false; n];
8280 let mut all_bool = vec![true; n];
8281 let mut edges: Vec<(usize, usize)> = Vec::new();
8282 walk_all(body, &mut |st| {
8283 let (var, value) = match st {
8284 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
8285 Expr::Local { var, .. } => (var, &**value),
8286 _ => return,
8287 },
8288 Stmt::LocalDef {
8289 var, init: Some(e), ..
8290 } => (var, e),
8291 _ => return,
8292 };
8293 let i = *var as usize;
8294 if i < n {
8295 assigned_any[i] = true;
8296 if !is_boolean_valued(value, vt) {
8297 all_bool[i] = false;
8298 }
8299 if let Expr::Local { var: src, .. } = value {
8305 if (*src as usize) < n {
8306 edges.push((i, *src as usize));
8307 }
8308 }
8309 }
8310 });
8311 loop {
8316 let mut changed = false;
8317 for &(tgt, src) in &edges {
8318 if in_cond[tgt] && !in_cond[src] && all_bool[src] {
8319 in_cond[src] = true;
8320 changed = true;
8321 }
8322 if in_cond[src] && !in_cond[tgt] && all_bool[tgt] {
8328 in_cond[tgt] = true;
8329 changed = true;
8330 }
8331 }
8332 if !changed {
8333 break;
8334 }
8335 }
8336 let reads = count_locals_stmts(std::slice::from_ref(body));
8337 let mut boolean_vars: HashSet<u32> = HashSet::default();
8338 for i in 0..n {
8339 if vt.vars[i].is_param {
8340 continue;
8341 }
8342 if derefed[i] {
8343 continue;
8344 }
8345 if !in_cond[i] && !(reads.get(&(i as u32)).copied().unwrap_or(0) == 0) {
8346 continue;
8347 }
8348 if assigned_any[i] && all_bool[i] {
8349 boolean_vars.insert(i as u32);
8350 }
8351 }
8352 if boolean_vars.is_empty() {
8353 return 0;
8354 }
8355 for v in &boolean_vars {
8356 vt.vars[*v as usize].ty = TypeRef::J(JavaType::Boolean);
8357 }
8358 let types: Vec<&TypeRef> = vt.vars.iter().map(|v| &v.ty).collect();
8359 rewrite_exprs(body, &mut |e| {
8360 deep_rewrite(e, &mut |x| {
8361 if let Expr::Local { var, ty } = x {
8362 if let Some(want) = types.get(*var as usize) {
8363 if ty != *want {
8364 *ty = (*want).clone();
8365 }
8366 }
8367 }
8368 });
8369 });
8370
8371 fold_bool_conditions(body, &boolean_vars);
8373 boolean_vars.len()
8374}
8375
8376fn is_boolean_valued(e: &Expr, vt: &VarTable) -> bool {
8377 match e {
8378 Expr::Const(ConstVal::Int(0)) | Expr::Const(ConstVal::Int(1)) => true,
8379 Expr::Bin { op: BinOp::Or | BinOp::And, l, r, .. } => {
8386 is_boolean_valued(l, vt) || is_boolean_valued(r, vt)
8387 }
8388 Expr::Bin {
8396 op: BinOp::Xor,
8397 l,
8398 r,
8399 ..
8400 } => is_boolean_valued(l, vt) && is_boolean_valued(r, vt),
8401 Expr::Bin { op, .. } => matches!(
8402 op,
8403 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
8404 ),
8405 Expr::InstanceOf { .. } => true,
8406 Expr::Local { var, ty } => {
8412 let t = if (*var as usize) < vt.vars.len() {
8413 vt.var(*var).ty.erased()
8414 } else {
8415 ty.erased()
8416 };
8417 t == JavaType::Boolean
8418 }
8419 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
8420 Expr::Cond { t, f, .. } => is_boolean_valued(t, vt) && is_boolean_valued(f, vt),
8421 Expr::Un { op: UnOp::Not, .. } => true,
8422 Expr::Field { ty, .. } => ty.erased() == JavaType::Boolean,
8426 _ => false,
8427 }
8428}
8429
8430fn fold_bool_conditions(s: &mut Stmt, bv: &HashSet<u32>) {
8431 let fold = |e: &mut Expr| {
8432 deep_rewrite(e, &mut |x| {
8433 if let Expr::Bin { op, l, r, .. } = x {
8434 let (var_side, const_side) = match (&**l, &**r) {
8435 (Expr::Local { .. }, Expr::Const(ConstVal::Int(_))) => (l, 0),
8436 (Expr::Const(ConstVal::Int(_)), Expr::Local { .. }) => (r, 1),
8437 _ => return,
8438 };
8439 let var = match &**var_side {
8440 Expr::Local { var, .. } => *var,
8441 _ => return,
8442 };
8443 if !bv.contains(&var) {
8444 return;
8445 }
8446 let _ = const_side;
8447 match op {
8448 BinOp::Ne => {
8449 let inner = var_side.clone();
8450 *x = *inner;
8451 }
8452 BinOp::Eq => {
8453 let inner = var_side.clone();
8454 *x = Expr::Un {
8455 op: UnOp::Not,
8456 e: Box::new(*inner),
8457 };
8458 }
8459 _ => {}
8460 }
8461 }
8462 });
8463 };
8464 match s {
8465 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => fold(cond),
8466 _ => {}
8467 }
8468 walk_mut_deep(s, &mut |st| {
8469 let cond = match st {
8470 Stmt::If { cond, .. } => Some(cond),
8471 Stmt::While { cond, .. } => Some(cond),
8472 Stmt::DoWhile { cond, .. } => Some(cond),
8473 _ => None,
8474 };
8475 if let Some(c) = cond {
8476 fold(c);
8477 }
8478 });
8479}
8480
8481pub fn flip_const_compares(s: &mut Stmt) {
8483 rewrite_exprs(s, &mut |e| {
8484 deep_rewrite(e, &mut |x| {
8485 if let Expr::Bin { op, l, r, .. } = x {
8486 if matches!(
8487 op,
8488 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
8489 ) {
8490 let l_const = matches!(&**l, Expr::Const(_));
8491 let r_var = matches!(&**r, Expr::Local { .. });
8492 if l_const && r_var {
8493 std::mem::swap(l, r);
8494 }
8495 }
8496 }
8497 });
8498 });
8499}
8500
8501pub fn null_compares(vt: &VarTable, body: &mut Stmt) {
8503 let obj_vars: HashSet<u32> = vt
8504 .vars
8505 .iter()
8506 .filter(|v| v.ty.erased().is_reference())
8507 .map(|v| v.id)
8508 .collect();
8509 rewrite_exprs(body, &mut |e| {
8510 deep_rewrite(e, &mut |x| {
8511 null_side_rewrite(x, &|v| obj_vars.contains(&v));
8512 });
8513 });
8514}
8515
8516pub fn drop_dead_locals(body: &mut Stmt) {
8525 loop {
8526 let mut reads: Vec<usize> = Vec::new();
8527 count_reads(body, &mut reads);
8528 let mut dropped = 0usize;
8529 walk_mut_deep(body, &mut |st| match st {
8530 Stmt::Block(items) => prune_dead_items(items, &reads, &mut dropped),
8531 Stmt::Switch { cases, .. } => {
8541 for c in cases {
8542 prune_dead_items(&mut c.body, &reads, &mut dropped);
8543 }
8544 }
8545 Stmt::For { init, .. } => prune_dead_items(init, &reads, &mut dropped),
8546 _ => {}
8547 });
8548 if dropped == 0 {
8549 break;
8550 }
8551 }
8552}
8553
8554fn prune_dead_items(items: &mut Vec<Stmt>, reads: &[usize], dropped: &mut usize) {
8559 let dead = |v: u32| reads.get(v as usize).copied().unwrap_or(0) == 0;
8560 items.retain_mut(|x| match x {
8561 Stmt::ExprStmt(Expr::Assign { target, value, op, .. }) => {
8562 let dead_target = matches!(&**target, Expr::Local { var, .. } if dead(*var));
8563 if dead_target && matches!(*op, AssignOp::Plain) {
8564 *dropped += 1;
8565 if has_side_effects(value) {
8566 let e = std::mem::replace(value, Box::new(Expr::This));
8567 *x = Stmt::ExprStmt(*e);
8568 true
8569 } else {
8570 false
8571 }
8572 } else {
8573 true
8574 }
8575 }
8576 Stmt::LocalDef { var, init, .. } if dead(*var) => {
8577 *dropped += 1;
8578 if let Some(e) = init.take() {
8579 if has_side_effects(&e) {
8580 *x = Stmt::ExprStmt(e);
8581 return true;
8582 }
8583 }
8584 false
8585 }
8586 _ => true,
8587 });
8588}
8589
8590
8591fn stmt_shallow_vars<F: FnMut(u32)>(st: &Stmt, out: &mut F) {
8596 fn ex<F: FnMut(u32)>(e: &Expr, out: &mut F) {
8597 visit_exprs(e, &mut |x| {
8598 if let Expr::Local { var, .. } = x {
8599 out(*var);
8600 }
8601 });
8602 }
8603 match st {
8604 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
8605 ex(e, out)
8606 }
8607 Stmt::TernaryValue { e } => ex(e, out),
8608 Stmt::Return(Some(e)) => ex(e, out),
8609 Stmt::LocalDef { var, init, .. } => {
8610 out(*var);
8611 if let Some(e) = init {
8612 ex(e, out);
8613 }
8614 }
8615 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
8616 ex(cond, out)
8617 }
8618 Stmt::For { cond, update, .. } => {
8619 if let Some(c) = cond {
8620 ex(c, out);
8621 }
8622 for u in update {
8623 ex(u, out);
8624 }
8625 }
8626 Stmt::ForEach { iterable, .. } => ex(iterable, out),
8627 Stmt::Switch { selector, cases, .. } => {
8628 ex(selector, out);
8629 for cg in cases {
8630 if let Some(g) = &cg.guard {
8631 ex(g, out);
8632 }
8633 }
8634 }
8635 Stmt::Assert { cond, msg } => {
8636 ex(cond, out);
8637 if let Some(m) = msg {
8638 ex(m, out);
8639 }
8640 }
8641 Stmt::Synchronized { lock, .. } => ex(lock, out),
8642 _ => {}
8643 }
8644}
8645
8646fn scope_pass_a(st: &Stmt, cur: u32, next: &mut u32, def_block: &mut [u32], sizes: &mut Vec<u32>) {
8658 if let Stmt::LocalDef { var, .. } = st {
8659 let i = *var as usize;
8660 if i < def_block.len() && def_block[i] == u32::MAX {
8661 def_block[i] = cur;
8662 }
8663 }
8664 fn scoped(c: &Stmt, next: &mut u32, def_block: &mut [u32], sizes: &mut Vec<u32>) {
8665 let id = *next;
8666 *next += 1;
8667 sizes.push(0);
8668 scope_pass_a(c, id, next, def_block, sizes);
8669 sizes[id as usize] = *next - id;
8670 }
8671 match st {
8672 Stmt::Block(v) => {
8673 for x in v {
8674 scope_pass_a(x, cur, next, def_block, sizes);
8675 }
8676 }
8677 Stmt::If {
8678 then_stmt,
8679 else_stmt,
8680 ..
8681 } => {
8682 scoped(then_stmt, next, def_block, sizes);
8683 if let Some(e) = else_stmt {
8684 scoped(e, next, def_block, sizes);
8685 }
8686 }
8687 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => {
8688 scoped(body, next, def_block, sizes)
8689 }
8690 Stmt::For { init, body, .. } => {
8691 let id = *next;
8694 *next += 1;
8695 sizes.push(0);
8696 for x in init {
8697 scope_pass_a(x, id, next, def_block, sizes);
8698 }
8699 scoped(body, next, def_block, sizes);
8700 sizes[id as usize] = *next - id;
8701 }
8702 Stmt::ForEach { body, .. } => scoped(body, next, def_block, sizes),
8703 Stmt::Switch { cases, default, .. } => {
8704 for c in cases {
8705 let id = *next;
8710 *next += 1;
8711 sizes.push(0);
8712 for x in &c.body {
8713 if matches!(x, Stmt::Block(_)) {
8714 scoped(x, next, def_block, sizes);
8715 } else {
8716 scope_pass_a(x, id, next, def_block, sizes);
8717 }
8718 }
8719 sizes[id as usize] = *next - id;
8720 }
8721 if let Some(d) = default {
8722 scoped(d, next, def_block, sizes);
8723 }
8724 }
8725 Stmt::Try {
8726 body,
8727 catches,
8728 finally,
8729 } => {
8730 scoped(body, next, def_block, sizes);
8731 for c in catches {
8732 scoped(&c.body, next, def_block, sizes);
8733 }
8734 if let Some(f) = finally {
8735 scoped(f, next, def_block, sizes);
8736 }
8737 }
8738 Stmt::TryWithResources {
8739 resources,
8740 body,
8741 catches,
8742 finally,
8743 } => {
8744 let id = *next;
8746 *next += 1;
8747 sizes.push(0);
8748 for r in resources {
8749 scope_pass_a(r, id, next, def_block, sizes);
8750 }
8751 scoped(body, next, def_block, sizes);
8752 for c in catches {
8753 scoped(&c.body, next, def_block, sizes);
8754 }
8755 if let Some(f) = finally {
8756 scoped(f, next, def_block, sizes);
8757 }
8758 sizes[id as usize] = *next - id;
8759 }
8760 Stmt::Synchronized { body, .. } => scoped(body, next, def_block, sizes),
8761 Stmt::Labeled { body, .. } => scope_pass_a(body, cur, next, def_block, sizes),
8762 _ => {}
8763 }
8764}
8765
8766fn scope_pass_b(
8771 st: &Stmt,
8772 cur: u32,
8773 next: &mut u32,
8774 def_block: &[u32],
8775 sizes: &[u32],
8776 within: &mut [u32],
8777 total: &mut [u32],
8778) {
8779 fn cnt(
8780 v: u32,
8781 at: u32,
8782 def_block: &[u32],
8783 sizes: &[u32],
8784 within: &mut [u32],
8785 total: &mut [u32],
8786 ) {
8787 let i = v as usize;
8788 if i < total.len() {
8789 total[i] += 1;
8790 let d = def_block[i];
8791 if d != u32::MAX && d <= at && at < d + sizes[d as usize] {
8792 within[i] += 1;
8793 }
8794 }
8795 }
8796 fn scoped(
8797 c: &Stmt,
8798 next: &mut u32,
8799 def_block: &[u32],
8800 sizes: &[u32],
8801 within: &mut [u32],
8802 total: &mut [u32],
8803 ) {
8804 let id = *next;
8805 *next += 1;
8806 scope_pass_b(c, id, next, def_block, sizes, within, total);
8807 }
8808 fn ex_at(
8809 e: &Expr,
8810 at: u32,
8811 def_block: &[u32],
8812 sizes: &[u32],
8813 within: &mut [u32],
8814 total: &mut [u32],
8815 ) {
8816 visit_exprs(e, &mut |x| {
8817 if let Expr::Local { var, .. } = x {
8818 cnt(*var, at, def_block, sizes, within, total);
8819 }
8820 });
8821 }
8822 match st {
8823 Stmt::For {
8824 init,
8825 cond,
8826 update,
8827 body,
8828 } => {
8829 let id = *next;
8830 *next += 1;
8831 for x in init {
8832 scope_pass_b(x, id, next, def_block, sizes, within, total);
8833 }
8834 if let Some(c) = cond {
8835 ex_at(c, id, def_block, sizes, within, total);
8836 }
8837 for u in update {
8838 ex_at(u, id, def_block, sizes, within, total);
8839 }
8840 scoped(body, next, def_block, sizes, within, total);
8841 }
8842 Stmt::TryWithResources {
8843 resources,
8844 body,
8845 catches,
8846 finally,
8847 } => {
8848 let id = *next;
8849 *next += 1;
8850 for r in resources {
8851 scope_pass_b(r, id, next, def_block, sizes, within, total);
8852 }
8853 scoped(body, next, def_block, sizes, within, total);
8854 for c in catches {
8855 scoped(&c.body, next, def_block, sizes, within, total);
8856 }
8857 if let Some(f) = finally {
8858 scoped(f, next, def_block, sizes, within, total);
8859 }
8860 }
8861 _ => {
8862 stmt_shallow_vars(st, &mut |v| cnt(v, cur, def_block, sizes, within, total));
8863 match st {
8864 Stmt::Block(v) => {
8865 for x in v {
8866 scope_pass_b(x, cur, next, def_block, sizes, within, total);
8867 }
8868 }
8869 Stmt::If {
8870 then_stmt,
8871 else_stmt,
8872 ..
8873 } => {
8874 scoped(then_stmt, next, def_block, sizes, within, total);
8875 if let Some(e) = else_stmt {
8876 scoped(e, next, def_block, sizes, within, total);
8877 }
8878 }
8879 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => {
8880 scoped(body, next, def_block, sizes, within, total)
8881 }
8882 Stmt::ForEach { body, .. } => {
8883 scoped(body, next, def_block, sizes, within, total)
8884 }
8885 Stmt::Switch { cases, default, .. } => {
8886 for c in cases {
8887 let id = *next;
8888 *next += 1;
8889 for x in &c.body {
8890 if matches!(x, Stmt::Block(_)) {
8891 scoped(x, next, def_block, sizes, within, total);
8892 } else {
8893 scope_pass_b(x, id, next, def_block, sizes, within, total);
8894 }
8895 }
8896 }
8897 if let Some(d) = default {
8898 scoped(d, next, def_block, sizes, within, total);
8899 }
8900 }
8901 Stmt::Try {
8902 body,
8903 catches,
8904 finally,
8905 } => {
8906 scoped(body, next, def_block, sizes, within, total);
8907 for c in catches {
8908 scoped(&c.body, next, def_block, sizes, within, total);
8909 }
8910 if let Some(f) = finally {
8911 scoped(f, next, def_block, sizes, within, total);
8912 }
8913 }
8914 Stmt::Synchronized { body, .. } => {
8915 scoped(body, next, def_block, sizes, within, total)
8916 }
8917 Stmt::Labeled { body, .. } => {
8918 scope_pass_b(body, cur, next, def_block, sizes, within, total)
8919 }
8920 _ => {}
8921 }
8922 }
8923 }
8924}
8925
8926pub fn strip_enum_const_inits(s: &mut Stmt, class: &crate::PoolClass) {
8935 let const_fields: jdc_core::FxHashSet<&str> = class
8936 .static_fields
8937 .iter()
8938 .filter(|f| f.access & crate::access::ACC_ENUM != 0)
8939 .map(|f| f.name.as_ref())
8940 .collect();
8941 if const_fields.is_empty() {
8942 return;
8943 }
8944 if let Stmt::Block(stmts) = s {
8945 stmts.retain(|st| {
8946 !matches!(
8947 st,
8948 Stmt::ExprStmt(Expr::Assign { target, value, .. })
8949 if matches!(&**target,
8950 Expr::Field { cls, name, is_static: true, .. }
8951 if cls.as_ref() == class.name
8952 && const_fields.contains(&**name))
8953 && matches!(&**value, Expr::New { cls: ncls, .. } if ncls.as_ref() == class.name)
8954 )
8955 });
8956 }
8957}
8958
8959fn contains_this_access(e: &Expr) -> bool {
8968 let mut hit = false;
8969 visit_exprs(e, &mut |x| {
8970 if hit {
8971 return;
8972 }
8973 match x {
8974 Expr::This => hit = true,
8975 Expr::Field { owner, is_static: false, .. } => {
8976 if owner.is_none() || matches!(&**owner.as_ref().unwrap(), Expr::This) {
8977 hit = true;
8978 }
8979 }
8980 Expr::Method { owner, is_static: false, .. } => {
8981 if owner.is_none() || matches!(&**owner.as_ref().unwrap(), Expr::This) {
8982 hit = true;
8983 }
8984 }
8985 _ => {}
8986 }
8987 });
8988 hit
8989}
8990
8991fn var_occurs_in(stmts: &[Stmt], var: u32) -> bool {
9006 stmts.iter().any(|st| {
9007 let mut hit = false;
9008 visit_stmt_exprs_ro(st, &mut |e| {
9009 if !hit {
9010 visit_exprs(e, &mut |x| {
9011 if let Expr::Local { var: v, .. } = x {
9012 if *v == var {
9013 hit = true;
9014 }
9015 }
9016 });
9017 }
9018 });
9019 hit
9020 })
9021}
9022
9023pub struct CtorHelper {
9033 pub name: String,
9034 pub ret: JavaType,
9035 pub params: Vec<(JavaType, String)>,
9036 pub body: Stmt,
9037 pub vt: VarTable,
9038}
9039
9040fn ctor_helpers() -> &'static std::sync::Mutex<std::collections::HashMap<String, Vec<CtorHelper>>> {
9041 static H: std::sync::OnceLock<std::sync::Mutex<std::collections::HashMap<String, Vec<CtorHelper>>>> =
9042 std::sync::OnceLock::new();
9043 H.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::default()))
9044}
9045
9046pub struct CtorRelay {
9051 pub formals: Vec<JavaType>,
9053 pub is_super: bool,
9054}
9055
9056type RelayMap = std::collections::HashMap<String, Vec<(String, CtorRelay)>>;
9057
9058fn ctor_relays() -> &'static std::sync::Mutex<RelayMap> {
9059 static H: std::sync::OnceLock<std::sync::Mutex<RelayMap>> = std::sync::OnceLock::new();
9060 H.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::default()))
9061}
9062
9063pub fn take_ctor_relays(class: &str) -> Vec<(String, CtorRelay)> {
9066 let mut m = ctor_relays().lock().unwrap_or_else(|e| e.into_inner());
9067 let mut v = m.remove(class).unwrap_or_default();
9068 v.sort_by(|a, b| a.0.cmp(&b.0));
9069 v
9070}
9071
9072fn array_init(args: Vec<Expr>) -> Expr {
9073 Expr::NewArray {
9074 elem: TypeRef::J(JavaType::Object(std::sync::Arc::from("java/lang/Object"))),
9075 dims: Vec::new(),
9076 trailing_dims: 0,
9077 init: Some(args),
9078 }
9079}
9080
9081fn helper_name(class: &str, desc: &str) -> String {
9084 let mut h: u64 = 0xcbf29ce484222325;
9085 for b in class.as_bytes().iter().chain(b"::").chain(desc.as_bytes()) {
9086 h ^= *b as u64;
9087 h = h.wrapping_mul(0x100000001b3);
9088 }
9089 format!("resolve${:x}", (h >> 16) & 0xffff_ffff)
9090}
9091
9092pub fn take_ctor_helpers(class: &str) -> Vec<CtorHelper> {
9095 let mut m = ctor_helpers().lock().unwrap_or_else(|e| e.into_inner());
9096 let mut v = m.remove(class).unwrap_or_default();
9097 v.sort_by(|a, b| a.name.cmp(&b.name));
9098 v
9099}
9100
9101fn strip_delegations_deep(stmts: &mut Vec<Stmt>) {
9102 stmts.retain(|s| !is_bare_ctor_call(s));
9103 for s in stmts.iter_mut() {
9104 strip_delegation_one(s);
9105 }
9106}
9107
9108fn strip_delegation_one(st: &mut Stmt) {
9109 if is_bare_ctor_call(st) {
9110 *st = Stmt::Block(Vec::new());
9111 return;
9112 }
9113 match st {
9114 Stmt::Block(v) => strip_delegations_deep(v),
9115 Stmt::If { then_stmt, else_stmt, .. } => {
9116 strip_delegation_one(then_stmt);
9117 if let Some(e) = else_stmt {
9118 strip_delegation_one(e);
9119 }
9120 }
9121 Stmt::While { body, .. }
9122 | Stmt::DoWhile { body, .. }
9123 | Stmt::ForEach { body, .. }
9124 | Stmt::Synchronized { body, .. }
9125 | Stmt::Labeled { body, .. } => strip_delegation_one(body),
9126 Stmt::For { init, body, .. } => {
9127 strip_delegations_deep(init);
9128 strip_delegation_one(body);
9129 }
9130 Stmt::Switch { cases, default, .. } => {
9131 for c in cases.iter_mut() {
9132 strip_delegations_deep(&mut c.body);
9133 }
9134 if let Some(d) = default {
9135 strip_delegation_one(d);
9136 }
9137 }
9138 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
9139 strip_delegation_one(body);
9140 for c in catches.iter_mut() {
9141 strip_delegation_one(&mut c.body);
9142 }
9143 if let Some(f) = finally {
9144 strip_delegation_one(f);
9145 }
9146 }
9147 _ => {}
9148 }
9149}
9150
9151#[allow(clippy::ptr_arg)]
9152fn rewrite_bare_returns(stmts: &mut Vec<Stmt>, val: &Expr) {
9153 for s in stmts.iter_mut() {
9154 rewrite_bare_returns_one(s, val);
9155 }
9156}
9157
9158fn rewrite_bare_returns_one(st: &mut Stmt, val: &Expr) {
9159 match st {
9160 Stmt::Return(None) => *st = Stmt::Return(Some(val.clone())),
9161 Stmt::Block(v) => rewrite_bare_returns(v, val),
9162 Stmt::If { then_stmt, else_stmt, .. } => {
9163 rewrite_bare_returns_one(then_stmt, val);
9164 if let Some(e) = else_stmt {
9165 rewrite_bare_returns_one(e, val);
9166 }
9167 }
9168 Stmt::While { body, .. }
9169 | Stmt::DoWhile { body, .. }
9170 | Stmt::ForEach { body, .. }
9171 | Stmt::Synchronized { body, .. }
9172 | Stmt::Labeled { body, .. } => rewrite_bare_returns_one(body, val),
9173 Stmt::For { init, body, .. } => {
9174 rewrite_bare_returns(init, val);
9175 rewrite_bare_returns_one(body, val);
9176 }
9177 Stmt::Switch { cases, default, .. } => {
9178 for c in cases.iter_mut() {
9179 rewrite_bare_returns(&mut c.body, val);
9180 }
9181 if let Some(d) = default {
9182 rewrite_bare_returns_one(d, val);
9183 }
9184 }
9185 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
9186 rewrite_bare_returns_one(body, val);
9187 for c in catches.iter_mut() {
9188 rewrite_bare_returns_one(&mut c.body, val);
9189 }
9190 if let Some(f) = finally {
9191 rewrite_bare_returns_one(f, val);
9192 }
9193 }
9194 _ => {}
9195 }
9196}
9197
9198fn collect_def_vars(stmts: &[Stmt], out: &mut jdc_core::FxHashSet<u32>) {
9199 for st in stmts {
9200 match st {
9201 Stmt::LocalDef { var, .. } => {
9202 out.insert(*var);
9203 }
9204 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
9205 if let Expr::Local { var, .. } = &**target {
9206 out.insert(*var);
9207 }
9208 }
9209 Stmt::Block(v) => collect_def_vars(v, out),
9210 Stmt::If { then_stmt, else_stmt, .. } => {
9211 collect_def_vars(std::slice::from_ref(then_stmt.as_ref()), out);
9212 if let Some(e) = else_stmt {
9213 collect_def_vars(std::slice::from_ref(e.as_ref()), out);
9214 }
9215 }
9216 Stmt::While { body, .. }
9217 | Stmt::DoWhile { body, .. }
9218 | Stmt::Synchronized { body, .. }
9219 | Stmt::Labeled { body, .. } => {
9220 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
9221 }
9222 Stmt::ForEach { var, body, .. } => {
9223 out.insert(*var);
9224 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
9225 }
9226 Stmt::For { init, body, .. } => {
9227 collect_def_vars(init, out);
9228 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
9229 }
9230 Stmt::Switch { cases, default, .. } => {
9231 for c in cases {
9232 collect_def_vars(&c.body, out);
9233 }
9234 if let Some(d) = default {
9235 collect_def_vars(std::slice::from_ref(d.as_ref()), out);
9236 }
9237 }
9238 Stmt::Try { body, catches, finally }
9239 | Stmt::TryWithResources { body, catches, finally, .. } => {
9240 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
9241 for c in catches {
9242 out.insert(c.var);
9243 collect_def_vars(std::slice::from_ref(c.body.as_ref()), out);
9244 }
9245 if let Some(f) = finally {
9246 collect_def_vars(std::slice::from_ref(f.as_ref()), out);
9247 }
9248 }
9249 _ => {}
9250 }
9251 }
9252}
9253
9254fn extraction_shape_ok(stmts: &[Stmt]) -> bool {
9257 stmts.iter().all(|st| match st {
9258 Stmt::Block(v) => extraction_shape_ok(v),
9259 Stmt::LocalDef { .. }
9260 | Stmt::ExprStmt(_)
9261 | Stmt::Return(_)
9262 | Stmt::Throw(_)
9263 | Stmt::Assert { .. }
9264 | Stmt::Break(_)
9265 | Stmt::Continue(_) => true,
9266 Stmt::If { then_stmt, else_stmt, .. } => {
9267 ok_one(then_stmt)
9268 && else_stmt.as_deref().map(ok_one).unwrap_or(true)
9269 }
9270 Stmt::While { body, .. }
9271 | Stmt::DoWhile { body, .. }
9272 | Stmt::ForEach { body, .. }
9273 | Stmt::Synchronized { body, .. } => ok_one(body),
9274 Stmt::Labeled { body, .. } => ok_one(body),
9275 Stmt::For { init, body, .. } => extraction_shape_ok(init) && ok_one(body),
9276 Stmt::Switch { cases, default, .. } => {
9277 cases.iter().all(|c| extraction_shape_ok(&c.body))
9278 && default.as_deref().map(ok_one).unwrap_or(true)
9279 }
9280 Stmt::Try { body, catches, finally }
9281 | Stmt::TryWithResources { body, catches, finally, .. } => {
9282 ok_one(body)
9283 && catches.iter().all(|c| ok_one(&c.body))
9284 && finally.as_deref().map(ok_one).unwrap_or(true)
9285 }
9286 _ => false,
9289 })
9290}
9291
9292fn ok_one(st: &Stmt) -> bool {
9293 extraction_shape_ok(std::slice::from_ref(st))
9294}
9295
9296fn static_safe(stmts: &[Stmt], this_id: Option<u32>) -> bool {
9297 let mut ok = true;
9298 for st in stmts {
9299 visit_stmt_exprs_ro(st, &mut |e| {
9300 visit_exprs(e, &mut |x| {
9301 match x {
9302 Expr::This => ok = false,
9303 Expr::Local { var, .. } => {
9309 if Some(*var) == this_id {
9310 ok = false;
9311 }
9312 }
9313 Expr::Field { owner: None, is_static: false, .. } => ok = false,
9315 Expr::Method { owner: None, is_static: false, .. } => ok = false,
9316 _ => {}
9317 }
9318 });
9319 });
9320 }
9321 ok
9322}
9323
9324pub fn strip_clinit_hotfix_guard(body: &mut Stmt) {
9335 if let Stmt::Block(vs) = body {
9339 flatten_top_blocks(vs);
9340 }
9341 let Stmt::Block(stmts) = body else { return };
9342 let mut gi = 0usize;
9345 while gi < stmts.len() && matches!(&stmts[gi], Stmt::LocalDef { init: None, .. }) {
9346 gi += 1;
9347 }
9348 let Some(first) = stmts.get(gi) else { return };
9349 let Stmt::If { then_stmt, else_stmt, .. } = first else {
9350 return;
9351 };
9352 if else_stmt.is_some() {
9353 return;
9354 }
9355 let mut has_ret = false;
9356 let mut has_proxy = false;
9357 let c = then_stmt.as_ref();
9358 walk_all(c, &mut |st| {
9359 if matches!(st, Stmt::Return(None)) {
9360 has_ret = true;
9361 }
9362 });
9363 let mut cc = c.clone();
9364 walk_stmt_exprs(&mut cc, &mut |e| {
9365 deep_rewrite(e, &mut |x| {
9366 if let Expr::Method { cls, .. } = x {
9367 if cls.contains("PatchProxy") {
9368 has_proxy = true;
9369 }
9370 }
9371 });
9372 });
9373 if has_ret && has_proxy {
9374 stmts.remove(gi);
9375 }
9376}
9377
9378pub(crate) fn first_leaf_stmt(s: &Stmt) -> &Stmt {
9399 match s {
9400 Stmt::Block(v) if !v.is_empty() => first_leaf_stmt(&v[0]),
9401 other => other,
9402 }
9403}
9404
9405pub fn strip_ctor_hotfix_guards(body: &mut Stmt) {
9424 fn marker_hit(e: &Expr) -> bool {
9425 let has = |n: &str| {
9426 let l = n.to_ascii_lowercase();
9427 l.contains("hotfix")
9428 || l.contains("instantrun")
9429 || l.contains("robust")
9430 || l.contains("titan")
9431 || l.contains("patchproxy")
9432 || l.contains("changequickredirect")
9433 || l.contains("constructorcode")
9434 };
9435 let mut hit = false;
9436 visit_exprs(e, &mut |x| {
9437 match x {
9438 Expr::Method { cls, .. } | Expr::New { cls, .. } | Expr::Field { cls, .. } => {
9439 if has(cls) {
9440 hit = true;
9441 }
9442 }
9443 _ => {}
9444 }
9445 if let Expr::Field { ty, .. } = x {
9446 if let TypeRef::J(JavaType::Object(n)) = ty {
9447 if has(n) {
9448 hit = true;
9449 }
9450 }
9451 }
9452 });
9453 hit
9454 }
9455 fn is_guard(st: &Stmt) -> bool {
9459 let mut cur = st;
9460 loop {
9461 match cur {
9462 Stmt::Block(v) if v.len() == 1 => cur = &v[0],
9463 Stmt::If { .. } => break,
9464 _ => return false,
9465 }
9466 }
9467 let mut has_del = false;
9468 walk_all(cur, &mut |s2| {
9469 if is_bare_ctor_call(s2) {
9470 has_del = true;
9471 }
9472 });
9473 if !has_del {
9474 return false;
9475 }
9476 let mut has_marker = false;
9477 visit_stmt_exprs_ro(cur, &mut |e| {
9478 if marker_hit(e) {
9479 has_marker = true;
9480 }
9481 });
9482 has_marker
9483 }
9484 fn strip_in(stmts: &mut Vec<Stmt>) {
9495 stmts.retain(|st| !is_guard(st));
9496 for st in stmts.iter_mut() {
9497 if let Stmt::Block(v) = st {
9498 strip_in(v);
9499 }
9500 }
9501 }
9502 let Stmt::Block(stmts) = body else { return };
9503 strip_in(stmts);
9504}
9505
9506pub fn hoist_branch_delegations(body: &mut Stmt, vt: &VarTable) {
9519 let Stmt::Block(stmts) = body else { return };
9520 if stmts.first().is_some_and(is_bare_ctor_call) {
9521 return; }
9523 let mut dels: Vec<Expr> = Vec::new();
9524 for st in stmts.iter() {
9525 visit_stmt_exprs_ro(st, &mut |e| {
9526 if is_delegation_expr(e) {
9527 dels.push(e.clone());
9528 }
9529 });
9530 }
9531 if dels.is_empty() {
9532 return;
9533 }
9534 if !dels.iter().all(|d| *d == dels[0]) {
9535 return;
9536 }
9537 let mut bad = false;
9538 visit_exprs(&dels[0], &mut |x| {
9539 if let Expr::Local { var, .. } = x {
9540 if (*var as usize) >= vt.vars.len() || !vt.vars[*var as usize].is_param {
9541 bad = true;
9542 }
9543 }
9544 });
9545 if bad {
9546 return;
9547 }
9548 let del = dels[0].clone();
9549 strip_delegations_deep(stmts);
9550 stmts.insert(0, Stmt::ExprStmt(del));
9551}
9552
9553pub fn extract_branched_delegation_helper(
9587 body: &mut Stmt,
9588 vt: &VarTable,
9589 pool: &DexPool,
9590 class: &crate::PoolClass,
9591 desc_str: &str,
9592) {
9593 if class.is_enum() {
9594 return;
9595 }
9596 if class.nesting.enclosing_class.is_some() && !crate::ctx::nested_is_static(pool, class) {
9601 return;
9602 }
9603 let Stmt::Block(stmts) = body else { return };
9604 let mut dels: Vec<Expr> = Vec::new();
9605 for st in stmts.iter() {
9606 visit_stmt_exprs_ro(st, &mut |e| {
9607 if is_delegation_expr(e) {
9608 dels.push(e.clone());
9609 }
9610 });
9611 }
9612 if dels.is_empty() {
9613 return;
9614 }
9615 if dels.len() == 1 && stmts.first().is_some_and(is_bare_ctor_call) {
9616 return; }
9618 let template = dels[0].clone();
9619 let Expr::Method { args: t_args, .. } = &template else {
9620 return;
9621 };
9622 enum Mode {
9626 Single(u32),
9627 Position(usize),
9628 Full,
9635 }
9636 let full_gate = (|| -> Option<()> {
9643 let Expr::Method {
9644 args: a0,
9645 desc: d0,
9646 is_super: s0,
9647 cls: c0,
9648 ..
9649 } = &dels[0]
9650 else {
9651 return None;
9652 };
9653 if a0.is_empty() {
9654 return None;
9655 }
9656 for d in &dels[1..] {
9657 match d {
9658 Expr::Method {
9659 args,
9660 desc,
9661 is_super,
9662 cls,
9663 ..
9664 } if args.len() == a0.len()
9665 && **desc == **d0
9666 && is_super == s0
9667 && cls == c0 => {}
9668 _ => return None,
9669 }
9670 }
9671 if class
9672 .all_methods()
9673 .any(|m| &*m.name == "<init>" && &*m.desc == "([Ljava/lang/Object;)V")
9674 {
9675 return None;
9676 }
9677 Some(())
9678 })()
9679 .is_some();
9680 let mode = if dels.iter().all(|d| *d == dels[0]) {
9681 let mut carrier: Option<u32> = None;
9682 let mut reads = 0usize;
9683 let mut multi = false;
9684 for a in t_args.iter() {
9685 let c = a.clone();
9686 visit_exprs(&c, &mut |x| {
9687 if let Expr::Local { var, .. } = x {
9688 if (*var as usize) < vt.vars.len() && !vt.vars[*var as usize].is_param {
9689 reads += 1;
9690 match carrier {
9691 Some(v) if v == *var => {}
9692 Some(_) => multi = true,
9693 None => carrier = Some(*var),
9694 }
9695 }
9696 }
9697 });
9698 }
9699 match carrier {
9700 Some(cv) if !multi && reads == 1 => Mode::Single(cv),
9701 _ if full_gate => Mode::Full,
9702 _ => return,
9703 }
9704 } else {
9705 let positioned: Option<Mode> = (|| {
9706 let arity = t_args.len();
9707 if dels
9711 .iter()
9712 .any(|d| !matches!(d, Expr::Method { args, .. } if args.len() == arity))
9713 {
9714 return None;
9715 }
9716 let mut diff: Vec<usize> = Vec::new();
9717 for j in 0..arity {
9718 if dels.iter().any(|d| match d {
9719 Expr::Method { args, .. } => args.get(j) != t_args.get(j),
9720 _ => true,
9721 }) {
9722 diff.push(j);
9723 }
9724 }
9725 if diff.len() != 1 {
9726 return None;
9727 }
9728 let k = diff[0];
9729 let mut base0 = dels[0].clone();
9733 if let Expr::Method { args, .. } = &mut base0 {
9734 args[k] = Expr::Const(ConstVal::Null);
9735 }
9736 for d in dels.iter() {
9737 let mut c = d.clone();
9738 if let Expr::Method { args, .. } = &mut c {
9739 args[k] = Expr::Const(ConstVal::Null);
9740 }
9741 if c != base0 {
9742 return None;
9743 }
9744 }
9745 let mut carrier_ty: Option<JavaType> = None;
9748 for d in dels.iter() {
9749 let Expr::Method { args, .. } = d else {
9750 return None;
9751 };
9752 let Expr::Local { var, .. } = &args[k] else {
9753 return None;
9754 };
9755 if (*var as usize) >= vt.vars.len() || vt.vars[*var as usize].is_param {
9756 return None;
9757 }
9758 let ty = vt.vars[*var as usize].ty.erased();
9759 match &carrier_ty {
9760 Some(t) if *t == ty => {}
9761 Some(_) => return None,
9762 None => carrier_ty = Some(ty),
9763 }
9764 for (j, a) in args.iter().enumerate() {
9770 if j == k {
9771 continue;
9772 }
9773 let mut bad = false;
9774 visit_exprs(a, &mut |x| {
9775 if let Expr::Local { var: v2, .. } = x {
9776 if (*v2 as usize) >= vt.vars.len() || !vt.vars[*v2 as usize].is_param {
9777 bad = true;
9778 }
9779 }
9780 });
9781 if bad {
9782 return None;
9783 }
9784 }
9785 }
9786 Some(Mode::Position(k))
9787 })();
9788 match positioned {
9789 Some(m) => m,
9790 None if full_gate => Mode::Full,
9791 None => return,
9792 }
9793 };
9794 let l = stmts.iter().rposition(is_bare_ctor_call);
9795 if l == Some(0) {
9796 return;
9797 }
9798 let (mut extracted, mut tail): (Vec<Stmt>, Vec<Stmt>) = match l {
9799 Some(l) => (stmts[..l].to_vec(), stmts[l + 1..].to_vec()),
9800 None => (stmts.clone(), Vec::new()),
9801 };
9802 if !extraction_shape_ok(&extracted) {
9803 return;
9804 }
9805 let mut site_tails: Vec<Vec<Stmt>> = Vec::new();
9812 {
9813 let mut probe = extracted.clone();
9814 site_tails_walk(&mut probe, &mut site_tails, false);
9815 }
9816 let hoist = !site_tails.is_empty() && {
9817 let n0 = normalize_tail(&site_tails[0]);
9818 site_tails.iter().all(|t| normalize_tail(t) == n0)
9819 && (l.is_none() || normalize_tail(&tail) == n0)
9820 };
9821 if hoist {
9822 site_tails_walk(&mut extracted, &mut Vec::new(), true);
9823 if l.is_none() {
9824 tail = normalize_tail(&site_tails[0]);
9825 }
9826 }
9827 let primary_del: Expr = match l {
9830 Some(l) => match &stmts[l] {
9831 Stmt::ExprStmt(e) => e.clone(),
9832 _ => return,
9833 },
9834 None => dels[0].clone(),
9835 };
9836 let ret = match mode {
9837 Mode::Single(cv) => vt.var(cv).ty.erased(),
9838 Mode::Position(k) => match &primary_del {
9839 Expr::Method { args, .. } => match args.get(k) {
9840 Some(Expr::Local { var, .. }) => vt.var(*var).ty.erased(),
9841 _ => return,
9842 },
9843 _ => return,
9844 },
9845 Mode::Full => JavaType::Array(Box::new(JavaType::Object(std::sync::Arc::from(
9846 "java/lang/Object",
9847 )))),
9848 };
9849 match mode {
9850 Mode::Single(cv) => {
9851 strip_delegations_deep(&mut extracted);
9852 let cv_local = Expr::Local {
9853 var: cv,
9854 ty: vt.var(cv).ty.clone(),
9855 };
9856 rewrite_bare_returns(&mut extracted, &cv_local);
9857 if !always_returns(&extracted) {
9858 extracted.push(Stmt::Return(Some(cv_local)));
9859 }
9860 }
9861 Mode::Position(k) => {
9862 fill_returns_by_position(&mut extracted, &|args: &[Expr]| args.get(k).cloned());
9866 let fallback = match &primary_del {
9867 Expr::Method { args, .. } => args[k].clone(),
9868 _ => return,
9869 };
9870 rewrite_bare_returns(&mut extracted, &fallback);
9871 if !always_returns(&extracted) {
9872 extracted.push(Stmt::Return(Some(fallback)));
9873 }
9874 }
9875 Mode::Full => {
9876 fill_returns_by_position(&mut extracted, &|args: &[Expr]| {
9883 Some(array_init(args.to_vec()))
9884 });
9885 if l.is_none() {
9886 if !always_returns(&extracted) {
9893 let cut = (1..=extracted.len())
9894 .find(|&i| always_returns(&extracted[..i]));
9895 match cut {
9896 Some(i) => extracted.truncate(i),
9897 None => return,
9898 }
9899 }
9900 }
9901 let fallback_args = match &primary_del {
9902 Expr::Method { args, .. } => args.clone(),
9903 _ => return,
9904 };
9905 let fallback = array_init(fallback_args);
9906 rewrite_bare_returns(&mut extracted, &fallback);
9907 if !always_returns(&extracted) {
9908 extracted.push(Stmt::Return(Some(fallback)));
9909 }
9910 }
9911 }
9912 let this_id = vt
9913 .vars
9914 .iter()
9915 .find(|v| v.is_param && v.name == "this")
9916 .map(|v| v.id);
9917 if !static_safe(&extracted, this_id) {
9918 return;
9919 }
9920 let mut defs: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
9921 collect_def_vars(&extracted, &mut defs);
9922 if defs.iter().any(|v| var_occurs_in(&tail, *v)) {
9923 return;
9924 }
9925 let params: Vec<(u32, JavaType, String)> = vt
9926 .vars
9927 .iter()
9928 .filter(|v| v.is_param && v.name != "this")
9929 .map(|v| (v.id, v.ty.erased(), v.name.clone()))
9930 .collect();
9931 let name = helper_name(&class.name, desc_str);
9932 let call = Expr::Method {
9933 owner: None,
9934 cls: std::sync::Arc::from(class.name.as_str()),
9935 name: std::sync::Arc::from(name.as_str()),
9936 desc: std::sync::Arc::new(jdc_core::types::MethodDescriptor {
9937 ret: ret.clone(),
9938 args: params.iter().map(|p| p.1.clone()).collect(),
9939 }),
9940 args: params
9941 .iter()
9942 .map(|(id, ty, _)| Expr::Local {
9943 var: *id,
9944 ty: TypeRef::J(ty.clone()),
9945 })
9946 .collect(),
9947 is_static: true,
9948 is_interface: false,
9949 is_special: false,
9950 is_super: false,
9951 is_dynamic: false,
9952 type_args: Vec::new(),
9953 };
9954 let mut new_del = primary_del.clone();
9955 match mode {
9956 Mode::Single(cv) => {
9957 deep_rewrite(&mut new_del, &mut |x| {
9958 if let Expr::Local { var, .. } = x {
9959 if *var == cv {
9960 *x = call.clone();
9961 }
9962 }
9963 });
9964 }
9965 Mode::Position(k) => {
9966 let Expr::Method { args, .. } = &mut new_del else {
9967 return;
9968 };
9969 if k >= args.len() {
9970 return;
9971 }
9972 args[k] = call.clone();
9973 }
9974 Mode::Full => {
9975 new_del = Expr::Method {
9979 owner: None,
9980 cls: std::sync::Arc::from(class.name.as_str()),
9981 name: "<init>".into(),
9982 desc: std::sync::Arc::new(jdc_core::types::MethodDescriptor {
9983 args: vec![JavaType::Array(Box::new(JavaType::Object(
9984 std::sync::Arc::from("java/lang/Object"),
9985 )))],
9986 ret: JavaType::Void,
9987 }),
9988 args: vec![call.clone()],
9989 is_static: false,
9990 is_interface: false,
9991 is_special: true,
9992 is_super: false,
9993 is_dynamic: false,
9994 type_args: Vec::new(),
9995 };
9996 }
9997 }
9998 {
9999 let mut m = ctor_helpers().lock().unwrap_or_else(|e| e.into_inner());
10000 let v = m.entry(class.name.clone()).or_default();
10001 if !v.iter().any(|h| h.name == name) {
10002 let mut hbody = Stmt::Block(extracted);
10006 invert_empty_thens(&mut hbody);
10007 v.push(CtorHelper {
10008 name,
10009 ret: ret.clone(),
10010 params: params.into_iter().map(|(_, ty, nm)| (ty, nm)).collect(),
10011 body: hbody,
10012 vt: vt.clone(),
10013 });
10014 if matches!(mode, Mode::Full) {
10015 let Expr::Method {
10016 desc: d0,
10017 is_super: s0,
10018 ..
10019 } = &primary_del
10020 else {
10021 return;
10022 };
10023 let mut rm = ctor_relays().lock().unwrap_or_else(|e| e.into_inner());
10024 let rv = rm.entry(class.name.clone()).or_default();
10025 if !rv.iter().any(|(d, _)| d == desc_str) {
10026 rv.push((
10027 desc_str.to_string(),
10028 CtorRelay {
10029 formals: d0.args.clone(),
10030 is_super: *s0,
10031 },
10032 ));
10033 }
10034 }
10035 } else {
10036 return; }
10038 }
10039 *stmts = std::iter::once(Stmt::ExprStmt(new_del)).chain(tail).collect();
10040}
10041
10042fn fill_returns_by_position(
10049 stmts: &mut Vec<Stmt>,
10050 pick: &dyn Fn(&[Expr]) -> Option<Expr>,
10051) {
10052 let mut i = 0;
10053 while i < stmts.len() {
10054 if is_bare_ctor_call(&stmts[i]) {
10055 let karg = match &stmts[i] {
10056 Stmt::ExprStmt(Expr::Method { args, .. }) => pick(args),
10057 _ => None,
10058 };
10059 if let Some(v) = karg {
10060 let mut j = i + 1;
10061 let mut found = None;
10062 while j < stmts.len() {
10063 if matches!(stmts[j], Stmt::Return(None)) {
10064 found = Some(j);
10065 break;
10066 }
10067 if is_bare_ctor_call(&stmts[j]) {
10068 break; }
10070 j += 1;
10071 }
10072 match found {
10073 Some(j) => {
10074 stmts[j] = Stmt::Return(Some(v));
10075 stmts.remove(i);
10076 continue;
10077 }
10078 None => {
10079 stmts[i] = Stmt::Return(Some(v));
10080 stmts.truncate(i + 1);
10081 break;
10082 }
10083 }
10084 }
10085 }
10086 fill_returns_one(&mut stmts[i], pick);
10087 i += 1;
10088 }
10089}
10090
10091fn fill_returns_one(st: &mut Stmt, pick: &dyn Fn(&[Expr]) -> Option<Expr>) {
10092 match st {
10093 Stmt::Block(v) => fill_returns_by_position(v, pick),
10094 Stmt::If { then_stmt, else_stmt, .. } => {
10095 fill_returns_stmt(then_stmt, pick);
10096 if let Some(e) = else_stmt {
10097 fill_returns_stmt(e, pick);
10098 }
10099 }
10100 Stmt::While { body, .. }
10101 | Stmt::DoWhile { body, .. }
10102 | Stmt::ForEach { body, .. }
10103 | Stmt::Synchronized { body, .. }
10104 | Stmt::Labeled { body, .. } => fill_returns_stmt(body, pick),
10105 Stmt::For { init, body, .. } => {
10106 fill_returns_by_position(init, pick);
10107 fill_returns_stmt(body, pick);
10108 }
10109 Stmt::Switch { cases, default, .. } => {
10110 for c in cases.iter_mut() {
10111 fill_returns_by_position(&mut c.body, pick);
10112 }
10113 if let Some(d) = default {
10114 fill_returns_stmt(d, pick);
10115 }
10116 }
10117 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
10118 fill_returns_stmt(body, pick);
10119 for c in catches.iter_mut() {
10120 fill_returns_stmt(&mut c.body, pick);
10121 }
10122 if let Some(f) = finally {
10123 fill_returns_stmt(f, pick);
10124 }
10125 }
10126 _ => {}
10127 }
10128}
10129
10130fn fill_returns_stmt(st: &mut Stmt, pick: &dyn Fn(&[Expr]) -> Option<Expr>) {
10131 if is_bare_ctor_call(st) {
10132 if let Stmt::ExprStmt(Expr::Method { args, .. }) = st {
10135 if let Some(v) = pick(args) {
10136 *st = Stmt::Return(Some(v));
10137 return;
10138 }
10139 }
10140 }
10141 fill_returns_one(st, pick);
10142}
10143
10144fn normalize_tail(t: &[Stmt]) -> Vec<Stmt> {
10145 let mut v = t.to_vec();
10146 while matches!(v.last(), Some(Stmt::Return(None))) {
10147 v.pop();
10148 }
10149 v
10150}
10151
10152fn site_tails_walk(stmts: &mut Vec<Stmt>, out: &mut Vec<Vec<Stmt>>, carve: bool) {
10157 let mut i = 0;
10158 while i < stmts.len() {
10159 if is_bare_ctor_call(&stmts[i]) {
10160 let mut j = i + 1;
10161 let mut end = stmts.len();
10162 while j < stmts.len() {
10163 if matches!(stmts[j], Stmt::Return(None)) {
10164 end = j;
10165 break;
10166 }
10167 if is_bare_ctor_call(&stmts[j]) {
10168 end = j;
10169 break;
10170 }
10171 j += 1;
10172 }
10173 out.push(stmts[i + 1..end].to_vec());
10174 if carve {
10175 stmts.splice(i + 1..end, []);
10176 }
10177 i += 1;
10178 continue;
10179 }
10180 site_tails_one(&mut stmts[i], out, carve);
10181 i += 1;
10182 }
10183}
10184
10185fn site_tails_one(st: &mut Stmt, out: &mut Vec<Vec<Stmt>>, carve: bool) {
10186 if is_bare_ctor_call(st) {
10187 out.push(Vec::new());
10188 return;
10189 }
10190 match st {
10191 Stmt::Block(v) => site_tails_walk(v, out, carve),
10192 Stmt::If { then_stmt, else_stmt, .. } => {
10193 site_tails_one(then_stmt, out, carve);
10194 if let Some(e) = else_stmt {
10195 site_tails_one(e, out, carve);
10196 }
10197 }
10198 Stmt::While { body, .. }
10199 | Stmt::DoWhile { body, .. }
10200 | Stmt::ForEach { body, .. }
10201 | Stmt::Synchronized { body, .. }
10202 | Stmt::Labeled { body, .. } => site_tails_one(body, out, carve),
10203 Stmt::For { init, body, .. } => {
10204 site_tails_walk(init, out, carve);
10205 site_tails_one(body, out, carve);
10206 }
10207 Stmt::Switch { cases, default, .. } => {
10208 for c in cases.iter_mut() {
10209 site_tails_walk(&mut c.body, out, carve);
10210 }
10211 if let Some(d) = default {
10212 site_tails_one(d, out, carve);
10213 }
10214 }
10215 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
10216 site_tails_one(body, out, carve);
10217 for c in catches.iter_mut() {
10218 site_tails_one(&mut c.body, out, carve);
10219 }
10220 if let Some(f) = finally {
10221 site_tails_one(f, out, carve);
10222 }
10223 }
10224 _ => {}
10225 }
10226}
10227
10228fn always_returns(stmts: &[Stmt]) -> bool {
10234 let Some(last) = stmts
10235 .iter()
10236 .rev()
10237 .find(|s| !matches!(s, Stmt::Block(v) if v.is_empty()))
10238 else {
10239 return false;
10240 };
10241 match last {
10242 Stmt::Return(Some(_)) | Stmt::Throw(_) => true,
10243 Stmt::Block(v) => always_returns(v),
10244 Stmt::If { then_stmt, else_stmt, .. } => match else_stmt {
10245 Some(e) => always_returns_one(then_stmt) && always_returns_one(e),
10246 None => false,
10247 },
10248 Stmt::Labeled { body, .. } | Stmt::Synchronized { body, .. } => {
10249 always_returns_one(body)
10250 }
10251 Stmt::Switch { cases, default, .. } => {
10252 default.as_ref().is_some_and(|d| always_returns_one(d))
10253 && cases.iter().all(|c| always_returns(&c.body))
10254 }
10255 Stmt::Try { body, catches, finally }
10256 | Stmt::TryWithResources { body, catches, finally, .. } => {
10257 finally.as_ref().is_some_and(|f| always_returns_one(f))
10258 || (always_returns_one(body)
10259 && catches.iter().all(|c| always_returns_one(&c.body)))
10260 }
10261 _ => false,
10262 }
10263}
10264
10265fn always_returns_one(st: &Stmt) -> bool {
10266 always_returns(std::slice::from_ref(st))
10267}
10268
10269fn inline_ctor_arg_defs(
10270 call: &mut Stmt,
10271 prefix: &[&Stmt],
10272 vt: &VarTable,
10273 consumed: &mut Vec<(u32, usize)>,
10274) -> bool {
10275 let Stmt::ExprStmt(cexpr) = call else {
10276 return false;
10277 };
10278 fn subst(e: &mut Expr, v: u32, val: &Option<Expr>) -> bool {
10281 let mut ok = true;
10282 deep_rewrite(e, &mut |x| {
10283 if let Expr::Local { var, .. } = x {
10284 if *var == v {
10285 match val {
10286 Some(e2) => *x = e2.clone(),
10287 None => ok = false,
10288 }
10289 }
10290 }
10291 });
10292 ok
10293 }
10294 let mut seen: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
10295 for _ in 0..32 {
10296 let mut need: Option<u32> = None;
10297 visit_exprs(cexpr, &mut |x| {
10298 if need.is_none() {
10299 if let Expr::Local { var, .. } = x {
10300 if (*var as usize) < vt.vars.len() && !vt.vars[*var as usize].is_param {
10301 need = Some(*var);
10302 }
10303 }
10304 }
10305 });
10306 let Some(v) = need else { break };
10307 if !seen.insert(v) {
10308 return false; }
10310 let mut val: Option<Expr> = None;
10322 let mut ok = true;
10323 for (si, st) in prefix.iter().enumerate() {
10324 match st {
10325 Stmt::LocalDef { var, init: None, .. } if *var == v => {}
10329 Stmt::LocalDef { var, init, .. } if *var == v => {
10330 let Some(e) = init else { ok = false; break };
10331 let mut e2 = (*e).clone();
10332 if !subst(&mut e2, v, &val) {
10333 ok = false;
10334 break;
10335 }
10336 val = Some(e2);
10337 consumed.push((v, si));
10338 }
10339 Stmt::ExprStmt(Expr::Assign { target, op, .. })
10340 if matches!(&**target, Expr::Local { var: tv, .. } if *tv == v) =>
10341 {
10342 if !matches!(op, AssignOp::Plain) {
10343 ok = false; break;
10345 }
10346 let Stmt::ExprStmt(Expr::Assign { value, .. }) = st else {
10347 unreachable!()
10348 };
10349 let mut e2 = value.as_ref().clone();
10350 if !subst(&mut e2, v, &val) {
10351 ok = false;
10352 break;
10353 }
10354 val = Some(e2);
10355 consumed.push((v, si));
10356 }
10357 _ => {
10358 let mut touched = false;
10359 visit_all_exprs(st, &mut |x| {
10360 if let Expr::Local { var, .. } = x {
10361 if *var == v {
10362 touched = true;
10363 }
10364 }
10365 });
10366 if touched {
10367 ok = false;
10368 break;
10369 }
10370 }
10371 }
10372 }
10373 if !ok {
10374 return false;
10375 }
10376 let Some(de) = val else { return false };
10377 if contains_this_access(&de) {
10378 return false;
10379 }
10380 let mut de_nodes = 0usize;
10382 visit_exprs(&de, &mut |_| de_nodes += 1);
10383 if de_nodes > 256 {
10384 return false;
10385 }
10386 deep_rewrite(cexpr, &mut |x| {
10387 if let Expr::Local { var, .. } = x {
10388 if *var == v {
10389 *x = de.clone();
10390 }
10391 }
10392 });
10393 let mut nodes = 0usize;
10398 visit_exprs(cexpr, &mut |_| nodes += 1);
10399 if nodes > 2_048 {
10400 return false;
10401 }
10402 }
10403 true
10404}
10405
10406fn fold_sb_chain(stmts: &mut Vec<Stmt>, pos: usize) -> usize {
10419 let mut sbv: Option<u32> = None;
10421 let mut def_i: Option<usize> = None;
10422 let mut def_init: Option<Expr> = None;
10423 for (i, st) in stmts[..pos].iter().enumerate() {
10424 let cand: Option<(u32, &Expr)> = match st {
10425 Stmt::LocalDef {
10426 var,
10427 init: Some(e),
10428 ..
10429 } => Some((*var, e)),
10430 Stmt::ExprStmt(Expr::Assign {
10431 target,
10432 value,
10433 op: AssignOp::Plain,
10434 ..
10435 }) => match &**target {
10436 Expr::Local { var, .. } => Some((*var, value.as_ref())),
10437 _ => None,
10438 },
10439 _ => None,
10440 };
10441 if let Some((v, init)) = cand {
10442 if let Expr::New { cls, .. } = init {
10443 if cls.as_ref() == "java/lang/StringBuilder" {
10444 sbv = Some(v);
10445 def_i = Some(i);
10446 def_init = Some(init.clone());
10447 break;
10448 }
10449 }
10450 }
10451 }
10452 let (Some(sbv), Some(def_i), Some(init)) = (sbv, def_i, def_init) else {
10453 return pos;
10454 };
10455 if contains_this_access(&init) {
10456 return pos;
10457 }
10458 let mut appends: Vec<usize> = Vec::new();
10460 for (i, st) in stmts[..pos].iter().enumerate() {
10461 if let Stmt::ExprStmt(Expr::Method {
10462 owner: Some(o),
10463 cls,
10464 name,
10465 ..
10466 }) = st
10467 {
10468 if matches!(&**o, Expr::Local { var, .. } if *var == sbv)
10469 && cls.as_ref() == "java/lang/StringBuilder"
10470 && &**name == "append"
10471 {
10472 appends.push(i);
10473 }
10474 }
10475 }
10476 let mut post_read = false;
10478 for st in stmts.iter().skip(pos + 1) {
10479 visit_all_exprs(st, &mut |e| {
10480 if let Expr::Local { var, .. } = e {
10481 if *var == sbv {
10482 post_read = true;
10483 }
10484 }
10485 });
10486 }
10487 if post_read {
10488 return pos;
10489 }
10490 for (i, st) in stmts[..pos].iter().enumerate() {
10493 if i == def_i || appends.contains(&i) {
10494 if let Stmt::ExprStmt(Expr::Method { args, .. }) = st {
10496 if args.iter().any(contains_this_access) {
10497 return pos;
10498 }
10499 }
10500 continue;
10501 }
10502 let mut leak = false;
10503 visit_all_exprs(st, &mut |e| {
10504 if let Expr::Local { var, .. } = e {
10505 if *var == sbv {
10506 leak = true;
10507 }
10508 }
10509 });
10510 if leak {
10511 return pos;
10512 }
10513 }
10514 let mut local_reads = 0usize;
10516 let mut tostring_reads = 0usize;
10517 visit_all_exprs(&stmts[pos], &mut |e| match e {
10518 Expr::Local { var, .. } if *var == sbv => local_reads += 1,
10519 Expr::Method {
10520 owner: Some(o),
10521 name,
10522 args,
10523 ..
10524 } if args.is_empty()
10525 && &**name == "toString"
10526 && matches!(&**o, Expr::Local { var, .. } if *var == sbv) =>
10527 {
10528 tostring_reads += 1;
10529 }
10530 _ => {}
10531 });
10532 if local_reads != 1 || tostring_reads != 1 {
10533 return pos;
10534 }
10535 let mut chain = init;
10537 for ai in &appends {
10538 if let Stmt::ExprStmt(Expr::Method {
10539 owner: _,
10540 cls,
10541 name,
10542 desc,
10543 args,
10544 is_static,
10545 is_interface,
10546 is_special,
10547 is_super,
10548 is_dynamic,
10549 type_args,
10550 ..
10551 }) = &stmts[*ai]
10552 {
10553 chain = Expr::Method {
10554 owner: Some(Box::new(chain)),
10555 cls: cls.clone(),
10556 name: name.clone(),
10557 desc: desc.clone(),
10558 args: args.clone(),
10559 is_static: *is_static,
10560 is_interface: *is_interface,
10561 is_special: *is_special,
10562 is_super: *is_super,
10563 is_dynamic: *is_dynamic,
10564 type_args: type_args.clone(),
10565 };
10566 }
10567 }
10568 let mut replaced = false;
10569 walk_stmt_exprs(&mut stmts[pos], &mut |e| {
10570 deep_rewrite(e, &mut |x| {
10571 if let Expr::Method {
10572 owner: Some(o),
10573 name,
10574 args,
10575 ..
10576 } = x
10577 {
10578 if args.is_empty()
10579 && &**name == "toString"
10580 && matches!(&**o, Expr::Local { var, .. } if *var == sbv)
10581 {
10582 **o = chain.clone();
10583 replaced = true;
10584 }
10585 }
10586 });
10587 });
10588 if !replaced {
10589 return pos; }
10591 let mut removed: Vec<usize> = appends.clone();
10593 removed.push(def_i);
10594 removed.sort_unstable();
10595 removed.dedup();
10596 for i in removed.iter().rev() {
10597 stmts.remove(*i);
10598 }
10599 pos - removed.len()
10600}
10601
10602pub fn fix_ctor_super_first(body: &mut Stmt, vt: &VarTable) {
10603 let Stmt::Block(stmts) = body else { return };
10604 if stmts.is_empty() || is_bare_ctor_call(stmts.first().unwrap()) {
10605 return;
10606 }
10607 if let Some(pos0) = stmts.iter().position(is_bare_ctor_call) {
10609 let pos = fold_sb_chain(stmts, pos0);
10610 let mut call = stmts[pos].clone();
10611 let prefix: Vec<&Stmt> = stmts[..pos].iter().collect();
10612 let mut consumed: Vec<(u32, usize)> = Vec::new();
10613 if !inline_ctor_arg_defs(&mut call, &prefix, vt, &mut consumed) {
10614 return;
10615 }
10616 stmts.remove(pos);
10617 let mut drop_idx: Vec<usize> = consumed
10624 .iter()
10625 .filter(|(v, _)| !var_occurs_in(&stmts[pos..], *v))
10626 .map(|(_, k)| *k)
10627 .collect();
10628 drop_idx.sort_unstable();
10629 drop_idx.dedup();
10630 for k in drop_idx.iter().rev() {
10631 stmts.remove(*k);
10632 }
10633 stmts.insert(0, call);
10634 return;
10635 }
10636 let mut found: Option<(usize, usize)> = None;
10646 for (i, st) in stmts.iter().enumerate() {
10647 if let Stmt::Block(inner) = st {
10648 if let Some(p) = inner.iter().position(is_bare_ctor_call) {
10649 if inner[..p]
10650 .iter()
10651 .all(|s| matches!(s, Stmt::LocalDef { .. } | Stmt::ExprStmt(_)))
10652 {
10653 found = Some((i, p));
10654 break;
10655 }
10656 }
10657 }
10658 }
10659 if let Some((i, p)) = found {
10660 let mut call = match &stmts[i] {
10661 Stmt::Block(inner) => inner[p].clone(),
10662 _ => unreachable!(),
10663 };
10664 let mut prefix: Vec<&Stmt> = stmts[..i].iter().collect();
10665 if let Stmt::Block(inner) = &stmts[i] {
10666 prefix.extend(inner[..p].iter());
10667 }
10668 let mut consumed: Vec<(u32, usize)> = Vec::new();
10669 if !inline_ctor_arg_defs(&mut call, &prefix, vt, &mut consumed) {
10670 return;
10671 }
10672 let mut ks: Vec<usize> = Vec::new();
10679 {
10680 let inner_tail: &[Stmt] = match &stmts[i] {
10681 Stmt::Block(inner) if p < inner.len() => &inner[p + 1..],
10682 _ => &[],
10683 };
10684 for (v, k) in &consumed {
10685 if !var_occurs_in(inner_tail, *v) && !var_occurs_in(&stmts[i + 1..], *v) {
10686 ks.push(*k);
10687 }
10688 }
10689 }
10690 ks.sort_unstable();
10691 ks.dedup();
10692 if let Stmt::Block(inner) = &mut stmts[i] {
10693 inner.remove(p);
10694 for k in ks.iter().rev().filter(|k| **k >= i) {
10695 inner.remove(k - i);
10696 }
10697 }
10698 for k in ks.iter().rev().filter(|k| **k < i) {
10699 stmts.remove(*k);
10700 }
10701 stmts.insert(0, call);
10702 }
10703}
10704
10705fn is_delegation_expr(e: &Expr) -> bool {
10714 matches!(
10715 e,
10716 Expr::Method { name, is_special: true, owner, .. }
10717 if &**name == "<init>"
10718 && (owner.is_none() || matches!(owner.as_deref(), Some(Expr::This)))
10719 )
10720}
10721
10722pub(crate) fn is_bare_ctor_call(s: &Stmt) -> bool {
10723 matches!(s, Stmt::ExprStmt(e) if is_delegation_expr(e))
10724}
10725
10726pub(crate) fn local_is_written(body: &Stmt, var: u32) -> bool {
10730 let mut hit = false;
10731 visit_stmt_exprs_ro(body, &mut |e| {
10732 if hit {
10733 return;
10734 }
10735 visit_exprs(e, &mut |x| {
10736 if hit {
10737 return;
10738 }
10739 match x {
10740 Expr::Assign { target, .. }
10741 if matches!(&**target, Expr::Local { var: v, .. } if *v == var) =>
10742 {
10743 hit = true;
10744 }
10745 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. }
10746 if matches!(&**e, Expr::Local { var: v, .. } if *v == var) =>
10747 {
10748 hit = true;
10749 }
10750 _ => {}
10751 }
10752 });
10753 });
10754 hit
10755}
10756
10757pub(crate) fn rewrite_inner_ctor_outer_param(
10767 body: &mut Stmt,
10768 param0: u32,
10769 outer_display: &str,
10770 outer_ty: &TypeRef,
10771 eligible: &[String],
10772) {
10773 walk_stmt_exprs(body, &mut |e| {
10777 if let Expr::Method { name, cls, args, .. } = e {
10778 if &**name == "<init>"
10779 && eligible.iter().any(|c| c.as_str() == cls.as_ref())
10780 && args.first().is_some_and(|a| matches!(a, Expr::Local { var: v, .. } if *v == param0))
10781 {
10782 args.remove(0);
10783 }
10784 }
10785 });
10786 let text = format!("{outer_display}.this");
10788 let ty = outer_ty.clone();
10789 walk_stmt_exprs(body, &mut |e| {
10790 deep_rewrite_reads(e, &mut |x| {
10791 if let Expr::Local { var: v, .. } = x {
10792 if *v == param0 {
10793 *x = Expr::RawT(text.clone(), ty.clone());
10794 }
10795 }
10796 });
10797 });
10798}
10799
10800fn stmts_count_writes(stmts: &[Stmt], var: u32) -> usize {
10848 let mut n = 0usize;
10849 for s in stmts {
10850 visit_stmt_exprs_ro(s, &mut |e| {
10851 visit_exprs(e, &mut |x| match x {
10852 Expr::Assign { target, .. }
10853 if matches!(&**target, Expr::Local { var: v, .. } if *v == var) =>
10854 {
10855 n += 1;
10856 }
10857 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. }
10858 if matches!(&**e, Expr::Local { var: v, .. } if *v == var) =>
10859 {
10860 n += 1;
10861 }
10862 _ => {}
10863 });
10864 });
10865 }
10866 n
10867}
10868
10869fn prefix_written_vars(stmts: &[Stmt]) -> jdc_core::FxHashSet<u32> {
10873 let mut set = jdc_core::FxHashSet::default();
10874 for s in stmts {
10875 let mut c = s.clone();
10876 walk_stmt_exprs(&mut c, &mut |e| {
10877 deep_rewrite(e, &mut |x| match x {
10878 Expr::Assign { target, .. } => {
10879 if let Expr::Local { var, .. } = &**target {
10880 set.insert(*var);
10881 }
10882 }
10883 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => {
10884 if let Expr::Local { var, .. } = &**e {
10885 set.insert(*var);
10886 }
10887 }
10888 _ => {}
10889 });
10890 });
10891 if let Stmt::LocalDef { var, .. } = s {
10892 set.insert(*var);
10893 }
10894 }
10895 set
10896}
10897
10898fn pre_this_safe(e: &Expr, prefix_vars: &jdc_core::FxHashSet<u32>) -> bool {
10902 match e {
10903 Expr::Local { var, .. } => !prefix_vars.contains(var),
10904 Expr::Const(_) => true,
10905 Expr::Cast { e, .. } => pre_this_safe(e, prefix_vars),
10906 Expr::Bin { l, r, .. } => {
10907 pre_this_safe(l, prefix_vars) && pre_this_safe(r, prefix_vars)
10908 }
10909 Expr::Cond { c, t, f } => {
10910 pre_this_safe(c, prefix_vars)
10911 && pre_this_safe(t, prefix_vars)
10912 && pre_this_safe(f, prefix_vars)
10913 }
10914 Expr::Field { owner, is_static, .. } => {
10915 *is_static
10916 && owner
10917 .as_ref()
10918 .is_none_or(|o| pre_this_safe(o, prefix_vars))
10919 }
10920 Expr::Method {
10921 owner,
10922 args,
10923 is_static,
10924 ..
10925 } => {
10926 args.iter().all(|a| pre_this_safe(a, prefix_vars))
10927 && match owner {
10928 Some(o) => pre_this_safe(o, prefix_vars),
10929 None => *is_static,
10930 }
10931 }
10932 Expr::New { args, .. } => args.iter().all(|a| pre_this_safe(a, prefix_vars)),
10940 Expr::NewArray { dims, init, .. } => {
10941 dims.iter().all(|d| pre_this_safe(d, prefix_vars))
10942 && init
10943 .as_ref()
10944 .map(|l| l.iter().all(|x| pre_this_safe(x, prefix_vars)))
10945 .unwrap_or(true)
10946 }
10947 Expr::Un { e, .. } | Expr::InstanceOf { e, .. } => pre_this_safe(e, prefix_vars),
10949 Expr::ArrayIndex { array, index } => {
10950 pre_this_safe(array, prefix_vars) && pre_this_safe(index, prefix_vars)
10951 }
10952 _ => false,
10953 }
10954}
10955
10956fn single_assign_to_v(s: &Stmt, v: u32) -> Option<Expr> {
10960 match s {
10961 Stmt::ExprStmt(Expr::Assign {
10962 target,
10963 op: AssignOp::Plain,
10964 value,
10965 }) if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v) => {
10966 Some((**value).clone())
10967 }
10968 Stmt::Block(xs) if xs.len() == 1 => single_assign_to_v(&xs[0], v),
10969 _ => None,
10970 }
10971}
10972
10973pub(crate) fn flatten_top_blocks(stmts: &mut Vec<Stmt>) {
10995 fn rec(s: Stmt, out: &mut Vec<Stmt>) {
10996 match s {
10997 Stmt::Block(inner) => {
10998 for x in inner {
10999 rec(x, out);
11000 }
11001 }
11002 other => out.push(other),
11003 }
11004 }
11005 if !stmts.iter().any(|s| matches!(s, Stmt::Block(_))) {
11006 return;
11007 }
11008 let mut out: Vec<Stmt> = Vec::with_capacity(stmts.len());
11009 for s in std::mem::take(stmts) {
11010 rec(s, &mut out);
11011 }
11012 *stmts = out;
11013}
11014
11015fn fold_default_arg_bridge(stmts: &mut Vec<Stmt>) -> bool {
11016 flatten_top_blocks(stmts);
11017 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
11018 return false;
11019 };
11020 if pos == 0 {
11021 return false;
11022 }
11023 let Stmt::ExprStmt(Expr::Method { args, .. }) = &stmts[pos] else {
11024 return false;
11025 };
11026 let mut arg_vars: Vec<u32> = Vec::new();
11027 let mut reads = 0usize;
11028 for a in args {
11029 let mut c = a.clone();
11030 deep_rewrite_reads(&mut c, &mut |x| {
11031 if let Expr::Local { var, .. } = x {
11032 reads += 1;
11033 if !arg_vars.contains(var) {
11034 arg_vars.push(*var);
11035 }
11036 }
11037 });
11038 }
11039 if arg_vars.is_empty() || reads != arg_vars.len() {
11040 return false; }
11042 for &v in &arg_vars {
11045 if stmts_read_var(&stmts[pos + 1..], v) != 0
11046 || stmts_count_writes(&stmts[pos + 1..], v) != 0
11047 {
11048 return false;
11049 }
11050 }
11051 let prefix_vars = prefix_written_vars(&stmts[..pos]);
11052 let mut repl: Vec<(u32, Expr)> = Vec::new();
11053 let mut drop: Vec<usize> = Vec::new();
11054 for &v in &arg_vars {
11055 let mut base: Option<(usize, Expr)> = None;
11057 let mut decl: Option<usize> = None;
11058 let mut condf: Option<(usize, Expr, Option<Expr>, Option<Expr>)> = None;
11059 let mut ok = true;
11060 for (i, s) in stmts[..pos].iter().enumerate() {
11061 match s {
11062 Stmt::LocalDef { var, init, .. } if *var == v => match init {
11063 None => {
11064 if decl.is_some() {
11065 ok = false;
11066 break;
11067 }
11068 decl = Some(i);
11069 }
11070 Some(e) => {
11071 if base.is_some() {
11072 ok = false;
11073 break;
11074 }
11075 base = Some((i, e.clone()));
11076 }
11077 },
11078 Stmt::ExprStmt(Expr::Assign {
11079 target,
11080 op: AssignOp::Plain,
11081 value,
11082 }) if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v) => {
11083 if base.is_some() {
11084 ok = false;
11085 break;
11086 }
11087 base = Some((i, (**value).clone()));
11088 }
11089 Stmt::If {
11090 cond,
11091 then_stmt,
11092 else_stmt,
11093 } => {
11094 let t = single_assign_to_v(then_stmt, v);
11095 let f = else_stmt.as_ref().and_then(|b| single_assign_to_v(b, v));
11096 let then_touches = t.is_some()
11097 || stmts_read_var(std::slice::from_ref(&**then_stmt), v) != 0
11098 || stmts_count_writes(std::slice::from_ref(&**then_stmt), v) != 0;
11099 let else_touches = else_stmt.as_ref().is_some_and(|b| {
11100 f.is_some()
11101 || stmts_read_var(std::slice::from_ref(b), v) != 0
11102 || stmts_count_writes(std::slice::from_ref(b), v) != 0
11103 });
11104 if !then_touches && !else_touches {
11105 continue; }
11107 if condf.is_some()
11112 || base.is_none()
11113 || (then_touches && t.is_none())
11114 || (else_touches && f.is_none())
11115 {
11116 ok = false;
11117 break;
11118 }
11119 let cond_stmt = Stmt::ExprStmt(cond.clone());
11120 if stmts_read_var(std::slice::from_ref(&cond_stmt), v) != 0 {
11121 ok = false;
11122 break;
11123 }
11124 condf = Some((i, cond.clone(), t, f));
11125 }
11126 _ => {}
11127 }
11128 }
11129 if !ok {
11130 continue;
11131 }
11132 let Some((bi, be)) = base else { continue };
11133 let want_writes = 1
11136 + condf
11137 .as_ref()
11138 .map(|(_, _, t, f)| t.is_some() as usize + f.is_some() as usize)
11139 .unwrap_or(0);
11140 if stmts_count_writes(&stmts[..pos], v) != want_writes
11141 || stmts_read_var(&stmts[..pos], v) != 0
11142 {
11143 continue;
11144 }
11145 let rep = match condf {
11146 Some((ci, c, t, f)) => {
11147 drop.push(ci);
11148 match (t, f) {
11149 (Some(t), Some(f)) => Expr::Cond {
11150 c: Box::new(c),
11151 t: Box::new(t),
11152 f: Box::new(f),
11153 },
11154 (Some(t), None) => Expr::Cond {
11155 c: Box::new(c),
11156 t: Box::new(t),
11157 f: Box::new(be.clone()),
11158 },
11159 (None, Some(f)) => Expr::Cond {
11160 c: Box::new(c),
11161 t: Box::new(be.clone()),
11162 f: Box::new(f),
11163 },
11164 (None, None) => unreachable!("condf requires a branch assign"),
11165 }
11166 }
11167 None => be.clone(),
11168 };
11169 if !pre_this_safe(&rep, &prefix_vars) {
11170 continue;
11171 }
11172 drop.push(bi);
11173 if let Some(d) = decl {
11174 drop.push(d);
11175 }
11176 repl.push((v, rep));
11177 }
11178 if repl.is_empty() {
11179 return false;
11180 }
11181 if let Stmt::ExprStmt(Expr::Method { args, .. }) = &mut stmts[pos] {
11182 for a in args.iter_mut() {
11183 deep_rewrite_reads(a, &mut |x| {
11184 if let Expr::Local { var, .. } = x {
11185 if let Some((_, e)) = repl.iter().find(|(vv, _)| vv == var) {
11186 *x = e.clone();
11187 }
11188 }
11189 });
11190 }
11191 }
11192 drop.sort_unstable();
11193 drop.dedup();
11194 for i in drop.into_iter().rev() {
11195 stmts.remove(i);
11196 }
11197 true
11198}
11199
11200pub fn rewrite_kotlin_facades(body: &mut Stmt, pool: &crate::DexPool) {
11208 let map = pool.kotlin_facade_map();
11209 if map.is_empty() {
11210 return;
11211 }
11212 walk_stmt_exprs(body, &mut |e| {
11213 match e {
11214 Expr::Method {
11215 cls,
11216 name,
11217 desc,
11218 is_static,
11219 ..
11220 } if *is_static => {
11221 if let Some((base, cover)) = map.get(cls.as_ref()) {
11222 let hit = match cover {
11223 None => true,
11224 Some(c) => c.0.contains(&(name.to_string(), desc.to_string())),
11225 };
11226 if hit {
11227 *cls = std::sync::Arc::from(base.as_str());
11228 }
11229 }
11230 }
11231 Expr::Field {
11232 cls,
11233 name,
11234 is_static,
11235 ..
11236 } if *is_static => {
11237 if let Some((base, cover)) = map.get(cls.as_ref()) {
11238 let hit = match cover {
11239 None => true,
11240 Some(c) => c.1.contains(name.as_ref()),
11241 };
11242 if hit {
11243 *cls = std::sync::Arc::from(base.as_str());
11244 }
11245 }
11246 }
11247 _ => {}
11248 }
11249 });
11250}
11251
11252pub fn fold_enum_default_arg_bridge(body: &mut Stmt) {
11260 let Stmt::Block(stmts) = body else { return };
11261 fold_default_arg_bridge(stmts);
11262}
11263
11264pub fn fix_ctor_delegation_arg_defs(body: &mut Stmt) {
11265 let Stmt::Block(stmts) = body else { return };
11266 fold_default_arg_bridge(stmts);
11269 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
11273 return;
11274 };
11275 if pos == 0 {
11276 return;
11277 }
11278 let mut arg_vars: Vec<u32> = Vec::new();
11281 let mut total_reads = 0usize;
11282 {
11283 let Stmt::ExprStmt(Expr::Method { args, .. }) = &stmts[pos] else {
11284 return;
11285 };
11286 for a in args {
11287 let mut c = a.clone();
11288 deep_rewrite_reads(&mut c, &mut |x| {
11289 if let Expr::Local { var: v, .. } = x {
11290 total_reads += 1;
11291 if !arg_vars.contains(v) {
11292 arg_vars.push(*v);
11293 }
11294 }
11295 });
11296 }
11297 }
11298 if arg_vars.is_empty() || total_reads != arg_vars.len() {
11299 return;
11300 }
11301 let prefix_defs: Vec<u32> = stmts[..pos]
11304 .iter()
11305 .filter_map(|s| match s {
11306 Stmt::LocalDef { var, init: Some(_), .. } => Some(*var),
11307 _ => None,
11308 })
11309 .collect();
11310 let mut plan: Vec<(u32, usize, Option<usize>)> = Vec::new();
11315 for v in arg_vars {
11316 let defs: Vec<usize> = stmts[..pos]
11317 .iter()
11318 .enumerate()
11319 .filter(|(i, _)| {
11320 matches!(&stmts[*i], Stmt::LocalDef { var, init: Some(_), .. } if *var == v)
11321 })
11322 .map(|(i, _)| i)
11323 .collect();
11324 if defs.len() > 1 {
11325 return; }
11327 let Some(&q) = defs.first() else {
11328 continue; };
11330 let init = match &stmts[q] {
11331 Stmt::LocalDef { init: Some(e), .. } => e.clone(),
11332 _ => return,
11333 };
11334 if stmts_read_var(&stmts[q + 1..pos], v) != 0 {
11335 return; }
11337 let mut init_vars: Vec<u32> = Vec::new();
11340 deep_rewrite_reads(&mut { init.clone() }, &mut |x| {
11341 if let Expr::Local { var: vv, .. } = x {
11342 if !init_vars.contains(vv) {
11343 init_vars.push(*vv);
11344 }
11345 }
11346 });
11347 if init_vars.iter().any(|iv| prefix_defs.contains(iv)) {
11348 return;
11349 }
11350 match stmts[pos + 1..]
11351 .iter()
11352 .position(|s| {
11353 matches!(s, Stmt::ExprStmt(Expr::Assign { target, .. })
11354 if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v))
11355 }) {
11356 Some(w) => {
11357 if stmts_read_var(&stmts[pos + 1..pos + 1 + w], v) != 0 {
11359 return;
11360 }
11361 plan.push((v, q, Some(pos + 1 + w)));
11362 }
11363 None => {
11364 if stmts_read_var(&stmts[pos + 1..], v) != 0 {
11366 return;
11367 }
11368 plan.push((v, q, None));
11369 }
11370 }
11371 }
11372 if plan.is_empty() {
11373 return;
11374 }
11375 let values: Vec<(u32, Expr)> = plan
11377 .iter()
11378 .map(|(_v, q, _)| match &stmts[*q] {
11379 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e.clone()),
11380 _ => unreachable!("plan entries carry a LocalDef with init"),
11381 })
11382 .collect();
11383 if let Stmt::ExprStmt(Expr::Method { args, .. }) = &mut stmts[pos] {
11384 for a in args {
11385 deep_rewrite_reads(a, &mut |x| {
11386 if let Expr::Local { var: v, .. } = x {
11387 if let Some((_, e)) = values.iter().find(|(vv, _)| vv == v) {
11388 *x = e.clone();
11389 }
11390 }
11391 });
11392 }
11393 }
11394 for (v, _, w) in &plan {
11398 if let Some(w) = w {
11399 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = &mut stmts[*w] {
11400 if matches!(&**target, Expr::Local { var: vv, .. } if vv == v) {
11401 let taken = std::mem::replace(&mut **value, Expr::Const(ConstVal::Null));
11402 stmts[*w] = Stmt::LocalDef {
11403 var: *v,
11404 init: Some(taken),
11405 is_final: false,
11406 force_type: false,
11407 };
11408 }
11409 }
11410 }
11411 }
11412 let mut order: Vec<usize> = plan.iter().map(|(_, q, _)| *q).collect();
11413 order.sort_unstable();
11414 order.reverse();
11415 for q in order {
11416 stmts.remove(q);
11417 }
11418}
11419
11420pub fn fix_bool_returns(vt: &VarTable, body: &mut Stmt) {
11431 walk_mut_deep(body, &mut |st| {
11432 if let Stmt::Return(Some(e)) = st {
11433 let numeric = match &*e {
11434 Expr::Local { var, ty } => {
11435 let t = if (*var as usize) < vt.vars.len() {
11436 vt.var(*var).ty.erased()
11437 } else {
11438 ty.erased()
11439 };
11440 matches!(
11441 t,
11442 JavaType::Int
11443 | JavaType::Long
11444 | JavaType::Short
11445 | JavaType::Byte
11446 | JavaType::Char
11447 )
11448 }
11449 other => matches!(
11450 other.type_ref().erased(),
11451 JavaType::Int | JavaType::Long | JavaType::Short | JavaType::Byte
11452 | JavaType::Char
11453 ),
11454 };
11455 if numeric {
11456 let taken = e.clone();
11457 *e = Expr::Bin {
11458 op: BinOp::Ne,
11459 l: Box::new(taken),
11460 r: Box::new(Expr::Const(ConstVal::Int(0))),
11461 ty: Some(TypeRef::J(JavaType::Boolean)),
11462 };
11463 }
11464 }
11465 });
11466}
11467
11468pub fn fix_int_returns(vt: &VarTable, body: &mut Stmt) {
11469 walk_mut_deep(body, &mut |st| {
11470 if let Stmt::Return(Some(e)) = st {
11471 let is_bool = match e {
11472 Expr::Local { var, .. } => {
11473 (*var as usize) < vt.vars.len()
11474 && vt.vars[*var as usize].ty.erased() == JavaType::Boolean
11475 }
11476 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
11477 _ => false,
11478 };
11479 if is_bool {
11480 *e = Expr::Cond {
11481 c: Box::new(e.clone()),
11482 t: Box::new(Expr::Const(ConstVal::Int(1))),
11483 f: Box::new(Expr::Const(ConstVal::Int(0))),
11484 };
11485 }
11486 }
11487 });
11488}
11489
11490static WIDEN_COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
11506
11507pub fn split_generations(vt: &mut VarTable, body: &mut Stmt, pool: &DexPool) {
11508 if std::env::var("DDC_DBG_PHI").is_ok() {
11509 eprintln!("[widen] cumulative={}", WIDEN_COUNT.load(std::sync::atomic::Ordering::Relaxed));
11510 }
11511 let mut counter: u32 = 0;
11512 let mut gen: std::collections::HashMap<u32, u32> = std::collections::HashMap::default();
11513 split_walk_stmt(body, vt, &mut gen, &mut counter, pool);
11514}
11515
11516fn split_needed(vt: &VarTable, target: u32, value: &Expr, pool: &DexPool) -> bool {
11518 let n = vt.vars.len();
11519 if target as usize >= n {
11520 return false;
11521 }
11522 let declared = vt.vars[target as usize].ty.erased();
11523 let vt_is_ref = |t: &JavaType| matches!(t, JavaType::Object(_) | JavaType::Array(_));
11524 fn side_bool(e: &Expr, vt: &VarTable) -> bool {
11533 match e {
11534 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
11535 matches!(vt.var(*var).ty.erased(), JavaType::Boolean)
11536 }
11537 Expr::Const(ConstVal::Int(0 | 1)) => true,
11538 Expr::Bin { op: BinOp::Or | BinOp::And | BinOp::Xor, l, r, .. } => {
11539 side_bool(l, vt) && side_bool(r, vt)
11540 }
11541 other => matches!(other.type_ref().erased(), JavaType::Boolean),
11542 }
11543 }
11544 let value_ty = match value {
11545 Expr::Local { var, ty } if (*var as usize) < vt.vars.len() => vt.var(*var).ty.erased(),
11546 Expr::Bin {
11547 op: BinOp::Or | BinOp::And | BinOp::Xor,
11548 l,
11549 r,
11550 ..
11551 } if side_bool(l, vt) && side_bool(r, vt) => JavaType::Boolean,
11552 other => other.type_ref().erased(),
11553 };
11554 if vt_is_ref(&declared) && vt_is_ref(&value_ty) {
11555 let obj = JavaType::Object("java/lang/Object".into());
11565 if declared == value_ty || declared == obj || value_ty == obj {
11566 return false;
11567 }
11568 if let (JavaType::Object(v), JavaType::Object(d)) = (&value_ty, &declared) {
11581 if matches!(value, Expr::New { .. })
11582 && (pool.get(v).is_none() || pool.get(d).is_none())
11583 {
11584 return false;
11585 }
11586 }
11587 true
11588 } else {
11589 if vt_is_ref(&declared) != vt_is_ref(&value_ty) {
11593 return true;
11594 }
11595 let d_bool = matches!(declared, JavaType::Boolean);
11603 let v_bool = matches!(value_ty, JavaType::Boolean);
11604 let zero_one = matches!(value, Expr::Const(ConstVal::Int(0 | 1)));
11605 d_bool != v_bool && !zero_one
11606 }
11607}
11608
11609fn rewrite_gen_reads(e: &mut Expr, gen: &std::collections::HashMap<u32, u32>, vt: &VarTable) {
11610 deep_rewrite(e, &mut |x| {
11611 if let Expr::Local { var, ty } = x {
11612 if let Some(&to) = gen.get(var) {
11613 if to != *var {
11614 *var = to;
11615 *ty = vt.var(to).ty.clone();
11616 }
11617 }
11618 }
11619 });
11620}
11621
11622fn do_split(
11623 vt: &mut VarTable,
11624 target: &mut Expr,
11625 old: u32,
11626 value_ty: &JavaType,
11627 gen: &mut std::collections::HashMap<u32, u32>,
11628 counter: &mut u32,
11629) {
11630 *counter += 1;
11631 let info = &vt.vars[old as usize];
11632 let id = vt.vars.len() as u32;
11633 let name = format!("{}_g{}", info.name, counter);
11634 let slot = info.slot;
11635 let is_param = false;
11643 vt.vars.push(jdc_core::var::VarInfo {
11644 id,
11645 slot,
11646 name,
11647 ty: TypeRef::J(value_ty.clone()),
11648 is_param,
11649 range_start: 0,
11650 range_end: u16::MAX,
11651 synthetic_name: true,
11652 });
11653 while vt.by_slot.len() <= slot as usize {
11654 vt.by_slot.push(Vec::new());
11655 }
11656 vt.by_slot[slot as usize].push((0, u16::MAX, id));
11657 gen.insert(old, id);
11658 if let Expr::Local { var, ty, .. } = target {
11659 *var = id;
11660 *ty = TypeRef::J(value_ty.clone());
11661 }
11662}
11663
11664fn rename_gen_back_batch(s: &mut Stmt, pairs: &[(u32, u32)], vt: &VarTable) {
11679 use jdc_core::FxHashMap;
11680 if pairs.is_empty() {
11681 return;
11682 }
11683 let map: FxHashMap<u32, (u32, JavaType)> = pairs
11684 .iter()
11685 .map(|&(from, to)| (from, (to, vt.vars[to as usize].ty.erased())))
11686 .collect();
11687 walk_mut_deep(s, &mut |st| {
11688 if let Stmt::LocalDef { var, .. } = st {
11689 if let Some(&(to, _)) = map.get(var) {
11690 *var = to;
11691 }
11692 }
11693 });
11694 rewrite_exprs(s, &mut |e| {
11695 deep_rewrite(e, &mut |x| {
11696 if let Expr::Local { var, ty } = x {
11697 if let Some(&(to, ref to_ty)) = map.get(var) {
11698 *var = to;
11699 *ty = TypeRef::J(to_ty.clone());
11700 }
11701 }
11702 });
11703 });
11704}
11705
11706fn drop_gen_slot(vt: &mut VarTable, dead: u32) {
11710 if (dead as usize) >= vt.vars.len() {
11711 return;
11712 }
11713 let slot = vt.vars[dead as usize].slot as usize;
11714 if let Some(seg) = vt.by_slot.get_mut(slot) {
11715 seg.retain(|&(_, _, id)| id != dead);
11716 }
11717}
11718
11719fn lub_pending() -> Option<String> {
11740 Some(String::new())
11741}
11742
11743enum Undo {
11745 Direct,
11747 Widen(String),
11749 Refuse,
11751}
11752
11753fn obj_name(t: &TypeRef) -> Option<&std::sync::Arc<str>> {
11754 match t {
11755 TypeRef::J(JavaType::Object(o)) => Some(o),
11756 _ => None,
11757 }
11758}
11759
11760fn pool_lub(a: &str, b: &str, pool: &DexPool) -> Option<String> {
11761 if a == b {
11762 return Some(a.to_string());
11763 }
11764 thread_local! {
11770 static MEMO: std::cell::RefCell<jdc_core::FxHashMap<(String, String), Option<String>>> =
11771 std::cell::RefCell::new(jdc_core::FxHashMap::default());
11772 }
11773 if let Some(hit) = MEMO.with(|m| m.borrow().get(&(a.to_string(), b.to_string())).cloned()) {
11774 return hit;
11775 }
11776 let r = pool_lub_uncached(a, b, pool);
11777 MEMO.with(|m| m.borrow_mut().insert((a.to_string(), b.to_string()), r.clone()));
11778 r
11779}
11780
11781fn pool_lub_uncached(a: &str, b: &str, pool: &DexPool) -> Option<String> {
11782 let mut anc: jdc_core::FxHashSet<String> = jdc_core::FxHashSet::default();
11783 let mut stack: Vec<String> = vec![a.to_string()];
11784 while let Some(c) = stack.pop() {
11785 if !anc.insert(c.clone()) {
11786 continue;
11787 }
11788 if anc.len() > 1024 {
11789 return None;
11790 }
11791 if let Some(cls) = pool.get(&c) {
11792 if let Some(s) = &cls.super_name {
11793 stack.push(s.clone());
11794 }
11795 for i in &cls.interfaces {
11796 stack.push(i.clone());
11797 }
11798 } else {
11799 if let Some(s) = crate::fwdb::super_of(&c) {
11800 stack.push(s.to_string());
11801 }
11802 crate::fwdb::for_each_interface(&c, |i| stack.push(i.to_string()));
11803 }
11804 }
11805 let mut seen: jdc_core::FxHashSet<String> = jdc_core::FxHashSet::default();
11806 let mut queue: std::collections::VecDeque<String> = std::collections::VecDeque::new();
11807 queue.push_back(b.to_string());
11808 while let Some(c) = queue.pop_front() {
11809 if !seen.insert(c.clone()) {
11810 continue;
11811 }
11812 if c != "java/lang/Object" && anc.contains(&c) {
11813 return Some(c);
11814 }
11815 if seen.len() > 1024 {
11816 return None;
11817 }
11818 if let Some(cls) = pool.get(&c) {
11819 for i in &cls.interfaces {
11820 queue.push_back(i.clone());
11821 }
11822 if let Some(s) = &cls.super_name {
11823 queue.push_back(s.clone());
11824 }
11825 } else {
11826 crate::fwdb::for_each_interface(&c, |i| queue.push_back(i.to_string()));
11827 if let Some(s) = crate::fwdb::super_of(&c) {
11828 queue.push_back(s.to_string());
11829 }
11830 }
11831 }
11832 None
11833}
11834
11835fn undo_type_safe(vt: &VarTable, k: u32, v: u32) -> bool {
11836 if (k as usize) >= vt.vars.len() || (v as usize) >= vt.vars.len() {
11837 return false;
11838 }
11839 let kt = vt.vars[k as usize].ty.erased();
11840 let vt_ty = vt.vars[v as usize].ty.erased();
11841 if kt == vt_ty {
11842 return true;
11843 }
11844 let is_prim = |t: &JavaType| {
11845 matches!(
11846 t,
11847 JavaType::Int
11848 | JavaType::Long
11849 | JavaType::Short
11850 | JavaType::Byte
11851 | JavaType::Char
11852 | JavaType::Float
11853 | JavaType::Double
11854 | JavaType::Boolean
11855 )
11856 };
11857 is_prim(&kt) && is_prim(&vt_ty)
11858}
11859
11860fn split_walk_stmt(
11861 st: &mut Stmt,
11862 vt: &mut VarTable,
11863 gen: &mut std::collections::HashMap<u32, u32>,
11864 counter: &mut u32,
11865 pool: &DexPool,
11866) {
11867 match st {
11868 Stmt::Block(v) => {
11869 for x in v.iter_mut() {
11870 split_walk_stmt(x, vt, gen, counter, pool);
11871 }
11872 }
11873 Stmt::ExprStmt(e) => {
11874 rewrite_gen_reads(e, gen, vt);
11875 if let Expr::Assign {
11876 target,
11877 value,
11878 op: AssignOp::Plain,
11879 ..
11880 } = e
11881 {
11882 if let Expr::Local { var, .. } = &**target {
11883 let old = *var;
11884 if split_needed(vt, old, value, pool) {
11885 let value_ty = value.type_ref().erased();
11886 do_split(vt, target, old, &value_ty, gen, counter);
11887 }
11888 }
11889 }
11890 }
11891 Stmt::LocalDef { var, init, .. } => {
11892 if let Some(e) = init {
11893 rewrite_gen_reads(e, gen, vt);
11894 if split_needed(vt, *var, e, pool) {
11895 let value_ty = e.type_ref().erased();
11899 let old = *var;
11900 let mut fake = Expr::Local {
11901 var: old,
11902 ty: vt.vars[old as usize].ty.clone(),
11903 };
11904 do_split(vt, &mut fake, old, &value_ty, gen, counter);
11905 if let Expr::Local { var: nv, .. } = fake {
11906 *var = nv;
11907 }
11908 }
11909 }
11910 }
11911 Stmt::If {
11912 cond,
11913 then_stmt,
11914 else_stmt,
11915 ..
11916 } => {
11917 rewrite_gen_reads(cond, gen, vt);
11918 let then_floor = vt.vars.len() as u32;
11919 let mut g_then = gen.clone();
11920 split_walk_stmt(then_stmt, vt, &mut g_then, counter, pool);
11921 let else_floor = vt.vars.len() as u32;
11922 let mut g_else = gen.clone();
11923 if let Some(e) = else_stmt {
11924 split_walk_stmt(e, vt, &mut g_else, counter, pool);
11925 }
11926 let mut undo_then: Vec<(u32, u32, Option<String>)> = g_then
11964 .iter()
11965 .filter(|(&k, &v)| {
11966 v >= then_floor
11967 && v < else_floor
11968 && g_else.get(&k).copied() == gen.get(&k).copied()
11969 })
11970 .filter_map(|(&k, &v)| {
11971 if undo_type_safe(vt, k, v) {
11972 Some((v, k, None))
11973 } else if let (Some(_), Some(_)) =
11974 (obj_name(&vt.vars[k as usize].ty), obj_name(&vt.vars[v as usize].ty))
11975 {
11976 Some((v, k, lub_pending()))
11977 } else {
11978 None
11979 }
11980 })
11981 .collect();
11982 if else_floor > then_floor && !undo_then.is_empty() {
11983 let vars: Vec<u32> = undo_then.iter().map(|&(v, _, _)| v).collect();
11984 let mut hits = vec![false; vars.len()];
11985 mark_read_vars(std::slice::from_ref(&**then_stmt), &vars, &mut hits);
11986 let mut i = 0usize;
11987 undo_then.retain(|_| {
11988 let keep = !hits[i];
11989 i += 1;
11990 keep
11991 });
11992 }
11993 undo_then.sort_unstable_by_key(|&(v, _, _)| std::cmp::Reverse(v));
11994 let mut pairs: Vec<(u32, u32)> = Vec::with_capacity(undo_then.len());
11997 for (v, k, pending) in undo_then {
11998 let kind = match pending {
12004 None => Undo::Direct,
12005 Some(_) => {
12006 let (a, b) = (
12007 obj_name(&vt.vars[k as usize].ty),
12008 obj_name(&vt.vars[v as usize].ty),
12009 );
12010 match (a, b) {
12011 (Some(a), Some(b)) if pool.is_subtype(b, a) => Undo::Direct,
12012 (Some(a), Some(b)) => match pool_lub(a, b, pool) {
12013 Some(lub) => Undo::Widen(lub),
12014 None => Undo::Refuse,
12015 },
12016 _ => Undo::Refuse,
12017 }
12018 }
12019 };
12020 match kind {
12021 Undo::Refuse => continue,
12022 Undo::Direct => pairs.push((v, k)),
12023 Undo::Widen(lub) => {
12024 WIDEN_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
12025 vt.vars[k as usize].ty = TypeRef::J(JavaType::Object(lub.as_str().into()));
12026 pairs.push((v, k));
12027 }
12028 }
12029 }
12030 rename_gen_back_batch(then_stmt, &pairs, vt);
12031 for &(v, _) in &pairs {
12032 drop_gen_slot(vt, v);
12033 }
12034 if let Some(e) = else_stmt.as_deref_mut() {
12035 let mut undo_else: Vec<(u32, u32, Option<String>)> = g_else
12036 .iter()
12037 .filter(|(&k, &v)| {
12038 v >= else_floor && g_then.get(&k).copied() == gen.get(&k).copied()
12039 })
12040 .filter_map(|(&k, &v)| {
12041 if undo_type_safe(vt, k, v) {
12042 Some((v, k, None))
12043 } else if let (Some(_), Some(_)) =
12044 (obj_name(&vt.vars[k as usize].ty), obj_name(&vt.vars[v as usize].ty))
12045 {
12046 Some((v, k, lub_pending()))
12047 } else {
12048 None
12049 }
12050 })
12051 .collect();
12052 if (vt.vars.len() as u32) > else_floor && !undo_else.is_empty() {
12053 let vars: Vec<u32> = undo_else.iter().map(|&(v, _, _)| v).collect();
12054 let mut hits = vec![false; vars.len()];
12055 mark_read_vars(std::slice::from_ref(&*e), &vars, &mut hits);
12056 let mut i = 0usize;
12057 undo_else.retain(|_| {
12058 let keep = !hits[i];
12059 i += 1;
12060 keep
12061 });
12062 }
12063 undo_else.sort_unstable_by_key(|&(v, _, _)| std::cmp::Reverse(v));
12064 let mut pairs: Vec<(u32, u32)> = Vec::with_capacity(undo_else.len());
12065 for (v, k, pending) in undo_else {
12066 let kind = match pending {
12067 None => Undo::Direct,
12068 Some(_) => {
12069 let (a, b) = (
12070 obj_name(&vt.vars[k as usize].ty),
12071 obj_name(&vt.vars[v as usize].ty),
12072 );
12073 match (a, b) {
12074 (Some(a), Some(b)) if pool.is_subtype(b, a) => Undo::Direct,
12075 (Some(a), Some(b)) => match pool_lub(a, b, pool) {
12076 Some(lub) => Undo::Widen(lub),
12077 None => Undo::Refuse,
12078 },
12079 _ => Undo::Refuse,
12080 }
12081 }
12082 };
12083 match kind {
12084 Undo::Refuse => continue,
12085 Undo::Direct => pairs.push((v, k)),
12086 Undo::Widen(lub) => {
12087 WIDEN_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
12088 vt.vars[k as usize].ty = TypeRef::J(JavaType::Object(lub.as_str().into()));
12089 pairs.push((v, k));
12090 }
12091 }
12092 }
12093 rename_gen_back_batch(e, &pairs, vt);
12094 for &(v, _) in &pairs {
12095 drop_gen_slot(vt, v);
12096 }
12097 }
12098 gen.retain(|k, v| g_then.get(k) == Some(v) && g_else.get(k) == Some(v));
12101 }
12102 Stmt::While { cond, body, .. } | Stmt::DoWhile { cond, body, .. } => {
12103 let entry = gen.clone();
12107 for _ in 0..3 {
12108 rewrite_gen_reads(cond, gen, vt);
12109 split_walk_stmt(body, vt, gen, counter, pool);
12110 }
12111 *gen = entry;
12114 }
12115 Stmt::For { init, body, .. } => {
12116 for x in init.iter_mut() {
12117 split_walk_stmt(x, vt, gen, counter, pool);
12118 }
12119 let entry = gen.clone();
12120 for _ in 0..3 {
12121 split_walk_stmt(body, vt, gen, counter, pool);
12122 }
12123 *gen = entry;
12124 }
12125 Stmt::ForEach { body, .. } => {
12126 let entry = gen.clone();
12127 for _ in 0..3 {
12128 split_walk_stmt(body, vt, gen, counter, pool);
12129 }
12130 *gen = entry;
12131 }
12132 Stmt::Switch { cases, default, .. } => {
12133 let entry = gen.clone();
12134 for c in cases.iter_mut() {
12135 let mut g_case = entry.clone();
12136 for x in c.body.iter_mut() {
12137 split_walk_stmt(x, vt, &mut g_case, counter, pool);
12138 }
12139 gen.retain(|k, v| g_case.get(k) == Some(v));
12140 }
12141 if let Some(d) = default {
12142 let mut g_def = entry.clone();
12143 split_walk_stmt(d, vt, &mut g_def, counter, pool);
12144 gen.retain(|k, v| g_def.get(k) == Some(v));
12145 }
12146 }
12147 Stmt::Try {
12148 body,
12149 catches,
12150 finally,
12151 } => {
12152 let entry = gen.clone();
12153 let mut g_try = entry.clone();
12154 split_walk_stmt(body, vt, &mut g_try, counter, pool);
12155 let mut g_all = g_try.clone();
12156 for c in catches.iter_mut() {
12157 let mut g_c = entry.clone();
12160 split_walk_stmt(&mut c.body, vt, &mut g_c, counter, pool);
12161 g_all.retain(|k, v| g_c.get(k) == Some(v));
12162 }
12163 *gen = g_all;
12164 if let Some(f) = finally {
12165 split_walk_stmt(f, vt, gen, counter, pool);
12166 }
12167 }
12168 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => {
12169 split_walk_stmt(body, vt, gen, counter, pool);
12170 }
12171 Stmt::Return(Some(e)) | Stmt::Throw(e) => {
12172 rewrite_gen_reads(e, gen, vt);
12173 }
12174 _ => {}
12175 }
12176}
12177
12178fn fw_super(c: &str) -> Option<&'static str> {
12185 crate::fwdb::super_of(c)
12186}
12187
12188fn exc_subtype(sub: &str, sup: &str, pool: &DexPool) -> bool {
12194 if sub == sup {
12195 return true;
12196 }
12197 if pool.is_subtype(sub, sup) {
12198 return true;
12199 }
12200 let mut cur = sub;
12201 for _ in 0..24 {
12202 let Some(next) = fw_super(cur) else { break };
12203 if next == sup {
12204 return true;
12205 }
12206 cur = next;
12207 }
12208 false
12209}
12210
12211pub fn dedupe_multicatch(s: &mut Stmt, pool: &DexPool) {
12217 dedupe_multicatch_walk(s, pool);
12218}
12219
12220fn dedupe_multicatch_walk(s: &mut Stmt, pool: &DexPool) {
12221 match s {
12222 Stmt::Try {
12223 body,
12224 catches,
12225 finally,
12226 }
12227 | Stmt::TryWithResources {
12228 body,
12229 catches,
12230 finally,
12231 ..
12232 } => {
12233 dedupe_multicatch_walk(body, pool);
12234 for c in catches.iter_mut() {
12235 if c.exc.len() > 1 {
12236 let mut keep: Vec<std::sync::Arc<str>> = Vec::with_capacity(c.exc.len());
12237 for e in c.exc.iter() {
12238 let shadowed = c.exc.iter().any(|o| {
12239 o.as_ref() != e.as_ref() && exc_subtype(e, o, pool)
12240 });
12241 if !shadowed && !keep.iter().any(|k| k.as_ref() == e.as_ref()) {
12242 keep.push(e.clone());
12243 }
12244 }
12245 c.exc = keep;
12246 }
12247 dedupe_multicatch_walk(&mut c.body, pool);
12248 }
12249 if let Some(f) = finally {
12250 dedupe_multicatch_walk(f, pool);
12251 }
12252 }
12253 Stmt::Block(v) => {
12254 for x in v.iter_mut() {
12255 dedupe_multicatch_walk(x, pool);
12256 }
12257 }
12258 Stmt::If {
12259 then_stmt,
12260 else_stmt,
12261 ..
12262 } => {
12263 dedupe_multicatch_walk(then_stmt, pool);
12264 if let Some(e) = else_stmt {
12265 dedupe_multicatch_walk(e, pool);
12266 }
12267 }
12268 Stmt::While { body, .. }
12269 | Stmt::DoWhile { body, .. }
12270 | Stmt::For { body, .. }
12271 | Stmt::ForEach { body, .. }
12272 | Stmt::Labeled { body, .. }
12273 | Stmt::Synchronized { body, .. } => dedupe_multicatch_walk(body, pool),
12274 Stmt::Switch { cases, default, .. } => {
12275 for c in cases.iter_mut() {
12276 for x in c.body.iter_mut() {
12277 dedupe_multicatch_walk(x, pool);
12278 }
12279 }
12280 if let Some(d) = default {
12281 dedupe_multicatch_walk(d, pool);
12282 }
12283 }
12284 _ => {}
12285 }
12286}
12287
12288pub fn resolve_dangling_gotos(s: &mut Stmt) {
12300 let mut labels: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
12301 walk_all(s, &mut |st| {
12304 if let Stmt::Label(id) = st {
12305 labels.insert(*id);
12306 }
12307 });
12308 resolve_gotos_walk(s, &labels, 0);
12309}
12310
12311fn resolve_gotos_walk(s: &mut Stmt, labels: &jdc_core::FxHashSet<u32>, loop_depth: u32) {
12312 let depth = match s {
12313 Stmt::While { .. } | Stmt::DoWhile { .. } | Stmt::For { .. } | Stmt::ForEach { .. } => {
12314 loop_depth + 1
12315 }
12316 _ => loop_depth,
12317 };
12318 match s {
12319 Stmt::Goto(id) => {
12320 if labels.contains(id) {
12321 return; }
12323 if depth > 0 {
12324 *s = Stmt::Break(None);
12325 } else {
12326 *s = Stmt::Comment(format!(
12327 "$DDC: unresolved jump to block {} (irreducible control flow)",
12328 id
12329 ));
12330 }
12331 }
12332 Stmt::Block(v) => {
12333 let mut i = 0usize;
12334 while i < v.len() {
12335 let dangling_here =
12340 depth == 0 && matches!(&v[i], Stmt::Goto(id) if !labels.contains(id));
12341 resolve_gotos_walk(&mut v[i], labels, depth);
12342 if dangling_here {
12343 v.truncate(i + 1);
12344 break;
12345 }
12346 i += 1;
12347 }
12348 }
12349 Stmt::If { then_stmt, else_stmt, .. } => {
12350 resolve_gotos_walk(then_stmt, labels, depth);
12351 if let Some(e) = else_stmt {
12352 resolve_gotos_walk(e, labels, depth);
12353 }
12354 }
12355 Stmt::While { body, .. }
12356 | Stmt::DoWhile { body, .. }
12357 | Stmt::For { body, .. }
12358 | Stmt::ForEach { body, .. }
12359 | Stmt::Labeled { body, .. }
12360 | Stmt::Synchronized { body, .. } => resolve_gotos_walk(body, labels, depth),
12361 Stmt::Try {
12362 body,
12363 catches,
12364 finally,
12365 } => {
12366 resolve_gotos_walk(body, labels, depth);
12367 for c in catches.iter_mut() {
12368 resolve_gotos_walk(&mut c.body, labels, depth);
12369 }
12370 if let Some(f) = finally {
12371 resolve_gotos_walk(f, labels, depth);
12372 }
12373 }
12374 Stmt::Switch { cases, default, .. } => {
12375 for c in cases.iter_mut() {
12376 for x in c.body.iter_mut() {
12377 resolve_gotos_walk(x, labels, depth);
12378 }
12379 }
12380 if let Some(d) = default {
12381 resolve_gotos_walk(d, labels, depth);
12382 }
12383 }
12384 _ => {}
12385 }
12386}
12387
12388fn mark_read_vars(stmts: &[Stmt], vars: &[u32], hits: &mut [bool]) {
12400 let mut remaining = vars.len();
12401 for s in stmts {
12402 if remaining == 0 {
12403 break;
12404 }
12405 visit_stmt_exprs_ro(s, &mut |e| {
12406 if remaining == 0 {
12407 return;
12408 }
12409 visit_exprs_reads(e, &mut |x| {
12410 if remaining == 0 {
12411 return;
12412 }
12413 if let Expr::Local { var, .. } = x {
12414 for (i, v) in vars.iter().enumerate() {
12415 if *v == *var && !hits[i] {
12416 hits[i] = true;
12417 remaining -= 1;
12418 }
12419 }
12420 }
12421 });
12422 });
12423 }
12424}
12425
12426fn stmts_read_var(stmts: &[Stmt], v: u32) -> usize {
12427 let mut n = 0usize;
12428 for s in stmts {
12429 visit_stmt_exprs_ro(s, &mut |e| {
12430 visit_exprs_reads(e, &mut |x| {
12431 if let Expr::Local { var: vv, .. } = x {
12432 if *vv == v {
12433 n += 1;
12434 }
12435 }
12436 });
12437 });
12438 }
12439 n
12440}
12441
12442pub fn fix_ctor_conditional_super(body: &mut Stmt) {
12443 if !matches!(body, Stmt::Block(_)) {
12446 let inner = std::mem::replace(body, Stmt::Block(Vec::new()));
12447 *body = Stmt::Block(vec![inner]);
12448 }
12449 let Stmt::Block(stmts) = body else { return };
12450 while stmts.len() == 1 {
12454 let Stmt::Block(inner) = &stmts[0] else { break };
12455 *stmts = inner.clone();
12456 }
12457 if stmts.is_empty() {
12458 return;
12459 }
12460 if is_bare_ctor_call(stmts.first().unwrap()) {
12461 dedupe_ctor_delegations(body);
12462 return;
12463 }
12464 if try_linear_super_inline(stmts) {
12465 return;
12466 }
12467 try_merge_branched_super(stmts);
12468}
12469
12470pub fn strip_secondary_delegations(body: &mut Stmt) {
12494 let Stmt::Block(stmts) = body else { return };
12495 if !stmts.first().is_some_and(is_bare_ctor_call) {
12496 return;
12497 }
12498 fn strip_in(s: &mut Stmt) {
12499 if is_bare_ctor_call(s) {
12500 *s = Stmt::Block(Vec::new());
12501 return;
12502 }
12503 match s {
12504 Stmt::Block(v) => v.iter_mut().for_each(strip_in),
12505 Stmt::If {
12506 then_stmt,
12507 else_stmt,
12508 ..
12509 } => {
12510 strip_in(then_stmt);
12511 if let Some(e) = else_stmt {
12512 strip_in(e);
12513 }
12514 }
12515 Stmt::While { body, .. }
12516 | Stmt::DoWhile { body, .. }
12517 | Stmt::ForEach { body, .. }
12518 | Stmt::Synchronized { body, .. }
12519 | Stmt::Labeled { body, .. } => strip_in(body),
12520 Stmt::For { init, body, .. } => {
12521 init.iter_mut().for_each(strip_in);
12522 strip_in(body);
12523 }
12524 Stmt::Switch { cases, default, .. } => {
12525 for c in cases.iter_mut() {
12526 c.body.iter_mut().for_each(strip_in);
12527 }
12528 if let Some(d) = default {
12529 strip_in(d);
12530 }
12531 }
12532 Stmt::Try {
12533 body,
12534 catches,
12535 finally,
12536 }
12537 | Stmt::TryWithResources {
12538 body,
12539 catches,
12540 finally,
12541 ..
12542 } => {
12543 strip_in(body);
12544 for c in catches.iter_mut() {
12545 strip_in(&mut c.body);
12546 }
12547 if let Some(f) = finally {
12548 strip_in(f);
12549 }
12550 }
12551 _ => {}
12552 }
12553 }
12554 for st in stmts.iter_mut().skip(1) {
12555 strip_in(st);
12556 }
12557}
12558
12559pub fn dedupe_ctor_delegations(body: &mut Stmt) {
12560 let Stmt::Block(stmts) = body else { return };
12561 let Some(d0) = stmts.first().and_then(|s| match s {
12562 Stmt::ExprStmt(e) if is_delegation_expr(e) => Some(e.clone()),
12563 _ => None,
12564 }) else {
12565 return;
12566 };
12567 fn rec(s: &mut Stmt, d0: &Expr) {
12568 match s {
12569 Stmt::Block(v) => {
12570 v.retain(|x| {
12571 !matches!(x, Stmt::ExprStmt(e) if is_delegation_expr(e) && same_delegation(e, d0))
12572 });
12573 for x in v.iter_mut() {
12574 rec(x, d0);
12575 }
12576 }
12577 Stmt::If { then_stmt, else_stmt, .. } => {
12578 rec(then_stmt, d0);
12579 if let Some(e) = else_stmt {
12580 rec(e, d0);
12581 }
12582 }
12583 _ => {}
12584 }
12585 }
12586 {
12590 let d0r = &d0;
12591 let mut idx = 0usize;
12592 stmts.retain(|s| {
12593 idx += 1;
12594 idx == 1
12595 || !(matches!(s, Stmt::ExprStmt(e)
12596 if is_delegation_expr(e) && same_delegation(e, d0r)))
12597 });
12598 }
12599 for s in stmts.iter_mut().skip(1) {
12600 rec(s, &d0);
12601 }
12602}
12603
12604fn same_delegation(a: &Expr, b: &Expr) -> bool {
12608 match (a, b) {
12609 (
12610 Expr::Method { name: n1, cls: c1, is_super: s1, args: a1, .. },
12611 Expr::Method { name: n2, cls: c2, is_super: s2, args: a2, .. },
12612 ) => {
12613 &**n1 == "<init>"
12614 && &**n2 == "<init>"
12615 && c1 == c2
12616 && s1 == s2
12617 && (a1 == a2 || (a1.is_empty() && a2.is_empty()))
12618 }
12619 _ => false,
12620 }
12621}
12622
12623fn ctor_call_expr(s: &Stmt) -> Option<&Expr> {
12625 match s {
12626 Stmt::ExprStmt(e) if is_delegation_expr(e) => Some(e),
12627 _ => None,
12628 }
12629}
12630
12631fn same_ctor_kind(a: &Expr, b: &Expr) -> bool {
12633 match (a, b) {
12634 (
12635 Expr::Method { cls: c1, is_super: s1, owner: o1, .. },
12636 Expr::Method { cls: c2, is_super: s2, owner: o2, .. },
12637 ) => c1 == c2 && s1 == s2 && o1 == o2,
12638 _ => false,
12639 }
12640}
12641
12642fn flat_list(s: &Stmt) -> Vec<&Stmt> {
12647 fn rec<'a>(s: &'a Stmt, out: &mut Vec<&'a Stmt>) {
12648 match s {
12649 Stmt::Block(v) => {
12650 for x in v {
12651 rec(x, out);
12652 }
12653 }
12654 other => out.push(other),
12655 }
12656 }
12657 let mut out = Vec::new();
12658 rec(s, &mut out);
12659 out
12660}
12661
12662fn strip_lost_alloc_owners(e: &mut Expr) {
12668 deep_rewrite(e, &mut |x| {
12669 if let Expr::Method { name, is_special: true, owner: Some(o), .. } = x {
12670 if &**name == "<init>" && !matches!(**o, Expr::This) {
12671 **o = Expr::Const(jdc_core::ir::expr::ConstVal::Int(0));
12672 }
12673 }
12674 });
12675}
12676
12677fn count_locals_expr(e: &Expr) -> jdc_core::FxHashMap<u32, usize> {
12680 let mut m = jdc_core::FxHashMap::default();
12681 count_expr_ro(e, &mut m);
12682 m
12683}
12684
12685pub(crate) fn count_locals_stmts(ss: &[Stmt]) -> jdc_core::FxHashMap<u32, usize> {
12686 let mut m = jdc_core::FxHashMap::default();
12687 for s in ss {
12688 visit_stmt_exprs_ro(s, &mut |e| count_expr_ro(e, &mut m));
12689 }
12690 m
12691}
12692
12693fn cnt(m: &jdc_core::FxHashMap<u32, usize>, v: u32) -> usize {
12694 m.get(&v).copied().unwrap_or(0)
12695}
12696
12697fn inline_locals(
12701 e: &Expr,
12702 map: &std::collections::HashMap<u32, Expr>,
12703 depth: u32,
12704) -> Option<Expr> {
12705 if depth > 4 {
12706 return None;
12707 }
12708 let mut out = e.clone();
12709 let mut fail = false;
12710 deep_rewrite(&mut out, &mut |x| {
12711 if let Expr::Local { var, .. } = x {
12712 if let Some(def) = map.get(var) {
12713 match inline_locals(def, map, depth + 1) {
12714 Some(r) => *x = r,
12715 None => fail = true,
12716 }
12717 }
12718 }
12719 });
12720 if fail {
12721 None
12722 } else {
12723 Some(out)
12724 }
12725}
12726
12727fn def_of(s: &Stmt) -> Option<(u32, &Expr)> {
12729 match s {
12730 Stmt::LocalDef { var, init: Some(e), .. } => Some((*var, e)),
12731 Stmt::ExprStmt(Expr::Assign {
12732 target,
12733 value,
12734 op: jdc_core::ir::expr::AssignOp::Plain,
12735 ..
12736 }) => match &**target {
12737 Expr::Local { var, .. } => Some((*var, value)),
12738 _ => None,
12739 },
12740 _ => None,
12741 }
12742}
12743
12744fn try_linear_super_inline(stmts: &mut Vec<Stmt>) -> bool {
12746 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
12747 return false;
12748 };
12749 if pos == 0 {
12750 return false; }
12752 if !stmts[..pos]
12754 .iter()
12755 .all(|s| matches!(s, Stmt::LocalDef { .. } | Stmt::ExprStmt(_)))
12756 {
12757 return false;
12758 }
12759 let Some(call) = ctor_call_expr(&stmts[pos]) else {
12760 return false;
12761 };
12762 let Expr::Method { args, .. } = call else {
12763 return false;
12764 };
12765 let mut map: std::collections::HashMap<u32, Expr> =
12767 std::collections::HashMap::new();
12768 let mut def_idx: jdc_core::FxHashMap<u32, usize> =
12769 jdc_core::FxHashMap::default();
12770 for (i, s) in stmts[..pos].iter().enumerate() {
12771 if let Some((v, e)) = def_of(s) {
12772 map.insert(v, e.clone());
12773 def_idx.insert(v, i);
12774 }
12775 }
12776 let mut needed: Vec<u32> = Vec::new();
12778 for a in args {
12779 let mut probe = a.clone();
12780 strip_lost_alloc_owners(&mut probe);
12781 deep_rewrite(&mut probe, &mut |x| {
12782 if let Expr::Local { var, .. } = x {
12783 if map.contains_key(var) && !needed.contains(var) {
12784 needed.push(*var);
12785 }
12786 }
12787 });
12788 }
12789 if needed.is_empty() {
12790 return false; }
12792 let mut consumed: Vec<usize> = Vec::new();
12795 let use_counts = count_locals_stmts(stmts);
12796 for &v in &needed {
12797 let def = &map[&v];
12798 let uses = cnt(&use_counts, v);
12799 if jdc_core::ir::build::has_side_effects(def) && uses != 1 {
12800 return false;
12801 }
12802 if uses == 1 {
12803 consumed.push(def_idx[&v]);
12804 }
12805 }
12806 let mut new_args: Vec<Expr> = Vec::with_capacity(args.len());
12808 for a in args {
12809 match inline_locals(a, &map, 0) {
12810 Some(r) => new_args.push(r),
12811 None => return false,
12812 }
12813 }
12814 let mut call_stmt = stmts.remove(pos);
12817 if let Stmt::ExprStmt(Expr::Method { args: slot, .. }) = &mut call_stmt {
12818 *slot = new_args;
12819 }
12820 let mut rest: Vec<Stmt> = Vec::with_capacity(stmts.len() + 1);
12821 rest.push(call_stmt);
12822 for (i, s) in stmts.drain(..).enumerate() {
12823 let orig = if i >= pos { i + 1 } else { i };
12826 if !consumed.contains(&orig) {
12827 rest.push(s);
12828 }
12829 }
12830 *stmts = rest;
12831 true
12832}
12833
12834fn try_merge_branched_super(stmts: &mut Vec<Stmt>) -> bool {
12836 let if_positions: Vec<usize> = stmts
12840 .iter()
12841 .enumerate()
12842 .filter(|(_, s)| matches!(s, Stmt::If { .. }))
12843 .map(|(i, _)| i)
12844 .collect();
12845 for if_pos in if_positions {
12846 let gate = match &stmts[if_pos] {
12852 Stmt::If { then_stmt, else_stmt, .. } => {
12853 contains_delegation(then_stmt)
12854 || else_stmt.as_ref().is_some_and(|e| contains_delegation(e))
12855 }
12856 _ => false,
12857 };
12858 if gate && merge_at(stmts, if_pos) {
12859 return true;
12860 }
12861 }
12862 false
12863}
12864
12865fn contains_delegation(s: &Stmt) -> bool {
12868 match s {
12869 Stmt::ExprStmt(e) => is_delegation_expr(e),
12870 Stmt::Block(v) => v.iter().any(contains_delegation),
12871 Stmt::If { then_stmt, else_stmt, .. } => {
12872 contains_delegation(then_stmt)
12873 || else_stmt.as_ref().is_some_and(|e| contains_delegation(e))
12874 }
12875 _ => false,
12876 }
12877}
12878
12879
12880
12881fn merge_at(stmts: &mut Vec<Stmt>, if_pos: usize) -> bool {
12894 enum Br {
12899 Call(usize, Vec<(u32, Expr)>, Vec<usize>),
12901 Clean,
12902 Dirty,
12903 }
12904 fn split_branch(list: &[&Stmt]) -> Br {
12905 let call_pos = list.iter().position(|s| ctor_call_expr(s).is_some());
12906 let Some(c) = call_pos else {
12907 return if list.iter().any(|s| contains_delegation(s)) {
12910 Br::Dirty
12911 } else {
12912 Br::Clean
12913 };
12914 };
12915 if list.iter().skip(c + 1).any(|s| ctor_call_expr(s).is_some()) {
12916 return Br::Dirty; }
12918 let mut defs: Vec<(u32, Expr)> = Vec::new();
12919 let mut def_idxs: Vec<usize> = Vec::new();
12920 for (i, s) in list.iter().enumerate().take(c) {
12921 match def_of(s) {
12922 Some((v, e)) => {
12923 defs.push((v, e.clone()));
12924 def_idxs.push(i);
12925 }
12926 None => match s {
12927 Stmt::ExprStmt(_) | Stmt::LocalDef { .. } => {}
12930 _ => return Br::Dirty, },
12932 }
12933 }
12934 Br::Call(c, defs, def_idxs)
12935 }
12936 fn cond_def_of(s: &Stmt, scope: &[Stmt]) -> Option<(u32, Expr)> {
12938 let Stmt::If { cond, then_stmt, else_stmt: Some(e), .. } = s else {
12939 return None;
12940 };
12941 fn one<'x>(b: &'x Stmt, scope: &[Stmt]) -> Option<(u32, &'x Expr)> {
12942 let l = flat_list(b);
12943 if l.len() == 1 {
12944 return def_of(l[0]);
12945 }
12946 if l.len() == 2 {
12955 let (w, ex) = def_of(l[0])?;
12956 let Stmt::ExprStmt(Expr::Assign {
12957 target,
12958 op: AssignOp::Plain,
12959 value,
12960 }) = l[1]
12961 else {
12962 return None;
12963 };
12964 let Expr::Local { var: tv, .. } = &**target else {
12965 return None;
12966 };
12967 let Expr::Local { var: rv, .. } = &**value else {
12968 return None;
12969 };
12970 if *rv != w || *tv == w {
12971 return None;
12972 }
12973 let mut ldef_writes = 0usize;
12977 for st in scope {
12978 walk_all(st, &mut |x| {
12979 if let Stmt::LocalDef {
12980 var,
12981 init: Some(_),
12982 ..
12983 } = x
12984 {
12985 if *var == w {
12986 ldef_writes += 1;
12987 }
12988 }
12989 });
12990 }
12991 if stmts_read_var(scope, w) != 1
12992 || stmts_count_writes(scope, w) + ldef_writes != 1
12993 {
12994 return None;
12995 }
12996 return Some((*tv, ex));
12997 }
12998 None
12999 }
13000 let (v1, e1) = one(then_stmt, scope)?;
13001 let (v2, e2) = one(e, scope)?;
13002 if v1 != v2 {
13003 return None;
13004 }
13005 Some((
13006 v1,
13007 Expr::Cond {
13008 c: Box::new(cond.clone()),
13009 t: Box::new(e1.clone()),
13010 f: Box::new(e2.clone()),
13011 },
13012 ))
13013 }
13014
13015 let mut pmap: std::collections::HashMap<u32, Expr> =
13017 std::collections::HashMap::new();
13018 let mut pidx: jdc_core::FxHashMap<u32, usize> =
13019 jdc_core::FxHashMap::default();
13020 for (i, s) in stmts[..if_pos].iter().enumerate() {
13021 match s {
13022 Stmt::LocalDef { .. } | Stmt::ExprStmt(_) => {
13023 if let Some((v, e)) = def_of(s) {
13024 pmap.insert(v, e.clone());
13025 pidx.insert(v, i);
13026 }
13027 }
13028 Stmt::If { .. } => match cond_def_of(s, stmts) {
13029 Some((v, e)) => {
13030 pmap.insert(v, e);
13031 pidx.insert(v, i);
13032 }
13033 None => return false,
13034 },
13035 _ => return false,
13036 }
13037 }
13038 let (cond, then_v, else_v, had_else) = match &stmts[if_pos] {
13039 Stmt::If { cond, then_stmt, else_stmt, .. } => (
13040 cond.clone(),
13041 then_stmt.as_ref().clone(),
13042 else_stmt.as_ref().map_or_else(|| Stmt::Block(Vec::new()), |e| e.as_ref().clone()),
13043 else_stmt.is_some(),
13044 ),
13045 _ => return false,
13046 };
13047 fn normalize_branch(b: &mut Stmt) {
13053 let slot: &mut Stmt = match b {
13058 Stmt::Block(v) if v.len() == 1 => &mut v[0],
13059 other => other,
13060 };
13061 if !matches!(slot, Stmt::If { .. }) || !contains_delegation(slot) {
13062 return;
13063 }
13064 let taken = std::mem::replace(slot, Stmt::Block(Vec::new()));
13065 let mut tmp = vec![taken];
13066 if merge_at(&mut tmp, 0) {
13067 *slot = Stmt::Block(tmp);
13068 } else {
13069 *slot = tmp.pop().unwrap();
13070 }
13071 }
13072 let mut then_v = then_v;
13073 let mut else_v = else_v;
13074 normalize_branch(&mut then_v);
13075 normalize_branch(&mut else_v);
13076 let then_list = flat_list(&then_v);
13077 let else_list = flat_list(&else_v);
13078
13079 enum Side {
13082 Call {
13083 at: usize,
13084 defs: Vec<(u32, Expr)>,
13085 def_idxs: Vec<usize>,
13086 },
13087 Clean,
13088 }
13089 let (t_side, e_side, two_sided) = match (split_branch(&then_list), split_branch(&else_list)) {
13090 (Br::Call(tc, td, ti), Br::Call(ec, ed, ei)) => (
13091 Side::Call { at: tc, defs: td, def_idxs: ti },
13092 Side::Call { at: ec, defs: ed, def_idxs: ei },
13093 true,
13094 ),
13095 (Br::Call(tc, td, ti), Br::Clean) => (
13096 Side::Call { at: tc, defs: td, def_idxs: ti },
13097 Side::Clean,
13098 false,
13099 ),
13100 (Br::Clean, Br::Call(ec, ed, ei)) => (
13101 Side::Clean,
13102 Side::Call { at: ec, defs: ed, def_idxs: ei },
13103 false,
13104 ),
13105 _ => return false,
13106 };
13107 let (t_call, e_call) = match (&t_side, &e_side) {
13109 (Side::Call { at: ta, .. }, Side::Call { at: ea, .. }) => (
13110 ctor_call_expr(then_list[*ta]),
13111 ctor_call_expr(else_list[*ea]),
13112 ),
13113 (Side::Call { at: ta, .. }, Side::Clean) => {
13114 (ctor_call_expr(then_list[*ta]), None)
13115 }
13116 (Side::Clean, Side::Call { at: ea, .. }) => {
13117 (None, ctor_call_expr(else_list[*ea]))
13118 }
13119 _ => return false,
13120 };
13121 let (call_list, call_at) = match &t_side {
13123 Side::Call { at, .. } => (&then_list, *at),
13124 Side::Clean => match &e_side {
13125 Side::Call { at, .. } => (&else_list, *at),
13126 Side::Clean => return false,
13127 },
13128 };
13129 let (base_args, other_args) = if two_sided {
13130 let (Some(tc), Some(ec)) = (t_call, e_call) else {
13131 return false;
13132 };
13133 if !same_ctor_kind(tc, ec) {
13134 return false;
13135 }
13136 let (Expr::Method { args: a1, .. }, Expr::Method { args: a2, .. }) = (tc, ec) else {
13137 return false;
13138 };
13139 if a1.len() != a2.len() {
13140 return false;
13141 }
13142 (a1, Some(a2))
13143 } else {
13144 let c = t_call.or(e_call);
13145 let Some(c) = c else { return false };
13146 let Expr::Method { args, .. } = c else {
13147 return false;
13148 };
13149 (args, None)
13150 };
13151
13152 let mut tmap: std::collections::HashMap<u32, Expr> = pmap.clone();
13154 let mut emap: std::collections::HashMap<u32, Expr> = pmap.clone();
13155 if let Side::Call { defs, .. } = &t_side {
13156 for (v, e) in defs {
13157 tmap.insert(*v, e.clone());
13158 }
13159 }
13160 if let Side::Call { defs, .. } = &e_side {
13161 for (v, e) in defs {
13162 emap.insert(*v, e.clone());
13163 }
13164 }
13165 let Some(cond_i) = inline_locals(&cond, &pmap, 0) else {
13166 return false;
13167 };
13168 let mut then_args: Vec<Expr> = Vec::with_capacity(base_args.len());
13170 let mut else_args: Vec<Expr> = Vec::new();
13171 if two_sided {
13172 for a in base_args {
13173 match inline_locals(a, &tmap, 0) {
13174 Some(r) => then_args.push(r),
13175 None => return false,
13176 }
13177 }
13178 for a in other_args.unwrap() {
13179 match inline_locals(a, &emap, 0) {
13180 Some(r) => else_args.push(r),
13181 None => return false,
13182 }
13183 }
13184 } else {
13185 let from_then = matches!(&t_side, Side::Call { .. });
13188 for a in base_args {
13189 let r = if from_then {
13190 inline_locals(a, &tmap, 0)
13191 } else {
13192 inline_locals(a, &emap, 0)
13193 };
13194 match r {
13195 Some(r) => then_args.push(r),
13196 None => return false,
13197 }
13198 }
13199 }
13200 let primary_args = base_args;
13201 let mut merged: Vec<Expr> = Vec::with_capacity(then_args.len());
13203 let mut differ = 0usize;
13204 for (j, t) in then_args.iter().enumerate() {
13205 if two_sided {
13206 let e = &else_args[j];
13207 if t == e {
13208 merged.push(t.clone());
13209 } else {
13210 differ += 1;
13211 merged.push(Expr::Cond {
13212 c: Box::new(cond_i.clone()),
13213 t: Box::new(t.clone()),
13214 f: Box::new(e.clone()),
13215 });
13216 }
13217 } else {
13218 merged.push(t.clone());
13219 }
13220 }
13221
13222 let vars_in = |e: &Expr| -> Vec<u32> {
13224 let mut vs = Vec::new();
13225 let mut probe = e.clone();
13226 strip_lost_alloc_owners(&mut probe);
13227 deep_rewrite(&mut probe, &mut |x| {
13228 if let Expr::Local { var, .. } = x {
13229 if !vs.contains(var) {
13230 vs.push(*var);
13231 }
13232 }
13233 });
13234 vs
13235 };
13236 let branch_remainder = |list: &[&Stmt], side: &Side| -> Vec<Stmt> {
13244 match side {
13245 Side::Clean => list.iter().map(|s| (*s).clone()).collect(),
13246 Side::Call { at, def_idxs, .. } => {
13247 list.iter()
13248 .enumerate()
13249 .filter(|(i, _)| *i != *at && !def_idxs.contains(i))
13250 .map(|(_, s)| (*s).clone())
13251 .collect()
13252 }
13253 }
13254 };
13255 let tail_then_v = branch_remainder(&then_list, &t_side);
13256 let tail_else_v = branch_remainder(&else_list, &e_side);
13257 let post = &stmts[if_pos + 1..];
13258 let mut inlined: Vec<u32> = vars_in(&cond);
13259 for a in primary_args.iter().chain(else_args.iter()) {
13260 for v in vars_in(a) {
13261 if !inlined.contains(&v) {
13262 inlined.push(v);
13263 }
13264 }
13265 }
13266 inlined.retain(|v| pmap.contains_key(v) || tmap.contains_key(v) || emap.contains_key(v));
13267 for &v in &inlined {
13269 let in_branch = match (&t_side, &e_side) {
13270 (Side::Call { defs: td, .. }, Side::Call { defs: ed, .. }) => {
13271 td.iter().any(|(d, _)| *d == v) || ed.iter().any(|(d, _)| *d == v)
13272 }
13273 (Side::Call { defs, .. }, Side::Clean)
13274 | (Side::Clean, Side::Call { defs, .. }) => {
13275 defs.iter().any(|(d, _)| *d == v)
13276 }
13277 _ => false,
13278 };
13279 if pmap.contains_key(&v) && in_branch {
13280 return false;
13281 }
13282 }
13283 type Counts = jdc_core::FxHashMap<u32, usize>;
13284 type GuardCounts = (
13285 Counts,
13286 Vec<Counts>,
13287 Vec<Counts>,
13288 Counts,
13289 Counts,
13290 Counts,
13291 Vec<(u32, Counts)>,
13292 );
13293 let (cond_counts, a1_counts, a2_counts, tail1_counts, tail2_counts, post_counts, def_counts): GuardCounts =
13294 if inlined.is_empty() {
13295 Default::default()
13296 } else {
13297 let all_defs: Vec<(u32, &Expr)> = pmap
13298 .iter()
13299 .map(|(k, v)| (*k, v))
13300 .chain(match &t_side {
13301 Side::Call { defs, .. } => defs.iter().map(|(k, v)| (*k, v)).collect(),
13302 Side::Clean => Vec::new(),
13303 })
13304 .chain(match &e_side {
13305 Side::Call { defs, .. } => defs.iter().map(|(k, v)| (*k, v)).collect(),
13306 Side::Clean => Vec::new(),
13307 })
13308 .collect();
13309 (
13310 count_locals_expr(&cond),
13311 primary_args.iter().map(count_locals_expr).collect(),
13312 else_args.iter().map(count_locals_expr).collect(),
13313 count_locals_stmts(&tail_then_v),
13314 count_locals_stmts(&tail_else_v),
13315 count_locals_stmts(post),
13316 all_defs.into_iter().map(|(v, e)| (v, count_locals_expr(e))).collect(),
13317 )
13318 };
13319 for &v in &inlined {
13320 let in_t = matches!(&t_side, Side::Call { defs, .. } if defs.iter().any(|(d, _)| *d == v));
13321 let in_e = matches!(&e_side, Side::Call { defs, .. } if defs.iter().any(|(d, _)| *d == v));
13322 let effectful = [
13323 in_t.then(|| tmap.get(&v)).flatten(),
13324 in_e.then(|| emap.get(&v)).flatten(),
13325 pmap.get(&v),
13326 ]
13327 .into_iter()
13328 .flatten()
13329 .any(jdc_core::ir::build::has_side_effects);
13330 if !effectful {
13331 continue;
13332 }
13333 let u_cond = cnt(&cond_counts, v);
13334 let u_post = cnt(&post_counts, v);
13335 let u_t1 = cnt(&tail1_counts, v);
13336 let u_t2 = cnt(&tail2_counts, v);
13337 let mut u_odef = 0usize;
13338 for (dv, dc) in &def_counts {
13339 if *dv != v && inlined.contains(dv) {
13340 u_odef += cnt(dc, v);
13341 }
13342 }
13343 let mut emit = u_cond * (1 + differ);
13344 let mut uses_args = 0usize;
13345 for j in 0..primary_args.len() {
13346 let c1 = cnt(&a1_counts[j], v);
13347 let c2 = if two_sided && j < a2_counts.len() {
13348 cnt(&a2_counts[j], v)
13349 } else {
13350 0
13351 };
13352 uses_args += c1 + c2;
13353 emit += if two_sided && then_args[j] != else_args[j] {
13354 c1 + c2
13355 } else {
13356 c1.max(c2)
13357 };
13358 }
13359 let ok = if in_t || in_e {
13360 u_cond == 0
13361 && u_post == 0
13362 && u_t1 == 0
13363 && u_t2 == 0
13364 && u_odef == 0
13365 && a1_counts.iter().map(|c| cnt(c, v)).sum::<usize>() <= 1
13366 && a2_counts.iter().map(|c| cnt(c, v)).sum::<usize>() <= 1
13367 && emit <= 2
13368 && (in_t || a1_counts.iter().all(|c| cnt(c, v) == 0))
13369 && (in_e || a2_counts.iter().all(|c| cnt(c, v) == 0))
13370 } else {
13371 u_post == 0
13372 && u_t1 == 0
13373 && u_t2 == 0
13374 && u_odef == 0
13375 && ((u_cond == 1 && uses_args == 0 && emit <= 2)
13376 || (u_cond == 0 && emit <= 1))
13377 };
13378 if !ok {
13379 return false;
13380 }
13381 }
13382
13383 let mut call_stmt = call_list[call_at].clone();
13385 if let Stmt::ExprStmt(Expr::Method { args: slot, .. }) = &mut call_stmt {
13386 *slot = merged;
13387 }
13388 let residual = |v: u32| -> usize {
13389 cnt(&tail1_counts, v) + cnt(&tail2_counts, v) + cnt(&post_counts, v)
13390 };
13391 let mut kept_prelude: Vec<Stmt> = Vec::new();
13392 for (i, s) in stmts[..if_pos].iter().enumerate() {
13393 let consumed = match s {
13394 Stmt::LocalDef { var, .. } => Some(*var),
13395 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
13396 Expr::Local { var, .. } => Some(*var),
13397 _ => None,
13398 },
13399 Stmt::If { .. } => cond_def_of(s, stmts).map(|(v, _)| v),
13400 _ => None,
13401 };
13402 if let Some(v) = consumed {
13403 if inlined.contains(&v) && pidx.get(&v) == Some(&i) && residual(v) == 0 {
13404 continue;
13405 }
13406 }
13407 kept_prelude.push(s.clone());
13408 }
13409 let build_keep = |list: &[&Stmt], side: &Side| -> Vec<Stmt> {
13410 match side {
13411 Side::Clean => list.iter().map(|s| (*s).clone()).collect(),
13412 Side::Call { at, .. } => {
13413 let mut out = Vec::new();
13414 for (i, s) in list.iter().enumerate() {
13415 if i == *at {
13416 continue;
13417 }
13418 if i < *at {
13419 if let Some((v, _)) = def_of(s) {
13420 if inlined.contains(&v) && residual(v) == 0 {
13421 continue;
13422 }
13423 }
13424 }
13425 out.push((*s).clone());
13426 }
13427 out
13428 }
13429 }
13430 };
13431 let then_keep = build_keep(&then_list, &t_side);
13432 let else_keep = build_keep(&else_list, &e_side);
13433 let else_part = if else_keep.is_empty() && !had_else {
13434 None
13435 } else {
13436 Some(Box::new(Stmt::Block(else_keep)))
13437 };
13438 let new_if = Stmt::If {
13439 cond: cond_i,
13440 then_stmt: Box::new(Stmt::Block(then_keep)),
13441 else_stmt: else_part,
13442 };
13443 let if_empty = match &new_if {
13444 Stmt::If { then_stmt, else_stmt, .. } => {
13445 let be = |s: &Stmt| matches!(s, Stmt::Block(v) if v.is_empty());
13446 be(then_stmt) && else_stmt.as_ref().is_none_or(|e| be(e))
13447 }
13448 _ => false,
13449 };
13450 let mut out: Vec<Stmt> = Vec::with_capacity(stmts.len());
13451 out.push(call_stmt);
13452 out.extend(kept_prelude);
13453 if !if_empty {
13454 out.push(new_if);
13455 }
13456 out.extend(stmts[if_pos + 1..].to_vec());
13457 *stmts = out;
13458 true
13459}
13460
13461
13462pub fn strip_enum_ctor_super(body: &mut Stmt) {
13463 fn is_enum_super(s: &Stmt) -> bool {
13464 matches!(
13465 s,
13466 Stmt::ExprStmt(Expr::Method { name, is_super: true, .. }) if &**name == "<init>"
13467 )
13468 }
13469 fn strip_deep(stmts: &mut Vec<Stmt>) {
13479 stmts.retain(|s| !is_enum_super(s));
13480 for st in stmts.iter_mut() {
13481 strip_one(st);
13482 }
13483 }
13484 fn strip_one(st: &mut Stmt) {
13485 match st {
13486 Stmt::Block(v) => strip_deep(v),
13487 Stmt::If { then_stmt, else_stmt, .. } => {
13488 strip_one(then_stmt);
13489 if let Some(e) = else_stmt {
13490 strip_one(e);
13491 }
13492 }
13493 Stmt::While { body, .. }
13494 | Stmt::DoWhile { body, .. }
13495 | Stmt::ForEach { body, .. }
13496 | Stmt::Synchronized { body, .. }
13497 | Stmt::Labeled { body, .. } => strip_one(body),
13498 Stmt::For { init, body, .. } => {
13499 strip_deep(init);
13500 strip_one(body);
13501 }
13502 Stmt::Switch { cases, default, .. } => {
13503 for c in cases.iter_mut() {
13504 strip_deep(&mut c.body);
13505 }
13506 if let Some(d) = default {
13507 strip_one(d);
13508 }
13509 }
13510 Stmt::Try { body, catches, finally }
13511 | Stmt::TryWithResources { body, catches, finally, .. } => {
13512 strip_one(body);
13513 for c in catches.iter_mut() {
13514 strip_one(&mut c.body);
13515 }
13516 if let Some(f) = finally {
13517 strip_one(f);
13518 }
13519 }
13520 _ => {}
13521 }
13522 }
13523 let Stmt::Block(stmts) = body else {
13524 return;
13525 };
13526 strip_deep(stmts);
13527}
13528
13529pub fn ensure_declared(body: &mut Stmt, vt: &VarTable) {
13530 let n = vt.vars.len().max(1);
13534
13535 let mut seen: HashSet<u32> = HashSet::default();
13538 demote_dup_decls(body, &mut seen, vt);
13539
13540 let mut def_block: Vec<u32> = vec![u32::MAX; n];
13548 let mut sizes: Vec<u32> = vec![0];
13549 let mut next_id: u32 = 1;
13550 scope_pass_a(body, 0, &mut next_id, &mut def_block, &mut sizes);
13551 sizes[0] = next_id;
13552 let mut within: Vec<u32> = vec![0; n];
13553 let mut total: Vec<u32> = vec![0; n];
13554 let mut next_b: u32 = 1;
13555 scope_pass_b(body, 0, &mut next_b, &def_block, &sizes, &mut within, &mut total);
13556 let mut hoist: HashSet<u32> = HashSet::default();
13557 for v in 0..n {
13558 if def_block[v] != u32::MAX && def_block[v] != 0 && within[v] < total[v] {
13559 hoist.insert(v as u32);
13560 }
13561 }
13562 if !hoist.is_empty() {
13563 let mut seed: HashSet<u32> = hoist.iter().copied().collect();
13564 demote_dup_decls(body, &mut seed, vt);
13565 }
13566
13567 let mut declared: Vec<bool> = vec![false; n];
13568 let mut is_param: Vec<bool> = vec![false; n];
13569 for v in &vt.vars {
13570 if v.is_param && (v.id as usize) < n {
13571 is_param[v.id as usize] = true;
13572 }
13573 }
13574 let mut is_catch: Vec<bool> = vec![false; n];
13580 walk_all(body, &mut |st| {
13581 if let Stmt::Try { catches, .. } = st {
13582 for c in catches {
13583 if (c.var as usize) < n {
13584 is_catch[c.var as usize] = true;
13585 }
13586 }
13587 }
13588 });
13589 walk_all(body, &mut |st| {
13590 if let Stmt::LocalDef { var, .. } = st {
13591 if (*var as usize) < n {
13592 declared[*var as usize] = true;
13593 }
13594 }
13595 });
13596 let mut used: HashSet<u32> = HashSet::default();
13597 stmt_collect_vars(body, &mut used, true);
13601
13602 let mut needs_decl: Vec<u32> = used
13605 .into_iter()
13606 .filter(|v| {
13607 let i = *v as usize;
13608 i >= n
13609 || ((!declared[i] && !is_param[i] && !is_catch[i]) || hoist.contains(v))
13610 })
13611 .collect();
13612 needs_decl.sort_unstable();
13613 needs_decl.dedup();
13614
13615 if !needs_decl.is_empty() {
13616 let mut decls: Vec<Stmt> = needs_decl
13617 .into_iter()
13618 .map(|v| Stmt::LocalDef {
13619 var: v,
13620 init: None,
13621 is_final: false,
13622 force_type: true,
13623 })
13624 .collect();
13625 decls.reverse();
13626 match body {
13627 Stmt::Block(v) => {
13628 for d in decls {
13629 v.insert(0, d);
13630 }
13631 }
13632 other => {
13633 let inner = std::mem::replace(other, Stmt::Block(vec![]));
13634 *other = Stmt::Block({
13635 let mut vs = decls;
13636 vs.push(inner);
13637 vs
13638 });
13639 }
13640 }
13641 }
13642}
13643
13644fn demote_dup_decls(s: &mut Stmt, seen: &mut HashSet<u32>, vt: &VarTable) {
13645 match s {
13646 Stmt::Block(v) => {
13649 for x in v.iter_mut() {
13650 demote_dup_decls(x, seen, vt);
13651 }
13652 }
13653 Stmt::LocalDef { var, init, .. } => {
13654 if seen.contains(var) {
13655 let ty = vt.var(*var).ty.clone();
13656 if let Some(e) = init.take() {
13657 *s = Stmt::ExprStmt(Expr::Assign {
13658 target: Box::new(Expr::Local { var: *var, ty }),
13659 op: AssignOp::Plain,
13660 value: Box::new(e),
13661 });
13662 } else {
13663 *s = Stmt::Block(vec![]);
13664 }
13665 } else {
13666 seen.insert(*var);
13667 }
13668 }
13669 other => {
13670 walk_mut(other, &mut |x| demote_dup_decls(x, seen, vt));
13671 }
13672 }
13673}
13674
13675#[allow(dead_code)]
13676fn unused(_: &Vec<CaseGroup>, _: &Catch) {}
13677
13678pub fn apply_local_names(vt: &mut VarTable, body: &Stmt) {
13692 use std::collections::{HashMap, HashSet};
13693
13694 let mut by_call: HashMap<u32, Vec<String>> = HashMap::new();
13697 let mut by_intrinsics: HashMap<u32, String> = HashMap::new();
13698
13699 visit_all_exprs(body, &mut |e| {
13700 if let Expr::Method { cls, name, args, .. } = e {
13701 if (cls.as_ref() == "kotlin/jvm/internal/Intrinsics"
13702 || cls.as_ref() == "kotlin/jvm/internal/IntrinsicsKt")
13703 && matches!(name.as_ref(), "checkNotNullParameter" | "checkParameterIsNotNull")
13704 && args.len() == 2
13705 {
13706 if let (Expr::Local { var, .. }, Expr::Const(ConstVal::Str(s))) = (&args[0], &args[1])
13707 {
13708 by_intrinsics
13709 .entry(*var)
13710 .or_insert_with(|| sanitize_name(s).unwrap_or_default());
13711 }
13712 }
13713 }
13714 });
13715
13716 walk_all(body, &mut |st| {
13717 let (var, value) = match st {
13718 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e),
13719 Stmt::ExprStmt(Expr::Assign { target, value, .. })
13720 if matches!(&**target, Expr::Local { .. }) =>
13721 {
13722 let Expr::Local { var, .. } = &**target else { return };
13723 (*var, value.as_ref())
13724 }
13725 _ => return,
13726 };
13727 if let Some(n) = defining_call_name(value) {
13728 by_call.entry(var).or_default().push(n);
13729 }
13730 });
13731
13732 let sanit = |n: &str| crate::classdec::java_ident(n).into_owned();
13743 let mut taken: HashSet<String> = HashSet::default();
13744 for v in vt.vars.iter() {
13749 if v.synthetic_name {
13754 taken.insert(sanit(&v.name));
13755 }
13756 }
13757 for v in vt.vars.iter_mut() {
13758 if v.synthetic_name {
13759 continue;
13760 }
13761 if !taken.insert(sanit(&v.name)) {
13762 let base = sanit(&v.name);
13763 for i in 2.. {
13764 let cand = format!("{base}{i}");
13765 if taken.insert(cand.clone()) {
13766 v.name = cand;
13767 break;
13768 }
13769 }
13770 }
13771 }
13772 let claim = |taken: &mut HashSet<String>, want: &str| -> String {
13773 let w = crate::classdec::java_ident(want).into_owned();
13774 if taken.insert(w.clone()) {
13775 return w;
13776 }
13777 for i in 2.. {
13778 let cand = format!("{w}{i}");
13779 if taken.insert(cand.clone()) {
13780 return cand;
13781 }
13782 }
13783 w
13784 };
13785
13786 for info in vt.vars.iter_mut() {
13787 if !info.synthetic_name {
13788 continue;
13789 }
13790 if let Some(n) = by_intrinsics.get(&info.id) {
13793 if !n.is_empty() {
13794 info.name = claim(&mut taken, n);
13795 continue;
13796 }
13797 }
13798 if let Some(names) = by_call.get(&info.id) {
13800 let unique: HashSet<&String> = names.iter().collect();
13801 if unique.len() == 1 {
13802 let n = unique.into_iter().next().unwrap();
13803 if !is_java_keyword(n) {
13804 info.name = claim(&mut taken, n);
13805 continue;
13806 }
13807 }
13808 }
13809 let want = type_alias(&info.ty).map(str::to_string).or_else(|| simple_type_name(&info.ty));
13812 if let Some(n) = want {
13813 info.name = claim(&mut taken, &n);
13814 }
13815 }
13816
13817 let mut seen: HashSet<String> = HashSet::default();
13822 for v in vt.vars.iter_mut() {
13823 if !seen.insert(sanit(&v.name)) {
13824 let base = sanit(&v.name);
13825 for i in 2.. {
13826 let cand = format!("{base}{i}");
13827 if seen.insert(cand.clone()) {
13828 v.name = cand;
13829 break;
13830 }
13831 }
13832 }
13833 }
13834}
13835
13836fn simple_type_name(ty: &TypeRef) -> Option<String> {
13838 let TypeRef::J(JavaType::Object(n)) = ty else {
13839 return None;
13840 };
13841 let simple = n.rsplit('/').next().unwrap_or(n);
13842 let simple = simple.rsplit('$').next().unwrap_or(simple);
13843 let mut chars = simple.chars();
13846 let mut out = String::with_capacity(simple.len());
13847 let mut lowered = false;
13848 for c in chars.by_ref() {
13849 if c.is_ascii_alphabetic() && !lowered {
13850 out.push(c.to_ascii_lowercase());
13851 lowered = true;
13852 } else {
13853 out.push(c);
13854 }
13855 if lowered {
13856 break;
13857 }
13858 }
13859 out.extend(chars);
13860 sanitize_name(&out)
13861}
13862
13863fn defining_call_name(e: &Expr) -> Option<String> {
13867 match e {
13868 Expr::Method { name, .. } => {
13869 let base = name
13870 .strip_prefix("get")
13871 .or_else(|| name.strip_prefix("is"))
13872 .filter(|rest| rest.chars().next().is_some_and(|c| c.is_ascii_uppercase()))
13873 .unwrap_or(name);
13874 let first = base.chars().next()?;
13875 let mut out = String::with_capacity(base.len());
13876 out.push(first.to_ascii_lowercase());
13877 out.extend(base.chars().skip(1));
13878 sanitize_name(&out)
13879 }
13880 Expr::New { cls, .. } => {
13881 let simple = cls.rsplit('/').next().unwrap_or(cls);
13882 let simple = simple.rsplit('$').next().unwrap_or(simple);
13883 let first = simple.chars().next()?;
13884 let mut out = String::with_capacity(simple.len());
13885 out.push(first.to_ascii_lowercase());
13886 out.extend(simple.chars().skip(1));
13887 sanitize_name(&out)
13888 }
13889 _ => None,
13890 }
13891}
13892
13893fn sanitize_name(s: &str) -> Option<String> {
13895 let mut out = String::with_capacity(s.len());
13896 for c in s.chars() {
13897 if c.is_ascii_alphanumeric() || c == '_' || c == '$' {
13898 out.push(c);
13899 } else {
13900 out.push('_');
13901 }
13902 }
13903 if out.len() < 2
13904 || out.chars().next().is_some_and(|c| c.is_ascii_digit())
13905 || is_java_keyword(&out)
13906 {
13907 return None;
13908 }
13909 Some(out)
13910}
13911
13912fn type_alias(ty: &TypeRef) -> Option<&'static str> {
13915 let TypeRef::J(JavaType::Object(n)) = ty else {
13916 return None;
13917 };
13918 Some(match n.as_ref() {
13919 "java/lang/String" | "kotlin/String" => "str",
13920 "java/lang/Class" => "cls",
13921 "java/lang/Throwable" => "th",
13922 "java/lang/Object" | "kotlin/Any" => "obj",
13923 "java/util/Iterator" | "kotlin/collections/Iterator" => "it",
13924 "java/lang/Boolean" => "bool",
13925 "java/lang/Integer" => "num",
13926 "java/lang/Character" => "ch",
13927 "java/lang/Byte" => "b",
13928 "java/lang/Short" => "sh",
13929 "java/lang/Float" => "f",
13930 "java/lang/Double" => "d",
13931 "java/lang/Long" => "i",
13932 "java/lang/StringBuilder" => "sb",
13933 "java/util/ArrayList" => "list",
13934 "java/util/HashMap" => "map",
13935 "android/content/Context" => "context",
13936 "android/content/Intent" => "intent",
13937 "android/os/Bundle" => "bundle",
13938 "android/view/View" => "view",
13939 "android/graphics/Bitmap" => "bitmap",
13940 "android/view/ViewGroup" => "viewGroup",
13941 _ => return None,
13942 })
13943}
13944
13945pub(crate) fn visit_all_exprs<F: FnMut(&Expr)>(s: &Stmt, f: &mut F) {
13947 walk_all(s, &mut |st| {
13948 let exprs: Vec<&Expr> = match st {
13949 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
13950 let mut v: Vec<&Expr> = vec![value];
13951 if !matches!(&**target, Expr::Local { .. }) {
13952 v.push(target);
13953 }
13954 v
13955 }
13956 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
13957 vec![e]
13958 }
13959 Stmt::Return(Some(e)) => vec![e],
13960 Stmt::LocalDef { init: Some(e), .. } => vec![e],
13961 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
13962 vec![cond]
13963 }
13964 _ => Vec::new(),
13965 };
13966 for e in exprs {
13967 visit_exprs(e, f);
13968 }
13969 });
13970}
13971
13972pub fn platform_constants(body: &mut Stmt) {
13977 if !crate::platform::installed() {
13978 return;
13979 }
13980 rewrite_exprs(body, &mut |e| {
13981 if let Expr::Method { cls, name, desc, args, .. } = e {
13982 for a in args.iter_mut() {
13983 let (value, is_long) = match a {
13984 Expr::Const(ConstVal::Int(v)) => (*v as i64, false),
13985 Expr::Const(ConstVal::Long(v)) => (*v, true),
13986 _ => continue,
13987 };
13988 if let Some(named) =
13989 crate::platform::named_constant(cls, name, &desc_to_string(desc), 0, value)
13990 {
13991 let _ = is_long;
13992 *a = Expr::Raw(named);
13993 }
13994 }
13995 }
13996 });
13997}
13998
13999fn desc_to_string(d: &jdc_core::types::MethodDescriptor) -> String {
14001 let mut out = String::from("(");
14002 for a in &d.args {
14003 type_to_desc(a, &mut out);
14004 }
14005 out.push(')');
14006 type_to_desc(&d.ret, &mut out);
14007 out
14008}
14009
14010fn type_to_desc(t: &jdc_core::types::JavaType, out: &mut String) {
14011 match t {
14012 JavaType::Void => out.push('V'),
14013 JavaType::Boolean => out.push('Z'),
14014 JavaType::Byte => out.push('B'),
14015 JavaType::Char => out.push('C'),
14016 JavaType::Short => out.push('S'),
14017 JavaType::Int => out.push('I'),
14018 JavaType::Float => out.push('F'),
14019 JavaType::Long => out.push('J'),
14020 JavaType::Double => out.push('D'),
14021 JavaType::Object(n) => {
14022 out.push('L');
14023 out.push_str(n);
14024 out.push(';');
14025 }
14026 JavaType::Array(inner) => {
14027 out.push('[');
14028 type_to_desc(inner, out);
14029 }
14030 }
14031}
14032
14033pub fn remove_kotlin_checks(s: &mut Stmt) {
14039 walk_mut_deep(s, &mut |st| {
14040 let Stmt::Block(v) = st else { return };
14041 v.retain(|x| !is_kotlin_check(x));
14042 });
14043}
14044
14045fn is_kotlin_check(s: &Stmt) -> bool {
14046 let Stmt::ExprStmt(e) = s else { return false };
14047 let Expr::Method { cls, name, .. } = e else { return false };
14048 (cls.as_ref() == "kotlin/jvm/internal/Intrinsics" || cls.as_ref() == "kotlin/jvm/internal/IntrinsicsKt__Jdk7Kt")
14049 && matches!(
14050 name.as_ref(),
14051 "checkNotNullParameter"
14052 | "checkParameterIsNotNull"
14053 | "checkExpressionValueIsNotNull"
14054 | "checkNotNullExpressionValue"
14055 | "checkReturnedValueIsNotNull"
14056 | "checkFieldIsNotNull"
14057 | "checkNotNull"
14058 )
14059}
14060
14061fn inline_ref_ok(pool: &DexPool, host: &str, target: &str, member: Option<(&str, bool)>) -> bool {
14081 let hpkg = match host.rfind('/') {
14082 Some(i) => &host[..i],
14083 None => "",
14084 };
14085 let mut cur = target;
14086 loop {
14087 let tpkg = match cur.rfind('/') {
14088 Some(i) => &cur[..i],
14089 None => "",
14090 };
14091 if tpkg != hpkg {
14092 if let Some(pc) = pool.get(cur) {
14093 if pc.access & crate::access::ACC_PUBLIC == 0 {
14094 return false;
14095 }
14096 if cur == target {
14097 if let Some((mname, is_method)) = member {
14098 let ok = if is_method {
14099 pc.all_methods().any(|m| {
14100 &*m.name == mname
14101 && m.access & crate::access::ACC_PUBLIC != 0
14102 })
14103 } else {
14104 pc.static_fields
14105 .iter()
14106 .chain(pc.instance_fields.iter())
14107 .any(|f| {
14108 f.name.as_ref() == mname
14109 && f.access & crate::access::ACC_PUBLIC != 0
14110 })
14111 };
14112 if !ok
14117 && (if is_method {
14118 pc.all_methods().any(|m| &*m.name == mname)
14119 } else {
14120 pc.static_fields
14121 .iter()
14122 .chain(pc.instance_fields.iter())
14123 .any(|f| f.name.as_ref() == mname)
14124 })
14125 {
14126 return false;
14127 }
14128 }
14129 }
14130 }
14131 }
14132 match cur.rfind('$') {
14133 Some(i) if i > 0 => cur = &cur[..i],
14134 _ => break,
14135 }
14136 }
14137 true
14138}
14139
14140pub fn inline_accessors(s: &mut Stmt, pool: &DexPool, host: &str) {
14141 rewrite_exprs(s, &mut |e| {
14142 let Expr::Method { cls, name, desc, args, is_static, .. } = e else { return };
14143 if !*is_static || args.is_empty() {
14144 return;
14145 }
14146 let Some(target) = pool.get(cls) else { return };
14147 let desc_s = desc_to_string(desc);
14148 let Some(m) = target.find_method(name, &desc_s) else { return };
14149 if !(m.is_static() && m.access & access::ACC_SYNTHETIC != 0) {
14150 return;
14151 }
14152 let Some(dex) = pool.dex(m.dex_idx) else { return };
14153 let code = match pool.accessor_code(m.dex_idx, m.code_off) {
14158 Some(bytes) => ddc_dex::CodeItem::parse(&bytes, 0),
14159 None => dex.code_at(m.code_off),
14160 };
14161 let Some(code) = code else { return };
14162 let insns: Vec<&Insn> =
14163 code.insns.iter().filter(|i| !matches!(i.kind, InsnKind::Nop)).collect();
14164 let core: Vec<&Insn> = insns
14166 .iter()
14167 .copied()
14168 .filter(|i| !is_const_only_trace(&i.kind, &insns))
14169 .collect();
14170 match accessor_shape(&dex, &core, &code) {
14171 Some(Shape::Identity(arg_i)) => {
14172 if let Some(a) = args.get(arg_i) {
14173 *e = a.clone();
14174 }
14175 }
14176 Some(Shape::FieldRead { arg, cls: field_cls, field, field_ty }) => {
14177 if !inline_ref_ok(pool, host, &field_cls, Some((&field, false))) {
14178 return;
14179 }
14180 if let Some(owner) = args.get(arg).cloned().map(Box::new) {
14181 let ty = TypeRef::J(desc_type(&field_ty));
14182 let name = jdc_core::rename::field_display(
14188 &field_cls,
14189 &field,
14190 &field_ty,
14191 )
14192 .map(std::sync::Arc::from)
14193 .unwrap_or_else(|| field.into());
14194 *e = Expr::Field {
14195 owner: Some(owner),
14196 cls: field_cls.into(),
14197 name,
14198 ty,
14199 is_static: false,
14200 };
14201 }
14202 }
14203 Some(Shape::Forward { cls: tcls, name: tname, desc: tdesc, instance }) => {
14204 if !inline_ref_ok(pool, host, &tcls, Some((&tname, true))) {
14205 return;
14206 }
14207 let disp = jdc_core::rename::field_display(
14211 &tcls,
14212 &tname,
14213 &tdesc.to_string(),
14214 )
14215 .map(std::sync::Arc::from)
14216 .unwrap_or_else(|| tname.into());
14217 *cls = tcls.into();
14218 *name = disp;
14219 *desc = std::sync::Arc::new(tdesc);
14220 if instance && !args.is_empty() {
14221 let recv = args.remove(0);
14223 if let Expr::Method { owner, .. } = e {
14224 *owner = Some(Box::new(recv));
14225 }
14226 }
14227 }
14228 None => {}
14229 }
14230 });
14231}
14232
14233enum Shape {
14235 Identity(usize),
14237 FieldRead { arg: usize, cls: String, field: String, field_ty: String },
14239 Forward { cls: String, name: String, desc: MethodDescriptor, instance: bool },
14242}
14243
14244fn field_of(dex: &ddc_dex::DexFile, field_idx: u32) -> Option<(String, String, String)> {
14246 let f = dex.field(field_idx);
14247 Some((
14248 dex.class_name(f.class_idx),
14251 dex.string(f.name_idx).to_string(),
14252 dex.type_name(f.type_idx).to_string(),
14254 ))
14255}
14256
14257fn param_index(code: &ddc_dex::CodeItem, reg: u16) -> Option<usize> {
14260 let rs = code.registers_size as usize;
14261 let ins = code.ins_size as usize;
14262 let r = reg as usize;
14263 let first = rs.checked_sub(ins)?;
14264 (r >= first && r < rs).then(|| r - first)
14265}
14266
14267fn accessor_shape(
14268 dex: &ddc_dex::DexFile,
14269 core: &[&Insn],
14270 code: &ddc_dex::CodeItem,
14271) -> Option<Shape> {
14272 let reads = |rest: &[&Insn], r: u16| {
14282 rest.iter()
14283 .any(|i| crate::lift::src_regs(&i.kind).contains(&r))
14284 };
14285 let core: &[&Insn] = match core.split_first() {
14286 Some((first, rest))
14287 if matches!(first.kind, InsnKind::Const { dst, .. } if !reads(rest, dst)) =>
14288 {
14289 rest
14290 }
14291 _ => core,
14292 };
14293 let core: &[&Insn] = match core.split_last() {
14294 Some((last, rest))
14295 if matches!(last.kind, InsnKind::Const { dst, .. } if !reads(rest, dst)) =>
14296 {
14297 rest
14298 }
14299 _ => core,
14300 };
14301 match core {
14302 [Insn { kind: InsnKind::Return { src }, .. }] => {
14304 let idx = param_index(code, *src)?;
14305 Some(Shape::Identity(idx))
14306 }
14307 [
14309 Insn { kind: InsnKind::IGet { dst, obj, field_idx }, .. },
14310 Insn { kind: InsnKind::Return { src }, .. },
14311 ] if dst == src => {
14312 let arg = param_index(code, *obj)?;
14313 let (cls, field, field_ty) = field_of(dex, *field_idx)?;
14314 Some(Shape::FieldRead { arg, cls, field, field_ty })
14315 }
14316 [Insn { kind: InsnKind::Invoke { method_idx, regs, kind, .. }, .. }, rest @ ..] if rest.len() <= 2 => {
14318 if !regs.iter().all(|r| param_index(code, *r).is_some()) || regs.is_empty() {
14320 return None;
14321 }
14322 if rest.len() == 2 {
14325 let ok = matches!(rest[0].kind, InsnKind::MoveResult { .. })
14326 && matches!(rest[1].kind, InsnKind::Return { .. } | InsnKind::ReturnVoid);
14327 if !ok {
14328 return None;
14329 }
14330 }
14331 let mid = dex.method(*method_idx);
14332 let cls = dex.class_name(mid.class_idx);
14335 let name = dex.string(mid.name_idx).to_string();
14336 let mut s = String::from("(");
14337 for t in dex.proto_params(mid.proto_idx) {
14338 s.push_str(dex.type_name(*t));
14339 }
14340 s.push(')');
14341 s.push_str(dex.type_name(dex.proto(mid.proto_idx).return_type_idx));
14342 let desc = parse_method_descriptor(&s)?;
14343 Some(Shape::Forward { cls, name, desc, instance: !matches!(kind, InvokeKind::Static) })
14344 }
14345 _ => None,
14346 }
14347}
14348
14349
14350fn is_const_only_trace(kind: &InsnKind, insns: &[&Insn]) -> bool {
14356 let InsnKind::Invoke { kind, regs, .. } = kind else { return false };
14357 if !matches!(kind, InvokeKind::Static) || regs.is_empty() {
14358 return false;
14359 }
14360 regs.iter().all(|r| const_loaded(*r, insns, 0))
14361}
14362
14363fn const_loaded(r: u16, insns: &[&Insn], depth: u8) -> bool {
14366 if depth > 3 {
14367 return false;
14368 }
14369 insns.iter().any(|i| match &i.kind {
14370 InsnKind::Const { dst, .. } => *dst == r,
14371 InsnKind::Move { dst, src } => *dst == r && const_loaded(*src, insns, depth + 1),
14372 _ => false,
14373 })
14374}