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 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 impure: Vec<bool> = (0..n_vars).map(|v| single[v] && !pure[v]).collect();
3521
3522 if pure.iter().any(|&b| b) {
3524 let vals: Vec<Option<Expr>> = (0..n_vars)
3525 .map(|v| {
3526 if pure[v] {
3527 values.get(v).cloned().flatten()
3528 } else {
3529 None
3530 }
3531 })
3532 .collect();
3533 rewrite_exprs(s, &mut |e| {
3534 deep_rewrite_reads(e, &mut |x| {
3535 if let Expr::Local { var, .. } = x {
3536 if let Some(Some(v)) = vals.get(*var as usize) {
3537 *x = v.clone();
3538 }
3539 }
3540 });
3541 });
3542 drop_defs(s, &pure);
3543 }
3544
3545 if impure.iter().any(|&b| b) {
3548 forward_adjacent_impure(s, &values, &impure);
3549 }
3550}
3551
3552fn count_reads(s: &Stmt, out: &mut Vec<usize>) {
3554 walk_all(s, &mut |st| {
3555 let exprs: Vec<&Expr> = match st {
3556 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
3559 let mut v: Vec<&Expr> = vec![value];
3560 if !matches!(&**target, Expr::Local { .. }) {
3561 v.push(target);
3562 }
3563 v
3564 }
3565 Stmt::ExprStmt(e @ (Expr::PreIncDec { .. } | Expr::PostIncDec { .. })) => {
3568 let inner = match e {
3569 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => e,
3570 _ => unreachable!(),
3571 };
3572 if matches!(**inner, Expr::Local { .. }) {
3573 Vec::new()
3574 } else {
3575 vec![e]
3576 }
3577 }
3578 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
3579 vec![e]
3580 }
3581 Stmt::Return(Some(e)) => vec![e],
3582 Stmt::LocalDef { init: Some(e), .. } => vec![e],
3583 Stmt::If { cond, .. } => vec![cond],
3584 Stmt::While { cond, .. } => vec![cond],
3585 Stmt::DoWhile { cond, .. } => vec![cond],
3586 Stmt::Switch { selector, .. } => vec![selector],
3592 Stmt::ForEach { iterable, .. } => vec![iterable],
3593 Stmt::Synchronized { lock, .. } => vec![lock],
3594 Stmt::For { cond, update, .. } => {
3595 let mut v: Vec<&Expr> = Vec::with_capacity(update.len() + 1);
3596 if let Some(c) = cond {
3597 v.push(c);
3598 }
3599 v.extend(update.iter());
3600 v
3601 }
3602 Stmt::Assert { cond, msg } => {
3603 let mut v: Vec<&Expr> = vec![cond];
3604 if let Some(m) = msg {
3605 v.push(m);
3606 }
3607 v
3608 }
3609 _ => Vec::new(),
3610 };
3611 for e in exprs {
3612 visit_exprs(e, &mut |x| {
3613 if let Expr::Local { var, .. } = x {
3614 grow_to(out, *var);
3615 out[*var as usize] += 1;
3616 }
3617 });
3618 }
3619 });
3620}
3621
3622fn drop_defs(s: &mut Stmt, vars: &[bool]) {
3623 walk_mut_deep(s, &mut |st| {
3624 if let Stmt::Block(v) = st {
3625 v.retain(|x| match x {
3626 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
3627 Expr::Local { var, .. } => !vars.get(*var as usize).copied().unwrap_or(false),
3628 _ => true,
3629 },
3630 Stmt::LocalDef { var, .. } => !vars.get(*var as usize).copied().unwrap_or(false),
3631 _ => true,
3632 });
3633 }
3634 });
3635}
3636
3637fn forward_adjacent_impure(s: &mut Stmt, values: &[Option<Expr>], impure: &[bool]) {
3639 if let Stmt::Block(v) = s {
3640 let mut i = 0;
3641 while i + 1 < v.len() {
3642 let def_var = match &v[i] {
3643 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
3644 Expr::Local { var, .. } => Some(*var),
3645 _ => None,
3646 },
3647 Stmt::LocalDef { var, .. } => Some(*var),
3648 _ => None,
3649 };
3650 if let Some(var) = def_var {
3651 if impure.get(var as usize).copied().unwrap_or(false) {
3652 let mut reads_here = vec![0usize; values.len().max(1)];
3654 count_reads(&v[i + 1], &mut reads_here);
3655 if reads_here.get(var as usize).copied().unwrap_or(0) == 1 {
3656 let val = match &v[i] {
3660 Stmt::ExprStmt(Expr::Assign { value, .. }) => (**value).clone(),
3661 Stmt::LocalDef { init: Some(e), .. } => (*e).clone(),
3662 _ => {
3663 i += 1;
3664 continue;
3665 }
3666 };
3667 rewrite_exprs(&mut v[i + 1], &mut |e| {
3668 deep_rewrite_reads(e, &mut |x| {
3669 if let Expr::Local { var: v2, .. } = x {
3670 if *v2 == var {
3671 *x = val.clone();
3672 }
3673 }
3674 });
3675 });
3676 v.remove(i);
3677 i = i.saturating_sub(1);
3680 continue;
3681 }
3682 }
3683 }
3684 i += 1;
3685 }
3686 }
3687 walk_mut(s, &mut |x| forward_adjacent_impure(x, values, impure));
3688}
3689
3690pub fn infer_types(vt: &mut VarTable, body: &mut Stmt, ret: &JavaType, env: &MethodEnv) {
3696 let n = vt.vars.len();
3697 let mut evidence: Vec<Vec<(JavaType, bool)>> = vec![Vec::new(); n];
3705 let ev = |evidence: &mut Vec<Vec<(JavaType, bool)>>, var: u32, t: JavaType, strong: bool| {
3706 if (var as usize) < evidence.len() {
3707 evidence[var as usize].push((t, strong));
3708 }
3709 };
3710
3711 walk_all(body, &mut |st| match st {
3712 Stmt::LocalDef { var, init, .. } => {
3713 if let Some(e) = init {
3714 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
3715 let strong = match e {
3723 Expr::Const(_) => false,
3724 Expr::Local { var: src, .. } => {
3725 let src_ty = vt.vars.get(*src as usize).map(|v| v.ty.erased());
3726 let tgt_ty = vt.vars.get(*var as usize).map(|v| v.ty.erased());
3727 match (src_ty, tgt_ty) {
3728 (Some(s), Some(g)) => {
3729 (s.is_numeric() && g.is_numeric())
3730 || (s.is_reference() && g.is_reference())
3731 }
3732 _ => false,
3733 }
3734 }
3735 _ => true,
3736 };
3737 ev(&mut evidence, *var, e.type_ref().erased(), strong);
3738 }
3739 }
3740 Stmt::ExprStmt(e) => {
3741 if let Expr::Assign { target, value, .. } = e {
3748 if let Expr::Field { owner: Some(o), cls, is_static: false, .. } = &**target {
3755 if cls.is_empty() {
3756 } else if let Expr::Local { var, .. } = &**o {
3758 ev(
3759 &mut evidence,
3760 *var,
3761 JavaType::Object(cls.clone()),
3762 true,
3763 );
3764 }
3765 }
3766 if let Expr::Local { var, .. } = &**target {
3767 let strong = match &**value {
3768 Expr::Const(_) => false,
3769 Expr::Local { var: src, .. } => {
3770 let src_ty = vt.vars.get(*src as usize).map(|v| v.ty.erased());
3777 let tgt_ty = vt.vars.get(*var as usize).map(|v| v.ty.erased());
3778 match (src_ty, tgt_ty) {
3779 (Some(s), Some(g)) => {
3780 (s.is_numeric() && g.is_numeric())
3781 || (s.is_reference() && g.is_reference())
3782 }
3783 _ => false,
3784 }
3785 }
3786 _ => true,
3787 };
3788 ev(&mut evidence, *var, value.type_ref().erased(), strong);
3789 }
3790 }
3791 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
3792 }
3793 Stmt::Throw(e) => {
3794 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
3795 if let Expr::Local { var, .. } = e {
3796 ev(
3797 &mut evidence,
3798 *var,
3799 JavaType::Object("java/lang/Throwable".into()),
3800 true,
3801 );
3802 }
3803 }
3804 Stmt::Return(Some(e)) => {
3805 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
3806 if let Expr::Local { var, .. } = e {
3807 ev(&mut evidence, *var, ret.clone(), true);
3808 }
3809 }
3810 Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
3811 if let Expr::Local { var, .. } = e {
3812 ev(
3813 &mut evidence,
3814 *var,
3815 JavaType::Object("java/lang/Object".into()),
3816 true,
3817 );
3818 }
3819 }
3820 Stmt::ForEach {
3821 var,
3822 iterable,
3823 is_array,
3824 ..
3825 } => {
3826 if let Expr::Local { var: v0, .. } = iterable {
3827 if *is_array {
3828 ev(&mut evidence, *v0, JavaType::Array(Box::new(JavaType::Int)), true);
3829 } else {
3830 ev(
3831 &mut evidence,
3832 *v0,
3833 JavaType::Object("java/lang/Object".into()),
3834 true,
3835 );
3836 }
3837 }
3838 let _ = var;
3839 }
3840 _ => {}
3841 });
3842 let _ = env;
3843
3844 let mut derefed: Vec<bool> = vec![false; vt.vars.len()];
3854 mark_derefs(body, &mut derefed);
3855 let mut arr_derefed: Vec<bool> = vec![false; vt.vars.len()];
3860 visit_all_exprs(body, &mut |x| {
3861 if let Expr::ArrayIndex { array, .. } = x {
3862 if let Expr::Local { var, .. } = &**array {
3863 if (*var as usize) < arr_derefed.len() {
3864 arr_derefed[*var as usize] = true;
3865 }
3866 }
3867 }
3868 if let Expr::Bin { op, l, r, .. } = x {
3876 if matches!(op, BinOp::Eq | BinOp::Ne)
3877 && (matches!(&**r, Expr::Const(ConstVal::Null))
3878 || matches!(&**l, Expr::Const(ConstVal::Null)))
3879 {
3880 for side in [l, r] {
3881 if let Expr::Local { var, .. } = &**side {
3882 ev(
3883 &mut evidence,
3884 *var,
3885 JavaType::Object("java/lang/Object".into()),
3886 true,
3887 );
3888 }
3889 }
3890 }
3891 }
3892 });
3893 const FINAL_JDK: &[&str] = &[
3894 "java/lang/Boolean",
3895 "java/lang/String",
3896 "java/lang/Integer",
3897 "java/lang/Long",
3898 "java/lang/Short",
3899 "java/lang/Byte",
3900 "java/lang/Character",
3901 "java/lang/Float",
3902 "java/lang/Double",
3903 ];
3904 for (i, evs) in evidence.iter().enumerate() {
3905 let info = &vt.vars[i];
3906 if info.is_param {
3907 continue;
3908 }
3909 let strong: Vec<JavaType> = evs
3910 .iter()
3911 .filter(|(_, s)| *s)
3912 .map(|(t, _)| t.clone())
3913 .collect();
3914 let all: Vec<JavaType> = evs.iter().map(|(t, _)| t.clone()).collect();
3915 if info.ty.erased().is_reference() {
3916 if strong.is_empty() {
3922 let refs: std::collections::HashSet<&str> = all
3923 .iter()
3924 .filter_map(|t| match t {
3925 JavaType::Object(n) if n.as_ref() != "java/lang/Object" => {
3926 Some(n.as_ref())
3927 }
3928 _ => None,
3929 })
3930 .collect();
3931 let mixed = all.iter().any(|t| t.is_numeric());
3932 if refs.len() >= 2 || (mixed && !refs.is_empty()) || (mixed && all.iter().any(|t| matches!(t, JavaType::Array(_)))) {
3933 vt.vars[i].ty =
3934 TypeRef::J(JavaType::Object("java/lang/Object".into()));
3935 continue;
3936 }
3937 }
3938 if arr_derefed[i] {
3941 let arr = strong
3942 .iter()
3943 .chain(all.iter())
3944 .find(|t| matches!(t, JavaType::Array(_)))
3945 .cloned();
3946 if let Some(t) = arr {
3947 vt.vars[i].ty = TypeRef::J(t);
3948 continue;
3949 }
3950 }
3951 let deref_pick = || {
3955 if !derefed[i] {
3956 return None;
3957 }
3958 strong
3959 .iter()
3960 .find(|t| {
3961 matches!(t, JavaType::Object(n)
3962 if !n.is_empty()
3963 && n.as_ref() != "java/lang/Object"
3964 && !FINAL_JDK.contains(&n.as_ref()))
3965 })
3966 .cloned()
3967 };
3968 if let Some(t) = deref_pick()
3969 .or_else(|| pick_object(&strong))
3970 .or_else(|| pick_object(&all))
3971 {
3972 vt.vars[i].ty = TypeRef::J(t);
3973 }
3974 } else if let Some(t) = pick_object(&strong) {
3975 vt.vars[i].ty = TypeRef::J(t);
3979 } else if strong.contains(&JavaType::Boolean) {
3980 vt.vars[i].ty = TypeRef::J(JavaType::Boolean);
3984 } else if !all.is_empty() && all.iter().all(|t| t.is_numeric()) {
3985 if let Some(t) = pick_numeric(&all) {
3986 vt.vars[i].ty = TypeRef::J(t);
3987 }
3988 } else {
3989 let refs = all.iter().filter(|t| t.is_reference()).count();
3993 let nums = all.iter().filter(|t| t.is_numeric()).count();
3994 if refs > 0 && nums > 0 {
3995 vt.vars[i].ty =
3996 TypeRef::J(JavaType::Object("java/lang/Object".into()));
3997 } else if let Some(t) = pick_object(&strong).or_else(|| pick_object(&all)) {
3998 vt.vars[i].ty = TypeRef::J(t);
3999 }
4000 }
4001 }
4002
4003 let types: Vec<&TypeRef> = vt.vars.iter().map(|v| &v.ty).collect();
4008 rewrite_exprs(body, &mut |e| {
4009 deep_rewrite(e, &mut |x| {
4010 if let Expr::Local { var, ty } = x {
4011 if let Some(want) = types.get(*var as usize) {
4012 if ty != *want {
4013 *ty = (*want).clone();
4014 }
4015 }
4016 }
4017 });
4018 });
4019
4020 coerce_num_consts(body, &types);
4023}
4024
4025fn coerce_num_consts(body: &mut Stmt, types: &[&TypeRef]) {
4026 walk_mut_deep(body, &mut |st| {
4027 let (var, val): (u32, &mut Expr) = match st {
4028 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &mut **target {
4029 Expr::Local { var, .. } => (*var, value),
4030 _ => return,
4031 },
4032 Stmt::LocalDef {
4033 var, init: Some(e), ..
4034 } => (*var, e),
4035 _ => return,
4036 };
4037 let want = match types.get(var as usize) {
4038 Some(TypeRef::J(t)) => t.clone(),
4039 _ => return,
4040 };
4041 let rewritten = match &*val {
4042 Expr::Const(ConstVal::Long(l)) => match &want {
4043 JavaType::Double => Some(Expr::Const(ConstVal::Double(*l as f64))),
4044 JavaType::Float => Some(Expr::Const(ConstVal::Float(*l as f32))),
4045 _ => None,
4046 },
4047 Expr::Const(ConstVal::Int(i)) => match &want {
4048 JavaType::Double => Some(Expr::Const(ConstVal::Double(*i as f64))),
4049 JavaType::Float => Some(Expr::Const(ConstVal::Float(*i as f32))),
4050 _ => None,
4051 },
4052 _ => None,
4053 };
4054 if let Some(e) = rewritten {
4055 *val = e;
4056 }
4057 });
4058}
4059
4060fn pick_object(evs: &[JavaType]) -> Option<JavaType> {
4061 evs.iter()
4062 .find(|t| {
4063 t.is_reference()
4064 && !matches!(t, JavaType::Object(n) if n.is_empty() || n.as_ref() == "java/lang/Object")
4065 })
4066 .cloned()
4067}
4068
4069fn pick_numeric(evs: &[JavaType]) -> Option<JavaType> {
4070 if evs.is_empty() {
4071 return None;
4072 }
4073 let mut t = evs[0].clone();
4074 for x in &evs[1..] {
4075 t = join_numeric(&t, x);
4076 }
4077 Some(t)
4078}
4079
4080fn join_numeric(a: &JavaType, b: &JavaType) -> JavaType {
4081 if a == b {
4082 return a.clone();
4083 }
4084 match (a, b) {
4085 (JavaType::Double, _) | (_, JavaType::Double) => JavaType::Double,
4086 (JavaType::Float, _) | (_, JavaType::Float) => JavaType::Float,
4087 (JavaType::Long, _) | (_, JavaType::Long) => JavaType::Long,
4088 (JavaType::Int, x) if x.is_integral() => JavaType::Int,
4089 (x, JavaType::Int) if x.is_integral() => JavaType::Int,
4090 (x, y) if x.is_integral() && y.is_integral() => JavaType::Int,
4091 _ => JavaType::Int,
4092 }
4093}
4094
4095fn expr_evidence<F: FnMut(u32, JavaType)>(e: &Expr, f: &mut F) {
4097 visit_exprs(e, &mut |x| match x {
4098 Expr::Method {
4099 owner,
4100 args,
4101 desc,
4102 is_static,
4103 cls,
4104 ..
4105 } => {
4106 if !*is_static {
4107 if let Some(o) = owner {
4108 if let Expr::Local { var, .. } = &**o {
4109 f(*var, JavaType::Object(cls.clone()));
4110 }
4111 }
4112 }
4113 for (i, a) in args.iter().enumerate() {
4114 if let Expr::Local { var, .. } = a {
4115 if let Some(t) = desc.args.get(i) {
4116 f(*var, t.clone());
4117 }
4118 }
4119 }
4120 }
4121 Expr::Field {
4122 owner,
4123 cls,
4124 ty,
4125 is_static,
4126 ..
4127 } => {
4128 if !*is_static && !cls.is_empty() {
4129 if let Some(o) = owner {
4130 if let Expr::Local { var, .. } = &**o {
4131 f(*var, JavaType::Object(cls.clone()));
4141 }
4142 }
4143 }
4144 let _ = ty;
4145 }
4146 Expr::Bin { op, l, r, .. }
4151 if matches!(op, BinOp::Eq | BinOp::Ne)
4152 && (matches!(&**r, Expr::Const(ConstVal::Null))
4153 || matches!(&**l, Expr::Const(ConstVal::Null))) =>
4154 {
4155 for side in [l, r] {
4156 if let Expr::Local { var, .. } = &**side {
4157 f(*var, JavaType::Object("java/lang/Object".into()));
4158 }
4159 }
4160 }
4161 Expr::ArrayIndex { array, .. } => {
4162 if let Expr::Local { var, ty, .. } = &**array {
4163 let t = ty.erased();
4168 if matches!(t, JavaType::Array(_)) {
4169 f(*var, t);
4170 } else {
4171 f(*var, JavaType::Array(Box::new(JavaType::Int)));
4172 }
4173 }
4174 }
4175 Expr::Assign { .. } => {
4176 }
4179 Expr::Cast { e: inner, .. } => {
4180 let _ = inner;
4186 }
4187 Expr::InstanceOf { e: inner, .. } => {
4188 let _ = inner;
4189 }
4190 _ => {}
4191 });
4192}
4193
4194pub fn mark_field_owner_concrete(vt: &mut VarTable, body: &mut Stmt) {
4206 let mut owners: HashSet<u32> = HashSet::default();
4207 rewrite_exprs(body, &mut |e| {
4208 visit_exprs(e, &mut |x| {
4209 if let Expr::Field {
4210 owner: Some(o),
4211 is_static: false,
4212 ..
4213 } = x
4214 {
4215 if let Expr::Local { var, .. } = &**o {
4216 owners.insert(*var);
4217 }
4218 }
4219 });
4220 });
4221 for var in owners {
4222 let i = var as usize;
4223 if i >= vt.vars.len() {
4224 continue;
4225 }
4226 if let JavaType::Object(cls) = vt.vars[i].ty.erased() {
4227 if let Some(last) = cls.rsplit('$').next() {
4228 if !last.is_empty() && last.chars().all(|c| c.is_ascii_digit()) {
4229 vt.force_concrete_vars.insert(var);
4230 }
4231 }
4232 }
4233 }
4234 let concrete: Vec<u32> = vt
4243 .vars
4244 .iter()
4245 .filter(|v| {
4246 if let JavaType::Object(cls) = v.ty.erased() {
4247 cls.rsplit('$')
4248 .next()
4249 .is_some_and(|l| !l.is_empty() && l.chars().all(|c| c.is_ascii_digit()))
4250 } else {
4251 false
4252 }
4253 })
4254 .map(|v| v.id)
4255 .collect();
4256 for var in concrete {
4257 vt.force_concrete_vars.insert(var);
4258 }
4259}
4260
4261pub fn insert_object_narrowing_casts(
4273 vt: &VarTable,
4274 body: &mut Stmt,
4275 pool: &DexPool,
4276 ret: &JavaType,
4277) {
4278 let value_ty = |e: &Expr| -> JavaType {
4285 match e {
4286 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4287 other => other.type_ref().erased(),
4288 }
4289 };
4290 let is_top_object_ty = |v: &JavaType| -> bool {
4291 matches!(v, JavaType::Object(c) if c.as_ref() == "java/lang/Object")
4292 };
4293 let specific_ref = |ty: &TypeRef| -> Option<TypeRef> {
4298 match ty {
4299 TypeRef::J(JavaType::Object(c)) if c.as_ref() != "java/lang/Object" => {
4300 Some(ty.clone())
4301 }
4302 TypeRef::J(JavaType::Array(_)) => Some(ty.clone()),
4303 _ => match ty.erased() {
4304 JavaType::Object(c) if c.as_ref() != "java/lang/Object" => Some(ty.clone()),
4305 JavaType::Array(_) => Some(ty.clone()),
4306 _ => None,
4307 },
4308 }
4309 };
4310 let non_cast_shape = |e: &Expr| -> bool {
4311 !matches!(e, Expr::Const(_) | Expr::Cast { .. } | Expr::InstanceOf { .. })
4312 };
4313 let needs_downcast = |vty: &JavaType, tt: &JavaType| -> bool {
4323 let (JavaType::Object(vn), JavaType::Object(tn)) = (vty, tt) else {
4324 return false;
4325 };
4326 if vn.as_ref() == "java/lang/Object" || tn.as_ref() == "java/lang/Object" {
4327 return false;
4328 }
4329 vn != tn && !pool.is_subtype(vn, tn)
4330 };
4331 let wants_cast = |e: &Expr, vty: &JavaType, tt: &JavaType| -> bool {
4333 non_cast_shape(e) && (is_top_object_ty(vty) || needs_downcast(vty, tt))
4334 };
4335 walk_mut_deep(body, &mut |st| match st {
4336 Stmt::ExprStmt(Expr::Assign {
4337 target,
4338 value,
4339 op: AssignOp::Plain,
4340 ..
4341 }) => {
4342 let tgt_erased: JavaType;
4346 let tgt_ref: Option<&TypeRef>;
4347 match &**target {
4348 Expr::Local { var, .. } => {
4349 let ty = &vt.var(*var).ty;
4350 tgt_erased = ty.erased();
4351 tgt_ref = Some(ty);
4352 }
4353 Expr::Field { ty, .. } => {
4354 tgt_erased = ty.erased();
4355 tgt_ref = Some(ty);
4356 }
4357 Expr::ArrayIndex { array, .. } => {
4361 let aty = match &**array {
4362 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4363 other => other.type_ref().erased(),
4364 };
4365 match aty {
4366 JavaType::Array(inner) => {
4367 tgt_erased = *inner;
4368 tgt_ref = None;
4369 }
4370 _ => return,
4371 }
4372 }
4373 _ => return,
4374 }
4375 let specific = match tgt_ref {
4379 Some(tr) => specific_ref(tr),
4380 None => specific_ref(&TypeRef::J(tgt_erased.clone())),
4381 };
4382 let Some(t) = specific else {
4383 return;
4384 };
4385 let vty = value_ty(value);
4386 if wants_cast(value, &vty, &tgt_erased) {
4387 let v = std::mem::replace(value, Box::new(Expr::This));
4388 **value = Expr::Cast { ty: t, e: v };
4389 }
4390 }
4391 Stmt::LocalDef { var, init: Some(value), .. } => {
4392 if let Some(t) = specific_ref(&vt.var(*var).ty) {
4393 let tt = vt.var(*var).ty.erased();
4394 let vty = value_ty(value);
4395 if wants_cast(value, &vty, &tt) {
4396 let v = std::mem::replace(value, Expr::This);
4397 *value = Expr::Cast {
4398 ty: t,
4399 e: Box::new(v),
4400 };
4401 }
4402 }
4403 }
4404 Stmt::Return(Some(value)) => {
4405 if let Some(t) = specific_ref(&TypeRef::J(ret.clone())) {
4406 let vty = value_ty(value);
4407 if wants_cast(value, &vty, ret) {
4408 let v = std::mem::replace(value, Expr::This);
4409 *value = Expr::Cast { ty: t, e: Box::new(v) };
4410 }
4411 }
4412 }
4413 _ => {}
4414 });
4415 walk_stmt_exprs(body, &mut |e| {
4422 deep_rewrite(e, &mut |x| {
4423 let (args, arg_tys): (&mut Vec<Expr>, &[JavaType]) = match x {
4424 Expr::Method { desc, args, is_dynamic: false, .. } => {
4425 (args, desc.args.as_slice())
4426 }
4427 Expr::New { arg_tys, args, .. } => (args, arg_tys.as_slice()),
4431 _ => return,
4432 };
4433 for (i, a) in args.iter_mut().enumerate() {
4434 let Some(formal) = arg_tys.get(i) else {
4435 continue;
4436 };
4437 let Some(t) = specific_ref(&TypeRef::J(formal.clone())) else {
4438 continue;
4439 };
4440 let aty = value_ty(a);
4441 if !wants_cast(a, &aty, formal) {
4442 continue;
4443 }
4444 let v = std::mem::replace(a, Expr::Const(ConstVal::Null));
4445 *a = Expr::Cast { ty: t, e: Box::new(v) };
4446 }
4447 });
4448 });
4449}
4450
4451pub fn fix_primitive_assign_casts(vt: &VarTable, body: &mut Stmt, ret_ty: &JavaType) {
4460 fn numlike(t: &JavaType) -> bool {
4461 matches!(
4462 t,
4463 JavaType::Byte
4464 | JavaType::Short
4465 | JavaType::Int
4466 | JavaType::Char
4467 | JavaType::Long
4468 | JavaType::Float
4469 | JavaType::Double
4470 )
4471 }
4472 fn val_ty(e: &Expr, vt: &VarTable) -> JavaType {
4473 match e {
4474 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
4475 vt.var(*var).ty.erased()
4476 }
4477 other => other.type_ref().erased(),
4478 }
4479 }
4480 fn wrap(ty: &JavaType, value: &mut Expr) {
4481 if matches!(value, Expr::Cast { .. }) {
4482 return;
4483 }
4484 if val_ty_static(value) == *ty {
4485 return;
4486 }
4487 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
4488 *value = Expr::Cast {
4489 ty: TypeRef::J(ty.clone()),
4490 e: Box::new(taken),
4491 };
4492 }
4493 fn val_ty_static(e: &Expr) -> JavaType {
4494 e.type_ref().erased()
4495 }
4496 fn coerce(tt: &JavaType, value: &mut Expr, vt: &VarTable) {
4504 match (&*value, tt) {
4509 (Expr::Const(ConstVal::Int(b)), JavaType::Float) => {
4510 *value = Expr::Const(ConstVal::Float(f32::from_bits(*b as u32)));
4511 return;
4512 }
4513 (Expr::Const(ConstVal::Long(b)), JavaType::Double) => {
4514 *value = Expr::Const(ConstVal::Double(f64::from_bits(*b as u64)));
4515 return;
4516 }
4517 (Expr::Const(ConstVal::Float(f)), JavaType::Int) => {
4518 *value = Expr::Const(ConstVal::Int(f.to_bits() as i32));
4519 return;
4520 }
4521 (Expr::Const(ConstVal::Double(d)), JavaType::Long) => {
4522 *value = Expr::Const(ConstVal::Long(d.to_bits() as i64));
4523 return;
4524 }
4525 _ => {}
4526 }
4527 let vt_val = val_ty(value, vt);
4528 let tt_bool = matches!(tt, JavaType::Boolean);
4529 let val_bool = matches!(vt_val, JavaType::Boolean);
4530 if tt_bool && !val_bool && numlike(&vt_val) {
4531 if matches!(
4532 value,
4533 Expr::Const(_)
4534 | Expr::Bin { op: BinOp::Eq | BinOp::Ne, .. }
4535 | Expr::Un { op: UnOp::Not, .. }
4536 ) {
4537 return;
4542 }
4543 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
4544 *value = Expr::Bin {
4545 op: BinOp::Ne,
4546 l: Box::new(taken),
4547 r: Box::new(Expr::Const(ConstVal::Int(0))),
4548 ty: Some(TypeRef::J(JavaType::Boolean)),
4549 };
4550 } else if !tt_bool && val_bool && numlike(tt) {
4551 if matches!(value, Expr::Cast { .. }) {
4552 return;
4553 }
4554 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
4555 *value = Expr::Cast {
4556 ty: TypeRef::J(tt.clone()),
4557 e: Box::new(Expr::Cond {
4558 c: Box::new(taken),
4559 t: Box::new(Expr::Const(ConstVal::Int(1))),
4560 f: Box::new(Expr::Const(ConstVal::Int(0))),
4561 }),
4562 };
4563 } else if numlike(tt) && numlike(&vt_val) {
4564 wrap(tt, value);
4565 }
4566 }
4567 walk_mut_deep(body, &mut |st| match st {
4568 Stmt::ExprStmt(Expr::Assign {
4569 target,
4570 op: AssignOp::Plain,
4571 value,
4572 }) => {
4573 let tt = match &**target {
4574 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
4575 vt.var(*var).ty.erased()
4576 }
4577 Expr::Field { ty, .. } => ty.erased(),
4578 Expr::ArrayIndex { array, .. } => match array.type_ref().erased() {
4582 JavaType::Array(inner) => *inner,
4583 _ => return,
4584 },
4585 _ => return,
4586 };
4587 coerce(&tt, value, vt);
4588 }
4589 Stmt::LocalDef {
4590 var,
4591 init: Some(value),
4592 ..
4593 } => {
4594 if (*var as usize) >= vt.vars.len() {
4595 return;
4596 }
4597 let tt = vt.var(*var).ty.erased();
4598 coerce(&tt, value, vt);
4599 }
4600 Stmt::Return(Some(e)) => {
4601 if !numlike(ret_ty) || !numlike(&val_ty(e, vt)) {
4602 return;
4603 }
4604 wrap(ret_ty, e);
4605 }
4606 _ => {}
4607 });
4608}
4609
4610pub fn fix_shadowed_super_fields(
4634 body: &mut Stmt,
4635 vt: &VarTable,
4636 pool: &DexPool,
4637 class_name: &str,
4638) {
4639 fn display(pool: &DexPool, cls: &str, name: &str) -> Option<String> {
4643 let pc = pool.get(cls)?;
4644 let f = pc
4645 .static_fields
4646 .iter()
4647 .chain(pc.instance_fields.iter())
4648 .find(|f| f.name.as_ref() == name)?;
4649 Some(
4650 jdc_core::rename::field_display(cls, name, &f.desc)
4651 .map(|d| d.to_string())
4652 .unwrap_or_else(|| name.to_string()),
4653 )
4654 }
4655 fn owner_ct(
4656 o: &Option<Box<Expr>>,
4657 vt: &VarTable,
4658 class_name: &str,
4659 ) -> Option<std::sync::Arc<str>> {
4660 match o.as_deref() {
4661 None | Some(Expr::This) => Some(std::sync::Arc::from(class_name)),
4662 Some(Expr::Local { var, .. }) => match vt.var(*var).ty.erased() {
4663 JavaType::Object(n) => Some(n),
4664 _ => None,
4665 },
4666 Some(other) => match other.type_ref().erased() {
4667 JavaType::Object(n) => Some(n),
4668 _ => None,
4669 },
4670 }
4671 }
4672 walk_stmt_exprs(body, &mut |e| {
4673 deep_rewrite(e, &mut |x| {
4674 let Expr::Field {
4675 owner,
4676 cls,
4677 name,
4678 is_static: false,
4679 ..
4680 } = x
4681 else {
4682 return;
4683 };
4684 let Some(ot) = owner_ct(owner, vt, class_name) else {
4685 return;
4686 };
4687 if cls.as_ref() == ot.as_ref() {
4688 return;
4689 }
4690 let Some(ref_display) = display(pool, cls, name) else {
4691 return;
4692 };
4693 let mut cur = ot.to_string();
4697 let mut shadowed = false;
4698 let mut found = false;
4699 for _ in 0..64 {
4700 if display(pool, &cur, name).as_deref() == Some(ref_display.as_str()) {
4701 shadowed = true;
4702 }
4703 let Some(pc) = pool.get(&cur) else {
4704 return;
4705 };
4706 let Some(sup) = pc.super_name.clone() else {
4707 return;
4708 };
4709 if sup.as_str() == cls.as_ref() {
4710 found = true;
4711 break;
4712 }
4713 cur = sup;
4714 }
4715 if !found || !shadowed {
4716 return;
4717 }
4718 let ty = TypeRef::J(JavaType::Object(cls.clone()));
4719 match owner {
4720 Some(o) => {
4721 if let Expr::Cast { ty: ct, .. } = &mut **o {
4722 *ct = ty;
4723 } else {
4724 let taken = std::mem::replace(&mut **o, Expr::This);
4725 **o = Expr::Cast {
4726 ty,
4727 e: Box::new(taken),
4728 };
4729 }
4730 }
4731 None => {
4732 *owner = Some(Box::new(Expr::Cast {
4733 ty,
4734 e: Box::new(Expr::This),
4735 }))
4736 }
4737 }
4738 });
4739 });
4740}
4741
4742pub fn fix_field_owner_downcasts(body: &mut Stmt, vt: &VarTable, pool: &DexPool) {
4761 fn owner_internal(o: &Expr, vt: &VarTable) -> Option<std::sync::Arc<str>> {
4762 let t = match o {
4763 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4764 other => other.type_ref().erased(),
4765 };
4766 match t {
4767 JavaType::Object(n) => Some(n),
4768 _ => None,
4769 }
4770 }
4771 fn declares_up(pool: &DexPool, class: &str, name: &str) -> bool {
4772 let mut cur = Some(class.to_string());
4773 let mut hops = 0;
4774 while let Some(c) = cur {
4775 hops += 1;
4776 if hops > 64 {
4777 return false;
4778 }
4779 let Some(pc) = pool.get(&c) else { return false };
4780 if pc
4781 .instance_fields
4782 .iter()
4783 .chain(pc.static_fields.iter())
4784 .any(|f| f.name.as_ref() == name)
4785 {
4786 return true;
4787 }
4788 cur = pc.super_name.clone();
4789 }
4790 false
4791 }
4792 walk_stmt_exprs(body, &mut |e| {
4793 deep_rewrite(e, &mut |x| {
4794 if let Expr::Field {
4795 owner: Some(o),
4796 cls,
4797 name,
4798 is_static: false,
4799 ..
4800 } = x
4801 {
4802 let Some(ot) = owner_internal(o, vt) else {
4803 return;
4804 };
4805 if ot.as_ref() == cls.as_ref() || !pool.is_subtype(cls, &ot) {
4806 return;
4807 }
4808 if declares_up(pool, &ot, name) || !declares_up(pool, cls, name) {
4809 return;
4810 }
4811 let ty = TypeRef::J(JavaType::Object(cls.clone()));
4812 if let Expr::Cast { ty: ct, .. } = &mut **o {
4813 *ct = ty;
4814 } else {
4815 let taken = std::mem::replace(&mut **o, Expr::This);
4816 **o = Expr::Cast { ty, e: Box::new(taken) };
4817 }
4818 }
4819 let (tgt_ty, value) = match x {
4821 Expr::Assign { target, op: AssignOp::Plain, value } => {
4822 let t = match &**target {
4823 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4824 Expr::Field { ty, .. } => ty.erased(),
4825 _ => return,
4826 };
4827 (t, value)
4828 }
4829 _ => return,
4830 };
4831 let JavaType::Object(sn) = tgt_ty else { return };
4832 if let Expr::Cast { ty, .. } = &mut **value {
4833 if let TypeRef::J(JavaType::Object(tn)) = ty {
4834 if tn.as_ref() != sn.as_ref() && pool.is_subtype(&sn, tn) {
4835 *ty = TypeRef::J(JavaType::Object(sn.clone()));
4836 }
4837 }
4838 }
4839 });
4840 });
4841 walk_mut_deep(body, &mut |st| {
4843 if let Stmt::LocalDef { var, init: Some(value), .. } = st {
4844 let tt = if (*var as usize) < vt.vars.len() {
4845 vt.vars[*var as usize].ty.erased()
4846 } else {
4847 return;
4848 };
4849 let JavaType::Object(sn) = tt else { return };
4850 if let Expr::Cast { ty, .. } = value {
4851 if let TypeRef::J(JavaType::Object(tn)) = ty {
4852 if tn.as_ref() != sn.as_ref() && pool.is_subtype(&sn, tn) {
4853 *ty = TypeRef::J(JavaType::Object(sn.clone()));
4854 }
4855 }
4856 }
4857 }
4858 });
4859}
4860
4861pub fn fix_incomparable_equality(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
4868 fn side_ty(e: &Expr, vt: &VarTable) -> JavaType {
4869 match e {
4870 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
4871 vt.var(*var).ty.erased()
4872 }
4873 other => other.type_ref().erased(),
4874 }
4875 }
4876 let incomparable = |a: &JavaType, b: &JavaType| -> bool {
4877 use JavaType::{Array, Object};
4878 match (a, b) {
4879 (Object(x), Object(y)) => {
4880 if x == y || x.as_ref() == "java/lang/Object" || y.as_ref() == "java/lang/Object"
4881 {
4882 return false;
4883 }
4884 let xi = pool.get(x).map(|c| c.is_interface()).unwrap_or(true);
4885 let yi = pool.get(y).map(|c| c.is_interface()).unwrap_or(true);
4886 if xi || yi {
4890 return false;
4891 }
4892 !pool.is_subtype(x, y) && !pool.is_subtype(y, x)
4893 }
4894 (Array(_), Object(y)) | (Object(y), Array(_)) => {
4895 y.as_ref() != "java/lang/Object"
4896 && y.as_ref() != "java/lang/Cloneable"
4897 && y.as_ref() != "java/io/Serializable"
4898 && !pool.get(y).map(|c| c.is_interface()).unwrap_or(false)
4899 }
4900 _ => false,
4901 }
4902 };
4903 let is_ref = |t: &JavaType| matches!(t, JavaType::Object(_) | JavaType::Array(_));
4904 walk_stmt_exprs(body, &mut |e| {
4905 deep_rewrite(e, &mut |x| {
4906 if let Expr::Bin {
4907 op: BinOp::Eq | BinOp::Ne,
4908 l,
4909 r,
4910 ..
4911 } = x
4912 {
4913 let lt = side_ty(l, vt);
4914 let rt = side_ty(r, vt);
4915 if is_ref(<) && matches!(&**r, Expr::Const(ConstVal::Int(0))) {
4921 **r = Expr::Const(ConstVal::Null);
4922 return;
4923 }
4924 if is_ref(&rt) && matches!(&**l, Expr::Const(ConstVal::Int(0))) {
4925 **l = Expr::Const(ConstVal::Null);
4926 return;
4927 }
4928 if incomparable(<, &rt) {
4929 let taken = std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
4930 **l = Expr::Cast {
4931 ty: TypeRef::J(JavaType::Object("java/lang/Object".into())),
4932 e: taken,
4933 };
4934 }
4935 }
4936 });
4937 });
4938}
4939
4940pub fn fix_primitive_arg_bridges(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
4954 fn val_ty(e: &Expr, vt: &VarTable) -> JavaType {
4955 match e {
4956 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4957 other => other.type_ref().erased(),
4958 }
4959 }
4960 #[allow(clippy::type_complexity)]
4961 let is_bool = |t: &JavaType| matches!(t, JavaType::Boolean);
4962 let is_num = |t: &JavaType| {
4963 matches!(
4964 t,
4965 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
4966 | JavaType::Long | JavaType::Float | JavaType::Double
4967 )
4968 };
4969 fn bridge(actual: &JavaType, formal: &JavaType, arg: &Expr) -> Option<Expr> {
4974 match (arg, formal) {
4984 (Expr::Const(ConstVal::Int(b)), JavaType::Float) => {
4985 return Some(Expr::Const(ConstVal::Float(f32::from_bits(*b as u32))));
4986 }
4987 (Expr::Const(ConstVal::Long(b)), JavaType::Double) => {
4988 return Some(Expr::Const(ConstVal::Double(f64::from_bits(*b as u64))));
4989 }
4990 (Expr::Const(ConstVal::Float(f)), JavaType::Int) => {
4991 return Some(Expr::Const(ConstVal::Int(f.to_bits() as i32)));
4992 }
4993 (Expr::Const(ConstVal::Double(d)), JavaType::Long) => {
4994 return Some(Expr::Const(ConstVal::Long(d.to_bits() as i64)));
4995 }
4996 _ => {}
4997 }
4998 if matches!(actual, JavaType::Boolean)
4999 && matches!(
5000 formal,
5001 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
5002 | JavaType::Long | JavaType::Float | JavaType::Double
5003 )
5004 {
5005 Some(Expr::Cast {
5006 ty: TypeRef::J(formal.clone()),
5007 e: Box::new(Expr::Cond {
5008 c: Box::new(arg.clone()),
5009 t: Box::new(Expr::Const(ConstVal::Int(1))),
5010 f: Box::new(Expr::Const(ConstVal::Int(0))),
5011 }),
5012 })
5013 } else if matches!(formal, JavaType::Boolean)
5014 && matches!(
5015 actual,
5016 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
5017 | JavaType::Long | JavaType::Float | JavaType::Double
5018 )
5019 {
5020 Some(Expr::Bin {
5021 op: BinOp::Ne,
5022 l: Box::new(arg.clone()),
5023 r: Box::new(Expr::Const(ConstVal::Int(0))),
5024 ty: Some(TypeRef::J(JavaType::Boolean)),
5025 })
5026 } else if matches!(
5027 actual,
5028 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
5029 | JavaType::Long | JavaType::Float | JavaType::Double
5030 ) && matches!(
5031 formal,
5032 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
5033 | JavaType::Long | JavaType::Float | JavaType::Double
5034 ) && !implicit_widening(actual, formal)
5035 {
5036 Some(Expr::Cast {
5045 ty: TypeRef::J(formal.clone()),
5046 e: Box::new(arg.clone()),
5047 })
5048 } else {
5049 None
5050 }
5051 }
5052 fn fw_resolvable(n: &str) -> bool {
5068 crate::fwdb::exists(n)
5069 }
5070 fn null_arg_cast(a: &mut Expr, f: &JavaType, pool: &DexPool) -> bool {
5071 if !matches!(&*a, Expr::Const(ConstVal::Null)) {
5072 return false;
5073 }
5074 if let JavaType::Object(n) = f {
5075 let resolvable = pool.get(n).is_some() || fw_resolvable(n);
5081 if n.as_ref() != "java/lang/Object" && resolvable {
5082 *a = Expr::Cast {
5083 ty: TypeRef::J(f.clone()),
5084 e: Box::new(Expr::Const(ConstVal::Null)),
5085 };
5086 return true;
5087 }
5088 } else if let JavaType::Array(_) = f {
5089 let mut e: &JavaType = f;
5096 while let JavaType::Array(inner) = e {
5097 e = &**inner;
5098 }
5099 let resolvable = match e {
5100 JavaType::Object(n) => pool.get(n).is_some() || fw_resolvable(n),
5101 _ => true, };
5103 if resolvable {
5104 *a = Expr::Cast {
5105 ty: TypeRef::J(f.clone()),
5106 e: Box::new(Expr::Const(ConstVal::Null)),
5107 };
5108 return true;
5109 }
5110 }
5111 false
5114 }
5115 fn implicit_widening(a: &JavaType, f: &JavaType) -> bool {
5118 use JavaType::*;
5119 match (a, f) {
5120 (Byte, Short) | (Byte, Int) | (Byte, Long) | (Byte, Float) | (Byte, Double) => true,
5121 (Short, Int) | (Short, Long) | (Short, Float) | (Short, Double) => true,
5122 (Char, Int) | (Char, Long) | (Char, Float) | (Char, Double) => true,
5123 (Int, Long) | (Int, Float) | (Int, Double) => true,
5124 (Long, Float) | (Long, Double) => true,
5125 (Float, Double) => true,
5126 (x, y) => x == y,
5127 }
5128 }
5129 let overload_competes = |cls: &str, name: &str, args: &[JavaType]| -> bool {
5138 if let Some(c) = pool.get(cls) {
5139 return c.all_methods().any(|m| {
5140 &*m.name == name
5141 && m.access & (crate::access::ACC_BRIDGE | crate::access::ACC_SYNTHETIC) == 0
5147 && m.parsed_desc()
5148 .is_some_and(|d| d.args.len() == args.len() && d.args != args)
5149 });
5150 }
5151 let _ = args;
5160 false
5161 };
5162 let ctor_formals = |cls: &str, n: usize| -> Option<Vec<JavaType>> {
5165 let c = pool.get(cls)?;
5166 let mut out: Option<Vec<JavaType>> = None;
5167 for m in c.all_methods() {
5168 if &*m.name != "<init>" {
5169 continue;
5170 }
5171 let Some(d) = m.parsed_desc() else { continue };
5172 if d.args.len() != n {
5173 continue;
5174 }
5175 match &out {
5176 None => out = Some(d.args.clone()),
5177 Some(prev) if *prev != d.args => return None,
5178 Some(_) => {}
5179 }
5180 }
5181 out
5182 };
5183 walk_stmt_exprs(body, &mut |e| {
5184 deep_rewrite(e, &mut |x| match x {
5185 Expr::Method {
5186 cls,
5187 name,
5188 desc,
5189 args,
5190 is_static,
5191 ..
5192 } => {
5193 if args.len() != desc.args.len() {
5194 return;
5195 }
5196 let _ = is_static;
5197 let mut competes: Option<bool> = None;
5198 for (a, f) in args.iter_mut().zip(desc.args.iter()) {
5199 if null_arg_cast(a, f, pool) {
5200 continue;
5201 }
5202 if matches!(a, Expr::Cast { .. }) {
5203 continue;
5204 }
5205 let subtype_arg = match (&val_ty(a, vt), f) {
5216 (JavaType::Object(an), JavaType::Object(fn_)) => {
5217 (an != fn_ && pool.is_subtype(an, fn_))
5222 || (fn_.as_ref() == "java/lang/Object"
5223 && an.as_ref() != "java/lang/Object")
5224 }
5225 (JavaType::Array(_), JavaType::Object(fn_))
5226 if fn_.as_ref() == "java/lang/Object" =>
5227 {
5228 true
5229 }
5230 _ => false,
5231 };
5232 let null_obj =
5246 matches!(a, Expr::Const(ConstVal::Null)) && matches!(f, JavaType::Object(_));
5247 let framework_owner = pool.get(cls.as_ref()).is_none();
5248 let fw_null_ambiguous = framework_owner
5249 && matches!(
5250 (cls.as_ref(), name.as_ref()),
5251 ("java/lang/StringBuilder", "append")
5252 | ("java/lang/StringBuffer", "append")
5253 | ("java/io/PrintStream", "println")
5254 | ("java/io/PrintStream", "print")
5255 | ("java/io/PrintWriter", "println")
5256 | ("java/io/PrintWriter", "print")
5257 | ("java/lang/String", "valueOf")
5258 );
5259 let fw_subtype_pinnable = framework_owner
5270 && subtype_arg
5271 && !matches!(f, JavaType::Object(fn_) if fn_.as_ref() == "java/lang/Object")
5272 && matches!(
5273 (cls.as_ref(), name.as_ref()),
5274 ("android/content/Intent", "putExtra")
5275 | ("java/util/concurrent/ScheduledExecutorService", "schedule")
5276 | ("java/util/concurrent/ExecutorService", "submit")
5277 | ("java/util/concurrent/ExecutorService", "invokeAll")
5278 );
5279 if fw_subtype_pinnable
5280 || (subtype_arg
5281 && *competes.get_or_insert_with(|| {
5282 overload_competes(cls, name, &desc.args)
5283 }))
5284 || (null_obj
5285 && (fw_null_ambiguous
5286 || *competes.get_or_insert_with(|| {
5287 overload_competes(cls, name, &desc.args)
5288 })))
5289 {
5290 let old =
5291 std::mem::replace(a, Expr::Const(ConstVal::Null));
5292 *a = Expr::Cast {
5293 ty: TypeRef::J(f.clone()),
5294 e: Box::new(old),
5295 };
5296 continue;
5297 }
5298 if !is_bool(&val_ty(a, vt)) && !is_num(&val_ty(a, vt)) {
5299 continue;
5300 }
5301 if !is_bool(f) && !is_num(f) {
5302 continue;
5303 }
5304 if let Some(b) = bridge(&val_ty(a, vt), f, a) {
5305 *a = b;
5306 }
5307 }
5308 }
5309 Expr::New { cls, args, arg_tys, .. } => {
5310 let formals_owned = if arg_tys.len() == args.len() && !arg_tys.is_empty() {
5311 Some(arg_tys.clone())
5312 } else {
5313 ctor_formals(cls, args.len())
5314 };
5315 if let Some(formals) = formals_owned {
5316 for (a, f) in args.iter_mut().zip(formals.iter()) {
5317 if null_arg_cast(a, f, pool) {
5318 continue;
5319 }
5320 if matches!(a, Expr::Cast { .. }) {
5321 continue;
5322 }
5323 if !is_bool(f) && !is_num(f) {
5324 continue;
5325 }
5326 if let Some(b) = bridge(&val_ty(a, vt), f, a) {
5327 *a = b;
5328 }
5329 }
5330 }
5331 }
5332 _ => {}
5333 });
5334 });
5335}
5336
5337pub fn deshadow_locals(vt: &mut VarTable, pool: &crate::DexPool, class_name: &str, body: &Stmt) {
5349 let segs = pool.root_pkg_segs();
5350 let obscured = crate::classdec::obscured_simples_snapshot();
5354 let self_pkg = class_name.rfind('/').map(|i| &class_name[..i]).unwrap_or("");
5361 let mut same_pkg_simples: jdc_core::FxHashSet<String> = jdc_core::FxHashSet::default();
5362 visit_all_exprs(body, &mut |x| {
5363 let cls = match x {
5364 Expr::Field { cls, is_static: true, .. } => Some(cls.as_ref()),
5365 Expr::Method { cls, is_static: true, .. } => Some(cls.as_ref()),
5366 _ => None,
5367 };
5368 if let Some(c) = cls {
5369 let pkg = c.rfind('/').map(|i| &c[..i]).unwrap_or("");
5370 if pkg == self_pkg {
5371 let simple = &c[c.rfind('/').map(|i| i + 1).unwrap_or(0)..];
5372 let head = simple.split('$').next().unwrap_or(simple);
5373 if !head.is_empty() {
5374 same_pkg_simples.insert(head.to_string());
5375 }
5376 }
5377 }
5378 });
5379 let hit = |name: &str| {
5380 segs.contains(name) || obscured.contains(name) || same_pkg_simples.contains(name)
5381 };
5382 if (segs.is_empty() && obscured.is_empty() && same_pkg_simples.is_empty())
5383 || !vt.vars.iter().any(|v| hit(&v.name))
5384 {
5385 return;
5386 }
5387 let mut taken: jdc_core::FxHashSet<String> =
5388 vt.vars.iter().map(|v| v.name.clone()).collect();
5389 for v in vt.vars.iter_mut() {
5390 if !hit(&v.name) {
5391 continue;
5392 }
5393 let mut k = 0u32;
5394 let cand = loop {
5395 k += 1;
5396 let c = if k == 1 {
5397 format!("{}x", v.name)
5398 } else {
5399 format!("{}x{}", v.name, k)
5400 };
5401 if !taken.contains(&c) {
5402 break c;
5403 }
5404 };
5405 taken.insert(cand.clone());
5406 v.name = cand;
5407 }
5408}
5409
5410pub fn fix_ctor_this_aliases(body: &mut Stmt, vt: &VarTable) {
5436 let Some(this_var) = vt
5437 .vars
5438 .iter()
5439 .find(|v| v.is_param && v.name == "this")
5440 .map(|v| v.id)
5441 else {
5442 return;
5443 };
5444 let mut analysis = VarAnalysis {
5445 assigns: Vec::new(),
5446 reads: Vec::new(),
5447 values: Vec::new(),
5448 };
5449 analyze_vars(body, &mut analysis);
5450 enum Src {
5461 This,
5462 Loc(u32),
5463 Other,
5464 }
5465 let mut sources: jdc_core::FxHashMap<u32, Vec<Src>> = jdc_core::FxHashMap::default();
5466 walk_all(body, &mut |st| {
5467 let (tgt, val): (u32, &Expr) = match st {
5468 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
5469 Expr::Local { var, .. } => (*var, &**value),
5470 _ => return,
5471 },
5472 Stmt::LocalDef { var, init: Some(value), .. } => (*var, value),
5473 _ => return,
5474 };
5475 let s = match val {
5476 Expr::This => Src::This,
5477 Expr::Local { var: src, .. } if *src == this_var => Src::This,
5478 Expr::Local { var: src, .. } => Src::Loc(*src),
5479 _ => Src::Other,
5480 };
5481 sources.entry(tgt).or_default().push(s);
5482 });
5483 let n = vt
5484 .vars
5485 .len()
5486 .max(analysis.assigns.len())
5487 .max(sources.keys().map(|k| *k as usize + 1).max().unwrap_or(0));
5488 let mut alias = vec![false; n];
5489 loop {
5490 let mut changed = false;
5491 for (v, srcs) in &sources {
5492 let vi = *v as usize;
5493 if vi >= n || alias[vi] || srcs.is_empty() {
5494 continue;
5495 }
5496 let ok = srcs.iter().all(|s| match s {
5497 Src::This => true,
5498 Src::Loc(w) => (*w as usize) < n && alias[*w as usize],
5499 Src::Other => false,
5500 });
5501 if ok {
5502 alias[vi] = true;
5503 changed = true;
5504 }
5505 }
5506 if !changed {
5507 break;
5508 }
5509 }
5510 if !alias.iter().any(|&b| b) {
5511 return;
5512 }
5513 walk_stmt_exprs(body, &mut |e| {
5514 deep_rewrite_reads(e, &mut |x| {
5515 if let Expr::Local { var, .. } = x {
5516 if (*var as usize) < n && alias[*var as usize] {
5517 *x = Expr::This;
5518 }
5519 }
5520 });
5521 });
5522}
5523
5524pub fn fix_this0_owners(body: &mut Stmt, class_name: &str) {
5525 walk_stmt_exprs(body, &mut |e| {
5526 deep_rewrite(e, &mut |x| {
5527 if let Expr::Field { owner: Some(_), cls, name, .. } = x {
5528 if name.as_ref() == "this$0" && cls.as_ref() == class_name {
5529 if let Expr::Field { owner, .. } = x {
5530 *owner = None;
5531 }
5532 }
5533 }
5534 });
5535 });
5536}
5537
5538pub fn rescue_primitive_receivers(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
5539 fn prim(t: &JavaType) -> bool {
5540 match t {
5541 JavaType::Int
5542 | JavaType::Long
5543 | JavaType::Short
5544 | JavaType::Byte
5545 | JavaType::Char
5546 | JavaType::Float
5547 | JavaType::Double
5548 | JavaType::Boolean => true,
5549 JavaType::Object(n) => matches!(
5553 n.as_ref(),
5554 "java/lang/Boolean"
5555 | "java/lang/String"
5556 | "java/lang/Integer"
5557 | "java/lang/Long"
5558 | "java/lang/Short"
5559 | "java/lang/Byte"
5560 | "java/lang/Character"
5561 | "java/lang/Float"
5562 | "java/lang/Double"
5563 ),
5564 _ => false,
5565 }
5566 }
5567 walk_stmt_exprs(body, &mut |e| {
5568 deep_rewrite(e, &mut |x| {
5569 let (recv_var, recv_ty) = {
5571 let o: &Expr = match &*x {
5572 Expr::Method { owner: Some(o), .. } => o,
5573 Expr::Field { owner: Some(o), is_static: false, .. } => o,
5574 _ => return,
5575 };
5576 let Expr::Local { var, ty } = o else {
5577 return;
5578 };
5579 let vt_ty = if (*var as usize) < vt.vars.len() {
5580 vt.var(*var).ty.erased()
5581 } else {
5582 ty.erased()
5583 };
5584 if !prim(&vt_ty) {
5585 return;
5586 }
5587 (*var, vt_ty)
5588 };
5589 let cls: std::sync::Arc<str> = match &*x {
5590 Expr::Method { cls, .. } | Expr::Field { cls, .. } => cls.clone(),
5591 _ => return,
5592 };
5593 if let JavaType::Object(n) = &recv_ty {
5602 if n.as_ref() == cls.as_ref() || pool.is_subtype(n.as_ref(), cls.as_ref()) {
5603 return;
5604 }
5605 }
5606 let mut cands: Vec<u32> = Vec::new();
5608 for v in &vt.vars {
5609 if let JavaType::Object(n) = v.ty.erased() {
5610 if n.as_ref() == cls.as_ref()
5611 || pool.is_subtype(n.as_ref(), cls.as_ref())
5612 {
5613 cands.push(v.id);
5614 }
5615 }
5616 }
5617 let id = if cands.len() == 1 {
5618 cands[0]
5619 } else if cands.len() > 1 {
5620 let slot = if (recv_var as usize) < vt.vars.len() {
5625 Some(vt.var(recv_var).slot)
5626 } else {
5627 None
5628 };
5629 let mut hits: Vec<u32> = match slot {
5630 Some(sl) => cands
5631 .iter()
5632 .copied()
5633 .filter(|&c| {
5634 c != recv_var
5635 && (c as usize) < vt.vars.len()
5636 && vt.var(c).slot == sl
5637 })
5638 .collect(),
5639 None => Vec::new(),
5640 };
5641 if hits.is_empty() {
5642 hits = cands
5645 .iter()
5646 .copied()
5647 .filter(|&c| c != recv_var)
5648 .collect();
5649 }
5650 hits.sort_unstable();
5651 hits.dedup();
5652 if hits.is_empty() {
5653 return;
5654 }
5655 *hits.last().unwrap()
5656 } else {
5657 if cands.is_empty() {
5664 return;
5665 }
5666 let slot = if (recv_var as usize) < vt.vars.len() {
5667 vt.var(recv_var).slot
5668 } else {
5669 return;
5670 };
5671 let mut hits: Vec<u32> = cands
5672 .into_iter()
5673 .filter(|&c| {
5674 c != recv_var
5675 && (c as usize) < vt.vars.len()
5676 && vt.var(c).slot == slot
5677 })
5678 .collect();
5679 hits.sort_unstable();
5680 hits.dedup();
5681 if hits.len() != 1 {
5682 return;
5683 }
5684 hits[0]
5685 };
5686 let o: &mut Box<Expr> = match x {
5688 Expr::Method { owner: Some(o), .. } => o,
5689 Expr::Field { owner: Some(o), is_static: false, .. } => o,
5690 _ => return,
5691 };
5692 if let Expr::Local { var, ty } = &mut **o {
5693 if *var == recv_var {
5694 *var = id;
5695 *ty = vt.vars[id as usize].ty.clone();
5696 }
5697 }
5698 });
5699 });
5700
5701}
5702
5703pub fn rescue_arg_return_swaps(
5709 body: &mut Stmt,
5710 vt: &VarTable,
5711 pool: &crate::DexPool,
5712 ret_ty: &JavaType,
5713) {
5714 let mut top_ids: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
5722 for v in &vt.vars {
5723 if v.is_param {
5724 top_ids.insert(v.id);
5725 }
5726 }
5727 if let Stmt::Block(stmts) = body {
5728 for st in stmts.iter() {
5729 if let Stmt::LocalDef { var, .. } = st {
5730 top_ids.insert(*var);
5731 }
5732 }
5733 }
5734 let ret_wants_ref = match ret_ty {
5735 JavaType::Object(n) => n.as_ref() != "java/lang/Object",
5736 JavaType::Array(_) => true,
5737 _ => false,
5738 };
5739 let try_swap = |a: &mut Expr, want: &JavaType| {
5740 let Expr::Local { var, ty } = &mut *a else {
5741 return;
5742 };
5743 if (*var as usize) >= vt.vars.len() {
5744 return;
5745 }
5746 let avt = vt.var(*var).ty.erased();
5747 let avt_ref = matches!(avt, JavaType::Object(_) | JavaType::Array(_));
5748 let want_arr = matches!(want, JavaType::Array(_));
5749 if avt_ref && !(avt == JavaType::Object("java/lang/Object".into())) {
5752 return;
5753 }
5754 if avt_ref && !matches!(want, JavaType::Object(_)) {
5755 return;
5756 }
5757 let slot = vt.var(*var).slot;
5758 let mut hits: Vec<u32> = Vec::new();
5759 for v in &vt.vars {
5760 if v.slot != slot || !top_ids.contains(&v.id) || v.id == *var {
5761 continue;
5762 }
5763 let t = v.ty.erased();
5764 let ok = match (&t, want) {
5765 (JavaType::Object(n), JavaType::Object(w)) => {
5766 n.as_ref() == w.as_ref()
5767 || pool.is_subtype(n.as_ref(), w.as_ref())
5768 }
5769 (JavaType::Array(_), JavaType::Array(_)) => want_arr,
5770 _ => false,
5771 };
5772 if ok {
5773 hits.push(v.id);
5774 }
5775 }
5776 hits.sort_unstable();
5777 hits.dedup();
5778 if hits.is_empty() {
5779 return;
5780 }
5781 let id = *hits.last().unwrap();
5787 *var = id;
5788 *ty = vt.vars[id as usize].ty.clone();
5789 };
5790 walk_stmt_exprs(body, &mut |e| {
5791 deep_rewrite(e, &mut |x| {
5792 match x {
5793 Expr::Method { desc, args, is_dynamic: false, .. } => {
5794 for (i, a) in args.iter_mut().enumerate() {
5795 if let Some(f) = desc.args.get(i) {
5796 if matches!(f, JavaType::Object(n) if n.as_ref() != "java/lang/Object")
5797 || matches!(f, JavaType::Array(_))
5798 {
5799 try_swap(a, f);
5800 }
5801 }
5802 }
5803 }
5804 Expr::New { arg_tys, args, .. } => {
5805 for (i, a) in args.iter_mut().enumerate() {
5806 if let Some(f) = arg_tys.get(i) {
5807 if matches!(f, JavaType::Object(n) if n.as_ref() != "java/lang/Object")
5808 || matches!(f, JavaType::Array(_))
5809 {
5810 try_swap(a, f);
5811 }
5812 }
5813 }
5814 }
5815 _ => {}
5816 }
5817 });
5818 });
5819 if ret_wants_ref {
5820 walk_mut_deep(body, &mut |st| {
5821 if let Stmt::Return(Some(e)) = st {
5822 try_swap(e, ret_ty);
5823 }
5824 });
5825 }
5826}
5827
5828pub fn fix_ref_null_assigns(vt: &VarTable, body: &mut Stmt) {
5843 let is_ref_var = |var: u32| -> bool {
5844 (var as usize) < vt.vars.len()
5845 && matches!(
5846 vt.vars[var as usize].ty.erased(),
5847 JavaType::Object(_) | JavaType::Array(_)
5848 )
5849 };
5850 walk_mut_deep(body, &mut |st| match st {
5851 Stmt::ExprStmt(Expr::Assign {
5852 target,
5853 value,
5854 op: AssignOp::Plain,
5855 ..
5856 }) => {
5857 if let Expr::Local { var, .. } = &**target {
5858 if is_ref_var(*var) && matches!(&**value, Expr::Const(ConstVal::Int(0))) {
5859 **value = Expr::Const(ConstVal::Null);
5860 }
5861 }
5862 }
5863 Stmt::LocalDef {
5864 var,
5865 init: Some(e),
5866 ..
5867 } => {
5868 if is_ref_var(*var) && matches!(e, Expr::Const(ConstVal::Int(0))) {
5869 *e = Expr::Const(ConstVal::Null);
5870 }
5871 }
5872 _ => {}
5873 });
5874}
5875
5876pub fn fix_ref_array_null_consts(body: &mut Stmt) {
5877 walk_stmt_exprs(body, &mut |e| {
5878 deep_rewrite(e, &mut |x| {
5879 if let Expr::NewArray { elem, init: Some(list), .. } = x {
5880 if matches!(elem.erased(), JavaType::Object(_) | JavaType::Array(_)) {
5881 for slot in list.iter_mut() {
5882 if matches!(&*slot, Expr::Const(ConstVal::Int(0))) {
5883 *slot = Expr::Const(ConstVal::Null);
5884 }
5885 }
5886 }
5887 }
5888 });
5889 });
5890}
5891
5892pub fn fix_null_sentinels(body: &mut Stmt, vt: &VarTable, ret: &JavaType) {
5931 #[derive(Clone, Copy, PartialEq)]
5932 enum WK {
5933 Zero,
5934 Copy(u32),
5935 Other,
5936 }
5937 fn is_ref_jt(t: &JavaType) -> bool {
5938 matches!(t, JavaType::Object(_) | JavaType::Array(_))
5939 }
5940 fn cls_write(e: &Expr) -> WK {
5941 match e {
5942 Expr::Const(ConstVal::Int(0)) => WK::Zero,
5943 Expr::Local { var, .. } => WK::Copy(*var),
5944 _ => WK::Other,
5945 }
5946 }
5947 fn expr_writes(e: &Expr, writes: &mut jdc_core::FxHashMap<u32, Vec<WK>>) {
5948 visit_exprs(e, &mut |x| match x {
5949 Expr::Assign { target, op, value } => {
5950 if let Expr::Local { var, .. } = &**target {
5951 let k = if matches!(op, AssignOp::Plain) {
5952 cls_write(value)
5953 } else {
5954 WK::Other
5955 };
5956 writes.entry(*var).or_default().push(k);
5957 }
5958 }
5959 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
5960 if let Expr::Local { var, .. } = &**inner {
5961 writes.entry(*var).or_default().push(WK::Other);
5962 }
5963 }
5964 _ => {}
5965 });
5966 }
5967 fn collect_writes(
5968 s: &Stmt,
5969 writes: &mut jdc_core::FxHashMap<u32, Vec<WK>>,
5970 has_zero: &mut bool,
5971 ) {
5972 fn note(e: &Expr, has_zero: &mut bool, writes: &mut jdc_core::FxHashMap<u32, Vec<WK>>) {
5973 visit_exprs(e, &mut |x| {
5974 if let Expr::Const(ConstVal::Int(0)) = x {
5975 *has_zero = true;
5976 }
5977 });
5978 expr_writes(e, writes);
5979 }
5980 match s {
5981 Stmt::Block(v) => {
5982 for x in v {
5983 collect_writes(x, writes, has_zero);
5984 }
5985 }
5986 Stmt::LocalDef { var, init, .. } => {
5987 if let Some(e) = init {
5988 if matches!(e, Expr::Const(ConstVal::Int(0))) {
5989 *has_zero = true;
5990 }
5991 writes.entry(*var).or_default().push(cls_write(e));
5992 }
5993 }
5994 Stmt::ExprStmt(e) => note(e, has_zero, writes),
5995 Stmt::Return(e) => {
5996 if let Some(x) = e {
5997 note(x, has_zero, writes);
5998 }
5999 }
6000 Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
6001 note(e, has_zero, writes);
6002 }
6003 Stmt::TernaryValue { e } => note(e, has_zero, writes),
6004 Stmt::If { cond, then_stmt, else_stmt } => {
6005 note(cond, has_zero, writes);
6006 collect_writes(then_stmt, writes, has_zero);
6007 if let Some(b) = else_stmt {
6008 collect_writes(b, writes, has_zero);
6009 }
6010 }
6011 Stmt::While { cond, body } | Stmt::DoWhile { body, cond } => {
6012 note(cond, has_zero, writes);
6013 collect_writes(body, writes, has_zero);
6014 }
6015 Stmt::For { init, cond, update, body } => {
6016 for x in init {
6017 collect_writes(x, writes, has_zero);
6018 }
6019 if let Some(c) = cond {
6020 note(c, has_zero, writes);
6021 }
6022 for u in update {
6023 note(u, has_zero, writes);
6024 }
6025 collect_writes(body, writes, has_zero);
6026 }
6027 Stmt::ForEach { var, iterable, body, .. } => {
6028 writes.entry(*var).or_default().push(WK::Other);
6029 note(iterable, has_zero, writes);
6030 collect_writes(body, writes, has_zero);
6031 }
6032 Stmt::Switch { selector, cases, default, .. } => {
6033 note(selector, has_zero, writes);
6034 for c in cases {
6035 if let Some(g) = &c.guard {
6036 note(g, has_zero, writes);
6037 }
6038 for x in &c.body {
6039 collect_writes(x, writes, has_zero);
6040 }
6041 }
6042 if let Some(d) = default {
6043 collect_writes(d, writes, has_zero);
6044 }
6045 }
6046 Stmt::Try { body, catches, finally } => {
6047 collect_writes(body, writes, has_zero);
6048 for c in catches {
6049 writes.entry(c.var).or_default().push(WK::Other);
6050 collect_writes(&c.body, writes, has_zero);
6051 }
6052 if let Some(f) = finally {
6053 collect_writes(f, writes, has_zero);
6054 }
6055 }
6056 Stmt::TryWithResources { resources, body, catches, finally, .. } => {
6057 for r in resources {
6058 collect_writes(r, writes, has_zero);
6059 }
6060 collect_writes(body, writes, has_zero);
6061 for c in catches {
6062 writes.entry(c.var).or_default().push(WK::Other);
6063 collect_writes(&c.body, writes, has_zero);
6064 }
6065 if let Some(f) = finally {
6066 collect_writes(f, writes, has_zero);
6067 }
6068 }
6069 Stmt::Assert { cond, msg } => {
6070 note(cond, has_zero, writes);
6071 if let Some(m) = msg {
6072 note(m, has_zero, writes);
6073 }
6074 }
6075 Stmt::Synchronized { lock, body } => {
6076 note(lock, has_zero, writes);
6077 collect_writes(body, writes, has_zero);
6078 }
6079 Stmt::Labeled { body, .. } => collect_writes(body, writes, has_zero),
6080 _ => {}
6081 }
6082 }
6083
6084 let mut writes: jdc_core::FxHashMap<u32, Vec<WK>> =
6085 jdc_core::FxHashMap::default();
6086 let mut has_zero = false;
6087 collect_writes(body, &mut writes, &mut has_zero);
6088
6089 let mut eligible: HashSet<u32> = HashSet::default();
6091 let int_typed = |v: u32| {
6092 (v as usize) < vt.vars.len() && vt.vars[v as usize].ty.erased() == JavaType::Int
6093 };
6094 loop {
6095 let mut changed = false;
6096 for (&v, ws) in writes.iter() {
6097 if eligible.contains(&v) || ws.is_empty() || !int_typed(v) {
6098 continue;
6099 }
6100 if ws.iter().all(|k| match k {
6101 WK::Zero => true,
6102 WK::Copy(u) => *u != v && eligible.contains(u),
6103 WK::Other => false,
6104 }) {
6105 eligible.insert(v);
6106 changed = true;
6107 }
6108 }
6109 if !changed {
6110 break;
6111 }
6112 }
6113 if eligible.is_empty() && !has_zero {
6114 return; }
6116
6117 #[derive(Clone, Copy, PartialEq)]
6118 enum Mode {
6119 Scan,
6120 Rewrite,
6121 }
6122 struct St<'a> {
6123 mode: Mode,
6124 vt: &'a VarTable,
6125 eligible: &'a HashSet<u32>,
6126 ref_read: HashSet<u32>,
6127 dead: HashSet<u32>,
6128 sentinels: HashSet<u32>,
6129 n_lit: usize,
6130 n_var: usize,
6131 }
6132 fn arg_ctx(t: &JavaType) -> Option<JavaType> {
6136 match t {
6137 JavaType::Object(n) if n.as_ref() == "java/lang/Object" => None,
6138 t if is_ref_jt(t) => Some(t.clone()),
6139 _ => None,
6140 }
6141 }
6142 fn walk_expr(e: &mut Expr, ctx: Option<JavaType>, st: &mut St) {
6143 walk_expr_c(e, ctx, false, st);
6144 }
6145 fn walk_expr_c(e: &mut Expr, ctx: Option<JavaType>, cast_arg: bool, st: &mut St) {
6151 let slot_ref = ctx.as_ref().is_some_and(is_ref_jt);
6152 fn mk_null(ctx: &Option<JavaType>, cast_arg: bool) -> Expr {
6153 match (cast_arg, ctx) {
6154 (true, Some(t)) => Expr::Cast {
6155 ty: TypeRef::J(t.clone()),
6156 e: Box::new(Expr::Const(ConstVal::Null)),
6157 },
6158 _ => Expr::Const(ConstVal::Null),
6159 }
6160 }
6161 match e {
6162 Expr::Const(ConstVal::Int(0)) if slot_ref => {
6163 if st.mode == Mode::Rewrite {
6164 *e = mk_null(&ctx, cast_arg);
6165 st.n_lit += 1;
6166 }
6167 return;
6168 }
6169 Expr::Local { var, .. } => {
6170 if st.eligible.contains(var) {
6171 if slot_ref {
6172 st.ref_read.insert(*var);
6173 } else {
6174 st.dead.insert(*var);
6175 }
6176 }
6177 if st.mode == Mode::Rewrite && slot_ref && st.sentinels.contains(var) {
6178 *e = mk_null(&ctx, cast_arg);
6179 st.n_var += 1;
6180 }
6181 return;
6182 }
6183 _ => {}
6184 }
6185 match e {
6186 Expr::Method { owner, desc, args, .. } => {
6187 if let Some(o) = owner {
6188 walk_expr(o, None, st);
6189 }
6190 for (i, a) in args.iter_mut().enumerate() {
6191 let c = desc.args.get(i).and_then(arg_ctx);
6192 walk_expr_c(a, c, true, st);
6193 }
6194 }
6195 Expr::Invokedynamic { desc, args, .. } => {
6196 for (i, a) in args.iter_mut().enumerate() {
6197 let c = desc.args.get(i).and_then(arg_ctx);
6198 walk_expr_c(a, c, true, st);
6199 }
6200 }
6201 Expr::New { args, arg_tys, .. } => {
6202 for (i, a) in args.iter_mut().enumerate() {
6203 let c = arg_tys.get(i).and_then(arg_ctx);
6204 walk_expr_c(a, c, true, st);
6205 }
6206 }
6207 Expr::AnonNew { args, .. } => {
6208 for a in args.iter_mut() {
6209 walk_expr(a, None, st);
6210 }
6211 }
6212 Expr::Assign { target, value, .. } => {
6213 if let Expr::Local { var: u, .. } = &**target {
6217 if st.eligible.contains(u) {
6218 return;
6219 }
6220 }
6221 let vctx = match &**target {
6222 Expr::Field { ty, .. } => {
6223 let t = ty.erased();
6224 is_ref_jt(&t).then_some(t)
6225 }
6226 Expr::ArrayIndex { array, .. } => match array.type_ref().erased() {
6227 JavaType::Array(inner) if is_ref_jt(&inner) => Some(*inner),
6228 _ => None,
6229 },
6230 _ => None,
6231 };
6232 walk_expr(target, None, st);
6233 walk_expr(value, vctx, st);
6234 }
6235 Expr::Cast { ty, e: inner } => {
6236 let t = ty.erased();
6237 let c = is_ref_jt(&t).then_some(t);
6238 walk_expr(inner, c, st);
6239 }
6240 Expr::InstanceOf { e: inner, .. } => {
6241 walk_expr(inner, Some(JavaType::Object("java/lang/Object".into())), st);
6242 }
6243 Expr::Cond { c, t, f } => {
6244 walk_expr(c, None, st);
6245 let tc = ctx.clone();
6246 walk_expr(t, tc, st);
6247 walk_expr(f, ctx, st);
6248 }
6249 Expr::NewArray { elem, dims, init, .. } => {
6250 for d in dims.iter_mut() {
6251 walk_expr(d, None, st);
6252 }
6253 if let Some(list) = init {
6254 let et = elem.erased();
6255 let c = is_ref_jt(&et).then_some(et);
6256 for slot in list.iter_mut() {
6257 walk_expr(slot, c.clone(), st);
6258 }
6259 }
6260 }
6261 Expr::NewMultiArray { dims, .. } => {
6262 for d in dims.iter_mut() {
6263 walk_expr(d, None, st);
6264 }
6265 }
6266 Expr::ArrayIndex { array, index } => {
6267 walk_expr(array, None, st);
6268 walk_expr(index, None, st);
6269 }
6270 Expr::Field { owner, .. } => {
6271 if let Some(o) = owner {
6272 walk_expr(o, None, st);
6273 }
6274 }
6275 Expr::Bin { l, r, .. } => {
6276 walk_expr(l, None, st);
6277 walk_expr(r, None, st);
6278 }
6279 Expr::Un { e: inner, .. } => walk_expr(inner, None, st),
6280 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
6281 walk_expr(inner, None, st);
6282 }
6283 Expr::StringConcat(parts) => {
6284 for p in parts.iter_mut() {
6285 if let ConcatPart::Str(x) = p {
6286 walk_expr(x, None, st);
6287 }
6288 }
6289 }
6290 Expr::Lambda(l) => {
6291 for c in l.captures.iter_mut() {
6292 walk_expr(c, None, st);
6293 }
6294 }
6295 _ => {}
6296 }
6297 }
6298 fn walk_stmt(s: &mut Stmt, st: &mut St, ret: &JavaType) {
6299 match s {
6300 Stmt::Block(v) => {
6301 for x in v.iter_mut() {
6302 walk_stmt(x, st, ret);
6303 }
6304 }
6305 Stmt::ExprStmt(e) => walk_expr(e, None, st),
6306 Stmt::LocalDef { var, init, .. } => {
6307 if let Some(e) = init {
6308 let ctx = if (*var as usize) < st.vt.vars.len() {
6309 let t = st.vt.vars[*var as usize].ty.erased();
6310 is_ref_jt(&t).then_some(t)
6311 } else {
6312 None
6313 };
6314 walk_expr(e, ctx, st);
6315 }
6316 }
6317 Stmt::Return(e) => {
6318 if let Some(x) = e {
6319 let c = is_ref_jt(ret).then(|| ret.clone());
6320 walk_expr(x, c, st);
6321 }
6322 }
6323 Stmt::Throw(e) => {
6324 walk_expr(e, Some(JavaType::Object("java/lang/Throwable".into())), st);
6325 }
6326 Stmt::If { cond, then_stmt, else_stmt } => {
6327 walk_expr(cond, None, st);
6328 walk_stmt(then_stmt, st, ret);
6329 if let Some(b) = else_stmt {
6330 walk_stmt(b, st, ret);
6331 }
6332 }
6333 Stmt::While { cond, body } => {
6334 walk_expr(cond, None, st);
6335 walk_stmt(body, st, ret);
6336 }
6337 Stmt::DoWhile { body, cond } => {
6338 walk_stmt(body, st, ret);
6339 walk_expr(cond, None, st);
6340 }
6341 Stmt::For { init, cond, update, body } => {
6342 for x in init.iter_mut() {
6343 walk_stmt(x, st, ret);
6344 }
6345 if let Some(c) = cond {
6346 walk_expr(c, None, st);
6347 }
6348 for u in update.iter_mut() {
6349 walk_expr(u, None, st);
6350 }
6351 walk_stmt(body, st, ret);
6352 }
6353 Stmt::ForEach { iterable, body, .. } => {
6354 walk_expr(iterable, None, st);
6355 walk_stmt(body, st, ret);
6356 }
6357 Stmt::Switch { selector, cases, default, .. } => {
6358 walk_expr(selector, None, st);
6359 for c in cases.iter_mut() {
6360 if let Some(g) = &mut c.guard {
6361 walk_expr(g, None, st);
6362 }
6363 for x in c.body.iter_mut() {
6364 walk_stmt(x, st, ret);
6365 }
6366 }
6367 if let Some(d) = default {
6368 walk_stmt(d, st, ret);
6369 }
6370 }
6371 Stmt::Try { body, catches, finally } => {
6372 walk_stmt(body, st, ret);
6373 for c in catches.iter_mut() {
6374 walk_stmt(&mut c.body, st, ret);
6375 }
6376 if let Some(f) = finally {
6377 walk_stmt(f, st, ret);
6378 }
6379 }
6380 Stmt::TryWithResources { resources, body, catches, finally, .. } => {
6381 for r in resources.iter_mut() {
6382 walk_stmt(r, st, ret);
6383 }
6384 walk_stmt(body, st, ret);
6385 for c in catches.iter_mut() {
6386 walk_stmt(&mut c.body, st, ret);
6387 }
6388 if let Some(f) = finally {
6389 walk_stmt(f, st, ret);
6390 }
6391 }
6392 Stmt::Assert { cond, msg } => {
6393 walk_expr(cond, None, st);
6394 if let Some(m) = msg {
6395 walk_expr(m, None, st);
6396 }
6397 }
6398 Stmt::Synchronized { lock, body } => {
6399 walk_expr(lock, Some(JavaType::Object("java/lang/Object".into())), st);
6400 walk_stmt(body, st, ret);
6401 }
6402 Stmt::TernaryValue { e } => walk_expr(e, None, st),
6403 Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
6404 walk_expr(e, Some(JavaType::Object("java/lang/Object".into())), st);
6405 }
6406 Stmt::Labeled { body, .. } => walk_stmt(body, st, ret),
6407 _ => {}
6408 }
6409 }
6410
6411 let mut st = St {
6412 mode: Mode::Scan,
6413 vt,
6414 eligible: &eligible,
6415 ref_read: HashSet::default(),
6416 dead: HashSet::default(),
6417 sentinels: HashSet::default(),
6418 n_lit: 0,
6419 n_var: 0,
6420 };
6421 if !eligible.is_empty() {
6422 walk_stmt(body, &mut st, ret);
6423 st.sentinels = eligible
6424 .iter()
6425 .copied()
6426 .filter(|v| st.ref_read.contains(v) && !st.dead.contains(v))
6427 .collect();
6428 }
6429 if st.sentinels.is_empty() && !has_zero {
6430 return;
6431 }
6432 st.mode = Mode::Rewrite;
6433 walk_stmt(body, &mut st, ret);
6434 if std::env::var_os("DDC_SENT").is_some() {
6435 eprintln!(
6436 "[sent] elig={} confirmed={} lit={} var={}",
6437 eligible.len(),
6438 st.sentinels.len(),
6439 st.n_lit,
6440 st.n_var
6441 );
6442 }
6443}
6444
6445pub fn fix_int_operand_bridges(body: &mut Stmt, vt: &VarTable) {
6453 fn side_bool(e: &Expr, vt: &VarTable) -> bool {
6454 match e {
6455 Expr::Local { var, .. } => {
6456 matches!(vt.var(*var).ty.erased(), JavaType::Boolean)
6457 }
6458 Expr::Bin {
6468 op: BinOp::And | BinOp::Or | BinOp::Xor,
6469 l,
6470 r,
6471 ..
6472 } => side_bool(l, vt) && side_bool(r, vt),
6473 other => matches!(other.type_ref().erased(), JavaType::Boolean),
6474 }
6475 }
6476 fn side_int(e: &Expr, vt: &VarTable) -> bool {
6477 if side_bool(e, vt) {
6481 return false;
6482 }
6483 matches!(
6484 match e {
6485 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
6486 other => other.type_ref().erased(),
6487 },
6488 JavaType::Int | JavaType::Short | JavaType::Byte | JavaType::Char | JavaType::Long
6489 )
6490 }
6491 fn wrap(e: Box<Expr>) -> Box<Expr> {
6492 Box::new(Expr::Cond {
6493 c: e,
6494 t: Box::new(Expr::Const(ConstVal::Int(1))),
6495 f: Box::new(Expr::Const(ConstVal::Int(0))),
6496 })
6497 }
6498 walk_stmt_exprs(body, &mut |e| {
6499 deep_rewrite(e, &mut |x| {
6500 if let Expr::Assign { target, value, op: AssignOp::Plain } = x {
6507 let tgt_ty: Option<JavaType> = match &**target {
6508 Expr::Field { ty, .. } => Some(ty.erased()),
6509 Expr::ArrayIndex { array, .. } => {
6510 match array.type_ref().erased() {
6511 JavaType::Array(el) => Some(el.as_ref().clone()),
6512 _ => None,
6513 }
6514 }
6515 Expr::Local { var, .. } => {
6520 if (*var as usize) < vt.vars.len() {
6521 Some(vt.var(*var).ty.erased())
6522 } else {
6523 None
6524 }
6525 }
6526 _ => None,
6527 };
6528 if let Some(t) = tgt_ty {
6529 let tgt_int = matches!(
6530 t,
6531 JavaType::Int
6532 | JavaType::Short
6533 | JavaType::Byte
6534 | JavaType::Char
6535 | JavaType::Long
6536 );
6537 if tgt_int && side_bool(value, vt) && !side_int(value, vt) {
6538 let taken = std::mem::replace(
6539 value,
6540 Box::new(Expr::Const(ConstVal::Null)),
6541 );
6542 let mut wrapped = wrap(taken);
6543 if !matches!(t, JavaType::Int | JavaType::Long) {
6544 wrapped = Box::new(Expr::Cast {
6545 ty: TypeRef::J(t.clone()),
6546 e: wrapped,
6547 });
6548 }
6549 *value = wrapped;
6550 } else if matches!(t, JavaType::Boolean)
6551 && side_int(value, vt)
6552 && !side_bool(value, vt)
6553 {
6554 let taken = std::mem::replace(
6555 value,
6556 Box::new(Expr::Const(ConstVal::Null)),
6557 );
6558 **value = Expr::Bin {
6559 op: BinOp::Ne,
6560 l: taken,
6561 r: Box::new(Expr::Const(ConstVal::Int(0))),
6562 ty: Some(TypeRef::J(JavaType::Boolean)),
6563 };
6564 }
6565 }
6566 }
6567 if let Expr::Cast { ty, e } = x {
6573 let t = ty.erased();
6574 let t_num = matches!(
6575 t,
6576 JavaType::Int
6577 | JavaType::Short
6578 | JavaType::Byte
6579 | JavaType::Char
6580 | JavaType::Long
6581 | JavaType::Float
6582 | JavaType::Double
6583 );
6584 if t_num && side_bool(e, vt) && !side_int(e, vt) {
6585 let taken = std::mem::replace(e, Box::new(Expr::Const(ConstVal::Null)));
6586 **e = Expr::Cond {
6587 c: taken,
6588 t: Box::new(Expr::Const(ConstVal::Int(1))),
6589 f: Box::new(Expr::Const(ConstVal::Int(0))),
6590 };
6591 } else if matches!(t, JavaType::Boolean)
6592 && side_int(e, vt)
6593 && !side_bool(e, vt)
6594 {
6595 let taken = std::mem::replace(e, Box::new(Expr::Const(ConstVal::Null)));
6596 *x = Expr::Bin {
6597 op: BinOp::Ne,
6598 l: taken,
6599 r: Box::new(Expr::Const(ConstVal::Int(0))),
6600 ty: Some(TypeRef::J(JavaType::Boolean)),
6601 };
6602 }
6603 }
6604 if let Expr::Bin { op, l, r, .. } = x {
6613 if matches!(op, BinOp::Eq | BinOp::Ne) {
6614 if matches!(&**r, Expr::Const(ConstVal::Null))
6615 && side_int(l, vt)
6616 && !side_bool(l, vt)
6617 {
6618 **r = Expr::Const(ConstVal::Int(0));
6619 } else if matches!(&**l, Expr::Const(ConstVal::Null))
6620 && side_int(r, vt)
6621 && !side_bool(r, vt)
6622 {
6623 **l = Expr::Const(ConstVal::Int(0));
6624 } else if matches!(&**r, Expr::Const(ConstVal::Null))
6625 && side_bool(l, vt)
6626 {
6627 let taken =
6628 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
6629 *x = if matches!(op, BinOp::Eq) {
6630 Expr::Un { op: UnOp::Not, e: taken }
6631 } else {
6632 *taken
6633 };
6634 } else if matches!(&**l, Expr::Const(ConstVal::Null))
6635 && side_bool(r, vt)
6636 {
6637 let taken =
6638 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
6639 *x = if matches!(op, BinOp::Eq) {
6640 Expr::Un { op: UnOp::Not, e: taken }
6641 } else {
6642 *taken
6643 };
6644 }
6645 }
6646 }
6647 if let Expr::ArrayIndex { index, .. } = x {
6651 if side_bool(index, vt) && !side_int(index, vt) {
6652 let taken =
6653 std::mem::replace(index, Box::new(Expr::Const(ConstVal::Null)));
6654 *index = wrap(taken);
6655 }
6656 }
6657 if let Expr::NewArray { dims, .. } = x {
6663 for d in dims.iter_mut() {
6664 if side_bool(d, vt) && !side_int(d, vt) {
6665 let taken =
6666 std::mem::replace(d, Expr::Const(ConstVal::Null));
6667 *d = *wrap(Box::new(taken));
6668 }
6669 }
6670 }
6671 if let Expr::Bin { op, l, r, .. } = x {
6672 let int_kind = matches!(
6673 op,
6674 BinOp::Or
6675 | BinOp::And
6676 | BinOp::Xor
6677 | BinOp::Add
6678 | BinOp::Sub
6679 | BinOp::Mul
6680 | BinOp::Div
6681 | BinOp::Rem
6682 | BinOp::Shl
6683 | BinOp::Shr
6684 | BinOp::Ushr
6685 | BinOp::Eq
6686 | BinOp::Ne
6687 | BinOp::Lt
6688 | BinOp::Ge
6689 | BinOp::Gt
6690 | BinOp::Le
6691 );
6692 if !int_kind {
6693 return;
6694 }
6695 if matches!(
6708 op,
6709 BinOp::Or
6710 | BinOp::And
6711 | BinOp::Xor
6712 | BinOp::Add
6713 | BinOp::Sub
6714 | BinOp::Mul
6715 | BinOp::Div
6716 | BinOp::Rem
6717 | BinOp::Shl
6718 | BinOp::Shr
6719 | BinOp::Ushr
6720 ) {
6721 if matches!(&**r, Expr::Const(ConstVal::Null))
6722 && (side_bool(l, vt) || side_int(l, vt))
6723 {
6724 **r = Expr::Const(ConstVal::Int(0));
6725 } else if matches!(&**l, Expr::Const(ConstVal::Null))
6726 && (side_bool(r, vt) || side_int(r, vt))
6727 {
6728 **l = Expr::Const(ConstVal::Int(0));
6729 }
6730 }
6731 let arith = matches!(
6741 op,
6742 BinOp::Add
6743 | BinOp::Sub
6744 | BinOp::Mul
6745 | BinOp::Div
6746 | BinOp::Rem
6747 | BinOp::Shl
6748 | BinOp::Shr
6749 | BinOp::Ushr
6750 );
6751 let ordered = matches!(op, BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le);
6752 if arith || ordered {
6753 let side_str = |e: &Expr| {
6754 matches!(
6755 match e {
6756 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
6757 other => other.type_ref().erased(),
6758 },
6759 JavaType::Object(ref s) if s.as_ref() == "java/lang/String"
6760 )
6761 };
6762 let concat = arith && (side_str(l) || side_str(r));
6763 if !concat {
6764 if side_bool(l, vt) {
6765 let taken =
6766 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
6767 *l = wrap(taken);
6768 }
6769 if side_bool(r, vt) {
6770 let taken =
6771 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
6772 *r = wrap(taken);
6773 }
6774 }
6775 return;
6776 }
6777 if side_bool(l, vt) && side_int(r, vt) && !side_bool(r, vt) {
6780 let taken = std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
6781 *l = wrap(taken);
6782 } else if side_bool(r, vt) && side_int(l, vt) && !side_bool(l, vt) {
6783 let taken = std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
6784 *r = wrap(taken);
6785 }
6786 }
6787 });
6788 });
6789}
6790
6791pub fn fold_value_diamonds(body: &mut Stmt, vt: &VarTable) {
6829 let Stmt::Block(stmts) = body else { return };
6830 fold_diamonds_in(stmts, vt);
6831}
6832
6833fn diamond_types_ok(a: &Expr, b: &Expr) -> bool {
6834 fn num(t: &JavaType) -> bool {
6835 matches!(
6836 t,
6837 JavaType::Byte
6838 | JavaType::Short
6839 | JavaType::Int
6840 | JavaType::Char
6841 | JavaType::Long
6842 | JavaType::Float
6843 | JavaType::Double
6844 )
6845 }
6846 if let Some(other) = if matches!(a, Expr::Const(ConstVal::Null)) {
6854 Some(b)
6855 } else if matches!(b, Expr::Const(ConstVal::Null)) {
6856 Some(a)
6857 } else {
6858 None
6859 } {
6860 return !num(&other.type_ref().erased());
6861 }
6862 let ta = a.type_ref().erased();
6863 let tb = b.type_ref().erased();
6864 ta == tb || (num(&ta) && num(&tb))
6865}
6866
6867fn single_local_assign(st: &Stmt) -> Option<(u32, Expr, Expr)> {
6875 let mut inner = st;
6876 while let Stmt::Block(v) = inner {
6877 if v.len() != 1 {
6878 return None;
6879 }
6880 inner = &v[0];
6881 }
6882 match inner {
6883 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain }) => {
6884 match &**target {
6885 Expr::Local { var, .. } => Some((*var, *target.clone(), *value.clone())),
6886 _ => None,
6887 }
6888 }
6889 _ => None,
6890 }
6891}
6892
6893fn arm_assign_inlined(st: &Stmt) -> Option<(u32, Expr, Expr)> {
6902 if let Some(r) = single_local_assign(st) {
6903 return Some(r);
6904 }
6905 let mut inner = st;
6906 while let Stmt::Block(v) = inner {
6907 if v.len() == 1 {
6908 inner = &v[0];
6909 } else {
6910 break;
6911 }
6912 }
6913 let Stmt::Block(v) = inner else { return None };
6914 if v.len() != 2 {
6915 return None;
6916 }
6917 let Stmt::LocalDef {
6918 var: x,
6919 init: Some(init),
6920 ..
6921 } = &v[0]
6922 else {
6923 return None;
6924 };
6925 let (var, tgt, val) = single_local_assign(&v[1])?;
6926 let mut count = 0usize;
6927 let mut c = val.clone();
6928 deep_rewrite(&mut c, &mut |e| {
6929 if let Expr::Local { var: lv, .. } = e {
6930 if lv == x {
6931 count += 1;
6932 }
6933 }
6934 });
6935 if count != 1 {
6936 return None;
6937 }
6938 let mut out = val.clone();
6939 deep_rewrite(&mut out, &mut |e| {
6940 if let Expr::Local { var: lv, .. } = e {
6941 if lv == x {
6942 *e = init.clone();
6943 }
6944 }
6945 });
6946 Some((var, tgt, out))
6947}
6948
6949fn mk_ternary_assign(tgt: Expr, c: Expr, a: Expr, b: Expr) -> Stmt {
6950 Stmt::ExprStmt(Expr::Assign {
6951 target: Box::new(tgt),
6952 op: AssignOp::Plain,
6953 value: Box::new(Expr::Cond {
6954 c: Box::new(c),
6955 t: Box::new(a),
6956 f: Box::new(b),
6957 }),
6958 })
6959}
6960
6961fn same_render_var(v1: u32, v2: u32, vt: &VarTable) -> bool {
6966 let (a, b) = (vt.vars.get(v1 as usize), vt.vars.get(v2 as usize));
6967 match (a, b) {
6968 (Some(x), Some(y)) => x.name == y.name && x.ty.erased() == y.ty.erased(),
6969 _ => false,
6970 }
6971}
6972
6973fn canon_stmts_eq(a: &[Stmt], b: &[Stmt], vt: &VarTable) -> bool {
6980 if a.len() != b.len() {
6981 return false;
6982 }
6983 let mut map: std::collections::HashMap<String, u32> = std::collections::HashMap::default();
6984 let canon = |s: &Stmt, map: &mut std::collections::HashMap<String, u32>| -> Stmt {
6985 let mut c = s.clone();
6986 walk_stmt_exprs(&mut c, &mut |e| {
6987 deep_rewrite(e, &mut |x| {
6988 if let Expr::Local { var, .. } = x {
6989 let key = match vt.vars.get(*var as usize) {
6990 Some(vi) => format!("{}#{:?}", vi.name, vi.ty.erased()),
6991 None => format!("v{}", var),
6992 };
6993 let id = *map.entry(key).or_insert(*var);
6994 *x = Expr::Local {
6995 var: id,
6996 ty: vt.var(id).ty.clone(),
6997 };
6998 }
6999 });
7000 });
7001 c
7002 };
7003 a.iter()
7004 .zip(b.iter())
7005 .all(|(x, y)| canon(x, &mut map) == canon(y, &mut map))
7006}
7007
7008fn fold_diamonds_in(stmts: &mut Vec<Stmt>, vt: &VarTable) {
7009 let mut i = 0usize;
7010 while i < stmts.len() {
7011 match &mut stmts[i] {
7012 Stmt::Block(v) => fold_diamonds_in(v, vt),
7013 Stmt::If { then_stmt, else_stmt, .. } => {
7014 if let Stmt::Block(v) = &mut **then_stmt {
7015 fold_diamonds_in(v, vt);
7016 }
7017 if let Some(e) = else_stmt {
7018 if let Stmt::Block(v) = &mut **e {
7019 fold_diamonds_in(v, vt);
7020 }
7021 }
7022 }
7023 _ => {}
7024 }
7025 let shape1 = if let Stmt::If { cond, then_stmt, else_stmt: Some(el), .. } = &stmts[i] {
7027 match (arm_assign_inlined(then_stmt), arm_assign_inlined(el)) {
7028 (Some((v1, tgt, a)), Some((v2, _, b)))
7029 if v1 == v2 && diamond_types_ok(&a, &b) =>
7030 {
7031 Some(mk_ternary_assign(tgt, cond.clone(), a, b))
7032 }
7033 _ => None,
7034 }
7035 } else {
7036 None
7037 };
7038 if let Some(f) = shape1 {
7039 stmts[i] = f;
7040 i += 1;
7041 continue;
7042 }
7043 let shape3 = if let Stmt::If {
7056 cond,
7057 then_stmt,
7058 else_stmt: Some(el),
7059 ..
7060 } = &stmts[i]
7061 {
7062 let arm = |st: &Stmt| -> Option<((u32, Expr, Expr), Vec<Stmt>)> {
7063 let Stmt::Block(v) = st else { return None };
7064 if v.len() < 2 {
7065 return None;
7066 }
7067 let first = single_local_assign(&v[0])?;
7068 Some((first, v[1..].to_vec()))
7069 };
7070 match (arm(then_stmt), arm(el)) {
7071 (Some(((v1, tgt1, a), mut rest_t)), Some(((v2, _t2, b), rest_e)))
7072 if !rest_t.is_empty()
7073 && is_bare_ctor_call(first_leaf_stmt(&rest_t[0]))
7074 && stmts_count_writes(&rest_t, v1) == 0
7075 && stmts_count_writes(&rest_t, v2) == 0
7076 && diamond_types_ok(&a, &b) =>
7077 {
7078 if v1 == v2 || same_render_var(v1, v2, vt) {
7079 if canon_stmts_eq(&rest_t, &rest_e, vt) {
7081 Some((mk_ternary_assign(tgt1, cond.clone(), a, b), rest_t))
7082 } else {
7083 None
7084 }
7085 } else if stmts_read_var(&rest_t, v1) == 1
7086 && stmts_read_var(&rest_e, v2) == 1
7087 && stmts_read_var(&rest_t, v2) == 0
7088 && stmts_read_var(&rest_e, v1) == 0
7089 {
7090 let mut unified = rest_e.clone();
7098 for st in unified.iter_mut() {
7099 let mut c2 = st.clone();
7100 walk_stmt_exprs(&mut c2, &mut |e| {
7101 deep_rewrite(e, &mut |x| {
7102 if let Expr::Local { var, .. } = x {
7103 if *var == v2 {
7104 *x = Expr::Local {
7105 var: v1,
7106 ty: vt.var(v1).ty.clone(),
7107 };
7108 }
7109 }
7110 });
7111 });
7112 *st = c2;
7113 }
7114 if unified != rest_t {
7115 None
7116 } else {
7117 let tern = Expr::Cond {
7118 c: Box::new(cond.clone()),
7119 t: Box::new(a.clone()),
7120 f: Box::new(b.clone()),
7121 };
7122 for st in rest_t.iter_mut() {
7123 let mut c2 = st.clone();
7124 walk_stmt_exprs(&mut c2, &mut |e| {
7125 deep_rewrite(e, &mut |x| {
7126 if let Expr::Local { var, .. } = x {
7127 if *var == v1 {
7128 *x = tern.clone();
7129 }
7130 }
7131 });
7132 });
7133 *st = c2;
7134 }
7135 let first = rest_t.remove(0);
7139 Some((first, rest_t))
7140 }
7141 } else {
7142 None
7143 }
7144 }
7145 _ => None,
7146 }
7147 } else {
7148 None
7149 };
7150 if let Some((folded, rest)) = shape3 {
7151 stmts[i] = folded;
7152 stmts.splice(i + 1..i + 1, rest);
7153 i += 1;
7154 continue;
7155 }
7156 if i + 1 < stmts.len() {
7166 let shape2 = {
7186 let s_i = single_local_assign(&stmts[i]);
7187 if let Stmt::If {
7188 cond,
7189 then_stmt,
7190 else_stmt,
7191 ..
7192 } = &stmts[i + 1]
7193 {
7194 let then_a = single_local_assign(then_stmt);
7195 let else_a = else_stmt.as_deref().and_then(single_local_assign);
7196 let then_empty =
7197 matches!(&**then_stmt, Stmt::Block(v) if v.is_empty());
7198 let else_empty = else_stmt
7199 .as_deref()
7200 .map(|e| matches!(e, Stmt::Block(v) if v.is_empty()))
7201 .unwrap_or(true);
7202 let try_fold = |over: Option<(u32, Expr, Expr)>,
7203 keep_over: bool|
7204 -> Option<Stmt> {
7205 let (v2, tgt2, a) = over?;
7206 let (v1, _t1, b) = s_i.as_ref()?;
7207 if !(*v1 == v2 || same_render_var(*v1, v2, vt)) {
7208 return None;
7209 }
7210 let keep = if *v1 == v2 {
7211 b.clone()
7212 } else {
7213 Expr::Local {
7214 var: *v1,
7215 ty: vt.var(*v1).ty.clone(),
7216 }
7217 };
7218 if !diamond_types_ok(&a, &keep) {
7219 return None;
7220 }
7221 if keep_over {
7222 Some(mk_ternary_assign(tgt2, cond.clone(), keep, a))
7223 } else {
7224 Some(mk_ternary_assign(tgt2, cond.clone(), a, keep))
7225 }
7226 };
7227 if then_a.is_some() && else_empty {
7228 try_fold(then_a, false)
7229 } else if else_a.is_some() && then_empty {
7230 try_fold(else_a, true)
7231 } else {
7232 None
7233 }
7234 } else {
7235 None
7236 }
7237 };
7238 let mut lead_fold: Option<(Stmt, Stmt)> = None;
7247 if shape2.is_none() && i + 1 < stmts.len() {
7248 let s_i2 = single_local_assign(&stmts[i]);
7249 let leading = |st: &Stmt| -> Option<((u32, Expr, Expr), Vec<Stmt>)> {
7250 let Stmt::Block(v) = st else { return None };
7251 if v.len() < 2 {
7252 return None;
7253 }
7254 let first = single_local_assign(&v[0])?;
7255 Some((first, v[1..].to_vec()))
7256 };
7257 if let (Some((v1, _t1, b)), Stmt::If { cond, then_stmt, else_stmt, .. }) =
7258 (&s_i2, &stmts[i + 1])
7259 {
7260 let else_absent = else_stmt.is_none()
7261 || else_stmt
7262 .as_deref()
7263 .is_some_and(|e| matches!(e, Stmt::Block(v) if v.is_empty()));
7264 let then_absent = matches!(&**then_stmt, Stmt::Block(v) if v.is_empty());
7265 let try_lead = |over: Option<((u32, Expr, Expr), Vec<Stmt>)>,
7266 keep_over: bool|
7267 -> Option<(Stmt, Stmt)> {
7268 let ((v2, tgt2, a), rest) = over?;
7269 if !(*v1 == v2 || same_render_var(*v1, v2, vt)) {
7270 return None;
7271 }
7272 if stmts_count_writes(&rest, *v1) != 0
7273 || stmts_count_writes(&rest, v2) != 0
7274 {
7275 return None;
7276 }
7277 let keep = if *v1 == v2 {
7278 b.clone()
7279 } else {
7280 Expr::Local {
7281 var: *v1,
7282 ty: vt.var(*v1).ty.clone(),
7283 }
7284 };
7285 if !diamond_types_ok(&a, &keep) {
7286 return None;
7287 }
7288 let t = if keep_over {
7289 mk_ternary_assign(tgt2, cond.clone(), keep, a)
7290 } else {
7291 mk_ternary_assign(tgt2, cond.clone(), a, keep)
7292 };
7293 let mut new_if = stmts[i + 1].clone();
7294 if let Stmt::If { then_stmt, else_stmt, .. } = &mut new_if {
7295 if keep_over {
7296 *else_stmt = Some(Box::new(Stmt::Block(rest)));
7297 } else {
7298 **then_stmt = Stmt::Block(rest);
7299 }
7300 }
7301 Some((t, new_if))
7302 };
7303 if else_absent {
7304 lead_fold = try_lead(leading(then_stmt), false);
7305 } else if then_absent {
7306 if let Some(e) = else_stmt {
7307 lead_fold = try_lead(leading(e), true);
7308 }
7309 }
7310 }
7311 }
7312 if let Some((folded, new_if)) = lead_fold {
7313 stmts[i] = folded;
7314 stmts[i + 1] = new_if;
7315 continue;
7316 }
7317 if let Some(f) = shape2 {
7318 stmts[i] = f;
7319 stmts.remove(i + 1);
7320 continue; }
7322 }
7323 i += 1;
7324 }
7325}
7326
7327pub fn fold_bool_value_diamonds(body: &mut Stmt, vt: &VarTable) {
7328 fn is_bool_var(v: u32, vt: &VarTable) -> bool {
7329 (v as usize) < vt.vars.len()
7330 && matches!(vt.vars[v as usize].ty.erased(), JavaType::Boolean)
7331 }
7332 fn bool_const(e: &Expr) -> Option<bool> {
7333 fn cint(e: &Expr) -> Option<i32> {
7334 match e {
7335 Expr::Const(ConstVal::Int(n)) => Some(*n),
7336 _ => None,
7337 }
7338 }
7339 match e {
7340 Expr::Const(ConstVal::Int(0)) => Some(false),
7341 Expr::Const(ConstVal::Int(1)) => Some(true),
7342 Expr::Bin { op: op @ (BinOp::Eq | BinOp::Ne), l, r, .. } => {
7346 let (a, b) = (cint(l)?, cint(r)?);
7347 let eq = a == b;
7348 Some(if matches!(op, BinOp::Eq) { eq } else { !eq })
7349 }
7350 _ => None,
7351 }
7352 }
7353 fn reads_var(e: &Expr, v: u32) -> bool {
7354 let mut hit = false;
7355 visit_exprs(e, &mut |x| {
7356 if let Expr::Local { var, .. } = x {
7357 if *var == v {
7358 hit = true;
7359 }
7360 }
7361 });
7362 hit
7363 }
7364 fn bool_valued(e: &Expr, vt: &VarTable) -> bool {
7368 fn side(e: &Expr, vt: &VarTable) -> bool {
7369 bool_const(e).is_some() || bool_valued(e, vt)
7370 }
7371 match e {
7372 Expr::Local { var, .. } => is_bool_var(*var, vt),
7373 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
7374 Expr::InstanceOf { .. } => true,
7375 Expr::Un { op: UnOp::Not, e: x, .. } => bool_valued(x, vt) || bool_const(x).is_some(),
7376 Expr::Bin { op, l, r, .. } => match op {
7377 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le => true,
7378 BinOp::LogAnd | BinOp::LogOr => {
7381 side(l, vt) && side(r, vt)
7382 }
7383 _ => false,
7384 },
7385 Expr::Cond { t, f, .. } => side(t, vt) && side(f, vt),
7386 _ => false,
7387 }
7388 }
7389 fn lone_assign(
7398 b: &Stmt,
7399 v: u32,
7400 vt: &VarTable,
7401 an: &VarAnalysis,
7402 consumed: &mut HashSet<u32>,
7403 ) -> Option<Expr> {
7404 let l = flat_list(b);
7405 if l.len() == 2 {
7406 let (u, e): (u32, &Expr) = match l[0] {
7407 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e),
7408 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
7409 match &**target {
7410 Expr::Local { var, .. } => (*var, &**value),
7411 _ => return None,
7412 }
7413 }
7414 _ => return None,
7415 };
7416 if u == v || consumed.contains(&u) {
7417 return None;
7418 }
7419 if an.assigns.get(u as usize).copied().unwrap_or(0) != 1
7426 || an.reads.get(u as usize).copied().unwrap_or(0) != 1
7427 {
7428 return None;
7429 }
7430 let (tv, rv) = match l[1] {
7431 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
7432 match (&**target, &**value) {
7433 (Expr::Local { var: t, .. }, Expr::Local { var: r, .. }) => (*t, *r),
7434 _ => return None,
7435 }
7436 }
7437 Stmt::LocalDef { var, init: Some(Expr::Local { var: r, .. }), .. } => (*var, *r),
7438 _ => return None,
7439 };
7440 if tv != v || rv != u {
7441 return None;
7442 }
7443 if reads_var(e, v) || !(bool_valued(e, vt) || bool_const(e).is_some()) {
7444 return None;
7445 }
7446 consumed.insert(u);
7447 return Some(e.clone());
7448 }
7449 if l.len() != 1 {
7450 return None;
7451 }
7452 let (target, value) = match l[0] {
7453 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => (target, value),
7454 Stmt::LocalDef { var, init: Some(value), .. } if *var == v => {
7455 if reads_var(value, v) || !(bool_valued(value, vt) || bool_const(value).is_some()) {
7456 return None;
7457 }
7458 return Some(value.clone());
7459 }
7460 _ => return None,
7461 };
7462 let Expr::Local { var: tv, .. } = &**target else { return None };
7463 if *tv != v || reads_var(value, v) {
7464 return None;
7465 }
7466 if !(bool_valued(value, vt) || bool_const(value).is_some()) {
7467 return None;
7468 }
7469 Some((**value).clone())
7470 }
7471 fn not(e: Expr) -> Expr {
7472 Expr::Un { op: UnOp::Not, e: Box::new(e) }
7473 }
7474 fn simplify_bool(e: Expr) -> Expr {
7479 match e {
7480 Expr::Un { op: UnOp::Not, e: inner, .. } => match *inner {
7481 Expr::Bin { op: BinOp::Ne, l, r, ty } => {
7486 simplify_bool(Expr::Bin { op: BinOp::Eq, l, r, ty })
7487 }
7488 Expr::Bin { op: BinOp::Eq, l, r, ty } => {
7489 simplify_bool(Expr::Bin { op: BinOp::Ne, l, r, ty })
7490 }
7491 other => Expr::Un { op: UnOp::Not, e: Box::new(simplify_bool(other)) },
7492 },
7493 Expr::Bin { op: op @ (BinOp::Eq | BinOp::Ne), l, r, ty } => {
7494 fn cond01(e: &Expr) -> Option<Expr> {
7496 if let Expr::Cond { c, t, f } = e {
7497 if matches!(&**t, Expr::Const(ConstVal::Int(1)))
7498 && matches!(&**f, Expr::Const(ConstVal::Int(0)))
7499 {
7500 return Some((**c).clone());
7501 }
7502 }
7503 None
7504 }
7505 let (lc, rc) = (cond01(&l), cond01(&r));
7506 match (lc, rc) {
7507 (Some(c), None) => match (&*r, op) {
7508 (Expr::Const(ConstVal::Int(1)), BinOp::Eq) => c,
7509 (Expr::Const(ConstVal::Int(1)), BinOp::Ne) => not(c),
7510 (Expr::Const(ConstVal::Int(0)), BinOp::Eq) => not(c),
7511 (Expr::Const(ConstVal::Int(0)), BinOp::Ne) => c,
7512 _ => Expr::Bin { op, l: Box::new(c), r, ty },
7513 },
7514 (None, Some(c)) => match (&*l, op) {
7515 (Expr::Const(ConstVal::Int(1)), BinOp::Eq) => c,
7516 (Expr::Const(ConstVal::Int(1)), BinOp::Ne) => not(c),
7517 (Expr::Const(ConstVal::Int(0)), BinOp::Eq) => not(c),
7518 (Expr::Const(ConstVal::Int(0)), BinOp::Ne) => c,
7519 _ => Expr::Bin { op, l, r: Box::new(c), ty },
7520 },
7521 _ => Expr::Bin { op, l, r, ty },
7522 }
7523 }
7524 other => other,
7525 }
7526 }
7527 fn bin(op: BinOp, l: Expr, r: Expr) -> Expr {
7528 Expr::Bin { op, l: Box::new(l), r: Box::new(r), ty: None }
7529 }
7530
7531 for _round in 0..4 {
7532 let mut changed = false;
7533 let mut analysis = VarAnalysis {
7534 assigns: Vec::new(),
7535 reads: Vec::new(),
7536 values: Vec::new(),
7537 };
7538 analyze_vars(body, &mut analysis);
7539 let mut consumed: HashSet<u32> = HashSet::default();
7540 walk_mut_deep(body, &mut |st| {
7541 let Stmt::Block(list) = st else { return };
7542 let mut i = 0usize;
7543 while i + 1 < list.len() {
7544 let folded = try_at(list, i, vt, &is_bool_var, &bool_const, &reads_var, &lone_assign, &analysis, &mut consumed);
7545 match folded {
7546 Some(expr) => {
7547 let is_def = matches!(&list[i], Stmt::LocalDef { .. });
7548 let var = match &list[i] {
7549 Stmt::LocalDef { var, .. } => *var,
7550 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
7551 match &**target { Expr::Local { var, .. } => *var, _ => unreachable!() }
7552 }
7553 _ => unreachable!(),
7554 };
7555 let (is_final, force_type) = match &list[i] {
7556 Stmt::LocalDef { is_final, force_type, .. } => (*is_final, *force_type),
7557 _ => (false, false),
7558 };
7559 list[i] = if is_def {
7560 Stmt::LocalDef { var, init: Some(expr), is_final, force_type }
7561 } else {
7562 Stmt::ExprStmt(Expr::Assign {
7563 target: Box::new(Expr::Local {
7564 var,
7565 ty: TypeRef::J(JavaType::Boolean),
7566 }),
7567 op: AssignOp::Plain,
7568 value: Box::new(expr),
7569 })
7570 };
7571 list.remove(i + 1);
7572 changed = true;
7573 }
7576 None => i += 1,
7577 }
7578 }
7579 });
7580 if !changed {
7581 break;
7582 }
7583 }
7584
7585 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
7586 fn try_at(
7587 list: &[Stmt],
7588 i: usize,
7589 vt: &VarTable,
7590 is_bool_var: &dyn Fn(u32, &VarTable) -> bool,
7591 bool_const: &dyn Fn(&Expr) -> Option<bool>,
7592 reads_var: &dyn Fn(&Expr, u32) -> bool,
7593 lone_assign: &dyn Fn(&Stmt, u32, &VarTable, &VarAnalysis, &mut HashSet<u32>) -> Option<Expr>,
7594 an: &VarAnalysis,
7595 consumed: &mut HashSet<u32>,
7596 ) -> Option<Expr> {
7597 let (v, base) = match &list[i] {
7599 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
7600 match &**target {
7601 Expr::Local { var, .. } => {
7602 let b = bool_const(value)?;
7605 if !is_bool_var(*var, vt) { return None; }
7606 (*var, b)
7607 }
7608 _ => return None,
7609 }
7610 }
7611 Stmt::LocalDef { var, init: Some(e), .. } => {
7612 let b = bool_const(e)?;
7613 if !is_bool_var(*var, vt) { return None; }
7614 (*var, b)
7615 }
7616 _ => return None,
7617 };
7618 let Stmt::If { cond, then_stmt, else_stmt } = &list[i + 1] else {
7619 return None;
7620 };
7621 if reads_var(cond, v) {
7622 return None;
7623 }
7624 let mut touches_v = false;
7625 visit_exprs(cond, &mut |x| {
7626 if let Expr::Assign { target, .. } = x {
7627 if let Expr::Local { var, .. } = &**target {
7628 if *var == v { touches_v = true; }
7629 }
7630 }
7631 });
7632 if touches_v {
7633 return None;
7634 }
7635 let (cond_n, then_arm, else_arm): (Expr, &Stmt, Option<&Stmt>) =
7638 if flat_list(then_stmt).is_empty() && else_stmt.is_some() {
7639 (not(cond.clone()), else_stmt.as_ref().unwrap(), None)
7640 } else {
7641 (cond.clone(), then_stmt.as_ref(), else_stmt.as_deref())
7642 };
7643 let e1 = lone_assign(then_arm, v, vt, an, consumed)?;
7644 let e2 = match else_arm {
7645 None => None,
7646 Some(b) => Some(lone_assign(b, v, vt, an, consumed)?),
7647 };
7648 let e1c = bool_const(&e1);
7649 let c = simplify_bool(cond_n);
7650 Some(match e2 {
7651 None => {
7652 if !base {
7653 match e1c {
7654 Some(true) => c,
7655 Some(false) => return None, None => bin(BinOp::LogAnd, c, e1),
7657 }
7658 } else {
7659 match e1c {
7660 Some(false) => not(c),
7661 Some(true) => return None,
7662 None => bin(BinOp::LogOr, not(c), e1),
7663 }
7664 }
7665 }
7666 Some(e2) => {
7667 if e1 == e2 {
7669 return None; }
7671 Expr::Cond { c: Box::new(c), t: Box::new(e1), f: Box::new(e2) }
7672 }
7673 })
7674 }
7675}
7676
7677pub fn idiom_compounds(body: &mut Stmt, vt: &VarTable) {
7678 walk_mut_deep(body, &mut |st| {
7679 let Stmt::ExprStmt(e) = st else { return };
7680 let Expr::Assign { target, op: AssignOp::Plain, value } = &mut *e else {
7681 return;
7682 };
7683 enum Tgt {
7686 Loc(u32),
7687 Fld,
7688 }
7689 let tgt = match &**target {
7690 Expr::Local { var, .. } => Tgt::Loc(*var),
7691 Expr::Field { owner, .. }
7692 if owner.is_none()
7693 || matches!(&**owner.as_ref().unwrap(), Expr::This) =>
7694 {
7695 Tgt::Fld
7696 }
7697 _ => return,
7698 };
7699 let Expr::Bin { op, l, r, .. } = &mut **value else {
7700 return;
7701 };
7702 match (&tgt, &**l) {
7704 (Tgt::Loc(v), Expr::Local { var, .. }) if *var == *v => {}
7705 (Tgt::Fld, Expr::Field { cls: c2, name: n2, .. }) => {
7706 let Expr::Field { cls: c1, name: n1, .. } = &**target else {
7707 return;
7708 };
7709 if c1 != c2 || n1 != n2 {
7710 return;
7711 }
7712 }
7713 _ => return,
7714 }
7715 let one = match &**r {
7717 Expr::Const(ConstVal::Int(1)) => Some(1i64),
7718 Expr::Const(ConstVal::Int(-1)) => Some(-1),
7719 Expr::Const(ConstVal::Long(1)) => Some(1),
7720 Expr::Const(ConstVal::Long(-1)) => Some(-1),
7721 _ => None,
7722 };
7723 if *op == BinOp::Add || *op == BinOp::Sub {
7724 if let Some(mut d) = one {
7725 if *op == BinOp::Sub {
7726 d = -d;
7727 }
7728 let tgt_ty = match &tgt {
7729 Tgt::Loc(v) => vt.var(*v).ty.erased(),
7730 Tgt::Fld => {
7731 let Expr::Field { ty, .. } = &**target else {
7732 return;
7733 };
7734 ty.erased()
7735 }
7736 };
7737 let numeric = matches!(
7738 tgt_ty,
7739 JavaType::Int
7740 | JavaType::Long
7741 | JavaType::Short
7742 | JavaType::Byte
7743 | JavaType::Char
7744 | JavaType::Float
7745 | JavaType::Double
7746 );
7747 if numeric {
7748 let wide = matches!(tgt_ty, JavaType::Long);
7749 let inner = match &tgt {
7750 Tgt::Loc(v) => Expr::Local {
7751 var: *v,
7752 ty: vt.var(*v).ty.clone(),
7753 },
7754 Tgt::Fld => (**target).clone(),
7755 };
7756 *e = Expr::PostIncDec {
7757 e: Box::new(inner),
7758 delta: d,
7759 wide,
7760 };
7761 return;
7762 }
7763 }
7764 }
7765 let cop = match op {
7766 BinOp::Add => AssignOp::Add,
7767 BinOp::Sub => AssignOp::Sub,
7768 BinOp::Mul => AssignOp::Mul,
7769 BinOp::Div => AssignOp::Div,
7770 BinOp::Rem => AssignOp::Rem,
7771 BinOp::Shl => AssignOp::Shl,
7772 BinOp::Shr => AssignOp::Shr,
7773 BinOp::Ushr => AssignOp::Ushr,
7774 BinOp::And => AssignOp::And,
7775 BinOp::Or => AssignOp::Or,
7776 BinOp::Xor => AssignOp::Xor,
7777 _ => return,
7778 };
7779 let rhs = std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
7780 let new_target = match &tgt {
7781 Tgt::Loc(v) => Box::new(Expr::Local {
7782 var: *v,
7783 ty: vt.var(*v).ty.clone(),
7784 }),
7785 Tgt::Fld => target.clone(),
7786 };
7787 *e = Expr::Assign {
7788 target: new_target,
7789 op: cop,
7790 value: rhs,
7791 };
7792 });
7793}
7794
7795pub fn fix_bool_xor(body: &mut Stmt, vt: &VarTable, ret_bool: bool) {
7796 fn is_one(e: &Expr) -> bool {
7797 matches!(e, Expr::Const(ConstVal::Int(1)))
7798 }
7799 fn bty(e: &Expr, vt: &VarTable) -> bool {
7800 match e {
7801 Expr::Local { var, .. } => {
7802 matches!(vt.var(*var).ty.erased(), JavaType::Boolean)
7803 }
7804 Expr::Bin {
7813 op: BinOp::And | BinOp::Or | BinOp::Xor,
7814 l,
7815 r,
7816 ..
7817 } => bty(l, vt) && bty(r, vt),
7818 other => matches!(other.type_ref().erased(), JavaType::Boolean),
7819 }
7820 }
7821 fn inv_of_xor(x: &Expr, vt: &VarTable) -> Option<Expr> {
7826 if let Expr::Bin { op: BinOp::Xor, l, r, .. } = x {
7827 let (int_side, one_side) = if is_one(r) {
7828 (l, r)
7829 } else if is_one(l) {
7830 (r, l)
7831 } else {
7832 return None;
7833 };
7834 let _ = one_side;
7835 if !bty(int_side, vt) {
7836 return Some(Expr::Bin {
7837 op: BinOp::Eq,
7838 l: int_side.clone(),
7839 r: Box::new(Expr::Const(ConstVal::Int(0))),
7840 ty: Some(TypeRef::J(JavaType::Boolean)),
7841 });
7842 }
7843 }
7844 None
7845 }
7846 let tgt_bool = |target: &Expr, vt: &VarTable| -> bool {
7847 match target {
7848 Expr::Local { var, .. } => matches!(vt.var(*var).ty.erased(), JavaType::Boolean),
7849 Expr::Field { ty, .. } => matches!(ty.erased(), JavaType::Boolean),
7850 _ => false,
7851 }
7852 };
7853 walk_mut_deep(body, &mut |st| match st {
7854 Stmt::Return(Some(e)) if ret_bool => {
7855 if let Some(r) = inv_of_xor(e, vt) {
7856 *e = r;
7857 }
7858 }
7859 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain, .. }) => {
7860 if tgt_bool(target, vt) {
7861 if let Some(r) = inv_of_xor(value, vt) {
7862 **value = r;
7863 }
7864 }
7865 }
7866 Stmt::LocalDef { var, init: Some(value), .. }
7867 if matches!(vt.var(*var).ty.erased(), JavaType::Boolean) =>
7868 {
7869 if let Some(r) = inv_of_xor(value, vt) {
7870 *value = r;
7871 }
7872 }
7873 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
7874 if let Some(r) = inv_of_xor(cond, vt) {
7875 *cond = r;
7876 }
7877 }
7878 _ => {}
7879 });
7880 fn ity(e: &Expr, vt: &VarTable) -> bool {
7881 matches!(
7882 match e {
7883 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
7884 other => other.type_ref().erased(),
7885 },
7886 JavaType::Int | JavaType::Short | JavaType::Byte | JavaType::Char
7887 )
7888 }
7889 fn mixed_rewrite(e: &mut Expr, vt: &VarTable) {
7898 deep_rewrite(e, &mut |x| {
7899 if let Expr::Bin { op, l, r, ty } = x {
7900 match op {
7901 BinOp::Xor => {
7902 if is_one(r) && bty(l, vt) {
7903 let taken =
7904 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
7905 *x = Expr::Un { op: UnOp::Not, e: taken };
7906 } else if is_one(l) && bty(r, vt) {
7907 let taken =
7908 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
7909 *x = Expr::Un { op: UnOp::Not, e: taken };
7910 }
7911 }
7912 BinOp::Or | BinOp::And => {
7913 if bty(l, vt) && ity(r, vt) {
7914 let taken =
7915 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
7916 **r = Expr::Bin {
7917 op: BinOp::Ne,
7918 l: taken,
7919 r: Box::new(Expr::Const(ConstVal::Int(0))),
7920 ty: Some(TypeRef::J(JavaType::Boolean)),
7921 };
7922 *ty = Some(TypeRef::J(JavaType::Boolean));
7923 } else if ity(l, vt) && bty(r, vt) {
7924 let taken =
7925 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
7926 **l = Expr::Bin {
7927 op: BinOp::Ne,
7928 l: taken,
7929 r: Box::new(Expr::Const(ConstVal::Int(0))),
7930 ty: Some(TypeRef::J(JavaType::Boolean)),
7931 };
7932 *ty = Some(TypeRef::J(JavaType::Boolean));
7933 }
7934 }
7935 _ => {}
7936 }
7937 }
7938 });
7939 }
7940 walk_mut_deep(body, &mut |st| match st {
7943 Stmt::Return(Some(e)) if ret_bool => mixed_rewrite(e, vt),
7944 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain, .. }) => {
7945 if tgt_bool(target, vt) {
7946 mixed_rewrite(value, vt);
7947 }
7948 }
7949 Stmt::LocalDef { var, init: Some(e), .. }
7950 if matches!(vt.var(*var).ty.erased(), JavaType::Boolean) =>
7951 {
7952 mixed_rewrite(e, vt);
7953 }
7954 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
7955 mixed_rewrite(cond, vt);
7956 }
7957 _ => {}
7958 });
7959 walk_stmt_exprs(body, &mut |e| {
7962 deep_rewrite(e, &mut |x| {
7963 if let Expr::Cond { c, .. } = x {
7964 mixed_rewrite(c, vt);
7965 }
7966 });
7967 });
7968}
7969
7970pub fn booleanize(vt: &mut VarTable, body: &mut Stmt, ret_bool: bool) -> usize {
7976 let n0 = vt.vars.len();
7983 let mut derefed = vec![false; n0];
7984 mark_derefs(body, &mut derefed);
7985 let mut total = 0usize;
7986 for _ in 0..4 {
7987 let n = booleanize_round(vt, body, ret_bool, &derefed);
7988 if n == 0 {
7989 break;
7990 }
7991 total += n;
7992 }
7993 total
7994}
7995
7996fn mark_deref_expr(e: &Expr, derefed: &mut [bool]) {
8000 match e {
8001 Expr::Field { owner: Some(o), is_static: false, .. }
8002 | Expr::Method { owner: Some(o), .. } => {
8003 if let Expr::Local { var, .. } = &**o {
8004 if (*var as usize) < derefed.len() {
8005 derefed[*var as usize] = true;
8006 }
8007 }
8008 }
8009 Expr::ArrayIndex { array, .. } => {
8010 if let Expr::Local { var, .. } = &**array {
8011 if (*var as usize) < derefed.len() {
8012 derefed[*var as usize] = true;
8013 }
8014 }
8015 }
8016 _ => {}
8017 }
8018 for_each_child(e, &mut |c| mark_deref_expr(c, derefed));
8019}
8020
8021fn mark_derefs(body: &Stmt, derefed: &mut [bool]) {
8022 walk_all(body, &mut |st| {
8023 match st {
8024 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
8025 mark_deref_expr(e, derefed);
8026 }
8027 Stmt::Return(Some(e)) => mark_deref_expr(e, derefed),
8028 Stmt::LocalDef { init: Some(e), .. } => mark_deref_expr(e, derefed),
8029 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
8030 mark_deref_expr(cond, derefed);
8031 }
8032 Stmt::For { init, .. } => {
8033 for x in init {
8034 mark_derefs(x, derefed);
8035 }
8036 }
8037 Stmt::Switch { selector, .. } => mark_deref_expr(selector, derefed),
8038 Stmt::ForEach { iterable, .. } => mark_deref_expr(iterable, derefed),
8039 _ => {}
8040 }
8041 });
8042}
8043
8044fn booleanize_round(
8045 vt: &mut VarTable,
8046 body: &mut Stmt,
8047 ret_bool: bool,
8048 derefed: &[bool],
8049) -> usize {
8050 let n = vt.vars.len();
8051 let mut in_cond = vec![false; n];
8052
8053 walk_all(body, &mut |st| {
8056 let cond: Option<&Expr> = match st {
8057 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
8058 Some(cond)
8059 }
8060 _ => None,
8061 };
8062 if let Some(c) = cond {
8063 if let Expr::Bin {
8064 op: BinOp::Eq | BinOp::Ne,
8065 l,
8066 r,
8067 ..
8068 } = c
8069 {
8070 for (x, other) in [(l, r), (r, l)] {
8071 if let (Expr::Local { var, .. }, Expr::Const(ConstVal::Int(0))) =
8072 (&**x, &**other)
8073 {
8074 if (*var as usize) < n {
8075 in_cond[*var as usize] = true;
8076 }
8077 }
8078 }
8079 }
8080 }
8081 if ret_bool {
8086 if let Stmt::Return(Some(Expr::Local { var, .. })) = st {
8087 if (*var as usize) < n {
8088 in_cond[*var as usize] = true;
8089 }
8090 }
8091 }
8092 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = st {
8099 if let Expr::Field { ty, .. } = &**target {
8100 if ty.erased() == JavaType::Boolean {
8101 if let Expr::Local { var, .. } = &**value {
8102 if (*var as usize) < n {
8103 in_cond[*var as usize] = true;
8104 }
8105 }
8106 }
8107 }
8108 }
8109 let val: Option<&Expr> = match st {
8110 Stmt::ExprStmt(Expr::Assign { value, .. }) => Some(value),
8111 Stmt::ExprStmt(e) => Some(e),
8114 Stmt::LocalDef { init: Some(e), .. } => Some(e),
8115 Stmt::Return(Some(e)) => Some(e),
8116 _ => None,
8117 };
8118 if let Some(v) = val {
8119 visit_exprs(v, &mut |x| {
8120 if let Expr::Method { desc, args, .. } = x {
8126 for (i, a) in args.iter().enumerate() {
8127 if desc.args.get(i) == Some(&JavaType::Boolean) {
8128 if let Expr::Local { var, .. } = a {
8129 if (*var as usize) < n {
8130 in_cond[*var as usize] = true;
8131 }
8132 }
8133 }
8134 }
8135 }
8136 if let Expr::Bin { op, l, r, .. } = x {
8137 if matches!(
8138 op,
8139 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
8140 ) {
8141 for side in [l, r] {
8142 if let Expr::Local { var, .. } = &**side {
8143 if (*var as usize) < n {
8144 in_cond[*var as usize] = true;
8145 }
8146 }
8147 }
8148 }
8149 if matches!(op, BinOp::Or | BinOp::And) {
8155 let l_bool = l.type_ref().erased() == JavaType::Boolean;
8156 let r_bool = r.type_ref().erased() == JavaType::Boolean;
8157 if l_bool != r_bool {
8158 let side = if l_bool { r } else { l };
8159 if let Expr::Local { var, .. } = &**side {
8160 if (*var as usize) < n {
8161 in_cond[*var as usize] = true;
8162 }
8163 }
8164 }
8165 }
8166 }
8167 });
8168 }
8169 });
8170
8171 let mut assigned_any = vec![false; n];
8178 let mut all_bool = vec![true; n];
8179 let mut edges: Vec<(usize, usize)> = Vec::new();
8180 walk_all(body, &mut |st| {
8181 let (var, value) = match st {
8182 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
8183 Expr::Local { var, .. } => (var, &**value),
8184 _ => return,
8185 },
8186 Stmt::LocalDef {
8187 var, init: Some(e), ..
8188 } => (var, e),
8189 _ => return,
8190 };
8191 let i = *var as usize;
8192 if i < n {
8193 assigned_any[i] = true;
8194 if !is_boolean_valued(value, vt) {
8195 all_bool[i] = false;
8196 }
8197 if let Expr::Local { var: src, .. } = value {
8203 if (*src as usize) < n {
8204 edges.push((i, *src as usize));
8205 }
8206 }
8207 }
8208 });
8209 loop {
8214 let mut changed = false;
8215 for &(tgt, src) in &edges {
8216 if in_cond[tgt] && !in_cond[src] && all_bool[src] {
8217 in_cond[src] = true;
8218 changed = true;
8219 }
8220 if in_cond[src] && !in_cond[tgt] && all_bool[tgt] {
8226 in_cond[tgt] = true;
8227 changed = true;
8228 }
8229 }
8230 if !changed {
8231 break;
8232 }
8233 }
8234 let reads = count_locals_stmts(std::slice::from_ref(body));
8235 let mut boolean_vars: HashSet<u32> = HashSet::default();
8236 for i in 0..n {
8237 if vt.vars[i].is_param {
8238 continue;
8239 }
8240 if derefed[i] {
8241 continue;
8242 }
8243 if !in_cond[i] && !(reads.get(&(i as u32)).copied().unwrap_or(0) == 0) {
8244 continue;
8245 }
8246 if assigned_any[i] && all_bool[i] {
8247 boolean_vars.insert(i as u32);
8248 }
8249 }
8250 if boolean_vars.is_empty() {
8251 return 0;
8252 }
8253 for v in &boolean_vars {
8254 vt.vars[*v as usize].ty = TypeRef::J(JavaType::Boolean);
8255 }
8256 let types: Vec<&TypeRef> = vt.vars.iter().map(|v| &v.ty).collect();
8257 rewrite_exprs(body, &mut |e| {
8258 deep_rewrite(e, &mut |x| {
8259 if let Expr::Local { var, ty } = x {
8260 if let Some(want) = types.get(*var as usize) {
8261 if ty != *want {
8262 *ty = (*want).clone();
8263 }
8264 }
8265 }
8266 });
8267 });
8268
8269 fold_bool_conditions(body, &boolean_vars);
8271 boolean_vars.len()
8272}
8273
8274fn is_boolean_valued(e: &Expr, vt: &VarTable) -> bool {
8275 match e {
8276 Expr::Const(ConstVal::Int(0)) | Expr::Const(ConstVal::Int(1)) => true,
8277 Expr::Bin { op: BinOp::Or | BinOp::And, l, r, .. } => {
8284 is_boolean_valued(l, vt) || is_boolean_valued(r, vt)
8285 }
8286 Expr::Bin {
8294 op: BinOp::Xor,
8295 l,
8296 r,
8297 ..
8298 } => is_boolean_valued(l, vt) && is_boolean_valued(r, vt),
8299 Expr::Bin { op, .. } => matches!(
8300 op,
8301 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
8302 ),
8303 Expr::InstanceOf { .. } => true,
8304 Expr::Local { var, ty } => {
8310 let t = if (*var as usize) < vt.vars.len() {
8311 vt.var(*var).ty.erased()
8312 } else {
8313 ty.erased()
8314 };
8315 t == JavaType::Boolean
8316 }
8317 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
8318 Expr::Cond { t, f, .. } => is_boolean_valued(t, vt) && is_boolean_valued(f, vt),
8319 Expr::Un { op: UnOp::Not, .. } => true,
8320 Expr::Field { ty, .. } => ty.erased() == JavaType::Boolean,
8324 _ => false,
8325 }
8326}
8327
8328fn fold_bool_conditions(s: &mut Stmt, bv: &HashSet<u32>) {
8329 let fold = |e: &mut Expr| {
8330 deep_rewrite(e, &mut |x| {
8331 if let Expr::Bin { op, l, r, .. } = x {
8332 let (var_side, const_side) = match (&**l, &**r) {
8333 (Expr::Local { .. }, Expr::Const(ConstVal::Int(_))) => (l, 0),
8334 (Expr::Const(ConstVal::Int(_)), Expr::Local { .. }) => (r, 1),
8335 _ => return,
8336 };
8337 let var = match &**var_side {
8338 Expr::Local { var, .. } => *var,
8339 _ => return,
8340 };
8341 if !bv.contains(&var) {
8342 return;
8343 }
8344 let _ = const_side;
8345 match op {
8346 BinOp::Ne => {
8347 let inner = var_side.clone();
8348 *x = *inner;
8349 }
8350 BinOp::Eq => {
8351 let inner = var_side.clone();
8352 *x = Expr::Un {
8353 op: UnOp::Not,
8354 e: Box::new(*inner),
8355 };
8356 }
8357 _ => {}
8358 }
8359 }
8360 });
8361 };
8362 match s {
8363 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => fold(cond),
8364 _ => {}
8365 }
8366 walk_mut_deep(s, &mut |st| {
8367 let cond = match st {
8368 Stmt::If { cond, .. } => Some(cond),
8369 Stmt::While { cond, .. } => Some(cond),
8370 Stmt::DoWhile { cond, .. } => Some(cond),
8371 _ => None,
8372 };
8373 if let Some(c) = cond {
8374 fold(c);
8375 }
8376 });
8377}
8378
8379pub fn flip_const_compares(s: &mut Stmt) {
8381 rewrite_exprs(s, &mut |e| {
8382 deep_rewrite(e, &mut |x| {
8383 if let Expr::Bin { op, l, r, .. } = x {
8384 if matches!(
8385 op,
8386 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
8387 ) {
8388 let l_const = matches!(&**l, Expr::Const(_));
8389 let r_var = matches!(&**r, Expr::Local { .. });
8390 if l_const && r_var {
8391 std::mem::swap(l, r);
8392 }
8393 }
8394 }
8395 });
8396 });
8397}
8398
8399pub fn null_compares(vt: &VarTable, body: &mut Stmt) {
8401 let obj_vars: HashSet<u32> = vt
8402 .vars
8403 .iter()
8404 .filter(|v| v.ty.erased().is_reference())
8405 .map(|v| v.id)
8406 .collect();
8407 rewrite_exprs(body, &mut |e| {
8408 deep_rewrite(e, &mut |x| {
8409 null_side_rewrite(x, &|v| obj_vars.contains(&v));
8410 });
8411 });
8412}
8413
8414pub fn drop_dead_locals(body: &mut Stmt) {
8423 loop {
8424 let mut reads: Vec<usize> = Vec::new();
8425 count_reads(body, &mut reads);
8426 let mut dropped = 0usize;
8427 walk_mut_deep(body, &mut |st| match st {
8428 Stmt::Block(items) => prune_dead_items(items, &reads, &mut dropped),
8429 Stmt::Switch { cases, .. } => {
8439 for c in cases {
8440 prune_dead_items(&mut c.body, &reads, &mut dropped);
8441 }
8442 }
8443 Stmt::For { init, .. } => prune_dead_items(init, &reads, &mut dropped),
8444 _ => {}
8445 });
8446 if dropped == 0 {
8447 break;
8448 }
8449 }
8450}
8451
8452fn prune_dead_items(items: &mut Vec<Stmt>, reads: &[usize], dropped: &mut usize) {
8457 let dead = |v: u32| reads.get(v as usize).copied().unwrap_or(0) == 0;
8458 items.retain_mut(|x| match x {
8459 Stmt::ExprStmt(Expr::Assign { target, value, op, .. }) => {
8460 let dead_target = matches!(&**target, Expr::Local { var, .. } if dead(*var));
8461 if dead_target && matches!(*op, AssignOp::Plain) {
8462 *dropped += 1;
8463 if has_side_effects(value) {
8464 let e = std::mem::replace(value, Box::new(Expr::This));
8465 *x = Stmt::ExprStmt(*e);
8466 true
8467 } else {
8468 false
8469 }
8470 } else {
8471 true
8472 }
8473 }
8474 Stmt::LocalDef { var, init, .. } if dead(*var) => {
8475 *dropped += 1;
8476 if let Some(e) = init.take() {
8477 if has_side_effects(&e) {
8478 *x = Stmt::ExprStmt(e);
8479 return true;
8480 }
8481 }
8482 false
8483 }
8484 _ => true,
8485 });
8486}
8487
8488
8489fn stmt_shallow_vars<F: FnMut(u32)>(st: &Stmt, out: &mut F) {
8494 fn ex<F: FnMut(u32)>(e: &Expr, out: &mut F) {
8495 visit_exprs(e, &mut |x| {
8496 if let Expr::Local { var, .. } = x {
8497 out(*var);
8498 }
8499 });
8500 }
8501 match st {
8502 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
8503 ex(e, out)
8504 }
8505 Stmt::TernaryValue { e } => ex(e, out),
8506 Stmt::Return(Some(e)) => ex(e, out),
8507 Stmt::LocalDef { var, init, .. } => {
8508 out(*var);
8509 if let Some(e) = init {
8510 ex(e, out);
8511 }
8512 }
8513 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
8514 ex(cond, out)
8515 }
8516 Stmt::For { cond, update, .. } => {
8517 if let Some(c) = cond {
8518 ex(c, out);
8519 }
8520 for u in update {
8521 ex(u, out);
8522 }
8523 }
8524 Stmt::ForEach { iterable, .. } => ex(iterable, out),
8525 Stmt::Switch { selector, cases, .. } => {
8526 ex(selector, out);
8527 for cg in cases {
8528 if let Some(g) = &cg.guard {
8529 ex(g, out);
8530 }
8531 }
8532 }
8533 Stmt::Assert { cond, msg } => {
8534 ex(cond, out);
8535 if let Some(m) = msg {
8536 ex(m, out);
8537 }
8538 }
8539 Stmt::Synchronized { lock, .. } => ex(lock, out),
8540 _ => {}
8541 }
8542}
8543
8544fn scope_pass_a(st: &Stmt, cur: u32, next: &mut u32, def_block: &mut [u32], sizes: &mut Vec<u32>) {
8556 if let Stmt::LocalDef { var, .. } = st {
8557 let i = *var as usize;
8558 if i < def_block.len() && def_block[i] == u32::MAX {
8559 def_block[i] = cur;
8560 }
8561 }
8562 fn scoped(c: &Stmt, next: &mut u32, def_block: &mut [u32], sizes: &mut Vec<u32>) {
8563 let id = *next;
8564 *next += 1;
8565 sizes.push(0);
8566 scope_pass_a(c, id, next, def_block, sizes);
8567 sizes[id as usize] = *next - id;
8568 }
8569 match st {
8570 Stmt::Block(v) => {
8571 for x in v {
8572 scope_pass_a(x, cur, next, def_block, sizes);
8573 }
8574 }
8575 Stmt::If {
8576 then_stmt,
8577 else_stmt,
8578 ..
8579 } => {
8580 scoped(then_stmt, next, def_block, sizes);
8581 if let Some(e) = else_stmt {
8582 scoped(e, next, def_block, sizes);
8583 }
8584 }
8585 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => {
8586 scoped(body, next, def_block, sizes)
8587 }
8588 Stmt::For { init, body, .. } => {
8589 let id = *next;
8592 *next += 1;
8593 sizes.push(0);
8594 for x in init {
8595 scope_pass_a(x, id, next, def_block, sizes);
8596 }
8597 scoped(body, next, def_block, sizes);
8598 sizes[id as usize] = *next - id;
8599 }
8600 Stmt::ForEach { body, .. } => scoped(body, next, def_block, sizes),
8601 Stmt::Switch { cases, default, .. } => {
8602 for c in cases {
8603 let id = *next;
8608 *next += 1;
8609 sizes.push(0);
8610 for x in &c.body {
8611 if matches!(x, Stmt::Block(_)) {
8612 scoped(x, next, def_block, sizes);
8613 } else {
8614 scope_pass_a(x, id, next, def_block, sizes);
8615 }
8616 }
8617 sizes[id as usize] = *next - id;
8618 }
8619 if let Some(d) = default {
8620 scoped(d, next, def_block, sizes);
8621 }
8622 }
8623 Stmt::Try {
8624 body,
8625 catches,
8626 finally,
8627 } => {
8628 scoped(body, next, def_block, sizes);
8629 for c in catches {
8630 scoped(&c.body, next, def_block, sizes);
8631 }
8632 if let Some(f) = finally {
8633 scoped(f, next, def_block, sizes);
8634 }
8635 }
8636 Stmt::TryWithResources {
8637 resources,
8638 body,
8639 catches,
8640 finally,
8641 } => {
8642 let id = *next;
8644 *next += 1;
8645 sizes.push(0);
8646 for r in resources {
8647 scope_pass_a(r, id, next, def_block, sizes);
8648 }
8649 scoped(body, next, def_block, sizes);
8650 for c in catches {
8651 scoped(&c.body, next, def_block, sizes);
8652 }
8653 if let Some(f) = finally {
8654 scoped(f, next, def_block, sizes);
8655 }
8656 sizes[id as usize] = *next - id;
8657 }
8658 Stmt::Synchronized { body, .. } => scoped(body, next, def_block, sizes),
8659 Stmt::Labeled { body, .. } => scope_pass_a(body, cur, next, def_block, sizes),
8660 _ => {}
8661 }
8662}
8663
8664fn scope_pass_b(
8669 st: &Stmt,
8670 cur: u32,
8671 next: &mut u32,
8672 def_block: &[u32],
8673 sizes: &[u32],
8674 within: &mut [u32],
8675 total: &mut [u32],
8676) {
8677 fn cnt(
8678 v: u32,
8679 at: u32,
8680 def_block: &[u32],
8681 sizes: &[u32],
8682 within: &mut [u32],
8683 total: &mut [u32],
8684 ) {
8685 let i = v as usize;
8686 if i < total.len() {
8687 total[i] += 1;
8688 let d = def_block[i];
8689 if d != u32::MAX && d <= at && at < d + sizes[d as usize] {
8690 within[i] += 1;
8691 }
8692 }
8693 }
8694 fn scoped(
8695 c: &Stmt,
8696 next: &mut u32,
8697 def_block: &[u32],
8698 sizes: &[u32],
8699 within: &mut [u32],
8700 total: &mut [u32],
8701 ) {
8702 let id = *next;
8703 *next += 1;
8704 scope_pass_b(c, id, next, def_block, sizes, within, total);
8705 }
8706 fn ex_at(
8707 e: &Expr,
8708 at: u32,
8709 def_block: &[u32],
8710 sizes: &[u32],
8711 within: &mut [u32],
8712 total: &mut [u32],
8713 ) {
8714 visit_exprs(e, &mut |x| {
8715 if let Expr::Local { var, .. } = x {
8716 cnt(*var, at, def_block, sizes, within, total);
8717 }
8718 });
8719 }
8720 match st {
8721 Stmt::For {
8722 init,
8723 cond,
8724 update,
8725 body,
8726 } => {
8727 let id = *next;
8728 *next += 1;
8729 for x in init {
8730 scope_pass_b(x, id, next, def_block, sizes, within, total);
8731 }
8732 if let Some(c) = cond {
8733 ex_at(c, id, def_block, sizes, within, total);
8734 }
8735 for u in update {
8736 ex_at(u, id, def_block, sizes, within, total);
8737 }
8738 scoped(body, next, def_block, sizes, within, total);
8739 }
8740 Stmt::TryWithResources {
8741 resources,
8742 body,
8743 catches,
8744 finally,
8745 } => {
8746 let id = *next;
8747 *next += 1;
8748 for r in resources {
8749 scope_pass_b(r, id, next, def_block, sizes, within, total);
8750 }
8751 scoped(body, next, def_block, sizes, within, total);
8752 for c in catches {
8753 scoped(&c.body, next, def_block, sizes, within, total);
8754 }
8755 if let Some(f) = finally {
8756 scoped(f, next, def_block, sizes, within, total);
8757 }
8758 }
8759 _ => {
8760 stmt_shallow_vars(st, &mut |v| cnt(v, cur, def_block, sizes, within, total));
8761 match st {
8762 Stmt::Block(v) => {
8763 for x in v {
8764 scope_pass_b(x, cur, next, def_block, sizes, within, total);
8765 }
8766 }
8767 Stmt::If {
8768 then_stmt,
8769 else_stmt,
8770 ..
8771 } => {
8772 scoped(then_stmt, next, def_block, sizes, within, total);
8773 if let Some(e) = else_stmt {
8774 scoped(e, next, def_block, sizes, within, total);
8775 }
8776 }
8777 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => {
8778 scoped(body, next, def_block, sizes, within, total)
8779 }
8780 Stmt::ForEach { body, .. } => {
8781 scoped(body, next, def_block, sizes, within, total)
8782 }
8783 Stmt::Switch { cases, default, .. } => {
8784 for c in cases {
8785 let id = *next;
8786 *next += 1;
8787 for x in &c.body {
8788 if matches!(x, Stmt::Block(_)) {
8789 scoped(x, next, def_block, sizes, within, total);
8790 } else {
8791 scope_pass_b(x, id, next, def_block, sizes, within, total);
8792 }
8793 }
8794 }
8795 if let Some(d) = default {
8796 scoped(d, next, def_block, sizes, within, total);
8797 }
8798 }
8799 Stmt::Try {
8800 body,
8801 catches,
8802 finally,
8803 } => {
8804 scoped(body, next, def_block, sizes, within, total);
8805 for c in catches {
8806 scoped(&c.body, next, def_block, sizes, within, total);
8807 }
8808 if let Some(f) = finally {
8809 scoped(f, next, def_block, sizes, within, total);
8810 }
8811 }
8812 Stmt::Synchronized { body, .. } => {
8813 scoped(body, next, def_block, sizes, within, total)
8814 }
8815 Stmt::Labeled { body, .. } => {
8816 scope_pass_b(body, cur, next, def_block, sizes, within, total)
8817 }
8818 _ => {}
8819 }
8820 }
8821 }
8822}
8823
8824pub fn strip_enum_const_inits(s: &mut Stmt, class: &crate::PoolClass) {
8833 let const_fields: jdc_core::FxHashSet<&str> = class
8834 .static_fields
8835 .iter()
8836 .filter(|f| f.access & crate::access::ACC_ENUM != 0)
8837 .map(|f| f.name.as_ref())
8838 .collect();
8839 if const_fields.is_empty() {
8840 return;
8841 }
8842 if let Stmt::Block(stmts) = s {
8843 stmts.retain(|st| {
8844 !matches!(
8845 st,
8846 Stmt::ExprStmt(Expr::Assign { target, value, .. })
8847 if matches!(&**target,
8848 Expr::Field { cls, name, is_static: true, .. }
8849 if cls.as_ref() == class.name
8850 && const_fields.contains(&**name))
8851 && matches!(&**value, Expr::New { cls: ncls, .. } if ncls.as_ref() == class.name)
8852 )
8853 });
8854 }
8855}
8856
8857fn contains_this_access(e: &Expr) -> bool {
8866 let mut hit = false;
8867 visit_exprs(e, &mut |x| {
8868 if hit {
8869 return;
8870 }
8871 match x {
8872 Expr::This => hit = true,
8873 Expr::Field { owner, is_static: false, .. } => {
8874 if owner.is_none() || matches!(&**owner.as_ref().unwrap(), Expr::This) {
8875 hit = true;
8876 }
8877 }
8878 Expr::Method { owner, is_static: false, .. } => {
8879 if owner.is_none() || matches!(&**owner.as_ref().unwrap(), Expr::This) {
8880 hit = true;
8881 }
8882 }
8883 _ => {}
8884 }
8885 });
8886 hit
8887}
8888
8889fn var_occurs_in(stmts: &[Stmt], var: u32) -> bool {
8904 stmts.iter().any(|st| {
8905 let mut hit = false;
8906 visit_stmt_exprs_ro(st, &mut |e| {
8907 if !hit {
8908 visit_exprs(e, &mut |x| {
8909 if let Expr::Local { var: v, .. } = x {
8910 if *v == var {
8911 hit = true;
8912 }
8913 }
8914 });
8915 }
8916 });
8917 hit
8918 })
8919}
8920
8921pub struct CtorHelper {
8931 pub name: String,
8932 pub ret: JavaType,
8933 pub params: Vec<(JavaType, String)>,
8934 pub body: Stmt,
8935 pub vt: VarTable,
8936}
8937
8938fn ctor_helpers() -> &'static std::sync::Mutex<std::collections::HashMap<String, Vec<CtorHelper>>> {
8939 static H: std::sync::OnceLock<std::sync::Mutex<std::collections::HashMap<String, Vec<CtorHelper>>>> =
8940 std::sync::OnceLock::new();
8941 H.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::default()))
8942}
8943
8944pub struct CtorRelay {
8949 pub formals: Vec<JavaType>,
8951 pub is_super: bool,
8952}
8953
8954type RelayMap = std::collections::HashMap<String, Vec<(String, CtorRelay)>>;
8955
8956fn ctor_relays() -> &'static std::sync::Mutex<RelayMap> {
8957 static H: std::sync::OnceLock<std::sync::Mutex<RelayMap>> = std::sync::OnceLock::new();
8958 H.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::default()))
8959}
8960
8961pub fn take_ctor_relays(class: &str) -> Vec<(String, CtorRelay)> {
8964 let mut m = ctor_relays().lock().unwrap_or_else(|e| e.into_inner());
8965 let mut v = m.remove(class).unwrap_or_default();
8966 v.sort_by(|a, b| a.0.cmp(&b.0));
8967 v
8968}
8969
8970fn array_init(args: Vec<Expr>) -> Expr {
8971 Expr::NewArray {
8972 elem: TypeRef::J(JavaType::Object(std::sync::Arc::from("java/lang/Object"))),
8973 dims: Vec::new(),
8974 trailing_dims: 0,
8975 init: Some(args),
8976 }
8977}
8978
8979fn helper_name(class: &str, desc: &str) -> String {
8982 let mut h: u64 = 0xcbf29ce484222325;
8983 for b in class.as_bytes().iter().chain(b"::").chain(desc.as_bytes()) {
8984 h ^= *b as u64;
8985 h = h.wrapping_mul(0x100000001b3);
8986 }
8987 format!("resolve${:x}", (h >> 16) & 0xffff_ffff)
8988}
8989
8990pub fn take_ctor_helpers(class: &str) -> Vec<CtorHelper> {
8993 let mut m = ctor_helpers().lock().unwrap_or_else(|e| e.into_inner());
8994 let mut v = m.remove(class).unwrap_or_default();
8995 v.sort_by(|a, b| a.name.cmp(&b.name));
8996 v
8997}
8998
8999fn strip_delegations_deep(stmts: &mut Vec<Stmt>) {
9000 stmts.retain(|s| !is_bare_ctor_call(s));
9001 for s in stmts.iter_mut() {
9002 strip_delegation_one(s);
9003 }
9004}
9005
9006fn strip_delegation_one(st: &mut Stmt) {
9007 if is_bare_ctor_call(st) {
9008 *st = Stmt::Block(Vec::new());
9009 return;
9010 }
9011 match st {
9012 Stmt::Block(v) => strip_delegations_deep(v),
9013 Stmt::If { then_stmt, else_stmt, .. } => {
9014 strip_delegation_one(then_stmt);
9015 if let Some(e) = else_stmt {
9016 strip_delegation_one(e);
9017 }
9018 }
9019 Stmt::While { body, .. }
9020 | Stmt::DoWhile { body, .. }
9021 | Stmt::ForEach { body, .. }
9022 | Stmt::Synchronized { body, .. }
9023 | Stmt::Labeled { body, .. } => strip_delegation_one(body),
9024 Stmt::For { init, body, .. } => {
9025 strip_delegations_deep(init);
9026 strip_delegation_one(body);
9027 }
9028 Stmt::Switch { cases, default, .. } => {
9029 for c in cases.iter_mut() {
9030 strip_delegations_deep(&mut c.body);
9031 }
9032 if let Some(d) = default {
9033 strip_delegation_one(d);
9034 }
9035 }
9036 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
9037 strip_delegation_one(body);
9038 for c in catches.iter_mut() {
9039 strip_delegation_one(&mut c.body);
9040 }
9041 if let Some(f) = finally {
9042 strip_delegation_one(f);
9043 }
9044 }
9045 _ => {}
9046 }
9047}
9048
9049#[allow(clippy::ptr_arg)]
9050fn rewrite_bare_returns(stmts: &mut Vec<Stmt>, val: &Expr) {
9051 for s in stmts.iter_mut() {
9052 rewrite_bare_returns_one(s, val);
9053 }
9054}
9055
9056fn rewrite_bare_returns_one(st: &mut Stmt, val: &Expr) {
9057 match st {
9058 Stmt::Return(None) => *st = Stmt::Return(Some(val.clone())),
9059 Stmt::Block(v) => rewrite_bare_returns(v, val),
9060 Stmt::If { then_stmt, else_stmt, .. } => {
9061 rewrite_bare_returns_one(then_stmt, val);
9062 if let Some(e) = else_stmt {
9063 rewrite_bare_returns_one(e, val);
9064 }
9065 }
9066 Stmt::While { body, .. }
9067 | Stmt::DoWhile { body, .. }
9068 | Stmt::ForEach { body, .. }
9069 | Stmt::Synchronized { body, .. }
9070 | Stmt::Labeled { body, .. } => rewrite_bare_returns_one(body, val),
9071 Stmt::For { init, body, .. } => {
9072 rewrite_bare_returns(init, val);
9073 rewrite_bare_returns_one(body, val);
9074 }
9075 Stmt::Switch { cases, default, .. } => {
9076 for c in cases.iter_mut() {
9077 rewrite_bare_returns(&mut c.body, val);
9078 }
9079 if let Some(d) = default {
9080 rewrite_bare_returns_one(d, val);
9081 }
9082 }
9083 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
9084 rewrite_bare_returns_one(body, val);
9085 for c in catches.iter_mut() {
9086 rewrite_bare_returns_one(&mut c.body, val);
9087 }
9088 if let Some(f) = finally {
9089 rewrite_bare_returns_one(f, val);
9090 }
9091 }
9092 _ => {}
9093 }
9094}
9095
9096fn collect_def_vars(stmts: &[Stmt], out: &mut jdc_core::FxHashSet<u32>) {
9097 for st in stmts {
9098 match st {
9099 Stmt::LocalDef { var, .. } => {
9100 out.insert(*var);
9101 }
9102 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
9103 if let Expr::Local { var, .. } = &**target {
9104 out.insert(*var);
9105 }
9106 }
9107 Stmt::Block(v) => collect_def_vars(v, out),
9108 Stmt::If { then_stmt, else_stmt, .. } => {
9109 collect_def_vars(std::slice::from_ref(then_stmt.as_ref()), out);
9110 if let Some(e) = else_stmt {
9111 collect_def_vars(std::slice::from_ref(e.as_ref()), out);
9112 }
9113 }
9114 Stmt::While { body, .. }
9115 | Stmt::DoWhile { body, .. }
9116 | Stmt::Synchronized { body, .. }
9117 | Stmt::Labeled { body, .. } => {
9118 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
9119 }
9120 Stmt::ForEach { var, body, .. } => {
9121 out.insert(*var);
9122 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
9123 }
9124 Stmt::For { init, body, .. } => {
9125 collect_def_vars(init, out);
9126 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
9127 }
9128 Stmt::Switch { cases, default, .. } => {
9129 for c in cases {
9130 collect_def_vars(&c.body, out);
9131 }
9132 if let Some(d) = default {
9133 collect_def_vars(std::slice::from_ref(d.as_ref()), out);
9134 }
9135 }
9136 Stmt::Try { body, catches, finally }
9137 | Stmt::TryWithResources { body, catches, finally, .. } => {
9138 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
9139 for c in catches {
9140 out.insert(c.var);
9141 collect_def_vars(std::slice::from_ref(c.body.as_ref()), out);
9142 }
9143 if let Some(f) = finally {
9144 collect_def_vars(std::slice::from_ref(f.as_ref()), out);
9145 }
9146 }
9147 _ => {}
9148 }
9149 }
9150}
9151
9152fn extraction_shape_ok(stmts: &[Stmt]) -> bool {
9155 stmts.iter().all(|st| match st {
9156 Stmt::Block(v) => extraction_shape_ok(v),
9157 Stmt::LocalDef { .. }
9158 | Stmt::ExprStmt(_)
9159 | Stmt::Return(_)
9160 | Stmt::Throw(_)
9161 | Stmt::Assert { .. }
9162 | Stmt::Break(_)
9163 | Stmt::Continue(_) => true,
9164 Stmt::If { then_stmt, else_stmt, .. } => {
9165 ok_one(then_stmt)
9166 && else_stmt.as_deref().map(ok_one).unwrap_or(true)
9167 }
9168 Stmt::While { body, .. }
9169 | Stmt::DoWhile { body, .. }
9170 | Stmt::ForEach { body, .. }
9171 | Stmt::Synchronized { body, .. } => ok_one(body),
9172 Stmt::Labeled { body, .. } => ok_one(body),
9173 Stmt::For { init, body, .. } => extraction_shape_ok(init) && ok_one(body),
9174 Stmt::Switch { cases, default, .. } => {
9175 cases.iter().all(|c| extraction_shape_ok(&c.body))
9176 && default.as_deref().map(ok_one).unwrap_or(true)
9177 }
9178 Stmt::Try { body, catches, finally }
9179 | Stmt::TryWithResources { body, catches, finally, .. } => {
9180 ok_one(body)
9181 && catches.iter().all(|c| ok_one(&c.body))
9182 && finally.as_deref().map(ok_one).unwrap_or(true)
9183 }
9184 _ => false,
9187 })
9188}
9189
9190fn ok_one(st: &Stmt) -> bool {
9191 extraction_shape_ok(std::slice::from_ref(st))
9192}
9193
9194fn static_safe(stmts: &[Stmt], this_id: Option<u32>) -> bool {
9195 let mut ok = true;
9196 for st in stmts {
9197 visit_stmt_exprs_ro(st, &mut |e| {
9198 visit_exprs(e, &mut |x| {
9199 match x {
9200 Expr::This => ok = false,
9201 Expr::Local { var, .. } => {
9207 if Some(*var) == this_id {
9208 ok = false;
9209 }
9210 }
9211 Expr::Field { owner: None, is_static: false, .. } => ok = false,
9213 Expr::Method { owner: None, is_static: false, .. } => ok = false,
9214 _ => {}
9215 }
9216 });
9217 });
9218 }
9219 ok
9220}
9221
9222pub fn strip_clinit_hotfix_guard(body: &mut Stmt) {
9233 if let Stmt::Block(vs) = body {
9237 flatten_top_blocks(vs);
9238 }
9239 let Stmt::Block(stmts) = body else { return };
9240 let mut gi = 0usize;
9243 while gi < stmts.len() && matches!(&stmts[gi], Stmt::LocalDef { init: None, .. }) {
9244 gi += 1;
9245 }
9246 let Some(first) = stmts.get(gi) else { return };
9247 let Stmt::If { then_stmt, else_stmt, .. } = first else {
9248 return;
9249 };
9250 if else_stmt.is_some() {
9251 return;
9252 }
9253 let mut has_ret = false;
9254 let mut has_proxy = false;
9255 let c = then_stmt.as_ref();
9256 walk_all(c, &mut |st| {
9257 if matches!(st, Stmt::Return(None)) {
9258 has_ret = true;
9259 }
9260 });
9261 let mut cc = c.clone();
9262 walk_stmt_exprs(&mut cc, &mut |e| {
9263 deep_rewrite(e, &mut |x| {
9264 if let Expr::Method { cls, .. } = x {
9265 if cls.contains("PatchProxy") {
9266 has_proxy = true;
9267 }
9268 }
9269 });
9270 });
9271 if has_ret && has_proxy {
9272 stmts.remove(gi);
9273 }
9274}
9275
9276pub(crate) fn first_leaf_stmt(s: &Stmt) -> &Stmt {
9297 match s {
9298 Stmt::Block(v) if !v.is_empty() => first_leaf_stmt(&v[0]),
9299 other => other,
9300 }
9301}
9302
9303pub fn strip_ctor_hotfix_guards(body: &mut Stmt) {
9322 fn marker_hit(e: &Expr) -> bool {
9323 let has = |n: &str| {
9324 let l = n.to_ascii_lowercase();
9325 l.contains("hotfix")
9326 || l.contains("instantrun")
9327 || l.contains("robust")
9328 || l.contains("titan")
9329 || l.contains("patchproxy")
9330 || l.contains("changequickredirect")
9331 || l.contains("constructorcode")
9332 };
9333 let mut hit = false;
9334 visit_exprs(e, &mut |x| {
9335 match x {
9336 Expr::Method { cls, .. } | Expr::New { cls, .. } | Expr::Field { cls, .. } => {
9337 if has(cls) {
9338 hit = true;
9339 }
9340 }
9341 _ => {}
9342 }
9343 if let Expr::Field { ty, .. } = x {
9344 if let TypeRef::J(JavaType::Object(n)) = ty {
9345 if has(n) {
9346 hit = true;
9347 }
9348 }
9349 }
9350 });
9351 hit
9352 }
9353 fn is_guard(st: &Stmt) -> bool {
9357 let mut cur = st;
9358 loop {
9359 match cur {
9360 Stmt::Block(v) if v.len() == 1 => cur = &v[0],
9361 Stmt::If { .. } => break,
9362 _ => return false,
9363 }
9364 }
9365 let mut has_del = false;
9366 walk_all(cur, &mut |s2| {
9367 if is_bare_ctor_call(s2) {
9368 has_del = true;
9369 }
9370 });
9371 if !has_del {
9372 return false;
9373 }
9374 let mut has_marker = false;
9375 visit_stmt_exprs_ro(cur, &mut |e| {
9376 if marker_hit(e) {
9377 has_marker = true;
9378 }
9379 });
9380 has_marker
9381 }
9382 fn strip_in(stmts: &mut Vec<Stmt>) {
9393 stmts.retain(|st| !is_guard(st));
9394 for st in stmts.iter_mut() {
9395 if let Stmt::Block(v) = st {
9396 strip_in(v);
9397 }
9398 }
9399 }
9400 let Stmt::Block(stmts) = body else { return };
9401 strip_in(stmts);
9402}
9403
9404pub fn hoist_branch_delegations(body: &mut Stmt, vt: &VarTable) {
9417 let Stmt::Block(stmts) = body else { return };
9418 if stmts.first().is_some_and(is_bare_ctor_call) {
9419 return; }
9421 let mut dels: Vec<Expr> = Vec::new();
9422 for st in stmts.iter() {
9423 visit_stmt_exprs_ro(st, &mut |e| {
9424 if is_delegation_expr(e) {
9425 dels.push(e.clone());
9426 }
9427 });
9428 }
9429 if dels.is_empty() {
9430 return;
9431 }
9432 if !dels.iter().all(|d| *d == dels[0]) {
9433 return;
9434 }
9435 let mut bad = false;
9436 visit_exprs(&dels[0], &mut |x| {
9437 if let Expr::Local { var, .. } = x {
9438 if (*var as usize) >= vt.vars.len() || !vt.vars[*var as usize].is_param {
9439 bad = true;
9440 }
9441 }
9442 });
9443 if bad {
9444 return;
9445 }
9446 let del = dels[0].clone();
9447 strip_delegations_deep(stmts);
9448 stmts.insert(0, Stmt::ExprStmt(del));
9449}
9450
9451pub fn extract_branched_delegation_helper(
9485 body: &mut Stmt,
9486 vt: &VarTable,
9487 pool: &DexPool,
9488 class: &crate::PoolClass,
9489 desc_str: &str,
9490) {
9491 if class.is_enum() {
9492 return;
9493 }
9494 if class.nesting.enclosing_class.is_some() && !crate::ctx::nested_is_static(pool, class) {
9499 return;
9500 }
9501 let Stmt::Block(stmts) = body else { return };
9502 let mut dels: Vec<Expr> = Vec::new();
9503 for st in stmts.iter() {
9504 visit_stmt_exprs_ro(st, &mut |e| {
9505 if is_delegation_expr(e) {
9506 dels.push(e.clone());
9507 }
9508 });
9509 }
9510 if dels.is_empty() {
9511 return;
9512 }
9513 if dels.len() == 1 && stmts.first().is_some_and(is_bare_ctor_call) {
9514 return; }
9516 let template = dels[0].clone();
9517 let Expr::Method { args: t_args, .. } = &template else {
9518 return;
9519 };
9520 enum Mode {
9524 Single(u32),
9525 Position(usize),
9526 Full,
9533 }
9534 let full_gate = (|| -> Option<()> {
9541 let Expr::Method {
9542 args: a0,
9543 desc: d0,
9544 is_super: s0,
9545 cls: c0,
9546 ..
9547 } = &dels[0]
9548 else {
9549 return None;
9550 };
9551 if a0.is_empty() {
9552 return None;
9553 }
9554 for d in &dels[1..] {
9555 match d {
9556 Expr::Method {
9557 args,
9558 desc,
9559 is_super,
9560 cls,
9561 ..
9562 } if args.len() == a0.len()
9563 && **desc == **d0
9564 && is_super == s0
9565 && cls == c0 => {}
9566 _ => return None,
9567 }
9568 }
9569 if class
9570 .all_methods()
9571 .any(|m| &*m.name == "<init>" && &*m.desc == "([Ljava/lang/Object;)V")
9572 {
9573 return None;
9574 }
9575 Some(())
9576 })()
9577 .is_some();
9578 let mode = if dels.iter().all(|d| *d == dels[0]) {
9579 let mut carrier: Option<u32> = None;
9580 let mut reads = 0usize;
9581 let mut multi = false;
9582 for a in t_args.iter() {
9583 let c = a.clone();
9584 visit_exprs(&c, &mut |x| {
9585 if let Expr::Local { var, .. } = x {
9586 if (*var as usize) < vt.vars.len() && !vt.vars[*var as usize].is_param {
9587 reads += 1;
9588 match carrier {
9589 Some(v) if v == *var => {}
9590 Some(_) => multi = true,
9591 None => carrier = Some(*var),
9592 }
9593 }
9594 }
9595 });
9596 }
9597 match carrier {
9598 Some(cv) if !multi && reads == 1 => Mode::Single(cv),
9599 _ if full_gate => Mode::Full,
9600 _ => return,
9601 }
9602 } else {
9603 let positioned: Option<Mode> = (|| {
9604 let arity = t_args.len();
9605 if dels
9609 .iter()
9610 .any(|d| !matches!(d, Expr::Method { args, .. } if args.len() == arity))
9611 {
9612 return None;
9613 }
9614 let mut diff: Vec<usize> = Vec::new();
9615 for j in 0..arity {
9616 if dels.iter().any(|d| match d {
9617 Expr::Method { args, .. } => args.get(j) != t_args.get(j),
9618 _ => true,
9619 }) {
9620 diff.push(j);
9621 }
9622 }
9623 if diff.len() != 1 {
9624 return None;
9625 }
9626 let k = diff[0];
9627 let mut base0 = dels[0].clone();
9631 if let Expr::Method { args, .. } = &mut base0 {
9632 args[k] = Expr::Const(ConstVal::Null);
9633 }
9634 for d in dels.iter() {
9635 let mut c = d.clone();
9636 if let Expr::Method { args, .. } = &mut c {
9637 args[k] = Expr::Const(ConstVal::Null);
9638 }
9639 if c != base0 {
9640 return None;
9641 }
9642 }
9643 let mut carrier_ty: Option<JavaType> = None;
9646 for d in dels.iter() {
9647 let Expr::Method { args, .. } = d else {
9648 return None;
9649 };
9650 let Expr::Local { var, .. } = &args[k] else {
9651 return None;
9652 };
9653 if (*var as usize) >= vt.vars.len() || vt.vars[*var as usize].is_param {
9654 return None;
9655 }
9656 let ty = vt.vars[*var as usize].ty.erased();
9657 match &carrier_ty {
9658 Some(t) if *t == ty => {}
9659 Some(_) => return None,
9660 None => carrier_ty = Some(ty),
9661 }
9662 for (j, a) in args.iter().enumerate() {
9668 if j == k {
9669 continue;
9670 }
9671 let mut bad = false;
9672 visit_exprs(a, &mut |x| {
9673 if let Expr::Local { var: v2, .. } = x {
9674 if (*v2 as usize) >= vt.vars.len() || !vt.vars[*v2 as usize].is_param {
9675 bad = true;
9676 }
9677 }
9678 });
9679 if bad {
9680 return None;
9681 }
9682 }
9683 }
9684 Some(Mode::Position(k))
9685 })();
9686 match positioned {
9687 Some(m) => m,
9688 None if full_gate => Mode::Full,
9689 None => return,
9690 }
9691 };
9692 let l = stmts.iter().rposition(is_bare_ctor_call);
9693 if l == Some(0) {
9694 return;
9695 }
9696 let (mut extracted, mut tail): (Vec<Stmt>, Vec<Stmt>) = match l {
9697 Some(l) => (stmts[..l].to_vec(), stmts[l + 1..].to_vec()),
9698 None => (stmts.clone(), Vec::new()),
9699 };
9700 if !extraction_shape_ok(&extracted) {
9701 return;
9702 }
9703 let mut site_tails: Vec<Vec<Stmt>> = Vec::new();
9710 {
9711 let mut probe = extracted.clone();
9712 site_tails_walk(&mut probe, &mut site_tails, false);
9713 }
9714 let hoist = !site_tails.is_empty() && {
9715 let n0 = normalize_tail(&site_tails[0]);
9716 site_tails.iter().all(|t| normalize_tail(t) == n0)
9717 && (l.is_none() || normalize_tail(&tail) == n0)
9718 };
9719 if hoist {
9720 site_tails_walk(&mut extracted, &mut Vec::new(), true);
9721 if l.is_none() {
9722 tail = normalize_tail(&site_tails[0]);
9723 }
9724 }
9725 let primary_del: Expr = match l {
9728 Some(l) => match &stmts[l] {
9729 Stmt::ExprStmt(e) => e.clone(),
9730 _ => return,
9731 },
9732 None => dels[0].clone(),
9733 };
9734 let ret = match mode {
9735 Mode::Single(cv) => vt.var(cv).ty.erased(),
9736 Mode::Position(k) => match &primary_del {
9737 Expr::Method { args, .. } => match args.get(k) {
9738 Some(Expr::Local { var, .. }) => vt.var(*var).ty.erased(),
9739 _ => return,
9740 },
9741 _ => return,
9742 },
9743 Mode::Full => JavaType::Array(Box::new(JavaType::Object(std::sync::Arc::from(
9744 "java/lang/Object",
9745 )))),
9746 };
9747 match mode {
9748 Mode::Single(cv) => {
9749 strip_delegations_deep(&mut extracted);
9750 let cv_local = Expr::Local {
9751 var: cv,
9752 ty: vt.var(cv).ty.clone(),
9753 };
9754 rewrite_bare_returns(&mut extracted, &cv_local);
9755 if !always_returns(&extracted) {
9756 extracted.push(Stmt::Return(Some(cv_local)));
9757 }
9758 }
9759 Mode::Position(k) => {
9760 fill_returns_by_position(&mut extracted, &|args: &[Expr]| args.get(k).cloned());
9764 let fallback = match &primary_del {
9765 Expr::Method { args, .. } => args[k].clone(),
9766 _ => return,
9767 };
9768 rewrite_bare_returns(&mut extracted, &fallback);
9769 if !always_returns(&extracted) {
9770 extracted.push(Stmt::Return(Some(fallback)));
9771 }
9772 }
9773 Mode::Full => {
9774 fill_returns_by_position(&mut extracted, &|args: &[Expr]| {
9781 Some(array_init(args.to_vec()))
9782 });
9783 if l.is_none() {
9784 if !always_returns(&extracted) {
9791 let cut = (1..=extracted.len())
9792 .find(|&i| always_returns(&extracted[..i]));
9793 match cut {
9794 Some(i) => extracted.truncate(i),
9795 None => return,
9796 }
9797 }
9798 }
9799 let fallback_args = match &primary_del {
9800 Expr::Method { args, .. } => args.clone(),
9801 _ => return,
9802 };
9803 let fallback = array_init(fallback_args);
9804 rewrite_bare_returns(&mut extracted, &fallback);
9805 if !always_returns(&extracted) {
9806 extracted.push(Stmt::Return(Some(fallback)));
9807 }
9808 }
9809 }
9810 let this_id = vt
9811 .vars
9812 .iter()
9813 .find(|v| v.is_param && v.name == "this")
9814 .map(|v| v.id);
9815 if !static_safe(&extracted, this_id) {
9816 return;
9817 }
9818 let mut defs: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
9819 collect_def_vars(&extracted, &mut defs);
9820 if defs.iter().any(|v| var_occurs_in(&tail, *v)) {
9821 return;
9822 }
9823 let params: Vec<(u32, JavaType, String)> = vt
9824 .vars
9825 .iter()
9826 .filter(|v| v.is_param && v.name != "this")
9827 .map(|v| (v.id, v.ty.erased(), v.name.clone()))
9828 .collect();
9829 let name = helper_name(&class.name, desc_str);
9830 let call = Expr::Method {
9831 owner: None,
9832 cls: std::sync::Arc::from(class.name.as_str()),
9833 name: std::sync::Arc::from(name.as_str()),
9834 desc: std::sync::Arc::new(jdc_core::types::MethodDescriptor {
9835 ret: ret.clone(),
9836 args: params.iter().map(|p| p.1.clone()).collect(),
9837 }),
9838 args: params
9839 .iter()
9840 .map(|(id, ty, _)| Expr::Local {
9841 var: *id,
9842 ty: TypeRef::J(ty.clone()),
9843 })
9844 .collect(),
9845 is_static: true,
9846 is_interface: false,
9847 is_special: false,
9848 is_super: false,
9849 is_dynamic: false,
9850 type_args: Vec::new(),
9851 };
9852 let mut new_del = primary_del.clone();
9853 match mode {
9854 Mode::Single(cv) => {
9855 deep_rewrite(&mut new_del, &mut |x| {
9856 if let Expr::Local { var, .. } = x {
9857 if *var == cv {
9858 *x = call.clone();
9859 }
9860 }
9861 });
9862 }
9863 Mode::Position(k) => {
9864 let Expr::Method { args, .. } = &mut new_del else {
9865 return;
9866 };
9867 if k >= args.len() {
9868 return;
9869 }
9870 args[k] = call.clone();
9871 }
9872 Mode::Full => {
9873 new_del = Expr::Method {
9877 owner: None,
9878 cls: std::sync::Arc::from(class.name.as_str()),
9879 name: "<init>".into(),
9880 desc: std::sync::Arc::new(jdc_core::types::MethodDescriptor {
9881 args: vec![JavaType::Array(Box::new(JavaType::Object(
9882 std::sync::Arc::from("java/lang/Object"),
9883 )))],
9884 ret: JavaType::Void,
9885 }),
9886 args: vec![call.clone()],
9887 is_static: false,
9888 is_interface: false,
9889 is_special: true,
9890 is_super: false,
9891 is_dynamic: false,
9892 type_args: Vec::new(),
9893 };
9894 }
9895 }
9896 {
9897 let mut m = ctor_helpers().lock().unwrap_or_else(|e| e.into_inner());
9898 let v = m.entry(class.name.clone()).or_default();
9899 if !v.iter().any(|h| h.name == name) {
9900 let mut hbody = Stmt::Block(extracted);
9904 invert_empty_thens(&mut hbody);
9905 v.push(CtorHelper {
9906 name,
9907 ret: ret.clone(),
9908 params: params.into_iter().map(|(_, ty, nm)| (ty, nm)).collect(),
9909 body: hbody,
9910 vt: vt.clone(),
9911 });
9912 if matches!(mode, Mode::Full) {
9913 let Expr::Method {
9914 desc: d0,
9915 is_super: s0,
9916 ..
9917 } = &primary_del
9918 else {
9919 return;
9920 };
9921 let mut rm = ctor_relays().lock().unwrap_or_else(|e| e.into_inner());
9922 let rv = rm.entry(class.name.clone()).or_default();
9923 if !rv.iter().any(|(d, _)| d == desc_str) {
9924 rv.push((
9925 desc_str.to_string(),
9926 CtorRelay {
9927 formals: d0.args.clone(),
9928 is_super: *s0,
9929 },
9930 ));
9931 }
9932 }
9933 } else {
9934 return; }
9936 }
9937 *stmts = std::iter::once(Stmt::ExprStmt(new_del)).chain(tail).collect();
9938}
9939
9940fn fill_returns_by_position(
9947 stmts: &mut Vec<Stmt>,
9948 pick: &dyn Fn(&[Expr]) -> Option<Expr>,
9949) {
9950 let mut i = 0;
9951 while i < stmts.len() {
9952 if is_bare_ctor_call(&stmts[i]) {
9953 let karg = match &stmts[i] {
9954 Stmt::ExprStmt(Expr::Method { args, .. }) => pick(args),
9955 _ => None,
9956 };
9957 if let Some(v) = karg {
9958 let mut j = i + 1;
9959 let mut found = None;
9960 while j < stmts.len() {
9961 if matches!(stmts[j], Stmt::Return(None)) {
9962 found = Some(j);
9963 break;
9964 }
9965 if is_bare_ctor_call(&stmts[j]) {
9966 break; }
9968 j += 1;
9969 }
9970 match found {
9971 Some(j) => {
9972 stmts[j] = Stmt::Return(Some(v));
9973 stmts.remove(i);
9974 continue;
9975 }
9976 None => {
9977 stmts[i] = Stmt::Return(Some(v));
9978 stmts.truncate(i + 1);
9979 break;
9980 }
9981 }
9982 }
9983 }
9984 fill_returns_one(&mut stmts[i], pick);
9985 i += 1;
9986 }
9987}
9988
9989fn fill_returns_one(st: &mut Stmt, pick: &dyn Fn(&[Expr]) -> Option<Expr>) {
9990 match st {
9991 Stmt::Block(v) => fill_returns_by_position(v, pick),
9992 Stmt::If { then_stmt, else_stmt, .. } => {
9993 fill_returns_stmt(then_stmt, pick);
9994 if let Some(e) = else_stmt {
9995 fill_returns_stmt(e, pick);
9996 }
9997 }
9998 Stmt::While { body, .. }
9999 | Stmt::DoWhile { body, .. }
10000 | Stmt::ForEach { body, .. }
10001 | Stmt::Synchronized { body, .. }
10002 | Stmt::Labeled { body, .. } => fill_returns_stmt(body, pick),
10003 Stmt::For { init, body, .. } => {
10004 fill_returns_by_position(init, pick);
10005 fill_returns_stmt(body, pick);
10006 }
10007 Stmt::Switch { cases, default, .. } => {
10008 for c in cases.iter_mut() {
10009 fill_returns_by_position(&mut c.body, pick);
10010 }
10011 if let Some(d) = default {
10012 fill_returns_stmt(d, pick);
10013 }
10014 }
10015 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
10016 fill_returns_stmt(body, pick);
10017 for c in catches.iter_mut() {
10018 fill_returns_stmt(&mut c.body, pick);
10019 }
10020 if let Some(f) = finally {
10021 fill_returns_stmt(f, pick);
10022 }
10023 }
10024 _ => {}
10025 }
10026}
10027
10028fn fill_returns_stmt(st: &mut Stmt, pick: &dyn Fn(&[Expr]) -> Option<Expr>) {
10029 if is_bare_ctor_call(st) {
10030 if let Stmt::ExprStmt(Expr::Method { args, .. }) = st {
10033 if let Some(v) = pick(args) {
10034 *st = Stmt::Return(Some(v));
10035 return;
10036 }
10037 }
10038 }
10039 fill_returns_one(st, pick);
10040}
10041
10042fn normalize_tail(t: &[Stmt]) -> Vec<Stmt> {
10043 let mut v = t.to_vec();
10044 while matches!(v.last(), Some(Stmt::Return(None))) {
10045 v.pop();
10046 }
10047 v
10048}
10049
10050fn site_tails_walk(stmts: &mut Vec<Stmt>, out: &mut Vec<Vec<Stmt>>, carve: bool) {
10055 let mut i = 0;
10056 while i < stmts.len() {
10057 if is_bare_ctor_call(&stmts[i]) {
10058 let mut j = i + 1;
10059 let mut end = stmts.len();
10060 while j < stmts.len() {
10061 if matches!(stmts[j], Stmt::Return(None)) {
10062 end = j;
10063 break;
10064 }
10065 if is_bare_ctor_call(&stmts[j]) {
10066 end = j;
10067 break;
10068 }
10069 j += 1;
10070 }
10071 out.push(stmts[i + 1..end].to_vec());
10072 if carve {
10073 stmts.splice(i + 1..end, []);
10074 }
10075 i += 1;
10076 continue;
10077 }
10078 site_tails_one(&mut stmts[i], out, carve);
10079 i += 1;
10080 }
10081}
10082
10083fn site_tails_one(st: &mut Stmt, out: &mut Vec<Vec<Stmt>>, carve: bool) {
10084 if is_bare_ctor_call(st) {
10085 out.push(Vec::new());
10086 return;
10087 }
10088 match st {
10089 Stmt::Block(v) => site_tails_walk(v, out, carve),
10090 Stmt::If { then_stmt, else_stmt, .. } => {
10091 site_tails_one(then_stmt, out, carve);
10092 if let Some(e) = else_stmt {
10093 site_tails_one(e, out, carve);
10094 }
10095 }
10096 Stmt::While { body, .. }
10097 | Stmt::DoWhile { body, .. }
10098 | Stmt::ForEach { body, .. }
10099 | Stmt::Synchronized { body, .. }
10100 | Stmt::Labeled { body, .. } => site_tails_one(body, out, carve),
10101 Stmt::For { init, body, .. } => {
10102 site_tails_walk(init, out, carve);
10103 site_tails_one(body, out, carve);
10104 }
10105 Stmt::Switch { cases, default, .. } => {
10106 for c in cases.iter_mut() {
10107 site_tails_walk(&mut c.body, out, carve);
10108 }
10109 if let Some(d) = default {
10110 site_tails_one(d, out, carve);
10111 }
10112 }
10113 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
10114 site_tails_one(body, out, carve);
10115 for c in catches.iter_mut() {
10116 site_tails_one(&mut c.body, out, carve);
10117 }
10118 if let Some(f) = finally {
10119 site_tails_one(f, out, carve);
10120 }
10121 }
10122 _ => {}
10123 }
10124}
10125
10126fn always_returns(stmts: &[Stmt]) -> bool {
10132 let Some(last) = stmts
10133 .iter()
10134 .rev()
10135 .find(|s| !matches!(s, Stmt::Block(v) if v.is_empty()))
10136 else {
10137 return false;
10138 };
10139 match last {
10140 Stmt::Return(Some(_)) | Stmt::Throw(_) => true,
10141 Stmt::Block(v) => always_returns(v),
10142 Stmt::If { then_stmt, else_stmt, .. } => match else_stmt {
10143 Some(e) => always_returns_one(then_stmt) && always_returns_one(e),
10144 None => false,
10145 },
10146 Stmt::Labeled { body, .. } | Stmt::Synchronized { body, .. } => {
10147 always_returns_one(body)
10148 }
10149 Stmt::Switch { cases, default, .. } => {
10150 default.as_ref().is_some_and(|d| always_returns_one(d))
10151 && cases.iter().all(|c| always_returns(&c.body))
10152 }
10153 Stmt::Try { body, catches, finally }
10154 | Stmt::TryWithResources { body, catches, finally, .. } => {
10155 finally.as_ref().is_some_and(|f| always_returns_one(f))
10156 || (always_returns_one(body)
10157 && catches.iter().all(|c| always_returns_one(&c.body)))
10158 }
10159 _ => false,
10160 }
10161}
10162
10163fn always_returns_one(st: &Stmt) -> bool {
10164 always_returns(std::slice::from_ref(st))
10165}
10166
10167fn inline_ctor_arg_defs(
10168 call: &mut Stmt,
10169 prefix: &[&Stmt],
10170 vt: &VarTable,
10171 consumed: &mut Vec<(u32, usize)>,
10172) -> bool {
10173 let Stmt::ExprStmt(cexpr) = call else {
10174 return false;
10175 };
10176 fn subst(e: &mut Expr, v: u32, val: &Option<Expr>) -> bool {
10179 let mut ok = true;
10180 deep_rewrite(e, &mut |x| {
10181 if let Expr::Local { var, .. } = x {
10182 if *var == v {
10183 match val {
10184 Some(e2) => *x = e2.clone(),
10185 None => ok = false,
10186 }
10187 }
10188 }
10189 });
10190 ok
10191 }
10192 let mut seen: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
10193 for _ in 0..32 {
10194 let mut need: Option<u32> = None;
10195 visit_exprs(cexpr, &mut |x| {
10196 if need.is_none() {
10197 if let Expr::Local { var, .. } = x {
10198 if (*var as usize) < vt.vars.len() && !vt.vars[*var as usize].is_param {
10199 need = Some(*var);
10200 }
10201 }
10202 }
10203 });
10204 let Some(v) = need else { break };
10205 if !seen.insert(v) {
10206 return false; }
10208 let mut val: Option<Expr> = None;
10220 let mut ok = true;
10221 for (si, st) in prefix.iter().enumerate() {
10222 match st {
10223 Stmt::LocalDef { var, init: None, .. } if *var == v => {}
10227 Stmt::LocalDef { var, init, .. } if *var == v => {
10228 let Some(e) = init else { ok = false; break };
10229 let mut e2 = (*e).clone();
10230 if !subst(&mut e2, v, &val) {
10231 ok = false;
10232 break;
10233 }
10234 val = Some(e2);
10235 consumed.push((v, si));
10236 }
10237 Stmt::ExprStmt(Expr::Assign { target, op, .. })
10238 if matches!(&**target, Expr::Local { var: tv, .. } if *tv == v) =>
10239 {
10240 if !matches!(op, AssignOp::Plain) {
10241 ok = false; break;
10243 }
10244 let Stmt::ExprStmt(Expr::Assign { value, .. }) = st else {
10245 unreachable!()
10246 };
10247 let mut e2 = value.as_ref().clone();
10248 if !subst(&mut e2, v, &val) {
10249 ok = false;
10250 break;
10251 }
10252 val = Some(e2);
10253 consumed.push((v, si));
10254 }
10255 _ => {
10256 let mut touched = false;
10257 visit_all_exprs(st, &mut |x| {
10258 if let Expr::Local { var, .. } = x {
10259 if *var == v {
10260 touched = true;
10261 }
10262 }
10263 });
10264 if touched {
10265 ok = false;
10266 break;
10267 }
10268 }
10269 }
10270 }
10271 if !ok {
10272 return false;
10273 }
10274 let Some(de) = val else { return false };
10275 if contains_this_access(&de) {
10276 return false;
10277 }
10278 let mut de_nodes = 0usize;
10280 visit_exprs(&de, &mut |_| de_nodes += 1);
10281 if de_nodes > 256 {
10282 return false;
10283 }
10284 deep_rewrite(cexpr, &mut |x| {
10285 if let Expr::Local { var, .. } = x {
10286 if *var == v {
10287 *x = de.clone();
10288 }
10289 }
10290 });
10291 let mut nodes = 0usize;
10296 visit_exprs(cexpr, &mut |_| nodes += 1);
10297 if nodes > 2_048 {
10298 return false;
10299 }
10300 }
10301 true
10302}
10303
10304fn fold_sb_chain(stmts: &mut Vec<Stmt>, pos: usize) -> usize {
10317 let mut sbv: Option<u32> = None;
10319 let mut def_i: Option<usize> = None;
10320 let mut def_init: Option<Expr> = None;
10321 for (i, st) in stmts[..pos].iter().enumerate() {
10322 let cand: Option<(u32, &Expr)> = match st {
10323 Stmt::LocalDef {
10324 var,
10325 init: Some(e),
10326 ..
10327 } => Some((*var, e)),
10328 Stmt::ExprStmt(Expr::Assign {
10329 target,
10330 value,
10331 op: AssignOp::Plain,
10332 ..
10333 }) => match &**target {
10334 Expr::Local { var, .. } => Some((*var, value.as_ref())),
10335 _ => None,
10336 },
10337 _ => None,
10338 };
10339 if let Some((v, init)) = cand {
10340 if let Expr::New { cls, .. } = init {
10341 if cls.as_ref() == "java/lang/StringBuilder" {
10342 sbv = Some(v);
10343 def_i = Some(i);
10344 def_init = Some(init.clone());
10345 break;
10346 }
10347 }
10348 }
10349 }
10350 let (Some(sbv), Some(def_i), Some(init)) = (sbv, def_i, def_init) else {
10351 return pos;
10352 };
10353 if contains_this_access(&init) {
10354 return pos;
10355 }
10356 let mut appends: Vec<usize> = Vec::new();
10358 for (i, st) in stmts[..pos].iter().enumerate() {
10359 if let Stmt::ExprStmt(Expr::Method {
10360 owner: Some(o),
10361 cls,
10362 name,
10363 ..
10364 }) = st
10365 {
10366 if matches!(&**o, Expr::Local { var, .. } if *var == sbv)
10367 && cls.as_ref() == "java/lang/StringBuilder"
10368 && &**name == "append"
10369 {
10370 appends.push(i);
10371 }
10372 }
10373 }
10374 let mut post_read = false;
10376 for st in stmts.iter().skip(pos + 1) {
10377 visit_all_exprs(st, &mut |e| {
10378 if let Expr::Local { var, .. } = e {
10379 if *var == sbv {
10380 post_read = true;
10381 }
10382 }
10383 });
10384 }
10385 if post_read {
10386 return pos;
10387 }
10388 for (i, st) in stmts[..pos].iter().enumerate() {
10391 if i == def_i || appends.contains(&i) {
10392 if let Stmt::ExprStmt(Expr::Method { args, .. }) = st {
10394 if args.iter().any(contains_this_access) {
10395 return pos;
10396 }
10397 }
10398 continue;
10399 }
10400 let mut leak = false;
10401 visit_all_exprs(st, &mut |e| {
10402 if let Expr::Local { var, .. } = e {
10403 if *var == sbv {
10404 leak = true;
10405 }
10406 }
10407 });
10408 if leak {
10409 return pos;
10410 }
10411 }
10412 let mut local_reads = 0usize;
10414 let mut tostring_reads = 0usize;
10415 visit_all_exprs(&stmts[pos], &mut |e| match e {
10416 Expr::Local { var, .. } if *var == sbv => local_reads += 1,
10417 Expr::Method {
10418 owner: Some(o),
10419 name,
10420 args,
10421 ..
10422 } if args.is_empty()
10423 && &**name == "toString"
10424 && matches!(&**o, Expr::Local { var, .. } if *var == sbv) =>
10425 {
10426 tostring_reads += 1;
10427 }
10428 _ => {}
10429 });
10430 if local_reads != 1 || tostring_reads != 1 {
10431 return pos;
10432 }
10433 let mut chain = init;
10435 for ai in &appends {
10436 if let Stmt::ExprStmt(Expr::Method {
10437 owner: _,
10438 cls,
10439 name,
10440 desc,
10441 args,
10442 is_static,
10443 is_interface,
10444 is_special,
10445 is_super,
10446 is_dynamic,
10447 type_args,
10448 ..
10449 }) = &stmts[*ai]
10450 {
10451 chain = Expr::Method {
10452 owner: Some(Box::new(chain)),
10453 cls: cls.clone(),
10454 name: name.clone(),
10455 desc: desc.clone(),
10456 args: args.clone(),
10457 is_static: *is_static,
10458 is_interface: *is_interface,
10459 is_special: *is_special,
10460 is_super: *is_super,
10461 is_dynamic: *is_dynamic,
10462 type_args: type_args.clone(),
10463 };
10464 }
10465 }
10466 let mut replaced = false;
10467 walk_stmt_exprs(&mut stmts[pos], &mut |e| {
10468 deep_rewrite(e, &mut |x| {
10469 if let Expr::Method {
10470 owner: Some(o),
10471 name,
10472 args,
10473 ..
10474 } = x
10475 {
10476 if args.is_empty()
10477 && &**name == "toString"
10478 && matches!(&**o, Expr::Local { var, .. } if *var == sbv)
10479 {
10480 **o = chain.clone();
10481 replaced = true;
10482 }
10483 }
10484 });
10485 });
10486 if !replaced {
10487 return pos; }
10489 let mut removed: Vec<usize> = appends.clone();
10491 removed.push(def_i);
10492 removed.sort_unstable();
10493 removed.dedup();
10494 for i in removed.iter().rev() {
10495 stmts.remove(*i);
10496 }
10497 pos - removed.len()
10498}
10499
10500pub fn fix_ctor_super_first(body: &mut Stmt, vt: &VarTable) {
10501 let Stmt::Block(stmts) = body else { return };
10502 if stmts.is_empty() || is_bare_ctor_call(stmts.first().unwrap()) {
10503 return;
10504 }
10505 if let Some(pos0) = stmts.iter().position(is_bare_ctor_call) {
10507 let pos = fold_sb_chain(stmts, pos0);
10508 let mut call = stmts[pos].clone();
10509 let prefix: Vec<&Stmt> = stmts[..pos].iter().collect();
10510 let mut consumed: Vec<(u32, usize)> = Vec::new();
10511 if !inline_ctor_arg_defs(&mut call, &prefix, vt, &mut consumed) {
10512 return;
10513 }
10514 stmts.remove(pos);
10515 let mut drop_idx: Vec<usize> = consumed
10522 .iter()
10523 .filter(|(v, _)| !var_occurs_in(&stmts[pos..], *v))
10524 .map(|(_, k)| *k)
10525 .collect();
10526 drop_idx.sort_unstable();
10527 drop_idx.dedup();
10528 for k in drop_idx.iter().rev() {
10529 stmts.remove(*k);
10530 }
10531 stmts.insert(0, call);
10532 return;
10533 }
10534 let mut found: Option<(usize, usize)> = None;
10544 for (i, st) in stmts.iter().enumerate() {
10545 if let Stmt::Block(inner) = st {
10546 if let Some(p) = inner.iter().position(is_bare_ctor_call) {
10547 if inner[..p]
10548 .iter()
10549 .all(|s| matches!(s, Stmt::LocalDef { .. } | Stmt::ExprStmt(_)))
10550 {
10551 found = Some((i, p));
10552 break;
10553 }
10554 }
10555 }
10556 }
10557 if let Some((i, p)) = found {
10558 let mut call = match &stmts[i] {
10559 Stmt::Block(inner) => inner[p].clone(),
10560 _ => unreachable!(),
10561 };
10562 let mut prefix: Vec<&Stmt> = stmts[..i].iter().collect();
10563 if let Stmt::Block(inner) = &stmts[i] {
10564 prefix.extend(inner[..p].iter());
10565 }
10566 let mut consumed: Vec<(u32, usize)> = Vec::new();
10567 if !inline_ctor_arg_defs(&mut call, &prefix, vt, &mut consumed) {
10568 return;
10569 }
10570 let mut ks: Vec<usize> = Vec::new();
10577 {
10578 let inner_tail: &[Stmt] = match &stmts[i] {
10579 Stmt::Block(inner) if p < inner.len() => &inner[p + 1..],
10580 _ => &[],
10581 };
10582 for (v, k) in &consumed {
10583 if !var_occurs_in(inner_tail, *v) && !var_occurs_in(&stmts[i + 1..], *v) {
10584 ks.push(*k);
10585 }
10586 }
10587 }
10588 ks.sort_unstable();
10589 ks.dedup();
10590 if let Stmt::Block(inner) = &mut stmts[i] {
10591 inner.remove(p);
10592 for k in ks.iter().rev().filter(|k| **k >= i) {
10593 inner.remove(k - i);
10594 }
10595 }
10596 for k in ks.iter().rev().filter(|k| **k < i) {
10597 stmts.remove(*k);
10598 }
10599 stmts.insert(0, call);
10600 }
10601}
10602
10603fn is_delegation_expr(e: &Expr) -> bool {
10612 matches!(
10613 e,
10614 Expr::Method { name, is_special: true, owner, .. }
10615 if &**name == "<init>"
10616 && (owner.is_none() || matches!(owner.as_deref(), Some(Expr::This)))
10617 )
10618}
10619
10620pub(crate) fn is_bare_ctor_call(s: &Stmt) -> bool {
10621 matches!(s, Stmt::ExprStmt(e) if is_delegation_expr(e))
10622}
10623
10624pub(crate) fn local_is_written(body: &Stmt, var: u32) -> bool {
10628 let mut hit = false;
10629 visit_stmt_exprs_ro(body, &mut |e| {
10630 if hit {
10631 return;
10632 }
10633 visit_exprs(e, &mut |x| {
10634 if hit {
10635 return;
10636 }
10637 match x {
10638 Expr::Assign { target, .. }
10639 if matches!(&**target, Expr::Local { var: v, .. } if *v == var) =>
10640 {
10641 hit = true;
10642 }
10643 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. }
10644 if matches!(&**e, Expr::Local { var: v, .. } if *v == var) =>
10645 {
10646 hit = true;
10647 }
10648 _ => {}
10649 }
10650 });
10651 });
10652 hit
10653}
10654
10655pub(crate) fn rewrite_inner_ctor_outer_param(
10665 body: &mut Stmt,
10666 param0: u32,
10667 outer_display: &str,
10668 outer_ty: &TypeRef,
10669 eligible: &[String],
10670) {
10671 walk_stmt_exprs(body, &mut |e| {
10675 if let Expr::Method { name, cls, args, .. } = e {
10676 if &**name == "<init>"
10677 && eligible.iter().any(|c| c.as_str() == cls.as_ref())
10678 && args.first().is_some_and(|a| matches!(a, Expr::Local { var: v, .. } if *v == param0))
10679 {
10680 args.remove(0);
10681 }
10682 }
10683 });
10684 let text = format!("{outer_display}.this");
10686 let ty = outer_ty.clone();
10687 walk_stmt_exprs(body, &mut |e| {
10688 deep_rewrite_reads(e, &mut |x| {
10689 if let Expr::Local { var: v, .. } = x {
10690 if *v == param0 {
10691 *x = Expr::RawT(text.clone(), ty.clone());
10692 }
10693 }
10694 });
10695 });
10696}
10697
10698fn stmts_count_writes(stmts: &[Stmt], var: u32) -> usize {
10746 let mut n = 0usize;
10747 for s in stmts {
10748 visit_stmt_exprs_ro(s, &mut |e| {
10749 visit_exprs(e, &mut |x| match x {
10750 Expr::Assign { target, .. }
10751 if matches!(&**target, Expr::Local { var: v, .. } if *v == var) =>
10752 {
10753 n += 1;
10754 }
10755 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. }
10756 if matches!(&**e, Expr::Local { var: v, .. } if *v == var) =>
10757 {
10758 n += 1;
10759 }
10760 _ => {}
10761 });
10762 });
10763 }
10764 n
10765}
10766
10767fn prefix_written_vars(stmts: &[Stmt]) -> jdc_core::FxHashSet<u32> {
10771 let mut set = jdc_core::FxHashSet::default();
10772 for s in stmts {
10773 let mut c = s.clone();
10774 walk_stmt_exprs(&mut c, &mut |e| {
10775 deep_rewrite(e, &mut |x| match x {
10776 Expr::Assign { target, .. } => {
10777 if let Expr::Local { var, .. } = &**target {
10778 set.insert(*var);
10779 }
10780 }
10781 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => {
10782 if let Expr::Local { var, .. } = &**e {
10783 set.insert(*var);
10784 }
10785 }
10786 _ => {}
10787 });
10788 });
10789 if let Stmt::LocalDef { var, .. } = s {
10790 set.insert(*var);
10791 }
10792 }
10793 set
10794}
10795
10796fn pre_this_safe(e: &Expr, prefix_vars: &jdc_core::FxHashSet<u32>) -> bool {
10800 match e {
10801 Expr::Local { var, .. } => !prefix_vars.contains(var),
10802 Expr::Const(_) => true,
10803 Expr::Cast { e, .. } => pre_this_safe(e, prefix_vars),
10804 Expr::Bin { l, r, .. } => {
10805 pre_this_safe(l, prefix_vars) && pre_this_safe(r, prefix_vars)
10806 }
10807 Expr::Cond { c, t, f } => {
10808 pre_this_safe(c, prefix_vars)
10809 && pre_this_safe(t, prefix_vars)
10810 && pre_this_safe(f, prefix_vars)
10811 }
10812 Expr::Field { owner, is_static, .. } => {
10813 *is_static
10814 && owner
10815 .as_ref()
10816 .is_none_or(|o| pre_this_safe(o, prefix_vars))
10817 }
10818 Expr::Method {
10819 owner,
10820 args,
10821 is_static,
10822 ..
10823 } => {
10824 args.iter().all(|a| pre_this_safe(a, prefix_vars))
10825 && match owner {
10826 Some(o) => pre_this_safe(o, prefix_vars),
10827 None => *is_static,
10828 }
10829 }
10830 Expr::New { args, .. } => args.iter().all(|a| pre_this_safe(a, prefix_vars)),
10838 Expr::NewArray { dims, init, .. } => {
10839 dims.iter().all(|d| pre_this_safe(d, prefix_vars))
10840 && init
10841 .as_ref()
10842 .map(|l| l.iter().all(|x| pre_this_safe(x, prefix_vars)))
10843 .unwrap_or(true)
10844 }
10845 Expr::Un { e, .. } | Expr::InstanceOf { e, .. } => pre_this_safe(e, prefix_vars),
10847 Expr::ArrayIndex { array, index } => {
10848 pre_this_safe(array, prefix_vars) && pre_this_safe(index, prefix_vars)
10849 }
10850 _ => false,
10851 }
10852}
10853
10854fn single_assign_to_v(s: &Stmt, v: u32) -> Option<Expr> {
10858 match s {
10859 Stmt::ExprStmt(Expr::Assign {
10860 target,
10861 op: AssignOp::Plain,
10862 value,
10863 }) if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v) => {
10864 Some((**value).clone())
10865 }
10866 Stmt::Block(xs) if xs.len() == 1 => single_assign_to_v(&xs[0], v),
10867 _ => None,
10868 }
10869}
10870
10871pub(crate) fn flatten_top_blocks(stmts: &mut Vec<Stmt>) {
10893 fn rec(s: Stmt, out: &mut Vec<Stmt>) {
10894 match s {
10895 Stmt::Block(inner) => {
10896 for x in inner {
10897 rec(x, out);
10898 }
10899 }
10900 other => out.push(other),
10901 }
10902 }
10903 if !stmts.iter().any(|s| matches!(s, Stmt::Block(_))) {
10904 return;
10905 }
10906 let mut out: Vec<Stmt> = Vec::with_capacity(stmts.len());
10907 for s in std::mem::take(stmts) {
10908 rec(s, &mut out);
10909 }
10910 *stmts = out;
10911}
10912
10913fn fold_default_arg_bridge(stmts: &mut Vec<Stmt>) -> bool {
10914 flatten_top_blocks(stmts);
10915 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
10916 return false;
10917 };
10918 if pos == 0 {
10919 return false;
10920 }
10921 let Stmt::ExprStmt(Expr::Method { args, .. }) = &stmts[pos] else {
10922 return false;
10923 };
10924 let mut arg_vars: Vec<u32> = Vec::new();
10925 let mut reads = 0usize;
10926 for a in args {
10927 let mut c = a.clone();
10928 deep_rewrite_reads(&mut c, &mut |x| {
10929 if let Expr::Local { var, .. } = x {
10930 reads += 1;
10931 if !arg_vars.contains(var) {
10932 arg_vars.push(*var);
10933 }
10934 }
10935 });
10936 }
10937 if arg_vars.is_empty() || reads != arg_vars.len() {
10938 return false; }
10940 for &v in &arg_vars {
10943 if stmts_read_var(&stmts[pos + 1..], v) != 0
10944 || stmts_count_writes(&stmts[pos + 1..], v) != 0
10945 {
10946 return false;
10947 }
10948 }
10949 let prefix_vars = prefix_written_vars(&stmts[..pos]);
10950 let mut repl: Vec<(u32, Expr)> = Vec::new();
10951 let mut drop: Vec<usize> = Vec::new();
10952 for &v in &arg_vars {
10953 let mut base: Option<(usize, Expr)> = None;
10955 let mut decl: Option<usize> = None;
10956 let mut condf: Option<(usize, Expr, Option<Expr>, Option<Expr>)> = None;
10957 let mut ok = true;
10958 for (i, s) in stmts[..pos].iter().enumerate() {
10959 match s {
10960 Stmt::LocalDef { var, init, .. } if *var == v => match init {
10961 None => {
10962 if decl.is_some() {
10963 ok = false;
10964 break;
10965 }
10966 decl = Some(i);
10967 }
10968 Some(e) => {
10969 if base.is_some() {
10970 ok = false;
10971 break;
10972 }
10973 base = Some((i, e.clone()));
10974 }
10975 },
10976 Stmt::ExprStmt(Expr::Assign {
10977 target,
10978 op: AssignOp::Plain,
10979 value,
10980 }) if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v) => {
10981 if base.is_some() {
10982 ok = false;
10983 break;
10984 }
10985 base = Some((i, (**value).clone()));
10986 }
10987 Stmt::If {
10988 cond,
10989 then_stmt,
10990 else_stmt,
10991 } => {
10992 let t = single_assign_to_v(then_stmt, v);
10993 let f = else_stmt.as_ref().and_then(|b| single_assign_to_v(b, v));
10994 let then_touches = t.is_some()
10995 || stmts_read_var(std::slice::from_ref(&**then_stmt), v) != 0
10996 || stmts_count_writes(std::slice::from_ref(&**then_stmt), v) != 0;
10997 let else_touches = else_stmt.as_ref().is_some_and(|b| {
10998 f.is_some()
10999 || stmts_read_var(std::slice::from_ref(b), v) != 0
11000 || stmts_count_writes(std::slice::from_ref(b), v) != 0
11001 });
11002 if !then_touches && !else_touches {
11003 continue; }
11005 if condf.is_some()
11010 || base.is_none()
11011 || (then_touches && t.is_none())
11012 || (else_touches && f.is_none())
11013 {
11014 ok = false;
11015 break;
11016 }
11017 let cond_stmt = Stmt::ExprStmt(cond.clone());
11018 if stmts_read_var(std::slice::from_ref(&cond_stmt), v) != 0 {
11019 ok = false;
11020 break;
11021 }
11022 condf = Some((i, cond.clone(), t, f));
11023 }
11024 _ => {}
11025 }
11026 }
11027 if !ok {
11028 continue;
11029 }
11030 let Some((bi, be)) = base else { continue };
11031 let want_writes = 1
11034 + condf
11035 .as_ref()
11036 .map(|(_, _, t, f)| t.is_some() as usize + f.is_some() as usize)
11037 .unwrap_or(0);
11038 if stmts_count_writes(&stmts[..pos], v) != want_writes
11039 || stmts_read_var(&stmts[..pos], v) != 0
11040 {
11041 continue;
11042 }
11043 let rep = match condf {
11044 Some((ci, c, t, f)) => {
11045 drop.push(ci);
11046 match (t, f) {
11047 (Some(t), Some(f)) => Expr::Cond {
11048 c: Box::new(c),
11049 t: Box::new(t),
11050 f: Box::new(f),
11051 },
11052 (Some(t), None) => Expr::Cond {
11053 c: Box::new(c),
11054 t: Box::new(t),
11055 f: Box::new(be.clone()),
11056 },
11057 (None, Some(f)) => Expr::Cond {
11058 c: Box::new(c),
11059 t: Box::new(be.clone()),
11060 f: Box::new(f),
11061 },
11062 (None, None) => unreachable!("condf requires a branch assign"),
11063 }
11064 }
11065 None => be.clone(),
11066 };
11067 if !pre_this_safe(&rep, &prefix_vars) {
11068 continue;
11069 }
11070 drop.push(bi);
11071 if let Some(d) = decl {
11072 drop.push(d);
11073 }
11074 repl.push((v, rep));
11075 }
11076 if repl.is_empty() {
11077 return false;
11078 }
11079 if let Stmt::ExprStmt(Expr::Method { args, .. }) = &mut stmts[pos] {
11080 for a in args.iter_mut() {
11081 deep_rewrite_reads(a, &mut |x| {
11082 if let Expr::Local { var, .. } = x {
11083 if let Some((_, e)) = repl.iter().find(|(vv, _)| vv == var) {
11084 *x = e.clone();
11085 }
11086 }
11087 });
11088 }
11089 }
11090 drop.sort_unstable();
11091 drop.dedup();
11092 for i in drop.into_iter().rev() {
11093 stmts.remove(i);
11094 }
11095 true
11096}
11097
11098pub fn rewrite_kotlin_facades(body: &mut Stmt, pool: &crate::DexPool) {
11106 let map = pool.kotlin_facade_map();
11107 if map.is_empty() {
11108 return;
11109 }
11110 walk_stmt_exprs(body, &mut |e| {
11111 match e {
11112 Expr::Method {
11113 cls,
11114 name,
11115 desc,
11116 is_static,
11117 ..
11118 } if *is_static => {
11119 if let Some((base, cover)) = map.get(cls.as_ref()) {
11120 let hit = match cover {
11121 None => true,
11122 Some(c) => c.0.contains(&(name.to_string(), desc.to_string())),
11123 };
11124 if hit {
11125 *cls = std::sync::Arc::from(base.as_str());
11126 }
11127 }
11128 }
11129 Expr::Field {
11130 cls,
11131 name,
11132 is_static,
11133 ..
11134 } if *is_static => {
11135 if let Some((base, cover)) = map.get(cls.as_ref()) {
11136 let hit = match cover {
11137 None => true,
11138 Some(c) => c.1.contains(name.as_ref()),
11139 };
11140 if hit {
11141 *cls = std::sync::Arc::from(base.as_str());
11142 }
11143 }
11144 }
11145 _ => {}
11146 }
11147 });
11148}
11149
11150pub fn fold_enum_default_arg_bridge(body: &mut Stmt) {
11158 let Stmt::Block(stmts) = body else { return };
11159 fold_default_arg_bridge(stmts);
11160}
11161
11162pub fn fix_ctor_delegation_arg_defs(body: &mut Stmt) {
11163 let Stmt::Block(stmts) = body else { return };
11164 fold_default_arg_bridge(stmts);
11167 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
11171 return;
11172 };
11173 if pos == 0 {
11174 return;
11175 }
11176 let mut arg_vars: Vec<u32> = Vec::new();
11179 let mut total_reads = 0usize;
11180 {
11181 let Stmt::ExprStmt(Expr::Method { args, .. }) = &stmts[pos] else {
11182 return;
11183 };
11184 for a in args {
11185 let mut c = a.clone();
11186 deep_rewrite_reads(&mut c, &mut |x| {
11187 if let Expr::Local { var: v, .. } = x {
11188 total_reads += 1;
11189 if !arg_vars.contains(v) {
11190 arg_vars.push(*v);
11191 }
11192 }
11193 });
11194 }
11195 }
11196 if arg_vars.is_empty() || total_reads != arg_vars.len() {
11197 return;
11198 }
11199 let prefix_defs: Vec<u32> = stmts[..pos]
11202 .iter()
11203 .filter_map(|s| match s {
11204 Stmt::LocalDef { var, init: Some(_), .. } => Some(*var),
11205 _ => None,
11206 })
11207 .collect();
11208 let mut plan: Vec<(u32, usize, Option<usize>)> = Vec::new();
11213 for v in arg_vars {
11214 let defs: Vec<usize> = stmts[..pos]
11215 .iter()
11216 .enumerate()
11217 .filter(|(i, _)| {
11218 matches!(&stmts[*i], Stmt::LocalDef { var, init: Some(_), .. } if *var == v)
11219 })
11220 .map(|(i, _)| i)
11221 .collect();
11222 if defs.len() > 1 {
11223 return; }
11225 let Some(&q) = defs.first() else {
11226 continue; };
11228 let init = match &stmts[q] {
11229 Stmt::LocalDef { init: Some(e), .. } => e.clone(),
11230 _ => return,
11231 };
11232 if stmts_read_var(&stmts[q + 1..pos], v) != 0 {
11233 return; }
11235 let mut init_vars: Vec<u32> = Vec::new();
11238 deep_rewrite_reads(&mut { init.clone() }, &mut |x| {
11239 if let Expr::Local { var: vv, .. } = x {
11240 if !init_vars.contains(vv) {
11241 init_vars.push(*vv);
11242 }
11243 }
11244 });
11245 if init_vars.iter().any(|iv| prefix_defs.contains(iv)) {
11246 return;
11247 }
11248 match stmts[pos + 1..]
11249 .iter()
11250 .position(|s| {
11251 matches!(s, Stmt::ExprStmt(Expr::Assign { target, .. })
11252 if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v))
11253 }) {
11254 Some(w) => {
11255 if stmts_read_var(&stmts[pos + 1..pos + 1 + w], v) != 0 {
11257 return;
11258 }
11259 plan.push((v, q, Some(pos + 1 + w)));
11260 }
11261 None => {
11262 if stmts_read_var(&stmts[pos + 1..], v) != 0 {
11264 return;
11265 }
11266 plan.push((v, q, None));
11267 }
11268 }
11269 }
11270 if plan.is_empty() {
11271 return;
11272 }
11273 let values: Vec<(u32, Expr)> = plan
11275 .iter()
11276 .map(|(_v, q, _)| match &stmts[*q] {
11277 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e.clone()),
11278 _ => unreachable!("plan entries carry a LocalDef with init"),
11279 })
11280 .collect();
11281 if let Stmt::ExprStmt(Expr::Method { args, .. }) = &mut stmts[pos] {
11282 for a in args {
11283 deep_rewrite_reads(a, &mut |x| {
11284 if let Expr::Local { var: v, .. } = x {
11285 if let Some((_, e)) = values.iter().find(|(vv, _)| vv == v) {
11286 *x = e.clone();
11287 }
11288 }
11289 });
11290 }
11291 }
11292 for (v, _, w) in &plan {
11296 if let Some(w) = w {
11297 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = &mut stmts[*w] {
11298 if matches!(&**target, Expr::Local { var: vv, .. } if vv == v) {
11299 let taken = std::mem::replace(&mut **value, Expr::Const(ConstVal::Null));
11300 stmts[*w] = Stmt::LocalDef {
11301 var: *v,
11302 init: Some(taken),
11303 is_final: false,
11304 force_type: false,
11305 };
11306 }
11307 }
11308 }
11309 }
11310 let mut order: Vec<usize> = plan.iter().map(|(_, q, _)| *q).collect();
11311 order.sort_unstable();
11312 order.reverse();
11313 for q in order {
11314 stmts.remove(q);
11315 }
11316}
11317
11318pub fn fix_bool_returns(vt: &VarTable, body: &mut Stmt) {
11329 walk_mut_deep(body, &mut |st| {
11330 if let Stmt::Return(Some(e)) = st {
11331 let numeric = match &*e {
11332 Expr::Local { var, ty } => {
11333 let t = if (*var as usize) < vt.vars.len() {
11334 vt.var(*var).ty.erased()
11335 } else {
11336 ty.erased()
11337 };
11338 matches!(
11339 t,
11340 JavaType::Int
11341 | JavaType::Long
11342 | JavaType::Short
11343 | JavaType::Byte
11344 | JavaType::Char
11345 )
11346 }
11347 other => matches!(
11348 other.type_ref().erased(),
11349 JavaType::Int | JavaType::Long | JavaType::Short | JavaType::Byte
11350 | JavaType::Char
11351 ),
11352 };
11353 if numeric {
11354 let taken = e.clone();
11355 *e = Expr::Bin {
11356 op: BinOp::Ne,
11357 l: Box::new(taken),
11358 r: Box::new(Expr::Const(ConstVal::Int(0))),
11359 ty: Some(TypeRef::J(JavaType::Boolean)),
11360 };
11361 }
11362 }
11363 });
11364}
11365
11366pub fn fix_int_returns(vt: &VarTable, body: &mut Stmt) {
11367 walk_mut_deep(body, &mut |st| {
11368 if let Stmt::Return(Some(e)) = st {
11369 let is_bool = match e {
11370 Expr::Local { var, .. } => {
11371 (*var as usize) < vt.vars.len()
11372 && vt.vars[*var as usize].ty.erased() == JavaType::Boolean
11373 }
11374 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
11375 _ => false,
11376 };
11377 if is_bool {
11378 *e = Expr::Cond {
11379 c: Box::new(e.clone()),
11380 t: Box::new(Expr::Const(ConstVal::Int(1))),
11381 f: Box::new(Expr::Const(ConstVal::Int(0))),
11382 };
11383 }
11384 }
11385 });
11386}
11387
11388static WIDEN_COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
11404
11405pub fn split_generations(vt: &mut VarTable, body: &mut Stmt, pool: &DexPool) {
11406 if std::env::var("DDC_DBG_PHI").is_ok() {
11407 eprintln!("[widen] cumulative={}", WIDEN_COUNT.load(std::sync::atomic::Ordering::Relaxed));
11408 }
11409 let mut counter: u32 = 0;
11410 let mut gen: std::collections::HashMap<u32, u32> = std::collections::HashMap::default();
11411 split_walk_stmt(body, vt, &mut gen, &mut counter, pool);
11412}
11413
11414fn split_needed(vt: &VarTable, target: u32, value: &Expr, pool: &DexPool) -> bool {
11416 let n = vt.vars.len();
11417 if target as usize >= n {
11418 return false;
11419 }
11420 let declared = vt.vars[target as usize].ty.erased();
11421 let vt_is_ref = |t: &JavaType| matches!(t, JavaType::Object(_) | JavaType::Array(_));
11422 fn side_bool(e: &Expr, vt: &VarTable) -> bool {
11431 match e {
11432 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
11433 matches!(vt.var(*var).ty.erased(), JavaType::Boolean)
11434 }
11435 Expr::Const(ConstVal::Int(0 | 1)) => true,
11436 Expr::Bin { op: BinOp::Or | BinOp::And | BinOp::Xor, l, r, .. } => {
11437 side_bool(l, vt) && side_bool(r, vt)
11438 }
11439 other => matches!(other.type_ref().erased(), JavaType::Boolean),
11440 }
11441 }
11442 let value_ty = match value {
11443 Expr::Local { var, ty } if (*var as usize) < vt.vars.len() => vt.var(*var).ty.erased(),
11444 Expr::Bin {
11445 op: BinOp::Or | BinOp::And | BinOp::Xor,
11446 l,
11447 r,
11448 ..
11449 } if side_bool(l, vt) && side_bool(r, vt) => JavaType::Boolean,
11450 other => other.type_ref().erased(),
11451 };
11452 if vt_is_ref(&declared) && vt_is_ref(&value_ty) {
11453 let obj = JavaType::Object("java/lang/Object".into());
11463 if declared == value_ty || declared == obj || value_ty == obj {
11464 return false;
11465 }
11466 if let (JavaType::Object(v), JavaType::Object(d)) = (&value_ty, &declared) {
11479 if matches!(value, Expr::New { .. })
11480 && (pool.get(v).is_none() || pool.get(d).is_none())
11481 {
11482 return false;
11483 }
11484 }
11485 true
11486 } else {
11487 if vt_is_ref(&declared) != vt_is_ref(&value_ty) {
11491 return true;
11492 }
11493 let d_bool = matches!(declared, JavaType::Boolean);
11501 let v_bool = matches!(value_ty, JavaType::Boolean);
11502 let zero_one = matches!(value, Expr::Const(ConstVal::Int(0 | 1)));
11503 d_bool != v_bool && !zero_one
11504 }
11505}
11506
11507fn rewrite_gen_reads(e: &mut Expr, gen: &std::collections::HashMap<u32, u32>, vt: &VarTable) {
11508 deep_rewrite(e, &mut |x| {
11509 if let Expr::Local { var, ty } = x {
11510 if let Some(&to) = gen.get(var) {
11511 if to != *var {
11512 *var = to;
11513 *ty = vt.var(to).ty.clone();
11514 }
11515 }
11516 }
11517 });
11518}
11519
11520fn do_split(
11521 vt: &mut VarTable,
11522 target: &mut Expr,
11523 old: u32,
11524 value_ty: &JavaType,
11525 gen: &mut std::collections::HashMap<u32, u32>,
11526 counter: &mut u32,
11527) {
11528 *counter += 1;
11529 let info = &vt.vars[old as usize];
11530 let id = vt.vars.len() as u32;
11531 let name = format!("{}_g{}", info.name, counter);
11532 let slot = info.slot;
11533 let is_param = false;
11541 vt.vars.push(jdc_core::var::VarInfo {
11542 id,
11543 slot,
11544 name,
11545 ty: TypeRef::J(value_ty.clone()),
11546 is_param,
11547 range_start: 0,
11548 range_end: u16::MAX,
11549 synthetic_name: true,
11550 });
11551 while vt.by_slot.len() <= slot as usize {
11552 vt.by_slot.push(Vec::new());
11553 }
11554 vt.by_slot[slot as usize].push((0, u16::MAX, id));
11555 gen.insert(old, id);
11556 if let Expr::Local { var, ty, .. } = target {
11557 *var = id;
11558 *ty = TypeRef::J(value_ty.clone());
11559 }
11560}
11561
11562fn rename_gen_back_batch(s: &mut Stmt, pairs: &[(u32, u32)], vt: &VarTable) {
11577 use jdc_core::FxHashMap;
11578 if pairs.is_empty() {
11579 return;
11580 }
11581 let map: FxHashMap<u32, (u32, JavaType)> = pairs
11582 .iter()
11583 .map(|&(from, to)| (from, (to, vt.vars[to as usize].ty.erased())))
11584 .collect();
11585 walk_mut_deep(s, &mut |st| {
11586 if let Stmt::LocalDef { var, .. } = st {
11587 if let Some(&(to, _)) = map.get(var) {
11588 *var = to;
11589 }
11590 }
11591 });
11592 rewrite_exprs(s, &mut |e| {
11593 deep_rewrite(e, &mut |x| {
11594 if let Expr::Local { var, ty } = x {
11595 if let Some(&(to, ref to_ty)) = map.get(var) {
11596 *var = to;
11597 *ty = TypeRef::J(to_ty.clone());
11598 }
11599 }
11600 });
11601 });
11602}
11603
11604fn drop_gen_slot(vt: &mut VarTable, dead: u32) {
11608 if (dead as usize) >= vt.vars.len() {
11609 return;
11610 }
11611 let slot = vt.vars[dead as usize].slot as usize;
11612 if let Some(seg) = vt.by_slot.get_mut(slot) {
11613 seg.retain(|&(_, _, id)| id != dead);
11614 }
11615}
11616
11617fn lub_pending() -> Option<String> {
11638 Some(String::new())
11639}
11640
11641enum Undo {
11643 Direct,
11645 Widen(String),
11647 Refuse,
11649}
11650
11651fn obj_name(t: &TypeRef) -> Option<&std::sync::Arc<str>> {
11652 match t {
11653 TypeRef::J(JavaType::Object(o)) => Some(o),
11654 _ => None,
11655 }
11656}
11657
11658fn pool_lub(a: &str, b: &str, pool: &DexPool) -> Option<String> {
11659 if a == b {
11660 return Some(a.to_string());
11661 }
11662 thread_local! {
11668 static MEMO: std::cell::RefCell<jdc_core::FxHashMap<(String, String), Option<String>>> =
11669 std::cell::RefCell::new(jdc_core::FxHashMap::default());
11670 }
11671 if let Some(hit) = MEMO.with(|m| m.borrow().get(&(a.to_string(), b.to_string())).cloned()) {
11672 return hit;
11673 }
11674 let r = pool_lub_uncached(a, b, pool);
11675 MEMO.with(|m| m.borrow_mut().insert((a.to_string(), b.to_string()), r.clone()));
11676 r
11677}
11678
11679fn pool_lub_uncached(a: &str, b: &str, pool: &DexPool) -> Option<String> {
11680 let mut anc: jdc_core::FxHashSet<String> = jdc_core::FxHashSet::default();
11681 let mut stack: Vec<String> = vec![a.to_string()];
11682 while let Some(c) = stack.pop() {
11683 if !anc.insert(c.clone()) {
11684 continue;
11685 }
11686 if anc.len() > 1024 {
11687 return None;
11688 }
11689 if let Some(cls) = pool.get(&c) {
11690 if let Some(s) = &cls.super_name {
11691 stack.push(s.clone());
11692 }
11693 for i in &cls.interfaces {
11694 stack.push(i.clone());
11695 }
11696 } else {
11697 if let Some(s) = crate::fwdb::super_of(&c) {
11698 stack.push(s.to_string());
11699 }
11700 crate::fwdb::for_each_interface(&c, |i| stack.push(i.to_string()));
11701 }
11702 }
11703 let mut seen: jdc_core::FxHashSet<String> = jdc_core::FxHashSet::default();
11704 let mut queue: std::collections::VecDeque<String> = std::collections::VecDeque::new();
11705 queue.push_back(b.to_string());
11706 while let Some(c) = queue.pop_front() {
11707 if !seen.insert(c.clone()) {
11708 continue;
11709 }
11710 if c != "java/lang/Object" && anc.contains(&c) {
11711 return Some(c);
11712 }
11713 if seen.len() > 1024 {
11714 return None;
11715 }
11716 if let Some(cls) = pool.get(&c) {
11717 for i in &cls.interfaces {
11718 queue.push_back(i.clone());
11719 }
11720 if let Some(s) = &cls.super_name {
11721 queue.push_back(s.clone());
11722 }
11723 } else {
11724 crate::fwdb::for_each_interface(&c, |i| queue.push_back(i.to_string()));
11725 if let Some(s) = crate::fwdb::super_of(&c) {
11726 queue.push_back(s.to_string());
11727 }
11728 }
11729 }
11730 None
11731}
11732
11733fn undo_type_safe(vt: &VarTable, k: u32, v: u32) -> bool {
11734 if (k as usize) >= vt.vars.len() || (v as usize) >= vt.vars.len() {
11735 return false;
11736 }
11737 let kt = vt.vars[k as usize].ty.erased();
11738 let vt_ty = vt.vars[v as usize].ty.erased();
11739 if kt == vt_ty {
11740 return true;
11741 }
11742 let is_prim = |t: &JavaType| {
11743 matches!(
11744 t,
11745 JavaType::Int
11746 | JavaType::Long
11747 | JavaType::Short
11748 | JavaType::Byte
11749 | JavaType::Char
11750 | JavaType::Float
11751 | JavaType::Double
11752 | JavaType::Boolean
11753 )
11754 };
11755 is_prim(&kt) && is_prim(&vt_ty)
11756}
11757
11758fn split_walk_stmt(
11759 st: &mut Stmt,
11760 vt: &mut VarTable,
11761 gen: &mut std::collections::HashMap<u32, u32>,
11762 counter: &mut u32,
11763 pool: &DexPool,
11764) {
11765 match st {
11766 Stmt::Block(v) => {
11767 for x in v.iter_mut() {
11768 split_walk_stmt(x, vt, gen, counter, pool);
11769 }
11770 }
11771 Stmt::ExprStmt(e) => {
11772 rewrite_gen_reads(e, gen, vt);
11773 if let Expr::Assign {
11774 target,
11775 value,
11776 op: AssignOp::Plain,
11777 ..
11778 } = e
11779 {
11780 if let Expr::Local { var, .. } = &**target {
11781 let old = *var;
11782 if split_needed(vt, old, value, pool) {
11783 let value_ty = value.type_ref().erased();
11784 do_split(vt, target, old, &value_ty, gen, counter);
11785 }
11786 }
11787 }
11788 }
11789 Stmt::LocalDef { var, init, .. } => {
11790 if let Some(e) = init {
11791 rewrite_gen_reads(e, gen, vt);
11792 if split_needed(vt, *var, e, pool) {
11793 let value_ty = e.type_ref().erased();
11797 let old = *var;
11798 let mut fake = Expr::Local {
11799 var: old,
11800 ty: vt.vars[old as usize].ty.clone(),
11801 };
11802 do_split(vt, &mut fake, old, &value_ty, gen, counter);
11803 if let Expr::Local { var: nv, .. } = fake {
11804 *var = nv;
11805 }
11806 }
11807 }
11808 }
11809 Stmt::If {
11810 cond,
11811 then_stmt,
11812 else_stmt,
11813 ..
11814 } => {
11815 rewrite_gen_reads(cond, gen, vt);
11816 let then_floor = vt.vars.len() as u32;
11817 let mut g_then = gen.clone();
11818 split_walk_stmt(then_stmt, vt, &mut g_then, counter, pool);
11819 let else_floor = vt.vars.len() as u32;
11820 let mut g_else = gen.clone();
11821 if let Some(e) = else_stmt {
11822 split_walk_stmt(e, vt, &mut g_else, counter, pool);
11823 }
11824 let mut undo_then: Vec<(u32, u32, Option<String>)> = g_then
11862 .iter()
11863 .filter(|(&k, &v)| {
11864 v >= then_floor
11865 && v < else_floor
11866 && g_else.get(&k).copied() == gen.get(&k).copied()
11867 })
11868 .filter_map(|(&k, &v)| {
11869 if undo_type_safe(vt, k, v) {
11870 Some((v, k, None))
11871 } else if let (Some(_), Some(_)) =
11872 (obj_name(&vt.vars[k as usize].ty), obj_name(&vt.vars[v as usize].ty))
11873 {
11874 Some((v, k, lub_pending()))
11875 } else {
11876 None
11877 }
11878 })
11879 .collect();
11880 if else_floor > then_floor && !undo_then.is_empty() {
11881 let vars: Vec<u32> = undo_then.iter().map(|&(v, _, _)| v).collect();
11882 let mut hits = vec![false; vars.len()];
11883 mark_read_vars(std::slice::from_ref(&**then_stmt), &vars, &mut hits);
11884 let mut i = 0usize;
11885 undo_then.retain(|_| {
11886 let keep = !hits[i];
11887 i += 1;
11888 keep
11889 });
11890 }
11891 undo_then.sort_unstable_by_key(|&(v, _, _)| std::cmp::Reverse(v));
11892 let mut pairs: Vec<(u32, u32)> = Vec::with_capacity(undo_then.len());
11895 for (v, k, pending) in undo_then {
11896 let kind = match pending {
11902 None => Undo::Direct,
11903 Some(_) => {
11904 let (a, b) = (
11905 obj_name(&vt.vars[k as usize].ty),
11906 obj_name(&vt.vars[v as usize].ty),
11907 );
11908 match (a, b) {
11909 (Some(a), Some(b)) if pool.is_subtype(b, a) => Undo::Direct,
11910 (Some(a), Some(b)) => match pool_lub(a, b, pool) {
11911 Some(lub) => Undo::Widen(lub),
11912 None => Undo::Refuse,
11913 },
11914 _ => Undo::Refuse,
11915 }
11916 }
11917 };
11918 match kind {
11919 Undo::Refuse => continue,
11920 Undo::Direct => pairs.push((v, k)),
11921 Undo::Widen(lub) => {
11922 WIDEN_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
11923 vt.vars[k as usize].ty = TypeRef::J(JavaType::Object(lub.as_str().into()));
11924 pairs.push((v, k));
11925 }
11926 }
11927 }
11928 rename_gen_back_batch(then_stmt, &pairs, vt);
11929 for &(v, _) in &pairs {
11930 drop_gen_slot(vt, v);
11931 }
11932 if let Some(e) = else_stmt.as_deref_mut() {
11933 let mut undo_else: Vec<(u32, u32, Option<String>)> = g_else
11934 .iter()
11935 .filter(|(&k, &v)| {
11936 v >= else_floor && g_then.get(&k).copied() == gen.get(&k).copied()
11937 })
11938 .filter_map(|(&k, &v)| {
11939 if undo_type_safe(vt, k, v) {
11940 Some((v, k, None))
11941 } else if let (Some(_), Some(_)) =
11942 (obj_name(&vt.vars[k as usize].ty), obj_name(&vt.vars[v as usize].ty))
11943 {
11944 Some((v, k, lub_pending()))
11945 } else {
11946 None
11947 }
11948 })
11949 .collect();
11950 if (vt.vars.len() as u32) > else_floor && !undo_else.is_empty() {
11951 let vars: Vec<u32> = undo_else.iter().map(|&(v, _, _)| v).collect();
11952 let mut hits = vec![false; vars.len()];
11953 mark_read_vars(std::slice::from_ref(&*e), &vars, &mut hits);
11954 let mut i = 0usize;
11955 undo_else.retain(|_| {
11956 let keep = !hits[i];
11957 i += 1;
11958 keep
11959 });
11960 }
11961 undo_else.sort_unstable_by_key(|&(v, _, _)| std::cmp::Reverse(v));
11962 let mut pairs: Vec<(u32, u32)> = Vec::with_capacity(undo_else.len());
11963 for (v, k, pending) in undo_else {
11964 let kind = match pending {
11965 None => Undo::Direct,
11966 Some(_) => {
11967 let (a, b) = (
11968 obj_name(&vt.vars[k as usize].ty),
11969 obj_name(&vt.vars[v as usize].ty),
11970 );
11971 match (a, b) {
11972 (Some(a), Some(b)) if pool.is_subtype(b, a) => Undo::Direct,
11973 (Some(a), Some(b)) => match pool_lub(a, b, pool) {
11974 Some(lub) => Undo::Widen(lub),
11975 None => Undo::Refuse,
11976 },
11977 _ => Undo::Refuse,
11978 }
11979 }
11980 };
11981 match kind {
11982 Undo::Refuse => continue,
11983 Undo::Direct => pairs.push((v, k)),
11984 Undo::Widen(lub) => {
11985 WIDEN_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
11986 vt.vars[k as usize].ty = TypeRef::J(JavaType::Object(lub.as_str().into()));
11987 pairs.push((v, k));
11988 }
11989 }
11990 }
11991 rename_gen_back_batch(e, &pairs, vt);
11992 for &(v, _) in &pairs {
11993 drop_gen_slot(vt, v);
11994 }
11995 }
11996 gen.retain(|k, v| g_then.get(k) == Some(v) && g_else.get(k) == Some(v));
11999 }
12000 Stmt::While { cond, body, .. } | Stmt::DoWhile { cond, body, .. } => {
12001 let entry = gen.clone();
12005 for _ in 0..3 {
12006 rewrite_gen_reads(cond, gen, vt);
12007 split_walk_stmt(body, vt, gen, counter, pool);
12008 }
12009 *gen = entry;
12012 }
12013 Stmt::For { init, body, .. } => {
12014 for x in init.iter_mut() {
12015 split_walk_stmt(x, vt, gen, counter, pool);
12016 }
12017 let entry = gen.clone();
12018 for _ in 0..3 {
12019 split_walk_stmt(body, vt, gen, counter, pool);
12020 }
12021 *gen = entry;
12022 }
12023 Stmt::ForEach { body, .. } => {
12024 let entry = gen.clone();
12025 for _ in 0..3 {
12026 split_walk_stmt(body, vt, gen, counter, pool);
12027 }
12028 *gen = entry;
12029 }
12030 Stmt::Switch { cases, default, .. } => {
12031 let entry = gen.clone();
12032 for c in cases.iter_mut() {
12033 let mut g_case = entry.clone();
12034 for x in c.body.iter_mut() {
12035 split_walk_stmt(x, vt, &mut g_case, counter, pool);
12036 }
12037 gen.retain(|k, v| g_case.get(k) == Some(v));
12038 }
12039 if let Some(d) = default {
12040 let mut g_def = entry.clone();
12041 split_walk_stmt(d, vt, &mut g_def, counter, pool);
12042 gen.retain(|k, v| g_def.get(k) == Some(v));
12043 }
12044 }
12045 Stmt::Try {
12046 body,
12047 catches,
12048 finally,
12049 } => {
12050 let entry = gen.clone();
12051 let mut g_try = entry.clone();
12052 split_walk_stmt(body, vt, &mut g_try, counter, pool);
12053 let mut g_all = g_try.clone();
12054 for c in catches.iter_mut() {
12055 let mut g_c = entry.clone();
12058 split_walk_stmt(&mut c.body, vt, &mut g_c, counter, pool);
12059 g_all.retain(|k, v| g_c.get(k) == Some(v));
12060 }
12061 *gen = g_all;
12062 if let Some(f) = finally {
12063 split_walk_stmt(f, vt, gen, counter, pool);
12064 }
12065 }
12066 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => {
12067 split_walk_stmt(body, vt, gen, counter, pool);
12068 }
12069 Stmt::Return(Some(e)) | Stmt::Throw(e) => {
12070 rewrite_gen_reads(e, gen, vt);
12071 }
12072 _ => {}
12073 }
12074}
12075
12076fn fw_super(c: &str) -> Option<&'static str> {
12083 crate::fwdb::super_of(c)
12084}
12085
12086fn exc_subtype(sub: &str, sup: &str, pool: &DexPool) -> bool {
12092 if sub == sup {
12093 return true;
12094 }
12095 if pool.is_subtype(sub, sup) {
12096 return true;
12097 }
12098 let mut cur = sub;
12099 for _ in 0..24 {
12100 let Some(next) = fw_super(cur) else { break };
12101 if next == sup {
12102 return true;
12103 }
12104 cur = next;
12105 }
12106 false
12107}
12108
12109pub fn dedupe_multicatch(s: &mut Stmt, pool: &DexPool) {
12115 dedupe_multicatch_walk(s, pool);
12116}
12117
12118fn dedupe_multicatch_walk(s: &mut Stmt, pool: &DexPool) {
12119 match s {
12120 Stmt::Try {
12121 body,
12122 catches,
12123 finally,
12124 }
12125 | Stmt::TryWithResources {
12126 body,
12127 catches,
12128 finally,
12129 ..
12130 } => {
12131 dedupe_multicatch_walk(body, pool);
12132 for c in catches.iter_mut() {
12133 if c.exc.len() > 1 {
12134 let mut keep: Vec<std::sync::Arc<str>> = Vec::with_capacity(c.exc.len());
12135 for e in c.exc.iter() {
12136 let shadowed = c.exc.iter().any(|o| {
12137 o.as_ref() != e.as_ref() && exc_subtype(e, o, pool)
12138 });
12139 if !shadowed && !keep.iter().any(|k| k.as_ref() == e.as_ref()) {
12140 keep.push(e.clone());
12141 }
12142 }
12143 c.exc = keep;
12144 }
12145 dedupe_multicatch_walk(&mut c.body, pool);
12146 }
12147 if let Some(f) = finally {
12148 dedupe_multicatch_walk(f, pool);
12149 }
12150 }
12151 Stmt::Block(v) => {
12152 for x in v.iter_mut() {
12153 dedupe_multicatch_walk(x, pool);
12154 }
12155 }
12156 Stmt::If {
12157 then_stmt,
12158 else_stmt,
12159 ..
12160 } => {
12161 dedupe_multicatch_walk(then_stmt, pool);
12162 if let Some(e) = else_stmt {
12163 dedupe_multicatch_walk(e, pool);
12164 }
12165 }
12166 Stmt::While { body, .. }
12167 | Stmt::DoWhile { body, .. }
12168 | Stmt::For { body, .. }
12169 | Stmt::ForEach { body, .. }
12170 | Stmt::Labeled { body, .. }
12171 | Stmt::Synchronized { body, .. } => dedupe_multicatch_walk(body, pool),
12172 Stmt::Switch { cases, default, .. } => {
12173 for c in cases.iter_mut() {
12174 for x in c.body.iter_mut() {
12175 dedupe_multicatch_walk(x, pool);
12176 }
12177 }
12178 if let Some(d) = default {
12179 dedupe_multicatch_walk(d, pool);
12180 }
12181 }
12182 _ => {}
12183 }
12184}
12185
12186pub fn resolve_dangling_gotos(s: &mut Stmt) {
12198 let mut labels: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
12199 walk_all(s, &mut |st| {
12202 if let Stmt::Label(id) = st {
12203 labels.insert(*id);
12204 }
12205 });
12206 resolve_gotos_walk(s, &labels, 0);
12207}
12208
12209fn resolve_gotos_walk(s: &mut Stmt, labels: &jdc_core::FxHashSet<u32>, loop_depth: u32) {
12210 let depth = match s {
12211 Stmt::While { .. } | Stmt::DoWhile { .. } | Stmt::For { .. } | Stmt::ForEach { .. } => {
12212 loop_depth + 1
12213 }
12214 _ => loop_depth,
12215 };
12216 match s {
12217 Stmt::Goto(id) => {
12218 if labels.contains(id) {
12219 return; }
12221 if depth > 0 {
12222 *s = Stmt::Break(None);
12223 } else {
12224 *s = Stmt::Comment(format!(
12225 "$DDC: unresolved jump to block {} (irreducible control flow)",
12226 id
12227 ));
12228 }
12229 }
12230 Stmt::Block(v) => {
12231 let mut i = 0usize;
12232 while i < v.len() {
12233 let dangling_here =
12238 depth == 0 && matches!(&v[i], Stmt::Goto(id) if !labels.contains(id));
12239 resolve_gotos_walk(&mut v[i], labels, depth);
12240 if dangling_here {
12241 v.truncate(i + 1);
12242 break;
12243 }
12244 i += 1;
12245 }
12246 }
12247 Stmt::If { then_stmt, else_stmt, .. } => {
12248 resolve_gotos_walk(then_stmt, labels, depth);
12249 if let Some(e) = else_stmt {
12250 resolve_gotos_walk(e, labels, depth);
12251 }
12252 }
12253 Stmt::While { body, .. }
12254 | Stmt::DoWhile { body, .. }
12255 | Stmt::For { body, .. }
12256 | Stmt::ForEach { body, .. }
12257 | Stmt::Labeled { body, .. }
12258 | Stmt::Synchronized { body, .. } => resolve_gotos_walk(body, labels, depth),
12259 Stmt::Try {
12260 body,
12261 catches,
12262 finally,
12263 } => {
12264 resolve_gotos_walk(body, labels, depth);
12265 for c in catches.iter_mut() {
12266 resolve_gotos_walk(&mut c.body, labels, depth);
12267 }
12268 if let Some(f) = finally {
12269 resolve_gotos_walk(f, labels, depth);
12270 }
12271 }
12272 Stmt::Switch { cases, default, .. } => {
12273 for c in cases.iter_mut() {
12274 for x in c.body.iter_mut() {
12275 resolve_gotos_walk(x, labels, depth);
12276 }
12277 }
12278 if let Some(d) = default {
12279 resolve_gotos_walk(d, labels, depth);
12280 }
12281 }
12282 _ => {}
12283 }
12284}
12285
12286fn mark_read_vars(stmts: &[Stmt], vars: &[u32], hits: &mut [bool]) {
12298 let mut remaining = vars.len();
12299 for s in stmts {
12300 if remaining == 0 {
12301 break;
12302 }
12303 visit_stmt_exprs_ro(s, &mut |e| {
12304 if remaining == 0 {
12305 return;
12306 }
12307 visit_exprs_reads(e, &mut |x| {
12308 if remaining == 0 {
12309 return;
12310 }
12311 if let Expr::Local { var, .. } = x {
12312 for (i, v) in vars.iter().enumerate() {
12313 if *v == *var && !hits[i] {
12314 hits[i] = true;
12315 remaining -= 1;
12316 }
12317 }
12318 }
12319 });
12320 });
12321 }
12322}
12323
12324fn stmts_read_var(stmts: &[Stmt], v: u32) -> usize {
12325 let mut n = 0usize;
12326 for s in stmts {
12327 visit_stmt_exprs_ro(s, &mut |e| {
12328 visit_exprs_reads(e, &mut |x| {
12329 if let Expr::Local { var: vv, .. } = x {
12330 if *vv == v {
12331 n += 1;
12332 }
12333 }
12334 });
12335 });
12336 }
12337 n
12338}
12339
12340pub fn fix_ctor_conditional_super(body: &mut Stmt) {
12341 if !matches!(body, Stmt::Block(_)) {
12344 let inner = std::mem::replace(body, Stmt::Block(Vec::new()));
12345 *body = Stmt::Block(vec![inner]);
12346 }
12347 let Stmt::Block(stmts) = body else { return };
12348 while stmts.len() == 1 {
12352 let Stmt::Block(inner) = &stmts[0] else { break };
12353 *stmts = inner.clone();
12354 }
12355 if stmts.is_empty() {
12356 return;
12357 }
12358 if is_bare_ctor_call(stmts.first().unwrap()) {
12359 dedupe_ctor_delegations(body);
12360 return;
12361 }
12362 if try_linear_super_inline(stmts) {
12363 return;
12364 }
12365 try_merge_branched_super(stmts);
12366}
12367
12368pub fn strip_secondary_delegations(body: &mut Stmt) {
12392 let Stmt::Block(stmts) = body else { return };
12393 if !stmts.first().is_some_and(is_bare_ctor_call) {
12394 return;
12395 }
12396 fn strip_in(s: &mut Stmt) {
12397 if is_bare_ctor_call(s) {
12398 *s = Stmt::Block(Vec::new());
12399 return;
12400 }
12401 match s {
12402 Stmt::Block(v) => v.iter_mut().for_each(strip_in),
12403 Stmt::If {
12404 then_stmt,
12405 else_stmt,
12406 ..
12407 } => {
12408 strip_in(then_stmt);
12409 if let Some(e) = else_stmt {
12410 strip_in(e);
12411 }
12412 }
12413 Stmt::While { body, .. }
12414 | Stmt::DoWhile { body, .. }
12415 | Stmt::ForEach { body, .. }
12416 | Stmt::Synchronized { body, .. }
12417 | Stmt::Labeled { body, .. } => strip_in(body),
12418 Stmt::For { init, body, .. } => {
12419 init.iter_mut().for_each(strip_in);
12420 strip_in(body);
12421 }
12422 Stmt::Switch { cases, default, .. } => {
12423 for c in cases.iter_mut() {
12424 c.body.iter_mut().for_each(strip_in);
12425 }
12426 if let Some(d) = default {
12427 strip_in(d);
12428 }
12429 }
12430 Stmt::Try {
12431 body,
12432 catches,
12433 finally,
12434 }
12435 | Stmt::TryWithResources {
12436 body,
12437 catches,
12438 finally,
12439 ..
12440 } => {
12441 strip_in(body);
12442 for c in catches.iter_mut() {
12443 strip_in(&mut c.body);
12444 }
12445 if let Some(f) = finally {
12446 strip_in(f);
12447 }
12448 }
12449 _ => {}
12450 }
12451 }
12452 for st in stmts.iter_mut().skip(1) {
12453 strip_in(st);
12454 }
12455}
12456
12457pub fn dedupe_ctor_delegations(body: &mut Stmt) {
12458 let Stmt::Block(stmts) = body else { return };
12459 let Some(d0) = stmts.first().and_then(|s| match s {
12460 Stmt::ExprStmt(e) if is_delegation_expr(e) => Some(e.clone()),
12461 _ => None,
12462 }) else {
12463 return;
12464 };
12465 fn rec(s: &mut Stmt, d0: &Expr) {
12466 match s {
12467 Stmt::Block(v) => {
12468 v.retain(|x| {
12469 !matches!(x, Stmt::ExprStmt(e) if is_delegation_expr(e) && same_delegation(e, d0))
12470 });
12471 for x in v.iter_mut() {
12472 rec(x, d0);
12473 }
12474 }
12475 Stmt::If { then_stmt, else_stmt, .. } => {
12476 rec(then_stmt, d0);
12477 if let Some(e) = else_stmt {
12478 rec(e, d0);
12479 }
12480 }
12481 _ => {}
12482 }
12483 }
12484 {
12488 let d0r = &d0;
12489 let mut idx = 0usize;
12490 stmts.retain(|s| {
12491 idx += 1;
12492 idx == 1
12493 || !(matches!(s, Stmt::ExprStmt(e)
12494 if is_delegation_expr(e) && same_delegation(e, d0r)))
12495 });
12496 }
12497 for s in stmts.iter_mut().skip(1) {
12498 rec(s, &d0);
12499 }
12500}
12501
12502fn same_delegation(a: &Expr, b: &Expr) -> bool {
12506 match (a, b) {
12507 (
12508 Expr::Method { name: n1, cls: c1, is_super: s1, args: a1, .. },
12509 Expr::Method { name: n2, cls: c2, is_super: s2, args: a2, .. },
12510 ) => {
12511 &**n1 == "<init>"
12512 && &**n2 == "<init>"
12513 && c1 == c2
12514 && s1 == s2
12515 && (a1 == a2 || (a1.is_empty() && a2.is_empty()))
12516 }
12517 _ => false,
12518 }
12519}
12520
12521fn ctor_call_expr(s: &Stmt) -> Option<&Expr> {
12523 match s {
12524 Stmt::ExprStmt(e) if is_delegation_expr(e) => Some(e),
12525 _ => None,
12526 }
12527}
12528
12529fn same_ctor_kind(a: &Expr, b: &Expr) -> bool {
12531 match (a, b) {
12532 (
12533 Expr::Method { cls: c1, is_super: s1, owner: o1, .. },
12534 Expr::Method { cls: c2, is_super: s2, owner: o2, .. },
12535 ) => c1 == c2 && s1 == s2 && o1 == o2,
12536 _ => false,
12537 }
12538}
12539
12540fn flat_list(s: &Stmt) -> Vec<&Stmt> {
12545 fn rec<'a>(s: &'a Stmt, out: &mut Vec<&'a Stmt>) {
12546 match s {
12547 Stmt::Block(v) => {
12548 for x in v {
12549 rec(x, out);
12550 }
12551 }
12552 other => out.push(other),
12553 }
12554 }
12555 let mut out = Vec::new();
12556 rec(s, &mut out);
12557 out
12558}
12559
12560fn strip_lost_alloc_owners(e: &mut Expr) {
12566 deep_rewrite(e, &mut |x| {
12567 if let Expr::Method { name, is_special: true, owner: Some(o), .. } = x {
12568 if &**name == "<init>" && !matches!(**o, Expr::This) {
12569 **o = Expr::Const(jdc_core::ir::expr::ConstVal::Int(0));
12570 }
12571 }
12572 });
12573}
12574
12575fn count_locals_expr(e: &Expr) -> jdc_core::FxHashMap<u32, usize> {
12578 let mut m = jdc_core::FxHashMap::default();
12579 count_expr_ro(e, &mut m);
12580 m
12581}
12582
12583pub(crate) fn count_locals_stmts(ss: &[Stmt]) -> jdc_core::FxHashMap<u32, usize> {
12584 let mut m = jdc_core::FxHashMap::default();
12585 for s in ss {
12586 visit_stmt_exprs_ro(s, &mut |e| count_expr_ro(e, &mut m));
12587 }
12588 m
12589}
12590
12591fn cnt(m: &jdc_core::FxHashMap<u32, usize>, v: u32) -> usize {
12592 m.get(&v).copied().unwrap_or(0)
12593}
12594
12595fn inline_locals(
12599 e: &Expr,
12600 map: &std::collections::HashMap<u32, Expr>,
12601 depth: u32,
12602) -> Option<Expr> {
12603 if depth > 4 {
12604 return None;
12605 }
12606 let mut out = e.clone();
12607 let mut fail = false;
12608 deep_rewrite(&mut out, &mut |x| {
12609 if let Expr::Local { var, .. } = x {
12610 if let Some(def) = map.get(var) {
12611 match inline_locals(def, map, depth + 1) {
12612 Some(r) => *x = r,
12613 None => fail = true,
12614 }
12615 }
12616 }
12617 });
12618 if fail {
12619 None
12620 } else {
12621 Some(out)
12622 }
12623}
12624
12625fn def_of(s: &Stmt) -> Option<(u32, &Expr)> {
12627 match s {
12628 Stmt::LocalDef { var, init: Some(e), .. } => Some((*var, e)),
12629 Stmt::ExprStmt(Expr::Assign {
12630 target,
12631 value,
12632 op: jdc_core::ir::expr::AssignOp::Plain,
12633 ..
12634 }) => match &**target {
12635 Expr::Local { var, .. } => Some((*var, value)),
12636 _ => None,
12637 },
12638 _ => None,
12639 }
12640}
12641
12642fn try_linear_super_inline(stmts: &mut Vec<Stmt>) -> bool {
12644 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
12645 return false;
12646 };
12647 if pos == 0 {
12648 return false; }
12650 if !stmts[..pos]
12652 .iter()
12653 .all(|s| matches!(s, Stmt::LocalDef { .. } | Stmt::ExprStmt(_)))
12654 {
12655 return false;
12656 }
12657 let Some(call) = ctor_call_expr(&stmts[pos]) else {
12658 return false;
12659 };
12660 let Expr::Method { args, .. } = call else {
12661 return false;
12662 };
12663 let mut map: std::collections::HashMap<u32, Expr> =
12665 std::collections::HashMap::new();
12666 let mut def_idx: jdc_core::FxHashMap<u32, usize> =
12667 jdc_core::FxHashMap::default();
12668 for (i, s) in stmts[..pos].iter().enumerate() {
12669 if let Some((v, e)) = def_of(s) {
12670 map.insert(v, e.clone());
12671 def_idx.insert(v, i);
12672 }
12673 }
12674 let mut needed: Vec<u32> = Vec::new();
12676 for a in args {
12677 let mut probe = a.clone();
12678 strip_lost_alloc_owners(&mut probe);
12679 deep_rewrite(&mut probe, &mut |x| {
12680 if let Expr::Local { var, .. } = x {
12681 if map.contains_key(var) && !needed.contains(var) {
12682 needed.push(*var);
12683 }
12684 }
12685 });
12686 }
12687 if needed.is_empty() {
12688 return false; }
12690 let mut consumed: Vec<usize> = Vec::new();
12693 let use_counts = count_locals_stmts(stmts);
12694 for &v in &needed {
12695 let def = &map[&v];
12696 let uses = cnt(&use_counts, v);
12697 if jdc_core::ir::build::has_side_effects(def) && uses != 1 {
12698 return false;
12699 }
12700 if uses == 1 {
12701 consumed.push(def_idx[&v]);
12702 }
12703 }
12704 let mut new_args: Vec<Expr> = Vec::with_capacity(args.len());
12706 for a in args {
12707 match inline_locals(a, &map, 0) {
12708 Some(r) => new_args.push(r),
12709 None => return false,
12710 }
12711 }
12712 let mut call_stmt = stmts.remove(pos);
12715 if let Stmt::ExprStmt(Expr::Method { args: slot, .. }) = &mut call_stmt {
12716 *slot = new_args;
12717 }
12718 let mut rest: Vec<Stmt> = Vec::with_capacity(stmts.len() + 1);
12719 rest.push(call_stmt);
12720 for (i, s) in stmts.drain(..).enumerate() {
12721 let orig = if i >= pos { i + 1 } else { i };
12724 if !consumed.contains(&orig) {
12725 rest.push(s);
12726 }
12727 }
12728 *stmts = rest;
12729 true
12730}
12731
12732fn try_merge_branched_super(stmts: &mut Vec<Stmt>) -> bool {
12734 let if_positions: Vec<usize> = stmts
12738 .iter()
12739 .enumerate()
12740 .filter(|(_, s)| matches!(s, Stmt::If { .. }))
12741 .map(|(i, _)| i)
12742 .collect();
12743 for if_pos in if_positions {
12744 let gate = match &stmts[if_pos] {
12750 Stmt::If { then_stmt, else_stmt, .. } => {
12751 contains_delegation(then_stmt)
12752 || else_stmt.as_ref().is_some_and(|e| contains_delegation(e))
12753 }
12754 _ => false,
12755 };
12756 if gate && merge_at(stmts, if_pos) {
12757 return true;
12758 }
12759 }
12760 false
12761}
12762
12763fn contains_delegation(s: &Stmt) -> bool {
12766 match s {
12767 Stmt::ExprStmt(e) => is_delegation_expr(e),
12768 Stmt::Block(v) => v.iter().any(contains_delegation),
12769 Stmt::If { then_stmt, else_stmt, .. } => {
12770 contains_delegation(then_stmt)
12771 || else_stmt.as_ref().is_some_and(|e| contains_delegation(e))
12772 }
12773 _ => false,
12774 }
12775}
12776
12777
12778
12779fn merge_at(stmts: &mut Vec<Stmt>, if_pos: usize) -> bool {
12792 enum Br {
12797 Call(usize, Vec<(u32, Expr)>, Vec<usize>),
12799 Clean,
12800 Dirty,
12801 }
12802 fn split_branch(list: &[&Stmt]) -> Br {
12803 let call_pos = list.iter().position(|s| ctor_call_expr(s).is_some());
12804 let Some(c) = call_pos else {
12805 return if list.iter().any(|s| contains_delegation(s)) {
12808 Br::Dirty
12809 } else {
12810 Br::Clean
12811 };
12812 };
12813 if list.iter().skip(c + 1).any(|s| ctor_call_expr(s).is_some()) {
12814 return Br::Dirty; }
12816 let mut defs: Vec<(u32, Expr)> = Vec::new();
12817 let mut def_idxs: Vec<usize> = Vec::new();
12818 for (i, s) in list.iter().enumerate().take(c) {
12819 match def_of(s) {
12820 Some((v, e)) => {
12821 defs.push((v, e.clone()));
12822 def_idxs.push(i);
12823 }
12824 None => match s {
12825 Stmt::ExprStmt(_) | Stmt::LocalDef { .. } => {}
12828 _ => return Br::Dirty, },
12830 }
12831 }
12832 Br::Call(c, defs, def_idxs)
12833 }
12834 fn cond_def_of(s: &Stmt, scope: &[Stmt]) -> Option<(u32, Expr)> {
12836 let Stmt::If { cond, then_stmt, else_stmt: Some(e), .. } = s else {
12837 return None;
12838 };
12839 fn one<'x>(b: &'x Stmt, scope: &[Stmt]) -> Option<(u32, &'x Expr)> {
12840 let l = flat_list(b);
12841 if l.len() == 1 {
12842 return def_of(l[0]);
12843 }
12844 if l.len() == 2 {
12853 let (w, ex) = def_of(l[0])?;
12854 let Stmt::ExprStmt(Expr::Assign {
12855 target,
12856 op: AssignOp::Plain,
12857 value,
12858 }) = l[1]
12859 else {
12860 return None;
12861 };
12862 let Expr::Local { var: tv, .. } = &**target else {
12863 return None;
12864 };
12865 let Expr::Local { var: rv, .. } = &**value else {
12866 return None;
12867 };
12868 if *rv != w || *tv == w {
12869 return None;
12870 }
12871 let mut ldef_writes = 0usize;
12875 for st in scope {
12876 walk_all(st, &mut |x| {
12877 if let Stmt::LocalDef {
12878 var,
12879 init: Some(_),
12880 ..
12881 } = x
12882 {
12883 if *var == w {
12884 ldef_writes += 1;
12885 }
12886 }
12887 });
12888 }
12889 if stmts_read_var(scope, w) != 1
12890 || stmts_count_writes(scope, w) + ldef_writes != 1
12891 {
12892 return None;
12893 }
12894 return Some((*tv, ex));
12895 }
12896 None
12897 }
12898 let (v1, e1) = one(then_stmt, scope)?;
12899 let (v2, e2) = one(e, scope)?;
12900 if v1 != v2 {
12901 return None;
12902 }
12903 Some((
12904 v1,
12905 Expr::Cond {
12906 c: Box::new(cond.clone()),
12907 t: Box::new(e1.clone()),
12908 f: Box::new(e2.clone()),
12909 },
12910 ))
12911 }
12912
12913 let mut pmap: std::collections::HashMap<u32, Expr> =
12915 std::collections::HashMap::new();
12916 let mut pidx: jdc_core::FxHashMap<u32, usize> =
12917 jdc_core::FxHashMap::default();
12918 for (i, s) in stmts[..if_pos].iter().enumerate() {
12919 match s {
12920 Stmt::LocalDef { .. } | Stmt::ExprStmt(_) => {
12921 if let Some((v, e)) = def_of(s) {
12922 pmap.insert(v, e.clone());
12923 pidx.insert(v, i);
12924 }
12925 }
12926 Stmt::If { .. } => match cond_def_of(s, stmts) {
12927 Some((v, e)) => {
12928 pmap.insert(v, e);
12929 pidx.insert(v, i);
12930 }
12931 None => return false,
12932 },
12933 _ => return false,
12934 }
12935 }
12936 let (cond, then_v, else_v, had_else) = match &stmts[if_pos] {
12937 Stmt::If { cond, then_stmt, else_stmt, .. } => (
12938 cond.clone(),
12939 then_stmt.as_ref().clone(),
12940 else_stmt.as_ref().map_or_else(|| Stmt::Block(Vec::new()), |e| e.as_ref().clone()),
12941 else_stmt.is_some(),
12942 ),
12943 _ => return false,
12944 };
12945 fn normalize_branch(b: &mut Stmt) {
12951 let slot: &mut Stmt = match b {
12956 Stmt::Block(v) if v.len() == 1 => &mut v[0],
12957 other => other,
12958 };
12959 if !matches!(slot, Stmt::If { .. }) || !contains_delegation(slot) {
12960 return;
12961 }
12962 let taken = std::mem::replace(slot, Stmt::Block(Vec::new()));
12963 let mut tmp = vec![taken];
12964 if merge_at(&mut tmp, 0) {
12965 *slot = Stmt::Block(tmp);
12966 } else {
12967 *slot = tmp.pop().unwrap();
12968 }
12969 }
12970 let mut then_v = then_v;
12971 let mut else_v = else_v;
12972 normalize_branch(&mut then_v);
12973 normalize_branch(&mut else_v);
12974 let then_list = flat_list(&then_v);
12975 let else_list = flat_list(&else_v);
12976
12977 enum Side {
12980 Call {
12981 at: usize,
12982 defs: Vec<(u32, Expr)>,
12983 def_idxs: Vec<usize>,
12984 },
12985 Clean,
12986 }
12987 let (t_side, e_side, two_sided) = match (split_branch(&then_list), split_branch(&else_list)) {
12988 (Br::Call(tc, td, ti), Br::Call(ec, ed, ei)) => (
12989 Side::Call { at: tc, defs: td, def_idxs: ti },
12990 Side::Call { at: ec, defs: ed, def_idxs: ei },
12991 true,
12992 ),
12993 (Br::Call(tc, td, ti), Br::Clean) => (
12994 Side::Call { at: tc, defs: td, def_idxs: ti },
12995 Side::Clean,
12996 false,
12997 ),
12998 (Br::Clean, Br::Call(ec, ed, ei)) => (
12999 Side::Clean,
13000 Side::Call { at: ec, defs: ed, def_idxs: ei },
13001 false,
13002 ),
13003 _ => return false,
13004 };
13005 let (t_call, e_call) = match (&t_side, &e_side) {
13007 (Side::Call { at: ta, .. }, Side::Call { at: ea, .. }) => (
13008 ctor_call_expr(then_list[*ta]),
13009 ctor_call_expr(else_list[*ea]),
13010 ),
13011 (Side::Call { at: ta, .. }, Side::Clean) => {
13012 (ctor_call_expr(then_list[*ta]), None)
13013 }
13014 (Side::Clean, Side::Call { at: ea, .. }) => {
13015 (None, ctor_call_expr(else_list[*ea]))
13016 }
13017 _ => return false,
13018 };
13019 let (call_list, call_at) = match &t_side {
13021 Side::Call { at, .. } => (&then_list, *at),
13022 Side::Clean => match &e_side {
13023 Side::Call { at, .. } => (&else_list, *at),
13024 Side::Clean => return false,
13025 },
13026 };
13027 let (base_args, other_args) = if two_sided {
13028 let (Some(tc), Some(ec)) = (t_call, e_call) else {
13029 return false;
13030 };
13031 if !same_ctor_kind(tc, ec) {
13032 return false;
13033 }
13034 let (Expr::Method { args: a1, .. }, Expr::Method { args: a2, .. }) = (tc, ec) else {
13035 return false;
13036 };
13037 if a1.len() != a2.len() {
13038 return false;
13039 }
13040 (a1, Some(a2))
13041 } else {
13042 let c = t_call.or(e_call);
13043 let Some(c) = c else { return false };
13044 let Expr::Method { args, .. } = c else {
13045 return false;
13046 };
13047 (args, None)
13048 };
13049
13050 let mut tmap: std::collections::HashMap<u32, Expr> = pmap.clone();
13052 let mut emap: std::collections::HashMap<u32, Expr> = pmap.clone();
13053 if let Side::Call { defs, .. } = &t_side {
13054 for (v, e) in defs {
13055 tmap.insert(*v, e.clone());
13056 }
13057 }
13058 if let Side::Call { defs, .. } = &e_side {
13059 for (v, e) in defs {
13060 emap.insert(*v, e.clone());
13061 }
13062 }
13063 let Some(cond_i) = inline_locals(&cond, &pmap, 0) else {
13064 return false;
13065 };
13066 let mut then_args: Vec<Expr> = Vec::with_capacity(base_args.len());
13068 let mut else_args: Vec<Expr> = Vec::new();
13069 if two_sided {
13070 for a in base_args {
13071 match inline_locals(a, &tmap, 0) {
13072 Some(r) => then_args.push(r),
13073 None => return false,
13074 }
13075 }
13076 for a in other_args.unwrap() {
13077 match inline_locals(a, &emap, 0) {
13078 Some(r) => else_args.push(r),
13079 None => return false,
13080 }
13081 }
13082 } else {
13083 let from_then = matches!(&t_side, Side::Call { .. });
13086 for a in base_args {
13087 let r = if from_then {
13088 inline_locals(a, &tmap, 0)
13089 } else {
13090 inline_locals(a, &emap, 0)
13091 };
13092 match r {
13093 Some(r) => then_args.push(r),
13094 None => return false,
13095 }
13096 }
13097 }
13098 let primary_args = base_args;
13099 let mut merged: Vec<Expr> = Vec::with_capacity(then_args.len());
13101 let mut differ = 0usize;
13102 for (j, t) in then_args.iter().enumerate() {
13103 if two_sided {
13104 let e = &else_args[j];
13105 if t == e {
13106 merged.push(t.clone());
13107 } else {
13108 differ += 1;
13109 merged.push(Expr::Cond {
13110 c: Box::new(cond_i.clone()),
13111 t: Box::new(t.clone()),
13112 f: Box::new(e.clone()),
13113 });
13114 }
13115 } else {
13116 merged.push(t.clone());
13117 }
13118 }
13119
13120 let vars_in = |e: &Expr| -> Vec<u32> {
13122 let mut vs = Vec::new();
13123 let mut probe = e.clone();
13124 strip_lost_alloc_owners(&mut probe);
13125 deep_rewrite(&mut probe, &mut |x| {
13126 if let Expr::Local { var, .. } = x {
13127 if !vs.contains(var) {
13128 vs.push(*var);
13129 }
13130 }
13131 });
13132 vs
13133 };
13134 let branch_remainder = |list: &[&Stmt], side: &Side| -> Vec<Stmt> {
13142 match side {
13143 Side::Clean => list.iter().map(|s| (*s).clone()).collect(),
13144 Side::Call { at, def_idxs, .. } => {
13145 list.iter()
13146 .enumerate()
13147 .filter(|(i, _)| *i != *at && !def_idxs.contains(i))
13148 .map(|(_, s)| (*s).clone())
13149 .collect()
13150 }
13151 }
13152 };
13153 let tail_then_v = branch_remainder(&then_list, &t_side);
13154 let tail_else_v = branch_remainder(&else_list, &e_side);
13155 let post = &stmts[if_pos + 1..];
13156 let mut inlined: Vec<u32> = vars_in(&cond);
13157 for a in primary_args.iter().chain(else_args.iter()) {
13158 for v in vars_in(a) {
13159 if !inlined.contains(&v) {
13160 inlined.push(v);
13161 }
13162 }
13163 }
13164 inlined.retain(|v| pmap.contains_key(v) || tmap.contains_key(v) || emap.contains_key(v));
13165 for &v in &inlined {
13167 let in_branch = match (&t_side, &e_side) {
13168 (Side::Call { defs: td, .. }, Side::Call { defs: ed, .. }) => {
13169 td.iter().any(|(d, _)| *d == v) || ed.iter().any(|(d, _)| *d == v)
13170 }
13171 (Side::Call { defs, .. }, Side::Clean)
13172 | (Side::Clean, Side::Call { defs, .. }) => {
13173 defs.iter().any(|(d, _)| *d == v)
13174 }
13175 _ => false,
13176 };
13177 if pmap.contains_key(&v) && in_branch {
13178 return false;
13179 }
13180 }
13181 type Counts = jdc_core::FxHashMap<u32, usize>;
13182 type GuardCounts = (
13183 Counts,
13184 Vec<Counts>,
13185 Vec<Counts>,
13186 Counts,
13187 Counts,
13188 Counts,
13189 Vec<(u32, Counts)>,
13190 );
13191 let (cond_counts, a1_counts, a2_counts, tail1_counts, tail2_counts, post_counts, def_counts): GuardCounts =
13192 if inlined.is_empty() {
13193 Default::default()
13194 } else {
13195 let all_defs: Vec<(u32, &Expr)> = pmap
13196 .iter()
13197 .map(|(k, v)| (*k, v))
13198 .chain(match &t_side {
13199 Side::Call { defs, .. } => defs.iter().map(|(k, v)| (*k, v)).collect(),
13200 Side::Clean => Vec::new(),
13201 })
13202 .chain(match &e_side {
13203 Side::Call { defs, .. } => defs.iter().map(|(k, v)| (*k, v)).collect(),
13204 Side::Clean => Vec::new(),
13205 })
13206 .collect();
13207 (
13208 count_locals_expr(&cond),
13209 primary_args.iter().map(count_locals_expr).collect(),
13210 else_args.iter().map(count_locals_expr).collect(),
13211 count_locals_stmts(&tail_then_v),
13212 count_locals_stmts(&tail_else_v),
13213 count_locals_stmts(post),
13214 all_defs.into_iter().map(|(v, e)| (v, count_locals_expr(e))).collect(),
13215 )
13216 };
13217 for &v in &inlined {
13218 let in_t = matches!(&t_side, Side::Call { defs, .. } if defs.iter().any(|(d, _)| *d == v));
13219 let in_e = matches!(&e_side, Side::Call { defs, .. } if defs.iter().any(|(d, _)| *d == v));
13220 let effectful = [
13221 in_t.then(|| tmap.get(&v)).flatten(),
13222 in_e.then(|| emap.get(&v)).flatten(),
13223 pmap.get(&v),
13224 ]
13225 .into_iter()
13226 .flatten()
13227 .any(jdc_core::ir::build::has_side_effects);
13228 if !effectful {
13229 continue;
13230 }
13231 let u_cond = cnt(&cond_counts, v);
13232 let u_post = cnt(&post_counts, v);
13233 let u_t1 = cnt(&tail1_counts, v);
13234 let u_t2 = cnt(&tail2_counts, v);
13235 let mut u_odef = 0usize;
13236 for (dv, dc) in &def_counts {
13237 if *dv != v && inlined.contains(dv) {
13238 u_odef += cnt(dc, v);
13239 }
13240 }
13241 let mut emit = u_cond * (1 + differ);
13242 let mut uses_args = 0usize;
13243 for j in 0..primary_args.len() {
13244 let c1 = cnt(&a1_counts[j], v);
13245 let c2 = if two_sided && j < a2_counts.len() {
13246 cnt(&a2_counts[j], v)
13247 } else {
13248 0
13249 };
13250 uses_args += c1 + c2;
13251 emit += if two_sided && then_args[j] != else_args[j] {
13252 c1 + c2
13253 } else {
13254 c1.max(c2)
13255 };
13256 }
13257 let ok = if in_t || in_e {
13258 u_cond == 0
13259 && u_post == 0
13260 && u_t1 == 0
13261 && u_t2 == 0
13262 && u_odef == 0
13263 && a1_counts.iter().map(|c| cnt(c, v)).sum::<usize>() <= 1
13264 && a2_counts.iter().map(|c| cnt(c, v)).sum::<usize>() <= 1
13265 && emit <= 2
13266 && (in_t || a1_counts.iter().all(|c| cnt(c, v) == 0))
13267 && (in_e || a2_counts.iter().all(|c| cnt(c, v) == 0))
13268 } else {
13269 u_post == 0
13270 && u_t1 == 0
13271 && u_t2 == 0
13272 && u_odef == 0
13273 && ((u_cond == 1 && uses_args == 0 && emit <= 2)
13274 || (u_cond == 0 && emit <= 1))
13275 };
13276 if !ok {
13277 return false;
13278 }
13279 }
13280
13281 let mut call_stmt = call_list[call_at].clone();
13283 if let Stmt::ExprStmt(Expr::Method { args: slot, .. }) = &mut call_stmt {
13284 *slot = merged;
13285 }
13286 let residual = |v: u32| -> usize {
13287 cnt(&tail1_counts, v) + cnt(&tail2_counts, v) + cnt(&post_counts, v)
13288 };
13289 let mut kept_prelude: Vec<Stmt> = Vec::new();
13290 for (i, s) in stmts[..if_pos].iter().enumerate() {
13291 let consumed = match s {
13292 Stmt::LocalDef { var, .. } => Some(*var),
13293 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
13294 Expr::Local { var, .. } => Some(*var),
13295 _ => None,
13296 },
13297 Stmt::If { .. } => cond_def_of(s, stmts).map(|(v, _)| v),
13298 _ => None,
13299 };
13300 if let Some(v) = consumed {
13301 if inlined.contains(&v) && pidx.get(&v) == Some(&i) && residual(v) == 0 {
13302 continue;
13303 }
13304 }
13305 kept_prelude.push(s.clone());
13306 }
13307 let build_keep = |list: &[&Stmt], side: &Side| -> Vec<Stmt> {
13308 match side {
13309 Side::Clean => list.iter().map(|s| (*s).clone()).collect(),
13310 Side::Call { at, .. } => {
13311 let mut out = Vec::new();
13312 for (i, s) in list.iter().enumerate() {
13313 if i == *at {
13314 continue;
13315 }
13316 if i < *at {
13317 if let Some((v, _)) = def_of(s) {
13318 if inlined.contains(&v) && residual(v) == 0 {
13319 continue;
13320 }
13321 }
13322 }
13323 out.push((*s).clone());
13324 }
13325 out
13326 }
13327 }
13328 };
13329 let then_keep = build_keep(&then_list, &t_side);
13330 let else_keep = build_keep(&else_list, &e_side);
13331 let else_part = if else_keep.is_empty() && !had_else {
13332 None
13333 } else {
13334 Some(Box::new(Stmt::Block(else_keep)))
13335 };
13336 let new_if = Stmt::If {
13337 cond: cond_i,
13338 then_stmt: Box::new(Stmt::Block(then_keep)),
13339 else_stmt: else_part,
13340 };
13341 let if_empty = match &new_if {
13342 Stmt::If { then_stmt, else_stmt, .. } => {
13343 let be = |s: &Stmt| matches!(s, Stmt::Block(v) if v.is_empty());
13344 be(then_stmt) && else_stmt.as_ref().is_none_or(|e| be(e))
13345 }
13346 _ => false,
13347 };
13348 let mut out: Vec<Stmt> = Vec::with_capacity(stmts.len());
13349 out.push(call_stmt);
13350 out.extend(kept_prelude);
13351 if !if_empty {
13352 out.push(new_if);
13353 }
13354 out.extend(stmts[if_pos + 1..].to_vec());
13355 *stmts = out;
13356 true
13357}
13358
13359
13360pub fn strip_enum_ctor_super(body: &mut Stmt) {
13361 fn is_enum_super(s: &Stmt) -> bool {
13362 matches!(
13363 s,
13364 Stmt::ExprStmt(Expr::Method { name, is_super: true, .. }) if &**name == "<init>"
13365 )
13366 }
13367 fn strip_deep(stmts: &mut Vec<Stmt>) {
13377 stmts.retain(|s| !is_enum_super(s));
13378 for st in stmts.iter_mut() {
13379 strip_one(st);
13380 }
13381 }
13382 fn strip_one(st: &mut Stmt) {
13383 match st {
13384 Stmt::Block(v) => strip_deep(v),
13385 Stmt::If { then_stmt, else_stmt, .. } => {
13386 strip_one(then_stmt);
13387 if let Some(e) = else_stmt {
13388 strip_one(e);
13389 }
13390 }
13391 Stmt::While { body, .. }
13392 | Stmt::DoWhile { body, .. }
13393 | Stmt::ForEach { body, .. }
13394 | Stmt::Synchronized { body, .. }
13395 | Stmt::Labeled { body, .. } => strip_one(body),
13396 Stmt::For { init, body, .. } => {
13397 strip_deep(init);
13398 strip_one(body);
13399 }
13400 Stmt::Switch { cases, default, .. } => {
13401 for c in cases.iter_mut() {
13402 strip_deep(&mut c.body);
13403 }
13404 if let Some(d) = default {
13405 strip_one(d);
13406 }
13407 }
13408 Stmt::Try { body, catches, finally }
13409 | Stmt::TryWithResources { body, catches, finally, .. } => {
13410 strip_one(body);
13411 for c in catches.iter_mut() {
13412 strip_one(&mut c.body);
13413 }
13414 if let Some(f) = finally {
13415 strip_one(f);
13416 }
13417 }
13418 _ => {}
13419 }
13420 }
13421 let Stmt::Block(stmts) = body else {
13422 return;
13423 };
13424 strip_deep(stmts);
13425}
13426
13427pub fn ensure_declared(body: &mut Stmt, vt: &VarTable) {
13428 let n = vt.vars.len().max(1);
13432
13433 let mut seen: HashSet<u32> = HashSet::default();
13436 demote_dup_decls(body, &mut seen, vt);
13437
13438 let mut def_block: Vec<u32> = vec![u32::MAX; n];
13446 let mut sizes: Vec<u32> = vec![0];
13447 let mut next_id: u32 = 1;
13448 scope_pass_a(body, 0, &mut next_id, &mut def_block, &mut sizes);
13449 sizes[0] = next_id;
13450 let mut within: Vec<u32> = vec![0; n];
13451 let mut total: Vec<u32> = vec![0; n];
13452 let mut next_b: u32 = 1;
13453 scope_pass_b(body, 0, &mut next_b, &def_block, &sizes, &mut within, &mut total);
13454 let mut hoist: HashSet<u32> = HashSet::default();
13455 for v in 0..n {
13456 if def_block[v] != u32::MAX && def_block[v] != 0 && within[v] < total[v] {
13457 hoist.insert(v as u32);
13458 }
13459 }
13460 if !hoist.is_empty() {
13461 let mut seed: HashSet<u32> = hoist.iter().copied().collect();
13462 demote_dup_decls(body, &mut seed, vt);
13463 }
13464
13465 let mut declared: Vec<bool> = vec![false; n];
13466 let mut is_param: Vec<bool> = vec![false; n];
13467 for v in &vt.vars {
13468 if v.is_param && (v.id as usize) < n {
13469 is_param[v.id as usize] = true;
13470 }
13471 }
13472 let mut is_catch: Vec<bool> = vec![false; n];
13478 walk_all(body, &mut |st| {
13479 if let Stmt::Try { catches, .. } = st {
13480 for c in catches {
13481 if (c.var as usize) < n {
13482 is_catch[c.var as usize] = true;
13483 }
13484 }
13485 }
13486 });
13487 walk_all(body, &mut |st| {
13488 if let Stmt::LocalDef { var, .. } = st {
13489 if (*var as usize) < n {
13490 declared[*var as usize] = true;
13491 }
13492 }
13493 });
13494 let mut used: HashSet<u32> = HashSet::default();
13495 stmt_collect_vars(body, &mut used, true);
13499
13500 let mut needs_decl: Vec<u32> = used
13503 .into_iter()
13504 .filter(|v| {
13505 let i = *v as usize;
13506 i >= n
13507 || ((!declared[i] && !is_param[i] && !is_catch[i]) || hoist.contains(v))
13508 })
13509 .collect();
13510 needs_decl.sort_unstable();
13511 needs_decl.dedup();
13512
13513 if !needs_decl.is_empty() {
13514 let mut decls: Vec<Stmt> = needs_decl
13515 .into_iter()
13516 .map(|v| Stmt::LocalDef {
13517 var: v,
13518 init: None,
13519 is_final: false,
13520 force_type: true,
13521 })
13522 .collect();
13523 decls.reverse();
13524 match body {
13525 Stmt::Block(v) => {
13526 for d in decls {
13527 v.insert(0, d);
13528 }
13529 }
13530 other => {
13531 let inner = std::mem::replace(other, Stmt::Block(vec![]));
13532 *other = Stmt::Block({
13533 let mut vs = decls;
13534 vs.push(inner);
13535 vs
13536 });
13537 }
13538 }
13539 }
13540}
13541
13542fn demote_dup_decls(s: &mut Stmt, seen: &mut HashSet<u32>, vt: &VarTable) {
13543 match s {
13544 Stmt::Block(v) => {
13547 for x in v.iter_mut() {
13548 demote_dup_decls(x, seen, vt);
13549 }
13550 }
13551 Stmt::LocalDef { var, init, .. } => {
13552 if seen.contains(var) {
13553 let ty = vt.var(*var).ty.clone();
13554 if let Some(e) = init.take() {
13555 *s = Stmt::ExprStmt(Expr::Assign {
13556 target: Box::new(Expr::Local { var: *var, ty }),
13557 op: AssignOp::Plain,
13558 value: Box::new(e),
13559 });
13560 } else {
13561 *s = Stmt::Block(vec![]);
13562 }
13563 } else {
13564 seen.insert(*var);
13565 }
13566 }
13567 other => {
13568 walk_mut(other, &mut |x| demote_dup_decls(x, seen, vt));
13569 }
13570 }
13571}
13572
13573#[allow(dead_code)]
13574fn unused(_: &Vec<CaseGroup>, _: &Catch) {}
13575
13576pub fn apply_local_names(vt: &mut VarTable, body: &Stmt) {
13590 use std::collections::{HashMap, HashSet};
13591
13592 let mut by_call: HashMap<u32, Vec<String>> = HashMap::new();
13595 let mut by_intrinsics: HashMap<u32, String> = HashMap::new();
13596
13597 visit_all_exprs(body, &mut |e| {
13598 if let Expr::Method { cls, name, args, .. } = e {
13599 if (cls.as_ref() == "kotlin/jvm/internal/Intrinsics"
13600 || cls.as_ref() == "kotlin/jvm/internal/IntrinsicsKt")
13601 && matches!(name.as_ref(), "checkNotNullParameter" | "checkParameterIsNotNull")
13602 && args.len() == 2
13603 {
13604 if let (Expr::Local { var, .. }, Expr::Const(ConstVal::Str(s))) = (&args[0], &args[1])
13605 {
13606 by_intrinsics
13607 .entry(*var)
13608 .or_insert_with(|| sanitize_name(s).unwrap_or_default());
13609 }
13610 }
13611 }
13612 });
13613
13614 walk_all(body, &mut |st| {
13615 let (var, value) = match st {
13616 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e),
13617 Stmt::ExprStmt(Expr::Assign { target, value, .. })
13618 if matches!(&**target, Expr::Local { .. }) =>
13619 {
13620 let Expr::Local { var, .. } = &**target else { return };
13621 (*var, value.as_ref())
13622 }
13623 _ => return,
13624 };
13625 if let Some(n) = defining_call_name(value) {
13626 by_call.entry(var).or_default().push(n);
13627 }
13628 });
13629
13630 let sanit = |n: &str| crate::classdec::java_ident(n).into_owned();
13641 let mut taken: HashSet<String> = HashSet::default();
13642 for v in vt.vars.iter() {
13647 if v.synthetic_name {
13652 taken.insert(sanit(&v.name));
13653 }
13654 }
13655 for v in vt.vars.iter_mut() {
13656 if v.synthetic_name {
13657 continue;
13658 }
13659 if !taken.insert(sanit(&v.name)) {
13660 let base = sanit(&v.name);
13661 for i in 2.. {
13662 let cand = format!("{base}{i}");
13663 if taken.insert(cand.clone()) {
13664 v.name = cand;
13665 break;
13666 }
13667 }
13668 }
13669 }
13670 let claim = |taken: &mut HashSet<String>, want: &str| -> String {
13671 let w = crate::classdec::java_ident(want).into_owned();
13672 if taken.insert(w.clone()) {
13673 return w;
13674 }
13675 for i in 2.. {
13676 let cand = format!("{w}{i}");
13677 if taken.insert(cand.clone()) {
13678 return cand;
13679 }
13680 }
13681 w
13682 };
13683
13684 for info in vt.vars.iter_mut() {
13685 if !info.synthetic_name {
13686 continue;
13687 }
13688 if let Some(n) = by_intrinsics.get(&info.id) {
13691 if !n.is_empty() {
13692 info.name = claim(&mut taken, n);
13693 continue;
13694 }
13695 }
13696 if let Some(names) = by_call.get(&info.id) {
13698 let unique: HashSet<&String> = names.iter().collect();
13699 if unique.len() == 1 {
13700 let n = unique.into_iter().next().unwrap();
13701 if !is_java_keyword(n) {
13702 info.name = claim(&mut taken, n);
13703 continue;
13704 }
13705 }
13706 }
13707 let want = type_alias(&info.ty).map(str::to_string).or_else(|| simple_type_name(&info.ty));
13710 if let Some(n) = want {
13711 info.name = claim(&mut taken, &n);
13712 }
13713 }
13714
13715 let mut seen: HashSet<String> = HashSet::default();
13720 for v in vt.vars.iter_mut() {
13721 if !seen.insert(sanit(&v.name)) {
13722 let base = sanit(&v.name);
13723 for i in 2.. {
13724 let cand = format!("{base}{i}");
13725 if seen.insert(cand.clone()) {
13726 v.name = cand;
13727 break;
13728 }
13729 }
13730 }
13731 }
13732}
13733
13734fn simple_type_name(ty: &TypeRef) -> Option<String> {
13736 let TypeRef::J(JavaType::Object(n)) = ty else {
13737 return None;
13738 };
13739 let simple = n.rsplit('/').next().unwrap_or(n);
13740 let simple = simple.rsplit('$').next().unwrap_or(simple);
13741 let mut chars = simple.chars();
13744 let mut out = String::with_capacity(simple.len());
13745 let mut lowered = false;
13746 for c in chars.by_ref() {
13747 if c.is_ascii_alphabetic() && !lowered {
13748 out.push(c.to_ascii_lowercase());
13749 lowered = true;
13750 } else {
13751 out.push(c);
13752 }
13753 if lowered {
13754 break;
13755 }
13756 }
13757 out.extend(chars);
13758 sanitize_name(&out)
13759}
13760
13761fn defining_call_name(e: &Expr) -> Option<String> {
13765 match e {
13766 Expr::Method { name, .. } => {
13767 let base = name
13768 .strip_prefix("get")
13769 .or_else(|| name.strip_prefix("is"))
13770 .filter(|rest| rest.chars().next().is_some_and(|c| c.is_ascii_uppercase()))
13771 .unwrap_or(name);
13772 let first = base.chars().next()?;
13773 let mut out = String::with_capacity(base.len());
13774 out.push(first.to_ascii_lowercase());
13775 out.extend(base.chars().skip(1));
13776 sanitize_name(&out)
13777 }
13778 Expr::New { cls, .. } => {
13779 let simple = cls.rsplit('/').next().unwrap_or(cls);
13780 let simple = simple.rsplit('$').next().unwrap_or(simple);
13781 let first = simple.chars().next()?;
13782 let mut out = String::with_capacity(simple.len());
13783 out.push(first.to_ascii_lowercase());
13784 out.extend(simple.chars().skip(1));
13785 sanitize_name(&out)
13786 }
13787 _ => None,
13788 }
13789}
13790
13791fn sanitize_name(s: &str) -> Option<String> {
13793 let mut out = String::with_capacity(s.len());
13794 for c in s.chars() {
13795 if c.is_ascii_alphanumeric() || c == '_' || c == '$' {
13796 out.push(c);
13797 } else {
13798 out.push('_');
13799 }
13800 }
13801 if out.len() < 2
13802 || out.chars().next().is_some_and(|c| c.is_ascii_digit())
13803 || is_java_keyword(&out)
13804 {
13805 return None;
13806 }
13807 Some(out)
13808}
13809
13810fn type_alias(ty: &TypeRef) -> Option<&'static str> {
13813 let TypeRef::J(JavaType::Object(n)) = ty else {
13814 return None;
13815 };
13816 Some(match n.as_ref() {
13817 "java/lang/String" | "kotlin/String" => "str",
13818 "java/lang/Class" => "cls",
13819 "java/lang/Throwable" => "th",
13820 "java/lang/Object" | "kotlin/Any" => "obj",
13821 "java/util/Iterator" | "kotlin/collections/Iterator" => "it",
13822 "java/lang/Boolean" => "bool",
13823 "java/lang/Integer" => "num",
13824 "java/lang/Character" => "ch",
13825 "java/lang/Byte" => "b",
13826 "java/lang/Short" => "sh",
13827 "java/lang/Float" => "f",
13828 "java/lang/Double" => "d",
13829 "java/lang/Long" => "i",
13830 "java/lang/StringBuilder" => "sb",
13831 "java/util/ArrayList" => "list",
13832 "java/util/HashMap" => "map",
13833 "android/content/Context" => "context",
13834 "android/content/Intent" => "intent",
13835 "android/os/Bundle" => "bundle",
13836 "android/view/View" => "view",
13837 "android/graphics/Bitmap" => "bitmap",
13838 "android/view/ViewGroup" => "viewGroup",
13839 _ => return None,
13840 })
13841}
13842
13843pub(crate) fn visit_all_exprs<F: FnMut(&Expr)>(s: &Stmt, f: &mut F) {
13845 walk_all(s, &mut |st| {
13846 let exprs: Vec<&Expr> = match st {
13847 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
13848 let mut v: Vec<&Expr> = vec![value];
13849 if !matches!(&**target, Expr::Local { .. }) {
13850 v.push(target);
13851 }
13852 v
13853 }
13854 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
13855 vec![e]
13856 }
13857 Stmt::Return(Some(e)) => vec![e],
13858 Stmt::LocalDef { init: Some(e), .. } => vec![e],
13859 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
13860 vec![cond]
13861 }
13862 _ => Vec::new(),
13863 };
13864 for e in exprs {
13865 visit_exprs(e, f);
13866 }
13867 });
13868}
13869
13870pub fn platform_constants(body: &mut Stmt) {
13875 if !crate::platform::installed() {
13876 return;
13877 }
13878 rewrite_exprs(body, &mut |e| {
13879 if let Expr::Method { cls, name, desc, args, .. } = e {
13880 for a in args.iter_mut() {
13881 let (value, is_long) = match a {
13882 Expr::Const(ConstVal::Int(v)) => (*v as i64, false),
13883 Expr::Const(ConstVal::Long(v)) => (*v, true),
13884 _ => continue,
13885 };
13886 if let Some(named) =
13887 crate::platform::named_constant(cls, name, &desc_to_string(desc), 0, value)
13888 {
13889 let _ = is_long;
13890 *a = Expr::Raw(named);
13891 }
13892 }
13893 }
13894 });
13895}
13896
13897fn desc_to_string(d: &jdc_core::types::MethodDescriptor) -> String {
13899 let mut out = String::from("(");
13900 for a in &d.args {
13901 type_to_desc(a, &mut out);
13902 }
13903 out.push(')');
13904 type_to_desc(&d.ret, &mut out);
13905 out
13906}
13907
13908fn type_to_desc(t: &jdc_core::types::JavaType, out: &mut String) {
13909 match t {
13910 JavaType::Void => out.push('V'),
13911 JavaType::Boolean => out.push('Z'),
13912 JavaType::Byte => out.push('B'),
13913 JavaType::Char => out.push('C'),
13914 JavaType::Short => out.push('S'),
13915 JavaType::Int => out.push('I'),
13916 JavaType::Float => out.push('F'),
13917 JavaType::Long => out.push('J'),
13918 JavaType::Double => out.push('D'),
13919 JavaType::Object(n) => {
13920 out.push('L');
13921 out.push_str(n);
13922 out.push(';');
13923 }
13924 JavaType::Array(inner) => {
13925 out.push('[');
13926 type_to_desc(inner, out);
13927 }
13928 }
13929}
13930
13931pub fn remove_kotlin_checks(s: &mut Stmt) {
13937 walk_mut_deep(s, &mut |st| {
13938 let Stmt::Block(v) = st else { return };
13939 v.retain(|x| !is_kotlin_check(x));
13940 });
13941}
13942
13943fn is_kotlin_check(s: &Stmt) -> bool {
13944 let Stmt::ExprStmt(e) = s else { return false };
13945 let Expr::Method { cls, name, .. } = e else { return false };
13946 (cls.as_ref() == "kotlin/jvm/internal/Intrinsics" || cls.as_ref() == "kotlin/jvm/internal/IntrinsicsKt__Jdk7Kt")
13947 && matches!(
13948 name.as_ref(),
13949 "checkNotNullParameter"
13950 | "checkParameterIsNotNull"
13951 | "checkExpressionValueIsNotNull"
13952 | "checkNotNullExpressionValue"
13953 | "checkReturnedValueIsNotNull"
13954 | "checkFieldIsNotNull"
13955 | "checkNotNull"
13956 )
13957}
13958
13959fn inline_ref_ok(pool: &DexPool, host: &str, target: &str, member: Option<(&str, bool)>) -> bool {
13979 let hpkg = match host.rfind('/') {
13980 Some(i) => &host[..i],
13981 None => "",
13982 };
13983 let mut cur = target;
13984 loop {
13985 let tpkg = match cur.rfind('/') {
13986 Some(i) => &cur[..i],
13987 None => "",
13988 };
13989 if tpkg != hpkg {
13990 if let Some(pc) = pool.get(cur) {
13991 if pc.access & crate::access::ACC_PUBLIC == 0 {
13992 return false;
13993 }
13994 if cur == target {
13995 if let Some((mname, is_method)) = member {
13996 let ok = if is_method {
13997 pc.all_methods().any(|m| {
13998 &*m.name == mname
13999 && m.access & crate::access::ACC_PUBLIC != 0
14000 })
14001 } else {
14002 pc.static_fields
14003 .iter()
14004 .chain(pc.instance_fields.iter())
14005 .any(|f| {
14006 f.name.as_ref() == mname
14007 && f.access & crate::access::ACC_PUBLIC != 0
14008 })
14009 };
14010 if !ok
14015 && (if is_method {
14016 pc.all_methods().any(|m| &*m.name == mname)
14017 } else {
14018 pc.static_fields
14019 .iter()
14020 .chain(pc.instance_fields.iter())
14021 .any(|f| f.name.as_ref() == mname)
14022 })
14023 {
14024 return false;
14025 }
14026 }
14027 }
14028 }
14029 }
14030 match cur.rfind('$') {
14031 Some(i) if i > 0 => cur = &cur[..i],
14032 _ => break,
14033 }
14034 }
14035 true
14036}
14037
14038pub fn inline_accessors(s: &mut Stmt, pool: &DexPool, host: &str) {
14039 rewrite_exprs(s, &mut |e| {
14040 let Expr::Method { cls, name, desc, args, is_static, .. } = e else { return };
14041 if !*is_static || args.is_empty() {
14042 return;
14043 }
14044 let Some(target) = pool.get(cls) else { return };
14045 let desc_s = desc_to_string(desc);
14046 let Some(m) = target.find_method(name, &desc_s) else { return };
14047 if !(m.is_static() && m.access & access::ACC_SYNTHETIC != 0) {
14048 return;
14049 }
14050 let Some(dex) = pool.dex(m.dex_idx) else { return };
14051 let code = match pool.accessor_code(m.dex_idx, m.code_off) {
14056 Some(bytes) => ddc_dex::CodeItem::parse(&bytes, 0),
14057 None => dex.code_at(m.code_off),
14058 };
14059 let Some(code) = code else { return };
14060 let insns: Vec<&Insn> =
14061 code.insns.iter().filter(|i| !matches!(i.kind, InsnKind::Nop)).collect();
14062 let core: Vec<&Insn> = insns
14064 .iter()
14065 .copied()
14066 .filter(|i| !is_const_only_trace(&i.kind, &insns))
14067 .collect();
14068 match accessor_shape(&dex, &core, &code) {
14069 Some(Shape::Identity(arg_i)) => {
14070 if let Some(a) = args.get(arg_i) {
14071 *e = a.clone();
14072 }
14073 }
14074 Some(Shape::FieldRead { arg, cls: field_cls, field, field_ty }) => {
14075 if !inline_ref_ok(pool, host, &field_cls, Some((&field, false))) {
14076 return;
14077 }
14078 if let Some(owner) = args.get(arg).cloned().map(Box::new) {
14079 let ty = TypeRef::J(desc_type(&field_ty));
14080 let name = jdc_core::rename::field_display(
14086 &field_cls,
14087 &field,
14088 &field_ty,
14089 )
14090 .map(std::sync::Arc::from)
14091 .unwrap_or_else(|| field.into());
14092 *e = Expr::Field {
14093 owner: Some(owner),
14094 cls: field_cls.into(),
14095 name,
14096 ty,
14097 is_static: false,
14098 };
14099 }
14100 }
14101 Some(Shape::Forward { cls: tcls, name: tname, desc: tdesc, instance }) => {
14102 if !inline_ref_ok(pool, host, &tcls, Some((&tname, true))) {
14103 return;
14104 }
14105 let disp = jdc_core::rename::field_display(
14109 &tcls,
14110 &tname,
14111 &tdesc.to_string(),
14112 )
14113 .map(std::sync::Arc::from)
14114 .unwrap_or_else(|| tname.into());
14115 *cls = tcls.into();
14116 *name = disp;
14117 *desc = std::sync::Arc::new(tdesc);
14118 if instance && !args.is_empty() {
14119 let recv = args.remove(0);
14121 if let Expr::Method { owner, .. } = e {
14122 *owner = Some(Box::new(recv));
14123 }
14124 }
14125 }
14126 None => {}
14127 }
14128 });
14129}
14130
14131enum Shape {
14133 Identity(usize),
14135 FieldRead { arg: usize, cls: String, field: String, field_ty: String },
14137 Forward { cls: String, name: String, desc: MethodDescriptor, instance: bool },
14140}
14141
14142fn field_of(dex: &ddc_dex::DexFile, field_idx: u32) -> Option<(String, String, String)> {
14144 let f = dex.field(field_idx);
14145 Some((
14146 dex.class_name(f.class_idx),
14149 dex.string(f.name_idx).to_string(),
14150 dex.type_name(f.type_idx).to_string(),
14152 ))
14153}
14154
14155fn param_index(code: &ddc_dex::CodeItem, reg: u16) -> Option<usize> {
14158 let rs = code.registers_size as usize;
14159 let ins = code.ins_size as usize;
14160 let r = reg as usize;
14161 let first = rs.checked_sub(ins)?;
14162 (r >= first && r < rs).then(|| r - first)
14163}
14164
14165fn accessor_shape(
14166 dex: &ddc_dex::DexFile,
14167 core: &[&Insn],
14168 code: &ddc_dex::CodeItem,
14169) -> Option<Shape> {
14170 let reads = |rest: &[&Insn], r: u16| {
14180 rest.iter()
14181 .any(|i| crate::lift::src_regs(&i.kind).contains(&r))
14182 };
14183 let core: &[&Insn] = match core.split_first() {
14184 Some((first, rest))
14185 if matches!(first.kind, InsnKind::Const { dst, .. } if !reads(rest, dst)) =>
14186 {
14187 rest
14188 }
14189 _ => core,
14190 };
14191 let core: &[&Insn] = match core.split_last() {
14192 Some((last, rest))
14193 if matches!(last.kind, InsnKind::Const { dst, .. } if !reads(rest, dst)) =>
14194 {
14195 rest
14196 }
14197 _ => core,
14198 };
14199 match core {
14200 [Insn { kind: InsnKind::Return { src }, .. }] => {
14202 let idx = param_index(code, *src)?;
14203 Some(Shape::Identity(idx))
14204 }
14205 [
14207 Insn { kind: InsnKind::IGet { dst, obj, field_idx }, .. },
14208 Insn { kind: InsnKind::Return { src }, .. },
14209 ] if dst == src => {
14210 let arg = param_index(code, *obj)?;
14211 let (cls, field, field_ty) = field_of(dex, *field_idx)?;
14212 Some(Shape::FieldRead { arg, cls, field, field_ty })
14213 }
14214 [Insn { kind: InsnKind::Invoke { method_idx, regs, kind, .. }, .. }, rest @ ..] if rest.len() <= 2 => {
14216 if !regs.iter().all(|r| param_index(code, *r).is_some()) || regs.is_empty() {
14218 return None;
14219 }
14220 if rest.len() == 2 {
14223 let ok = matches!(rest[0].kind, InsnKind::MoveResult { .. })
14224 && matches!(rest[1].kind, InsnKind::Return { .. } | InsnKind::ReturnVoid);
14225 if !ok {
14226 return None;
14227 }
14228 }
14229 let mid = dex.method(*method_idx);
14230 let cls = dex.class_name(mid.class_idx);
14233 let name = dex.string(mid.name_idx).to_string();
14234 let mut s = String::from("(");
14235 for t in dex.proto_params(mid.proto_idx) {
14236 s.push_str(dex.type_name(*t));
14237 }
14238 s.push(')');
14239 s.push_str(dex.type_name(dex.proto(mid.proto_idx).return_type_idx));
14240 let desc = parse_method_descriptor(&s)?;
14241 Some(Shape::Forward { cls, name, desc, instance: !matches!(kind, InvokeKind::Static) })
14242 }
14243 _ => None,
14244 }
14245}
14246
14247
14248fn is_const_only_trace(kind: &InsnKind, insns: &[&Insn]) -> bool {
14254 let InsnKind::Invoke { kind, regs, .. } = kind else { return false };
14255 if !matches!(kind, InvokeKind::Static) || regs.is_empty() {
14256 return false;
14257 }
14258 regs.iter().all(|r| const_loaded(*r, insns, 0))
14259}
14260
14261fn const_loaded(r: u16, insns: &[&Insn], depth: u8) -> bool {
14264 if depth > 3 {
14265 return false;
14266 }
14267 insns.iter().any(|i| match &i.kind {
14268 InsnKind::Const { dst, .. } => *dst == r,
14269 InsnKind::Move { dst, src } => *dst == r && const_loaded(*src, insns, depth + 1),
14270 _ => false,
14271 })
14272}