1#![allow(clippy::collapsible_match)]
14
15use jdc_core::FxHashSet as HashSet;
16
17use jdc_core::ir::build::has_side_effects;
21use jdc_core::emit::is_java_keyword;
22use jdc_core::ir::expr::{AssignOp, BinOp, ConcatPart, ConstVal, Expr, TypeRef, UnOp};
23use jdc_core::ir::stmt::{CaseGroup, Catch, Stmt};
24use jdc_core::types::{JavaType, MethodDescriptor};
25use jdc_core::var::VarTable;
26
27use crate::lift::MethodEnv;
28use crate::{access, desc_type, DexPool};
29use ddc_dex::insn::{Insn, InsnKind, InvokeKind};
30use jdc_core::types::parse_method_descriptor;
31
32pub fn rewrite_exprs<F: FnMut(&mut Expr)>(s: &mut Stmt, f: &mut F) {
38 walk_stmt_exprs(s, f);
39}
40
41pub(crate) fn walk_stmt_exprs<F: FnMut(&mut Expr)>(s: &mut Stmt, f: &mut F) {
42 match s {
43 Stmt::Block(v) => {
44 for x in v.iter_mut() {
45 walk_stmt_exprs(x, f);
46 }
47 }
48 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
49 f(e);
50 }
51 Stmt::Return(Some(e)) => f(e),
52 Stmt::LocalDef { init, .. } => {
53 if let Some(e) = init {
54 f(e);
55 }
56 }
57 Stmt::If {
58 cond,
59 then_stmt,
60 else_stmt,
61 } => {
62 f(cond);
63 walk_stmt_exprs(then_stmt, f);
64 if let Some(e) = else_stmt {
65 walk_stmt_exprs(e, f);
66 }
67 }
68 Stmt::While { cond, body } => {
69 f(cond);
70 walk_stmt_exprs(body, f);
71 }
72 Stmt::DoWhile { body, cond } => {
73 walk_stmt_exprs(body, f);
74 f(cond);
75 }
76 Stmt::For {
77 init,
78 cond,
79 update,
80 body,
81 } => {
82 for x in init.iter_mut() {
83 walk_stmt_exprs(x, f);
84 }
85 if let Some(c) = cond {
86 f(c);
87 }
88 for u in update.iter_mut() {
89 f(u);
90 }
91 walk_stmt_exprs(body, f);
92 }
93 Stmt::ForEach { iterable, body, .. } => {
94 f(iterable);
95 walk_stmt_exprs(body, f);
96 }
97 Stmt::Switch {
98 selector,
99 cases,
100 default,
101 ..
102 } => {
103 f(selector);
104 for c in cases {
105 for x in c.body.iter_mut() {
106 walk_stmt_exprs(x, f);
107 }
108 if let Some(g) = &mut c.guard {
109 f(g);
110 }
111 }
112 if let Some(d) = default {
113 walk_stmt_exprs(d, f);
114 }
115 }
116 Stmt::Try {
117 body,
118 catches,
119 finally,
120 } => {
121 walk_stmt_exprs(body, f);
122 for c in catches {
123 walk_stmt_exprs(&mut c.body, f);
124 }
125 if let Some(fl) = finally {
126 walk_stmt_exprs(fl, f);
127 }
128 }
129 Stmt::TryWithResources {
130 resources,
131 body,
132 catches,
133 finally,
134 } => {
135 for x in resources.iter_mut() {
136 walk_stmt_exprs(x, f);
137 }
138 walk_stmt_exprs(body, f);
139 for c in catches {
140 walk_stmt_exprs(&mut c.body, f);
141 }
142 if let Some(fl) = finally {
143 walk_stmt_exprs(fl, f);
144 }
145 }
146 Stmt::Assert { cond, msg } => {
147 f(cond);
148 if let Some(m) = msg {
149 f(m);
150 }
151 }
152 Stmt::Synchronized { lock, body } => {
153 f(lock);
154 walk_stmt_exprs(body, f);
155 }
156 Stmt::TernaryValue { e } => f(e),
157 Stmt::Labeled { body, .. } => walk_stmt_exprs(body, f),
158 Stmt::Goto(_) | Stmt::Label(_) | Stmt::Break(_) | Stmt::Continue(_) => {}
159 _ => {}
160 }
161}
162
163pub(crate) fn visit_stmt_exprs_ro<F: FnMut(&Expr)>(s: &Stmt, f: &mut F) {
168 match s {
169 Stmt::Block(v) => {
170 for x in v {
171 visit_stmt_exprs_ro(x, f);
172 }
173 }
174 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
175 f(e);
176 }
177 Stmt::Return(Some(e)) => f(e),
178 Stmt::LocalDef { init: Some(e), .. } => f(e),
179 Stmt::If {
180 cond,
181 then_stmt,
182 else_stmt,
183 } => {
184 f(cond);
185 visit_stmt_exprs_ro(then_stmt, f);
186 if let Some(e) = else_stmt {
187 visit_stmt_exprs_ro(e, f);
188 }
189 }
190 Stmt::While { cond, body } => {
191 f(cond);
192 visit_stmt_exprs_ro(body, f);
193 }
194 Stmt::DoWhile { body, cond } => {
195 visit_stmt_exprs_ro(body, f);
196 f(cond);
197 }
198 Stmt::For {
199 init,
200 cond,
201 update,
202 body,
203 } => {
204 for x in init {
205 visit_stmt_exprs_ro(x, f);
206 }
207 if let Some(c) = cond {
208 f(c);
209 }
210 for u in update {
211 f(u);
212 }
213 visit_stmt_exprs_ro(body, f);
214 }
215 Stmt::ForEach { iterable, body, .. } => {
216 f(iterable);
217 visit_stmt_exprs_ro(body, f);
218 }
219 Stmt::Switch {
220 selector,
221 cases,
222 default,
223 ..
224 } => {
225 f(selector);
226 for c in cases {
227 for x in &c.body {
228 visit_stmt_exprs_ro(x, f);
229 }
230 if let Some(g) = &c.guard {
231 f(g);
232 }
233 }
234 if let Some(d) = default {
235 visit_stmt_exprs_ro(d, f);
236 }
237 }
238 Stmt::Try {
239 body,
240 catches,
241 finally,
242 } => {
243 visit_stmt_exprs_ro(body, f);
244 for c in catches {
245 visit_stmt_exprs_ro(&c.body, f);
246 }
247 if let Some(fl) = finally {
248 visit_stmt_exprs_ro(fl, f);
249 }
250 }
251 Stmt::TryWithResources {
252 resources,
253 body,
254 catches,
255 finally,
256 ..
257 } => {
258 for x in resources {
259 visit_stmt_exprs_ro(x, f);
260 }
261 visit_stmt_exprs_ro(body, f);
262 for c in catches {
263 visit_stmt_exprs_ro(&c.body, f);
264 }
265 if let Some(fl) = finally {
266 visit_stmt_exprs_ro(fl, f);
267 }
268 }
269 Stmt::Assert { cond, msg } => {
270 f(cond);
271 if let Some(m) = msg {
272 f(m);
273 }
274 }
275 Stmt::Synchronized { lock, body } => {
276 f(lock);
277 visit_stmt_exprs_ro(body, f);
278 }
279 Stmt::TernaryValue { e } => f(e),
280 Stmt::Labeled { body, .. } => visit_stmt_exprs_ro(body, f),
281 _ => {}
282 }
283}
284
285fn count_expr_ro(e: &Expr, m: &mut std::collections::HashMap<u32, usize>) {
291 if let Expr::Local { var, .. } = e {
292 *m.entry(*var).or_insert(0) += 1;
293 return;
294 }
295 if let Expr::Method {
296 name,
297 is_special: true,
298 owner: Some(o),
299 args,
300 ..
301 } = e
302 {
303 if &**name == "<init>" && !matches!(**o, Expr::This) {
304 for a in args {
305 count_expr_ro(a, m);
306 }
307 return;
308 }
309 }
310 for_each_child(e, &mut |c| count_expr_ro(c, m));
311}
312
313fn visit_exprs<F: FnMut(&Expr)>(e: &Expr, f: &mut F) {
315 f(e);
316 for_each_child(e, &mut |c| visit_exprs(c, f));
317}
318
319fn for_each_child<F: FnMut(&Expr)>(e: &Expr, f: &mut F) {
320 match e {
321 Expr::Un { e, .. } | Expr::Cast { e, .. } | Expr::InstanceOf { e, .. } => f(e),
322 Expr::Bin { l, r, .. } => {
323 f(l);
324 f(r);
325 }
326 Expr::Cond { c, t, f: fe } => {
327 f(c);
328 f(t);
329 f(fe);
330 }
331 Expr::Assign { target, value, .. } => {
332 f(target);
333 f(value);
334 }
335 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => f(e),
336 Expr::Field { owner, .. } => {
337 if let Some(o) = owner {
338 f(o);
339 }
340 }
341 Expr::Method { owner, args, .. } => {
342 if let Some(o) = owner {
343 f(o);
344 }
345 for a in args {
346 f(a);
347 }
348 }
349 Expr::ArrayIndex { array, index } => {
350 f(array);
351 f(index);
352 }
353 Expr::New { args, .. } => {
354 for a in args {
355 f(a);
356 }
357 }
358 Expr::NewArray { dims, init, .. } => {
359 for d in dims {
360 f(d);
361 }
362 if let Some(v) = init {
363 for x in v {
364 f(x);
365 }
366 }
367 }
368 Expr::NewMultiArray { dims, .. } => {
369 for d in dims {
370 f(d);
371 }
372 }
373 Expr::StringConcat(parts) => {
374 for p in parts {
375 if let ConcatPart::Str(x) = p {
376 f(x);
377 }
378 }
379 }
380 Expr::Invokedynamic { args, .. } => {
381 for a in args {
382 f(a);
383 }
384 }
385 Expr::AnonNew { args, .. } => {
386 for a in args {
387 f(a);
388 }
389 }
390 _ => {}
391 }
392}
393
394fn for_each_child_mut<F: FnMut(&mut Expr)>(e: &mut Expr, f: &mut F) {
396 match e {
397 Expr::Un { e, .. } | Expr::Cast { e, .. } | Expr::InstanceOf { e, .. } => f(e),
398 Expr::Bin { l, r, .. } => {
399 f(l);
400 f(r);
401 }
402 Expr::Cond { c, t, f: fe } => {
403 f(c);
404 f(t);
405 f(fe);
406 }
407 Expr::Assign { target, value, .. } => {
408 f(target);
409 f(value);
410 }
411 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => f(e),
412 Expr::Field { owner, .. } => {
413 if let Some(o) = owner {
414 f(o);
415 }
416 }
417 Expr::Method { owner, args, .. } => {
418 if let Some(o) = owner {
419 f(o);
420 }
421 for a in args.iter_mut() {
422 f(a);
423 }
424 }
425 Expr::ArrayIndex { array, index } => {
426 f(array);
427 f(index);
428 }
429 Expr::New { args, .. } => {
430 for a in args.iter_mut() {
431 f(a);
432 }
433 }
434 Expr::NewArray { dims, init, .. } => {
435 for d in dims.iter_mut() {
436 f(d);
437 }
438 if let Some(v) = init {
439 for x in v.iter_mut() {
440 f(x);
441 }
442 }
443 }
444 Expr::NewMultiArray { dims, .. } => {
445 for d in dims.iter_mut() {
446 f(d);
447 }
448 }
449 Expr::StringConcat(parts) => {
450 for p in parts {
451 if let ConcatPart::Str(x) = p {
452 f(x);
453 }
454 }
455 }
456 Expr::Invokedynamic { args, .. } => {
457 for a in args.iter_mut() {
458 f(a);
459 }
460 }
461 Expr::AnonNew { args, .. } => {
462 for a in args.iter_mut() {
463 f(a);
464 }
465 }
466 _ => {}
467 }
468}
469
470pub(crate) fn deep_rewrite<F: FnMut(&mut Expr)>(e: &mut Expr, f: &mut F) {
472 f(e);
473 for_each_child_mut(e, &mut |c| deep_rewrite(c, f));
474}
475
476fn visit_exprs_reads<F: FnMut(&Expr)>(e: &Expr, f: &mut F) {
488 f(e);
489 match e {
490 Expr::Assign { target, value, .. } => {
491 if !matches!(**target, Expr::Local { .. }) {
492 visit_exprs_reads(target, f);
493 }
494 visit_exprs_reads(value, f);
495 }
496 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
497 if !matches!(**inner, Expr::Local { .. }) {
498 visit_exprs_reads(inner, f);
499 }
500 }
501 _ => for_each_child(e, &mut |c| visit_exprs_reads(c, f)),
502 }
503}
504
505fn deep_rewrite_reads<F: FnMut(&mut Expr)>(e: &mut Expr, f: &mut F) {
506 f(e);
507 match e {
508 Expr::Assign { target, value, .. } => {
509 if !matches!(**target, Expr::Local { .. }) {
510 deep_rewrite_reads(target, f);
511 }
512 deep_rewrite_reads(value, f);
513 }
514 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
515 if !matches!(**inner, Expr::Local { .. }) {
516 deep_rewrite_reads(inner, f);
517 }
518 }
519 _ => for_each_child_mut(e, &mut |c| deep_rewrite_reads(c, f)),
520 }
521}
522
523fn collect_vars(e: &Expr, out: &mut HashSet<u32>) {
524 visit_exprs(e, &mut |x| {
525 if let Expr::Local { var, .. } = x {
526 out.insert(*var);
527 }
528 });
529}
530
531fn stmt_collect_vars(s: &Stmt, out: &mut HashSet<u32>, assignments: bool) {
536 match s {
537 Stmt::Block(v) => {
538 for x in v {
539 stmt_collect_vars(x, out, assignments);
540 }
541 }
542 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
543 if let Expr::Local { var, .. } = &**target {
544 if assignments {
545 out.insert(*var);
546 }
547 } else {
548 collect_vars(target, out);
549 }
550 collect_vars(value, out);
551 }
552 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
553 collect_vars(e, out)
554 }
555 Stmt::Return(Some(e)) => collect_vars(e, out),
556 Stmt::LocalDef { var, init, .. } => {
557 if assignments {
558 out.insert(*var);
559 }
560 if let Some(e) = init {
561 collect_vars(e, out);
562 }
563 }
564 Stmt::If {
565 cond,
566 then_stmt,
567 else_stmt,
568 } => {
569 collect_vars(cond, out);
570 stmt_collect_vars(then_stmt, out, assignments);
571 if let Some(e) = else_stmt {
572 stmt_collect_vars(e, out, assignments);
573 }
574 }
575 Stmt::While { cond, body } => {
576 collect_vars(cond, out);
577 stmt_collect_vars(body, out, assignments);
578 }
579 Stmt::DoWhile { body, cond } => {
580 stmt_collect_vars(body, out, assignments);
581 collect_vars(cond, out);
582 }
583 Stmt::For {
584 init,
585 cond,
586 update,
587 body,
588 } => {
589 for x in init {
590 stmt_collect_vars(x, out, assignments);
591 }
592 if let Some(c) = cond {
593 collect_vars(c, out);
594 }
595 for u in update {
596 collect_vars(u, out);
597 }
598 stmt_collect_vars(body, out, assignments);
599 }
600 Stmt::ForEach {
601 var,
602 iterable,
603 body,
604 ..
605 } => {
606 if assignments {
607 out.insert(*var);
608 }
609 collect_vars(iterable, out);
610 stmt_collect_vars(body, out, assignments);
611 }
612 Stmt::Switch {
613 selector,
614 cases,
615 default,
616 ..
617 } => {
618 collect_vars(selector, out);
619 for c in cases {
620 for x in &c.body {
621 stmt_collect_vars(x, out, assignments);
622 }
623 }
624 if let Some(d) = default {
625 stmt_collect_vars(d, out, assignments);
626 }
627 }
628 Stmt::Try {
629 body,
630 catches,
631 finally,
632 } => {
633 stmt_collect_vars(body, out, assignments);
634 for c in catches {
635 stmt_collect_vars(&c.body, out, assignments);
636 }
637 if let Some(f) = finally {
638 stmt_collect_vars(f, out, assignments);
639 }
640 }
641 Stmt::TryWithResources {
642 resources,
643 body,
644 catches,
645 finally,
646 } => {
647 for x in resources {
648 stmt_collect_vars(x, out, assignments);
649 }
650 stmt_collect_vars(body, out, assignments);
651 for c in catches {
652 stmt_collect_vars(&c.body, out, assignments);
653 }
654 if let Some(f) = finally {
655 stmt_collect_vars(f, out, assignments);
656 }
657 }
658 Stmt::Synchronized { lock, body } => {
659 collect_vars(lock, out);
660 stmt_collect_vars(body, out, assignments);
661 }
662 Stmt::Assert { cond, msg } => {
663 collect_vars(cond, out);
664 if let Some(m) = msg {
665 collect_vars(m, out);
666 }
667 }
668 Stmt::Labeled { body, .. } => stmt_collect_vars(body, out, assignments),
669 _ => {}
670 }
671}
672
673fn walk_all<F: FnMut(&Stmt)>(s: &Stmt, f: &mut F) {
674 f(s);
675 match s {
676 Stmt::Block(v) => {
677 for x in v {
678 walk_all(x, f);
679 }
680 }
681 Stmt::If {
682 then_stmt,
683 else_stmt,
684 ..
685 } => {
686 walk_all(then_stmt, f);
687 if let Some(e) = else_stmt {
688 walk_all(e, f);
689 }
690 }
691 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => walk_all(body, f),
692 Stmt::For { init, body, .. } => {
693 for x in init {
694 walk_all(x, f);
695 }
696 walk_all(body, f);
697 }
698 Stmt::ForEach { body, .. } => walk_all(body, f),
699 Stmt::Switch { cases, default, .. } => {
700 for c in cases {
701 for x in &c.body {
702 walk_all(x, f);
703 }
704 }
705 if let Some(d) = default {
706 walk_all(d, f);
707 }
708 }
709 Stmt::Try {
710 body,
711 catches,
712 finally,
713 } => {
714 walk_all(body, f);
715 for c in catches {
716 walk_all(&c.body, f);
717 }
718 if let Some(fl) = finally {
719 walk_all(fl, f);
720 }
721 }
722 Stmt::TryWithResources {
723 resources,
724 body,
725 catches,
726 finally,
727 } => {
728 for x in resources {
729 walk_all(x, f);
730 }
731 walk_all(body, f);
732 for c in catches {
733 walk_all(&c.body, f);
734 }
735 if let Some(fl) = finally {
736 walk_all(fl, f);
737 }
738 }
739 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => walk_all(body, f),
740 _ => {}
741 }
742}
743
744pub fn bind_catches(s: &mut Stmt, vt: &mut VarTable) {
751 let mut defined: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
756 for v in &vt.vars {
757 if v.is_param {
758 defined.insert(v.id);
759 }
760 }
761 collect_defined_locals(s, &mut defined);
762 let reads_all = count_locals_stmts(std::slice::from_ref(s));
763 let mut assigned: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
767 collect_assigned_locals(s, &mut assigned);
768 let mut fallback_bound: Vec<u32> = Vec::new();
769 bind_catches_walk(s, vt, &defined, &reads_all, &assigned, &mut fallback_bound);
770 if !fallback_bound.is_empty() {
773 remove_bare_decls(s, &fallback_bound);
774 }
775}
776
777fn collect_defined_locals(s: &Stmt, out: &mut jdc_core::FxHashSet<u32>) {
778 crate::passes::walk_all(s, &mut |st| {
779 match st {
780 Stmt::LocalDef { var, .. } => {
781 out.insert(*var);
782 }
783 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
784 if let Expr::Local { var, .. } = &**target {
785 out.insert(*var);
786 }
787 }
788 _ => {}
789 }
790 });
791}
792
793fn collect_bare_decls(s: &Stmt, out: &mut Vec<u32>) {
796 walk_all(s, &mut |st| {
797 if let Stmt::LocalDef { var, init: None, .. } = st {
798 if !out.contains(var) {
799 out.push(*var);
800 }
801 }
802 });
803}
804
805
806fn collect_assigned_locals(s: &Stmt, out: &mut jdc_core::FxHashSet<u32>) {
811 crate::passes::walk_all(s, &mut |st| {
812 match st {
813 Stmt::LocalDef { var, init: Some(_), .. } => {
814 out.insert(*var);
815 }
816 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
817 if let Expr::Local { var, .. } = &**target {
818 out.insert(*var);
819 }
820 }
821 _ => {}
822 }
823 });
824}
825
826fn first_thrown_unassigned_local(
829 body: &Stmt,
830 vt: &VarTable,
831 assigned: &jdc_core::FxHashSet<u32>,
832) -> Option<u32> {
833 let mut found: Option<u32> = None;
834 let c = body.clone();
835 walk_all(&c, &mut |st| {
836 if let Stmt::Throw(th) = st {
837 if let Expr::Local { var, .. } = th {
838 let v = *var;
839 if !assigned.contains(&v) {
840 if let JavaType::Object(o) = vt.var(v).ty.erased() {
841 if o.as_ref() == "java/lang/Throwable" {
842 found = Some(v);
843 }
844 }
845 }
846 }
847 }
848 });
849 found
850}
851
852fn remove_first_leaf(s: &mut Stmt) {
856 if let Stmt::Block(v) = s {
857 if let Some(f) = v.first_mut() {
858 remove_first_leaf(f);
859 }
860 while matches!(v.first(), Some(Stmt::Block(b)) if b.is_empty()) {
861 v.remove(0);
862 }
863 } else {
864 *s = Stmt::Block(vec![]);
865 }
866}
867
868fn remove_bare_decls(s: &mut Stmt, vars: &[u32]) {
871 let vars: jdc_core::FxHashSet<u32> = vars.iter().copied().collect();
872 walk_mut_deep(s, &mut |st| {
873 if let Stmt::Block(v) = st {
874 v.retain(|x| {
875 !matches!(x, Stmt::LocalDef { var, init: None, .. } if vars.contains(var))
876 });
877 }
878 });
879}
880
881fn first_undefined_local_read(
882 body: &Stmt,
883 defined: &jdc_core::FxHashSet<u32>,
884) -> Option<u32> {
885 let mut found: Option<u32> = None;
886 visit_stmt_exprs_ro(body, &mut |e| {
887 if found.is_none() {
888 visit_exprs(e, &mut |x| {
889 if found.is_none() {
890 if let Expr::Local { var, .. } = x {
891 if !defined.contains(var) {
892 found = Some(*var);
893 }
894 }
895 }
896 });
897 }
898 });
899 found
900}
901
902#[allow(clippy::too_many_arguments)]
903fn bind_catches_walk(
904 s: &mut Stmt,
905 vt: &mut VarTable,
906 defined: &jdc_core::FxHashSet<u32>,
907 reads_all: &std::collections::HashMap<u32, usize>,
908 assigned: &jdc_core::FxHashSet<u32>,
909 fallback_bound: &mut Vec<u32>,
910) {
911 match s {
912 Stmt::Try {
913 body,
914 catches,
915 finally,
916 } => {
917 bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound);
918 for c in catches.iter_mut() {
919 if c.var == u32::MAX {
920 let stored = match first_leaf_stmt(c.body.as_ref()) {
927 Stmt::LocalDef { var, init: None, .. } => Some(*var),
939 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
943 match (&**target, &**value) {
944 (Expr::Local { var, .. }, Expr::Local { var: src, .. })
945 if !defined.contains(src) =>
946 {
947 Some(*var)
948 }
949 _ => None,
950 }
951 }
952 _ => None,
953 };
954 if let Some(v) = stored {
955 let exc = c
956 .exc
957 .first()
958 .cloned()
959 .unwrap_or_else(|| "java/lang/Throwable".into());
960 let slot = vt.var(v).slot;
961 let name = "e".to_string();
962 let new_var =
963 vt.add_catch_var(slot, name, TypeRef::J(JavaType::Object(exc)));
964 remove_first_leaf(c.body.as_mut());
965 rewrite_local_refs(c.body.as_mut(), v, new_var);
966 c.var = new_var;
967 } else if let Some(v) = first_undefined_local_read(c.body.as_ref(), defined)
968 .filter(|v| {
969 let in_catch = count_locals_stmts(std::slice::from_ref(c.body.as_ref()));
975 reads_all.get(v).copied().unwrap_or(0)
976 == in_catch.get(v).copied().unwrap_or(0)
977 })
978 .filter(|v| {
979 let ty = vt.var(*v).ty.erased();
987 let exc = c
988 .exc
989 .first()
990 .cloned()
991 .unwrap_or_else(|| "java/lang/Throwable".into());
992 matches!(&ty, JavaType::Object(o)
993 if o.as_ref() == exc.as_ref()
994 || o.as_ref() == "java/lang/Throwable")
995 })
996 {
997 c.var = v;
1007 fallback_bound.push(v);
1008 } else if let Some(v) =
1009 first_thrown_unassigned_local(c.body.as_ref(), vt, assigned)
1010 .filter(|v| {
1011 let in_catch =
1012 count_locals_stmts(std::slice::from_ref(c.body.as_ref()));
1013 reads_all.get(v).copied().unwrap_or(0)
1014 == in_catch.get(v).copied().unwrap_or(0)
1015 })
1016 .filter(|v| {
1017 let ty = vt.var(*v).ty.erased();
1021 let exc = c
1022 .exc
1023 .first()
1024 .cloned()
1025 .unwrap_or_else(|| "java/lang/Throwable".into());
1026 matches!(&ty, JavaType::Object(o)
1027 if o.as_ref() == exc.as_ref()
1028 || o.as_ref() == "java/lang/Throwable")
1029 })
1030 {
1031 c.var = v;
1037 fallback_bound.push(v);
1038 }
1039 }
1040 if c.var != u32::MAX {
1041 let in_catch =
1056 count_locals_stmts(std::slice::from_ref(c.body.as_ref()));
1057 let mut twins: Vec<u32> = Vec::new();
1058 collect_bare_decls(c.body.as_ref(), &mut twins);
1059 let mut swept: Vec<u32> = Vec::new();
1060 for x in twins {
1061 if x == c.var || assigned.contains(&x) {
1062 continue;
1063 }
1064 if reads_all.get(&x).copied().unwrap_or(0)
1065 != in_catch.get(&x).copied().unwrap_or(0)
1066 {
1067 continue;
1068 }
1069 if !matches!(vt.var(x).ty.erased(),
1070 JavaType::Object(o) if o.as_ref() == "java/lang/Throwable")
1071 {
1072 continue;
1073 }
1074 rewrite_local_refs(c.body.as_mut(), x, c.var);
1075 swept.push(x);
1076 }
1077 if !swept.is_empty() {
1078 remove_bare_decls(c.body.as_mut(), &swept);
1079 }
1080 }
1081 bind_catches_walk(&mut c.body, vt, defined, reads_all, assigned, fallback_bound);
1082 }
1083 if let Some(f) = finally {
1084 bind_catches_walk(f.as_mut(), vt, defined, reads_all, assigned, fallback_bound);
1085 }
1086 }
1087 Stmt::Block(v) => {
1088 for x in v.iter_mut() {
1089 bind_catches_walk(x, vt, defined, reads_all, assigned, fallback_bound);
1090 }
1091 }
1092 Stmt::If {
1093 then_stmt,
1094 else_stmt,
1095 ..
1096 } => {
1097 bind_catches_walk(then_stmt, vt, defined, reads_all, assigned, fallback_bound);
1098 if let Some(e) = else_stmt {
1099 bind_catches_walk(e, vt, defined, reads_all, assigned, fallback_bound);
1100 }
1101 }
1102 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound),
1103 Stmt::For { init, body, .. } => {
1104 for x in init.iter_mut() {
1105 bind_catches_walk(x, vt, defined, reads_all, assigned, fallback_bound);
1106 }
1107 bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound);
1108 }
1109 Stmt::ForEach { body, .. }
1110 | Stmt::Labeled { body, .. }
1111 | Stmt::Synchronized { body, .. } => bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound),
1112 Stmt::Switch { cases, default, .. } => {
1113 for c in cases.iter_mut() {
1114 for x in c.body.iter_mut() {
1115 bind_catches_walk(x, vt, defined, reads_all, assigned, fallback_bound);
1116 }
1117 }
1118 if let Some(d) = default {
1119 bind_catches_walk(d, vt, defined, reads_all, assigned, fallback_bound);
1120 }
1121 }
1122 _ => {}
1123 }
1124}
1125
1126fn rewrite_local_refs(s: &mut Stmt, from: u32, to: u32) {
1127 rewrite_exprs(s, &mut |e| {
1128 deep_rewrite(e, &mut |x| {
1129 if let Expr::Local { var, .. } = x {
1130 if *var == from {
1131 *var = to;
1132 }
1133 }
1134 });
1135 });
1136}
1137
1138pub fn cleanup(s: &mut Stmt) {
1140 jdc_core::ir::stmt::flatten(s);
1141 strip_empty(s);
1142 merge_singleton_stmts(s);
1143}
1144
1145fn strip_empty(s: &mut Stmt) {
1146 match s {
1147 Stmt::Block(v) => {
1148 v.retain(|x| !x.is_empty_block());
1149 for x in v.iter_mut() {
1150 strip_empty(x);
1151 }
1152 }
1153 Stmt::If {
1154 then_stmt,
1155 else_stmt,
1156 ..
1157 } => {
1158 strip_empty(then_stmt);
1159 if let Some(e) = else_stmt {
1160 strip_empty(e);
1161 if e.is_empty_block() {
1162 *else_stmt = None;
1163 }
1164 }
1165 }
1166 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => strip_empty(body),
1167 Stmt::For { init, body, .. } => {
1168 init.retain(|x| !x.is_empty_block());
1169 strip_empty(body);
1170 }
1171 Stmt::ForEach { body, .. } => strip_empty(body),
1172 Stmt::Switch { cases, default, .. } => {
1173 for c in cases {
1174 c.body.retain(|x| !x.is_empty_block());
1175 }
1176 if let Some(d) = default {
1177 strip_empty(d);
1178 }
1179 }
1180 Stmt::Try {
1181 body,
1182 catches,
1183 finally,
1184 } => {
1185 strip_empty(body);
1186 for c in catches {
1187 strip_empty(&mut c.body);
1188 }
1189 if let Some(f) = finally {
1190 strip_empty(f);
1191 }
1192 }
1193 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => strip_empty(body),
1194 _ => {}
1195 }
1196}
1197
1198fn merge_singleton_stmts(s: &mut Stmt) {
1199 if let Stmt::Block(v) = s {
1200 for x in v.iter_mut() {
1201 merge_singleton_stmts(x);
1202 }
1203 }
1204}
1205
1206pub fn prune_unreachable(s: &mut Stmt) {
1208 if let Stmt::Block(v) = s {
1209 let mut cut: Option<usize> = None;
1210 for (i, x) in v.iter().enumerate() {
1211 if matches!(x, Stmt::Return(_) | Stmt::Throw(_)) {
1212 cut = Some(i + 1);
1213 break;
1214 }
1215 }
1216 if let Some(c) = cut {
1217 if c < v.len() {
1218 v.truncate(c);
1219 }
1220 }
1221 for x in v.iter_mut() {
1222 prune_unreachable(x);
1223 }
1224 } else {
1225 walk_mut(s, &mut prune_unreachable);
1226 }
1227}
1228
1229fn walk_mut<F: FnMut(&mut Stmt)>(s: &mut Stmt, f: &mut F) {
1230 match s {
1231 Stmt::Block(v) => {
1232 for x in v.iter_mut() {
1233 f(x);
1234 }
1235 }
1236 Stmt::If {
1237 then_stmt,
1238 else_stmt,
1239 ..
1240 } => {
1241 f(then_stmt);
1242 if let Some(e) = else_stmt {
1243 f(e);
1244 }
1245 }
1246 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => f(body),
1247 Stmt::For { init, body, .. } => {
1248 for x in init.iter_mut() {
1249 f(x);
1250 }
1251 f(body);
1252 }
1253 Stmt::ForEach { body, .. } => f(body),
1254 Stmt::Switch { cases, default, .. } => {
1255 for c in cases {
1256 for x in c.body.iter_mut() {
1257 f(x);
1258 }
1259 }
1260 if let Some(d) = default {
1261 f(d);
1262 }
1263 }
1264 Stmt::Try {
1265 body,
1266 catches,
1267 finally,
1268 } => {
1269 f(body);
1270 for c in catches {
1271 f(&mut c.body);
1272 }
1273 if let Some(fl) = finally {
1274 f(fl);
1275 }
1276 }
1277 Stmt::TryWithResources {
1278 resources,
1279 body,
1280 catches,
1281 finally,
1282 } => {
1283 for r in resources.iter_mut() {
1284 f(r);
1285 }
1286 f(body);
1287 for c in catches {
1288 f(&mut c.body);
1289 }
1290 if let Some(fl) = finally {
1291 f(fl);
1292 }
1293 }
1294 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => f(body),
1295 _ => {}
1296 }
1297}
1298
1299pub fn count_localdefs(s: &Stmt, out: &mut usize) {
1301 if matches!(s, Stmt::LocalDef { .. }) {
1302 *out += 1;
1303 }
1304 walk_all(s, &mut |x| {
1305 if matches!(x, Stmt::LocalDef { .. }) {
1306 *out += 1;
1307 }
1308 });
1309}
1310
1311pub fn strip_trailing_void_return(s: &mut Stmt) {
1313 if let Stmt::Block(v) = s {
1314 loop {
1315 match v.last_mut() {
1316 Some(Stmt::Return(None)) => {
1317 v.pop();
1318 }
1319 Some(Stmt::Block(_)) => {
1324 let last = v.last_mut().unwrap();
1325 let before = match last {
1326 Stmt::Block(b) => b.len(),
1327 _ => 0,
1328 };
1329 strip_trailing_void_return(last);
1330 let after = match last {
1331 Stmt::Block(b) => b.len(),
1332 _ => 0,
1333 };
1334 if after == before {
1335 break; }
1337 }
1338 _ => break,
1339 }
1340 }
1341 }
1342}
1343
1344pub fn invert_empty_thens(s: &mut Stmt) {
1349 walk_mut_deep(s, &mut |st| {
1350 if let Stmt::If {
1351 cond,
1352 then_stmt,
1353 else_stmt,
1354 } = st
1355 {
1356 let then_empty = matches!(&**then_stmt, Stmt::Block(v) if v.is_empty());
1357 if then_empty {
1358 if let Some(els) = else_stmt.take() {
1359 let c = std::mem::replace(cond, Expr::Const(ConstVal::Int(0)));
1360 *cond = jdc_core::convert::negate(c);
1361 *then_stmt = els;
1362 }
1363 }
1364 }
1365 });
1366}
1367
1368pub fn fold_short_circuits(s: &mut Stmt) {
1379 for _ in 0..8 {
1380 let mut changed = false;
1381 walk_mut_deep(s, &mut |st| {
1382 if try_fold_diamond(st) {
1383 changed = true;
1384 }
1385 });
1386 if !changed {
1387 break;
1388 }
1389 }
1390}
1391
1392fn try_fold_diamond(st: &mut Stmt) -> bool {
1393 enum Shape {
1394 ThenAnd,
1395 ThenAndNot,
1396 ElseOr,
1397 ElseOrNot,
1398 BareAnd,
1399 BareAndNot,
1400 }
1401 let shape = match &*st {
1405 Stmt::If {
1408 then_stmt,
1409 else_stmt: None,
1410 ..
1411 } => match &**then_stmt {
1412 Stmt::If {
1413 then_stmt: t2,
1414 else_stmt: None,
1415 ..
1416 } if !matches!(&**t2, Stmt::Block(v) if v.is_empty()) => Some(Shape::BareAnd),
1417 Stmt::If {
1418 then_stmt: t2,
1419 else_stmt: Some(_),
1420 ..
1421 } if matches!(&**t2, Stmt::Block(v) if v.is_empty()) => Some(Shape::BareAndNot),
1422 _ => None,
1423 },
1424 Stmt::If {
1425 then_stmt,
1426 else_stmt: Some(els),
1427 ..
1428 } => match (&**then_stmt, &**els) {
1429 (Stmt::If { else_stmt: Some(f2), .. }, _) if f2.as_ref() == els.as_ref() => {
1430 Some(Shape::ThenAnd)
1431 }
1432 (Stmt::If { then_stmt: t2, else_stmt: Some(_), .. }, _)
1433 if t2.as_ref() == els.as_ref() =>
1434 {
1435 Some(Shape::ThenAndNot)
1436 }
1437 (_, Stmt::If { then_stmt: t2, else_stmt: Some(_), .. })
1438 if t2.as_ref() == then_stmt.as_ref() =>
1439 {
1440 Some(Shape::ElseOr)
1441 }
1442 (_, Stmt::If { else_stmt: Some(f2), .. })
1443 if f2.as_ref() == then_stmt.as_ref() =>
1444 {
1445 Some(Shape::ElseOrNot)
1446 }
1447 _ => None,
1448 },
1449 _ => None,
1450 };
1451 let Some(shape) = shape else {
1452 return false;
1453 };
1454
1455 let taken = std::mem::replace(st, Stmt::Block(vec![]));
1456 if matches!(shape, Shape::BareAnd | Shape::BareAndNot) {
1458 let Stmt::If {
1459 cond: c1,
1460 then_stmt,
1461 else_stmt: None,
1462 } = taken
1463 else {
1464 unreachable!()
1465 };
1466 let Stmt::If {
1467 cond: c2,
1468 then_stmt: t2,
1469 else_stmt: f2,
1470 } = *then_stmt
1471 else {
1472 unreachable!()
1473 };
1474 let sc = |op: BinOp, l: Expr, r: Expr| Expr::Bin {
1475 op,
1476 l: Box::new(l),
1477 r: Box::new(r),
1478 ty: None,
1479 };
1480 match shape {
1481 Shape::BareAnd => {
1483 *st = Stmt::If {
1484 cond: sc(BinOp::LogAnd, c1, c2),
1485 then_stmt: t2,
1486 else_stmt: None,
1487 };
1488 }
1489 _ => {
1491 let body = f2.unwrap();
1492 *st = Stmt::If {
1493 cond: sc(
1494 BinOp::LogAnd,
1495 c1,
1496 Expr::Un {
1497 op: UnOp::Not,
1498 e: Box::new(c2),
1499 },
1500 ),
1501 then_stmt: body,
1502 else_stmt: None,
1503 };
1504 }
1505 }
1506 return true;
1507 }
1508 let Stmt::If {
1509 cond: c1,
1510 then_stmt,
1511 else_stmt: Some(els),
1512 } = taken
1513 else {
1514 unreachable!("shape probe guaranteed an If with else")
1515 };
1516 let sc = |op: BinOp, l: Expr, r: Expr| Expr::Bin {
1517 op,
1518 l: Box::new(l),
1519 r: Box::new(r),
1520 ty: None,
1521 };
1522 let not = |e: Expr| Expr::Un {
1523 op: UnOp::Not,
1524 e: Box::new(e),
1525 };
1526 match shape {
1527 Shape::BareAnd | Shape::BareAndNot => unreachable!("handled above"),
1528 Shape::ThenAnd | Shape::ThenAndNot => {
1529 let Stmt::If {
1530 cond: c2,
1531 then_stmt: t2,
1532 else_stmt: Some(f2),
1533 } = *then_stmt
1534 else {
1535 unreachable!()
1536 };
1537 let (nc, body, alt) = match shape {
1538 Shape::ThenAnd => (sc(BinOp::LogAnd, c1, c2), t2, f2),
1540 _ => (sc(BinOp::LogAnd, c1, not(c2)), f2, t2),
1542 };
1543 *st = Stmt::If {
1544 cond: nc,
1545 then_stmt: body,
1546 else_stmt: Some(alt),
1547 };
1548 }
1549 Shape::ElseOr | Shape::ElseOrNot => {
1550 let Stmt::If {
1551 cond: c2,
1552 then_stmt: t2,
1553 else_stmt: Some(f2),
1554 } = *els
1555 else {
1556 unreachable!()
1557 };
1558 let body = then_stmt;
1561 let (nc, alt) = match shape {
1562 Shape::ElseOr => (sc(BinOp::LogOr, c1, c2), f2),
1564 _ => (sc(BinOp::LogOr, c1, not(c2)), t2),
1566 };
1567 *st = Stmt::If {
1568 cond: nc,
1569 then_stmt: body,
1570 else_stmt: Some(alt),
1571 };
1572 }
1573 }
1574 true
1575}
1576
1577pub fn strip_clinit_returns(s: &mut Stmt) {
1583 walk_mut_deep(s, &mut |st| {
1584 if let Stmt::Block(v) = st {
1585 v.retain(|x| !matches!(x, Stmt::Return(None)));
1586 }
1587 });
1588}
1589
1590pub fn prepend_comment(s: &mut Stmt, text: String) {
1591 let stmt = Stmt::Comment(text);
1592 match s {
1593 Stmt::Block(v) => v.insert(0, stmt),
1594 other => {
1595 let inner = std::mem::replace(other, Stmt::Block(vec![]));
1596 *other = Stmt::Block(vec![stmt, inner]);
1597 }
1598 }
1599}
1600
1601fn is_obj_expr(e: &Expr, obj_var: &dyn Fn(u32) -> bool) -> bool {
1609 match e {
1610 Expr::Local { var, .. } => obj_var(*var),
1611 Expr::Const(_) => false,
1612 _ => e.type_ref().erased().is_reference(),
1613 }
1614}
1615
1616fn null_side_rewrite(x: &mut Expr, obj_var: &dyn Fn(u32) -> bool) {
1621 if let Expr::Bin { op, l, r, .. } = x {
1622 if !matches!(op, BinOp::Eq | BinOp::Ne) {
1623 return;
1624 }
1625 let l_zero = matches!(&**l, Expr::Const(ConstVal::Int(0)));
1626 let r_zero = matches!(&**r, Expr::Const(ConstVal::Int(0)));
1627 if l_zero && !r_zero && is_obj_expr(r, obj_var) {
1628 **l = Expr::Const(ConstVal::Null);
1629 } else if r_zero && !l_zero && is_obj_expr(l, obj_var) {
1630 **r = Expr::Const(ConstVal::Null);
1631 }
1632 }
1633}
1634
1635pub fn fused_expr_rewrites(s: &mut Stmt, vt: &VarTable) {
1636 let mut obj_vars: Vec<bool> = Vec::with_capacity(vt.vars.len());
1638 for v in &vt.vars {
1639 obj_vars.push(v.ty.erased().is_reference());
1640 }
1641 rewrite_exprs(s, &mut |e| {
1642 deep_rewrite(e, &mut |x| {
1643 if let Expr::Invokedynamic { name, args, .. } = x {
1645 if name.starts_with('\0') && args.len() == 2 {
1646 let (cls, ty) = match name.as_str() {
1647 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
1648 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
1649 _ => ("java/lang/Double", JavaType::Double),
1650 };
1651 let desc = MethodDescriptor {
1652 args: vec![ty.clone(), ty],
1653 ret: JavaType::Int,
1654 };
1655 *x = Expr::Method {
1656 owner: None,
1657 cls: cls.into(),
1658 name: "compare".into(),
1659 desc: std::sync::Arc::new(desc),
1660 args: args.clone(),
1661 is_static: true,
1662 is_interface: false,
1663 is_special: false,
1664 is_super: false,
1665 is_dynamic: false,
1666 type_args: vec![],
1667 };
1668 return;
1669 }
1670 }
1671 null_side_rewrite(x, &|v| obj_vars.get(v as usize).copied().unwrap_or(false));
1674 if let Expr::Bin { op, l, r, .. } = x {
1676 if matches!(
1677 op,
1678 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
1679 ) {
1680 let l_const = matches!(&**l, Expr::Const(_));
1681 let r_var = matches!(&**r, Expr::Local { .. });
1682 if l_const && r_var {
1683 std::mem::swap(l, r);
1684 }
1685 }
1686 }
1687 });
1688 });
1689 walk_mut_deep(s, &mut |st| {
1697 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = st {
1698 let tgt_is_obj = match &**target {
1699 Expr::Local { var, .. } => {
1700 obj_vars.get(*var as usize).copied().unwrap_or(false)
1701 }
1702 other => other.type_ref().erased().is_reference(),
1703 };
1704 if tgt_is_obj {
1705 if let Expr::Const(ConstVal::Int(0)) = &**value {
1706 **value = Expr::Const(ConstVal::Null);
1707 }
1708 }
1709 } else if let Stmt::LocalDef { var, init, .. } = st {
1710 if obj_vars.get(*var as usize).copied().unwrap_or(false) {
1711 if let Some(iv) = init {
1712 if let Expr::Const(ConstVal::Int(0)) = iv {
1713 *iv = Expr::Const(ConstVal::Null);
1714 }
1715 }
1716 }
1717 }
1718 });
1719}
1720
1721#[allow(dead_code)]
1724pub fn desugar_cmp_residuals(s: &mut Stmt) {
1725 rewrite_exprs(s, &mut |e| {
1726 deep_rewrite(e, &mut |x| {
1727 if let Expr::Invokedynamic { name, args, .. } = x {
1728 if name.starts_with('\0') && args.len() == 2 {
1729 let (cls, ty) = match name.as_str() {
1730 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
1731 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
1732 _ => ("java/lang/Double", JavaType::Double),
1733 };
1734 let desc = jdc_core::types::MethodDescriptor {
1735 args: vec![ty.clone(), ty],
1736 ret: JavaType::Int,
1737 };
1738 *x = Expr::Method {
1739 owner: None,
1740 cls: cls.into(),
1741 name: "compare".into(),
1742 desc: std::sync::Arc::new(desc),
1743 args: args.clone(),
1744 is_static: true,
1745 is_interface: false,
1746 is_special: false,
1747 is_super: false,
1748 is_dynamic: false,
1749 type_args: vec![],
1750 };
1751 }
1752 }
1753 });
1754 });
1755}
1756
1757pub fn ternary_fold(s: &mut Stmt) {
1759 let mut changed = true;
1760 let mut guard = 0;
1761 while changed && guard < 8 {
1762 changed = false;
1763 guard += 1;
1764 ternary_fold_walk(s, &mut changed);
1765 strip_empty(s);
1766 }
1767}
1768
1769fn ternary_fold_walk(s: &mut Stmt, changed: &mut bool) {
1770 if let Stmt::Block(v) = s {
1771 for x in v.iter_mut() {
1772 ternary_fold_walk(x, changed);
1773 }
1774 return;
1775 }
1776 let Stmt::If {
1777 cond,
1778 then_stmt,
1779 else_stmt,
1780 } = s
1781 else {
1782 return;
1783 };
1784 ternary_fold_walk(then_stmt, changed);
1785 if let Some(e) = else_stmt {
1786 ternary_fold_walk(e, changed);
1787 }
1788 let Some(else_b) = else_stmt else { return };
1789 let target_of = |st: &Stmt| -> Option<u32> {
1790 let Stmt::ExprStmt(Expr::Assign { target, .. }) = st else {
1791 return None;
1792 };
1793 if let Expr::Local { var, .. } = &**target {
1794 Some(*var)
1795 } else {
1796 None
1797 }
1798 };
1799 let (Stmt::Block(tv), Stmt::Block(ev)) = (then_stmt.as_mut(), else_b.as_mut()) else {
1800 return;
1801 };
1802 if tv.len() != 1 || ev.len() != 1 {
1803 return;
1804 }
1805 let (Some(vt_), Some(ve)) = (target_of(&tv[0]), target_of(&ev[0])) else {
1806 return;
1807 };
1808 if vt_ != ve {
1809 return;
1810 }
1811 let mut touched = HashSet::default();
1815 let (Stmt::ExprStmt(a_then), Stmt::ExprStmt(a_else)) = (&tv[0], &ev[0]) else {
1816 return;
1817 };
1818 if let Expr::Assign { value, .. } = a_then {
1819 collect_vars(value, &mut touched);
1820 }
1821 if let Expr::Assign { value, .. } = a_else {
1822 collect_vars(value, &mut touched);
1823 }
1824 if touched.contains(&vt_) {
1825 return;
1826 }
1827 let value_then = match a_then {
1828 Expr::Assign { value, .. } => value.clone(),
1829 _ => return,
1830 };
1831 let value_else = match a_else {
1832 Expr::Assign { value, .. } => value.clone(),
1833 _ => return,
1834 };
1835 let cond_e = cond.clone();
1836 let folded = Stmt::ExprStmt(Expr::Assign {
1837 target: Box::new(Expr::Local {
1838 var: vt_,
1839 ty: vt_join_ty(&value_then, &value_else),
1840 }),
1841 op: AssignOp::Plain,
1842 value: Box::new(Expr::Cond {
1843 c: Box::new(cond_e),
1844 t: value_then,
1845 f: value_else,
1846 }),
1847 });
1848 *s = Stmt::Block(vec![folded]);
1849 *changed = true;
1850}
1851
1852fn vt_join_ty(a: &Expr, b: &Expr) -> TypeRef {
1853 let ta = a.type_ref();
1854 let tb = b.type_ref();
1855 if ta.erased() == tb.erased() {
1856 return ta;
1857 }
1858 TypeRef::J(JavaType::Object("java/lang/Object".into()))
1859}
1860
1861pub fn fold_string_builders(s: &mut Stmt, vt: &VarTable) {
1868 let n_vars = vt.vars.len().max(1);
1870 let mut counts = vec![0usize; n_vars];
1871 count_assignments(s, &mut counts);
1872 let mut values: Vec<Option<Expr>> = vec![None; n_vars];
1873 record_assignments(s, &counts, &mut values);
1874
1875 let mut appended_stmts: HashSet<u32> = HashSet::default();
1877 walk_all(s, &mut |st| {
1878 if let Stmt::ExprStmt(Expr::Method {
1879 cls,
1880 name,
1881 owner,
1882 args,
1883 ..
1884 }) = st
1885 {
1886 if name.as_ref() == "append" && is_string_builder(cls) && args.len() == 1 {
1887 if let Some(Expr::Local { var, .. }) = owner.as_deref() {
1888 appended_stmts.insert(*var);
1889 }
1890 }
1891 }
1892 });
1893
1894 rewrite_exprs(s, &mut |e| {
1895 fold_concat_in_expr(e, &values, &appended_stmts);
1896 });
1897
1898 for _ in 0..8 {
1902 let mut reads: HashSet<u32> = HashSet::default();
1903 stmt_collect_vars(s, &mut reads, false);
1904 if drop_unused_assigns(s, &reads) == 0 {
1905 break;
1906 }
1907 }
1908}
1909
1910pub fn count_stmts_deep(s: &Stmt, out: &mut usize) {
1912 *out += 1;
1913 walk_all(s, &mut |_| {
1914 *out += 1;
1915 });
1916}
1917
1918struct VarAnalysis {
1920 assigns: Vec<usize>,
1921 reads: Vec<usize>,
1922 values: Vec<Option<Expr>>,
1924}
1925
1926fn analyze_vars(s: &Stmt, a: &mut VarAnalysis) {
1927 walk_all(s, &mut |st| {
1928 let exprs: Vec<&Expr> = match st {
1929 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
1930 if let Expr::Local { var, .. } = &**target {
1931 grow_to(&mut a.assigns, *var);
1932 let idx = *var as usize;
1933 a.assigns[idx] += 1;
1934 if a.assigns[idx] == 1 {
1935 if idx >= a.values.len() {
1936 a.values.resize(idx + 1, None);
1937 }
1938 a.values[idx] = Some((**value).clone());
1939 } else if idx < a.values.len() {
1940 a.values[idx] = None;
1941 }
1942 }
1943 let mut v: Vec<&Expr> = vec![value];
1944 if !matches!(&**target, Expr::Local { .. }) {
1945 v.push(target);
1946 }
1947 v
1948 }
1949 Stmt::ExprStmt(e @ (Expr::PreIncDec { .. } | Expr::PostIncDec { .. })) => {
1950 let inner = match e {
1955 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => e,
1956 _ => unreachable!(),
1957 };
1958 if let Expr::Local { var, .. } = &**inner {
1959 grow_to(&mut a.assigns, *var);
1960 let idx = *var as usize;
1961 a.assigns[idx] += 1;
1962 if idx < a.values.len() {
1963 a.values[idx] = None;
1964 }
1965 return;
1966 }
1967 vec![e]
1968 }
1969 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
1970 vec![e]
1971 }
1972 Stmt::Return(Some(e)) => vec![e],
1973 Stmt::LocalDef {
1974 var, init: Some(e), ..
1975 } => {
1976 grow_to(&mut a.assigns, *var);
1977 let idx = *var as usize;
1978 a.assigns[idx] += 1;
1979 if a.assigns[idx] == 1 {
1980 if idx >= a.values.len() {
1981 a.values.resize(idx + 1, None);
1982 }
1983 a.values[idx] = Some(e.clone());
1984 } else if idx < a.values.len() {
1985 a.values[idx] = None;
1986 }
1987 vec![e]
1988 }
1989 Stmt::If { cond, .. } => vec![cond],
1990 Stmt::While { cond, .. } => vec![cond],
1991 Stmt::DoWhile { cond, .. } => vec![cond],
1992 _ => Vec::new(),
1993 };
1994 for e in exprs {
1995 visit_exprs(e, &mut |x| {
1996 if let Expr::Local { var, .. } = x {
1997 grow_to(&mut a.reads, *var);
1998 a.reads[*var as usize] += 1;
1999 }
2000 });
2001 }
2002 });
2003 let n = a.assigns.len().max(a.reads.len()).max(a.values.len());
2005 a.assigns.resize(n, 0);
2006 a.reads.resize(n, 0);
2007 a.values.resize(n, None);
2008}
2009
2010fn grow_to(v: &mut Vec<usize>, var: u32) {
2011 if var as usize >= v.len() {
2012 v.resize(var as usize + 1, 0);
2013 }
2014}
2015
2016fn count_assignments(s: &Stmt, counts: &mut Vec<usize>) {
2017 walk_all(s, &mut |st| match st {
2018 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
2019 if let Expr::Local { var, .. } = &**target {
2020 grow_to(counts, *var);
2021 counts[*var as usize] += 1;
2022 }
2023 }
2024 Stmt::LocalDef {
2025 var, init: Some(_), ..
2026 } => {
2027 grow_to(counts, *var);
2028 counts[*var as usize] += 1;
2029 }
2030 _ => {}
2031 });
2032}
2033
2034fn record_assignments(s: &Stmt, counts: &[usize], out: &mut Vec<Option<Expr>>) {
2035 walk_all(s, &mut |st| match st {
2036 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
2037 if let Expr::Local { var, .. } = &**target {
2038 if counts.get(*var as usize).copied().unwrap_or(0) == 1 {
2039 if *var as usize >= out.len() {
2040 out.resize(*var as usize + 1, None);
2041 }
2042 out[*var as usize] = Some((**value).clone());
2043 }
2044 }
2045 }
2046 Stmt::LocalDef {
2047 var, init: Some(e), ..
2048 } if counts.get(*var as usize).copied().unwrap_or(0) == 1 => {
2049 if *var as usize >= out.len() {
2050 out.resize(*var as usize + 1, None);
2051 }
2052 out[*var as usize] = Some(e.clone());
2053 }
2054 _ => {}
2055 });
2056}
2057
2058fn fold_concat_in_expr(e: &mut Expr, values: &[Option<Expr>], appended: &HashSet<u32>) {
2059 deep_rewrite(e, &mut |x| {
2060 if let Expr::Method {
2061 cls,
2062 name,
2063 args,
2064 owner,
2065 ..
2066 } = x
2067 {
2068 if name.as_ref() == "toString" && args.is_empty() && is_string_builder(cls) {
2069 if let Some(o) = owner {
2070 let mut has_stmt_appends = false;
2073 visit_exprs(o, &mut |y| {
2074 if let Expr::Local { var, .. } = y {
2075 if appended.contains(var) {
2076 has_stmt_appends = true;
2077 }
2078 }
2079 });
2080 if has_stmt_appends {
2081 return;
2082 }
2083 let resolved = resolve_local(o, values);
2084 if let Some(parts) = collect_sb_parts(&resolved, values, 0) {
2085 if !parts.is_empty() {
2094 *x = Expr::StringConcat(parts);
2095 }
2096 }
2097 }
2098 }
2099 }
2100 });
2101}
2102
2103fn is_string_builder(cls: &str) -> bool {
2104 cls == "java/lang/StringBuilder" || cls == "java/lang/StringBuffer"
2105}
2106
2107fn resolve_local(e: &Expr, values: &[Option<Expr>]) -> Expr {
2108 match e {
2109 Expr::Local { var, ty } => values
2110 .get(*var as usize)
2111 .and_then(|o| o.clone())
2112 .unwrap_or_else(|| Expr::Local {
2113 var: *var,
2114 ty: ty.clone(),
2115 }),
2116 other => other.clone(),
2117 }
2118}
2119
2120fn collect_sb_parts(e: &Expr, values: &[Option<Expr>], depth: u32) -> Option<Vec<ConcatPart>> {
2122 if depth > 16 {
2123 return None;
2124 }
2125 match e {
2126 Expr::Method {
2127 cls,
2128 name,
2129 args,
2130 owner,
2131 ..
2132 } if name.as_ref() == "append" && is_string_builder(cls) && args.len() == 1 => {
2133 let mut parts =
2134 collect_sb_parts(&resolve_local(owner.as_deref()?, values), values, depth + 1)?;
2135 parts.push(ConcatPart::Str(args[0].clone()));
2136 Some(parts)
2137 }
2138 Expr::New {
2139 cls,
2140 args,
2141 raw: false,
2142 ..
2143 } if is_string_builder(cls) => {
2144 let mut parts = Vec::new();
2145 for a in args {
2146 if let Expr::Const(ConstVal::Str(sv)) = a {
2147 parts.push(ConcatPart::Const(sv.to_string()));
2148 } else {
2149 parts.push(ConcatPart::Str(a.clone()));
2150 }
2151 }
2152 Some(parts)
2153 }
2154 _ => None,
2155 }
2156}
2157
2158fn drop_unused_assigns(s: &mut Stmt, reads: &HashSet<u32>) -> usize {
2159 let mut dropped = 0usize;
2160 walk_mut_deep(s, &mut |st| {
2161 if let Stmt::Block(v) = st {
2162 let before = v.len();
2163 v.retain(|x| match x {
2164 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
2167 Expr::Local { var, .. } => reads.contains(var) || !is_sbish(value),
2168 _ => true,
2169 },
2170 Stmt::LocalDef { var, init, .. } => {
2171 reads.contains(var) || !init.as_ref().map(is_sbish).unwrap_or(false)
2172 }
2173 _ => true,
2174 });
2175 dropped += before - v.len();
2176 }
2177 });
2178 dropped
2179}
2180
2181fn is_sbish(e: &Expr) -> bool {
2182 match e {
2183 Expr::New {
2184 cls, raw: false, ..
2185 } => is_string_builder(cls),
2186 Expr::Method { name, cls, .. } => name.as_ref() == "append" && is_string_builder(cls),
2187 _ => false,
2188 }
2189}
2190
2191fn walk_mut_deep<F: FnMut(&mut Stmt)>(s: &mut Stmt, f: &mut F) {
2192 f(s);
2193 walk_mut(s, &mut |x| walk_mut_deep(x, f));
2194}
2195
2196pub fn drop_dead_raw_news(body: &mut Stmt) {
2208 fn is_orphan(s: &Stmt) -> bool {
2209 matches!(s, Stmt::ExprStmt(Expr::New { raw: true, .. }))
2210 }
2211 walk_mut_deep(body, &mut |st| match st {
2212 Stmt::Block(v) => v.retain(|x| !is_orphan(x)),
2213 Stmt::Switch { cases, .. } => {
2214 for c in cases.iter_mut() {
2215 c.body.retain(|x| !is_orphan(x));
2216 }
2217 }
2218 Stmt::For { init, .. } => init.retain(|x| !is_orphan(x)),
2219 Stmt::TryWithResources { resources, .. } => resources.retain(|x| !is_orphan(x)),
2220 _ => {}
2221 });
2222}
2223
2224pub fn fold_synchronized(s: &mut Stmt) {
2228 fold_sync_walk(s);
2229}
2230
2231fn fold_sync_walk(s: &mut Stmt) {
2232 walk_mut_deep(s, &mut |st| {
2233 let Stmt::Block(v) = st else { return };
2234 let mut i = 0;
2235 while i < v.len() {
2236 if i + 1 < v.len() {
2237 if let Some(sync) = try_sync_at(v, i) {
2238 let _ = v.drain(i..i + 2);
2239 v.insert(i, sync);
2240 continue;
2241 }
2242 }
2243 i += 1;
2244 }
2245 });
2246}
2247
2248fn try_sync_at(v: &[Stmt], i: usize) -> Option<Stmt> {
2249 let Stmt::MonitorEnter(lock) = &v[i] else {
2250 return None;
2251 };
2252 let Stmt::Try {
2253 body,
2254 catches,
2255 finally,
2256 } = &v[i + 1]
2257 else {
2258 return None;
2259 };
2260 if finally.is_some() || catches.len() != 1 {
2261 return None;
2262 }
2263 let c = &catches[0];
2264 if !c.exc.is_empty() {
2265 return None;
2266 }
2267 let Stmt::Block(cv) = c.body.as_ref() else {
2269 return None;
2270 };
2271 let exit_matches = cv.len() >= 2
2272 && matches!(&cv[0], Stmt::MonitorExit(e) if expr_local_var(e) == expr_local_var(lock));
2273 let throws = cv.len() >= 2 && matches!(&cv[1], Stmt::Throw(_));
2274 if !(exit_matches && throws) {
2275 return None;
2276 }
2277 Some(Stmt::Synchronized {
2278 lock: lock.clone(),
2279 body: body.clone(),
2280 })
2281}
2282
2283fn expr_local_var(e: &Expr) -> Option<u32> {
2284 if let Expr::Local { var, .. } = e {
2285 Some(*var)
2286 } else {
2287 None
2288 }
2289}
2290
2291pub fn forward_single_use(s: &mut Stmt, _vt: &VarTable) {
2296 let mut analysis = VarAnalysis {
2300 assigns: Vec::new(),
2301 reads: Vec::new(),
2302 values: Vec::new(),
2303 };
2304 analyze_vars(s, &mut analysis);
2305 let mut assigns = analysis.assigns;
2306 let mut reads = analysis.reads;
2307 let n_vars = assigns.len().max(_vt.vars.len()).max(1);
2310 assigns.resize(n_vars, 0);
2311 reads.resize(n_vars, 0);
2312 let mut values = analysis.values;
2313 values.resize(n_vars, None);
2314
2315 let mut cand = vec![false; n_vars];
2320 let mut edges: Vec<Vec<u32>> = vec![Vec::new(); n_vars];
2321 for (v, cand_v) in cand.iter_mut().enumerate() {
2322 if assigns.get(v).copied().unwrap_or(0) != 1 || reads.get(v).copied().unwrap_or(0) != 1 {
2323 continue;
2324 }
2325 if let Some(val) = values.get(v).and_then(|o| o.as_ref()) {
2326 let mut refs = HashSet::default();
2327 collect_vars(val, &mut refs);
2328 if refs.iter().any(|r| assigns[*r as usize] > 1) {
2329 continue;
2330 }
2331 edges[v] = refs.into_iter().collect();
2335 }
2336 *cand_v = true;
2337 }
2338
2339 let mut color = vec![0u8; n_vars]; let mut bad = vec![false; n_vars];
2357 for root in 0..n_vars {
2358 if color[root] != 0 || edges[root].is_empty() {
2359 continue;
2360 }
2361 color[root] = 1;
2362 let mut stack: Vec<(usize, usize)> = vec![(root, 0)];
2363 while let Some(top) = stack.last_mut() {
2364 let v = top.0;
2365 let i = top.1;
2366 top.1 += 1;
2367 if i < edges[v].len() {
2368 let r = edges[v][i] as usize;
2369 if color[r] == 1 {
2370 for &(pn, _) in &stack {
2371 bad[pn] = true;
2372 }
2373 } else if color[r] == 0 {
2374 color[r] = 1;
2375 stack.push((r, 0));
2376 } else if bad[r] {
2377 for &(pn, _) in &stack {
2378 bad[pn] = true;
2379 }
2380 }
2381 } else {
2382 color[v] = 2;
2383 stack.pop();
2384 }
2385 }
2386 }
2387 let single: Vec<bool> = (0..n_vars).map(|v| cand[v] && !bad[v]).collect();
2388 if !single.iter().any(|&b| b) {
2389 return;
2390 }
2391 let pure: Vec<bool> = (0..n_vars)
2392 .map(|v| {
2393 single[v]
2394 && values
2395 .get(v)
2396 .and_then(|o| o.as_ref())
2397 .map(|e| !has_side_effects(e))
2398 .unwrap_or(false)
2399 })
2400 .collect();
2401 let impure: Vec<bool> = (0..n_vars).map(|v| single[v] && !pure[v]).collect();
2402
2403 if pure.iter().any(|&b| b) {
2405 let vals: Vec<Option<Expr>> = (0..n_vars)
2406 .map(|v| {
2407 if pure[v] {
2408 values.get(v).cloned().flatten()
2409 } else {
2410 None
2411 }
2412 })
2413 .collect();
2414 rewrite_exprs(s, &mut |e| {
2415 deep_rewrite_reads(e, &mut |x| {
2416 if let Expr::Local { var, .. } = x {
2417 if let Some(Some(v)) = vals.get(*var as usize) {
2418 *x = v.clone();
2419 }
2420 }
2421 });
2422 });
2423 drop_defs(s, &pure);
2424 }
2425
2426 if impure.iter().any(|&b| b) {
2429 forward_adjacent_impure(s, &values, &impure);
2430 }
2431}
2432
2433fn count_reads(s: &Stmt, out: &mut Vec<usize>) {
2435 walk_all(s, &mut |st| {
2436 let exprs: Vec<&Expr> = match st {
2437 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
2440 let mut v: Vec<&Expr> = vec![value];
2441 if !matches!(&**target, Expr::Local { .. }) {
2442 v.push(target);
2443 }
2444 v
2445 }
2446 Stmt::ExprStmt(e @ (Expr::PreIncDec { .. } | Expr::PostIncDec { .. })) => {
2449 let inner = match e {
2450 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => e,
2451 _ => unreachable!(),
2452 };
2453 if matches!(**inner, Expr::Local { .. }) {
2454 Vec::new()
2455 } else {
2456 vec![e]
2457 }
2458 }
2459 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
2460 vec![e]
2461 }
2462 Stmt::Return(Some(e)) => vec![e],
2463 Stmt::LocalDef { init: Some(e), .. } => vec![e],
2464 Stmt::If { cond, .. } => vec![cond],
2465 Stmt::While { cond, .. } => vec![cond],
2466 Stmt::DoWhile { cond, .. } => vec![cond],
2467 Stmt::Switch { selector, .. } => vec![selector],
2473 Stmt::ForEach { iterable, .. } => vec![iterable],
2474 Stmt::Synchronized { lock, .. } => vec![lock],
2475 Stmt::For { cond, update, .. } => {
2476 let mut v: Vec<&Expr> = Vec::with_capacity(update.len() + 1);
2477 if let Some(c) = cond {
2478 v.push(c);
2479 }
2480 v.extend(update.iter());
2481 v
2482 }
2483 Stmt::Assert { cond, msg } => {
2484 let mut v: Vec<&Expr> = vec![cond];
2485 if let Some(m) = msg {
2486 v.push(m);
2487 }
2488 v
2489 }
2490 _ => Vec::new(),
2491 };
2492 for e in exprs {
2493 visit_exprs(e, &mut |x| {
2494 if let Expr::Local { var, .. } = x {
2495 grow_to(out, *var);
2496 out[*var as usize] += 1;
2497 }
2498 });
2499 }
2500 });
2501}
2502
2503fn drop_defs(s: &mut Stmt, vars: &[bool]) {
2504 walk_mut_deep(s, &mut |st| {
2505 if let Stmt::Block(v) = st {
2506 v.retain(|x| match x {
2507 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
2508 Expr::Local { var, .. } => !vars.get(*var as usize).copied().unwrap_or(false),
2509 _ => true,
2510 },
2511 Stmt::LocalDef { var, .. } => !vars.get(*var as usize).copied().unwrap_or(false),
2512 _ => true,
2513 });
2514 }
2515 });
2516}
2517
2518fn forward_adjacent_impure(s: &mut Stmt, values: &[Option<Expr>], impure: &[bool]) {
2520 if let Stmt::Block(v) = s {
2521 let mut i = 0;
2522 while i + 1 < v.len() {
2523 let def_var = match &v[i] {
2524 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
2525 Expr::Local { var, .. } => Some(*var),
2526 _ => None,
2527 },
2528 Stmt::LocalDef { var, .. } => Some(*var),
2529 _ => None,
2530 };
2531 if let Some(var) = def_var {
2532 if impure.get(var as usize).copied().unwrap_or(false) {
2533 let mut reads_here = vec![0usize; values.len().max(1)];
2535 count_reads(&v[i + 1], &mut reads_here);
2536 if reads_here.get(var as usize).copied().unwrap_or(0) == 1 {
2537 let val = match &v[i] {
2541 Stmt::ExprStmt(Expr::Assign { value, .. }) => (**value).clone(),
2542 Stmt::LocalDef { init: Some(e), .. } => (*e).clone(),
2543 _ => {
2544 i += 1;
2545 continue;
2546 }
2547 };
2548 rewrite_exprs(&mut v[i + 1], &mut |e| {
2549 deep_rewrite_reads(e, &mut |x| {
2550 if let Expr::Local { var: v2, .. } = x {
2551 if *v2 == var {
2552 *x = val.clone();
2553 }
2554 }
2555 });
2556 });
2557 v.remove(i);
2558 i = i.saturating_sub(1);
2561 continue;
2562 }
2563 }
2564 }
2565 i += 1;
2566 }
2567 }
2568 walk_mut(s, &mut |x| forward_adjacent_impure(x, values, impure));
2569}
2570
2571pub fn infer_types(vt: &mut VarTable, body: &mut Stmt, ret: &JavaType, env: &MethodEnv) {
2577 let n = vt.vars.len();
2578 let mut evidence: Vec<Vec<(JavaType, bool)>> = vec![Vec::new(); n];
2586 let ev = |evidence: &mut Vec<Vec<(JavaType, bool)>>, var: u32, t: JavaType, strong: bool| {
2587 if (var as usize) < evidence.len() {
2588 evidence[var as usize].push((t, strong));
2589 }
2590 };
2591
2592 walk_all(body, &mut |st| match st {
2593 Stmt::LocalDef { var, init, .. } => {
2594 if let Some(e) = init {
2595 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
2596 let strong = match e {
2604 Expr::Const(_) => false,
2605 Expr::Local { var: src, .. } => {
2606 let src_ty = vt.vars.get(*src as usize).map(|v| v.ty.erased());
2607 let tgt_ty = vt.vars.get(*var as usize).map(|v| v.ty.erased());
2608 match (src_ty, tgt_ty) {
2609 (Some(s), Some(g)) => {
2610 (s.is_numeric() && g.is_numeric())
2611 || (s.is_reference() && g.is_reference())
2612 }
2613 _ => false,
2614 }
2615 }
2616 _ => true,
2617 };
2618 ev(&mut evidence, *var, e.type_ref().erased(), strong);
2619 }
2620 }
2621 Stmt::ExprStmt(e) => {
2622 if let Expr::Assign { target, value, .. } = e {
2629 if let Expr::Field { owner: Some(o), cls, is_static: false, .. } = &**target {
2636 if cls.is_empty() {
2637 } else if let Expr::Local { var, .. } = &**o {
2639 ev(
2640 &mut evidence,
2641 *var,
2642 JavaType::Object(cls.clone()),
2643 true,
2644 );
2645 }
2646 }
2647 if let Expr::Local { var, .. } = &**target {
2648 let strong = match &**value {
2649 Expr::Const(_) => false,
2650 Expr::Local { var: src, .. } => {
2651 let src_ty = vt.vars.get(*src as usize).map(|v| v.ty.erased());
2658 let tgt_ty = vt.vars.get(*var as usize).map(|v| v.ty.erased());
2659 match (src_ty, tgt_ty) {
2660 (Some(s), Some(g)) => {
2661 (s.is_numeric() && g.is_numeric())
2662 || (s.is_reference() && g.is_reference())
2663 }
2664 _ => false,
2665 }
2666 }
2667 _ => true,
2668 };
2669 ev(&mut evidence, *var, value.type_ref().erased(), strong);
2670 }
2671 }
2672 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
2673 }
2674 Stmt::Throw(e) => {
2675 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
2676 if let Expr::Local { var, .. } = e {
2677 ev(
2678 &mut evidence,
2679 *var,
2680 JavaType::Object("java/lang/Throwable".into()),
2681 true,
2682 );
2683 }
2684 }
2685 Stmt::Return(Some(e)) => {
2686 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
2687 if let Expr::Local { var, .. } = e {
2688 ev(&mut evidence, *var, ret.clone(), true);
2689 }
2690 }
2691 Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
2692 if let Expr::Local { var, .. } = e {
2693 ev(
2694 &mut evidence,
2695 *var,
2696 JavaType::Object("java/lang/Object".into()),
2697 true,
2698 );
2699 }
2700 }
2701 Stmt::ForEach {
2702 var,
2703 iterable,
2704 is_array,
2705 ..
2706 } => {
2707 if let Expr::Local { var: v0, .. } = iterable {
2708 if *is_array {
2709 ev(&mut evidence, *v0, JavaType::Array(Box::new(JavaType::Int)), true);
2710 } else {
2711 ev(
2712 &mut evidence,
2713 *v0,
2714 JavaType::Object("java/lang/Object".into()),
2715 true,
2716 );
2717 }
2718 }
2719 let _ = var;
2720 }
2721 _ => {}
2722 });
2723 let _ = env;
2724
2725 let mut derefed: Vec<bool> = vec![false; vt.vars.len()];
2735 mark_derefs(body, &mut derefed);
2736 let mut arr_derefed: Vec<bool> = vec![false; vt.vars.len()];
2741 visit_all_exprs(body, &mut |x| {
2742 if let Expr::ArrayIndex { array, .. } = x {
2743 if let Expr::Local { var, .. } = &**array {
2744 if (*var as usize) < arr_derefed.len() {
2745 arr_derefed[*var as usize] = true;
2746 }
2747 }
2748 }
2749 if let Expr::Bin { op, l, r, .. } = x {
2757 if matches!(op, BinOp::Eq | BinOp::Ne)
2758 && (matches!(&**r, Expr::Const(ConstVal::Null))
2759 || matches!(&**l, Expr::Const(ConstVal::Null)))
2760 {
2761 for side in [l, r] {
2762 if let Expr::Local { var, .. } = &**side {
2763 ev(
2764 &mut evidence,
2765 *var,
2766 JavaType::Object("java/lang/Object".into()),
2767 true,
2768 );
2769 }
2770 }
2771 }
2772 }
2773 });
2774 const FINAL_JDK: &[&str] = &[
2775 "java/lang/Boolean",
2776 "java/lang/String",
2777 "java/lang/Integer",
2778 "java/lang/Long",
2779 "java/lang/Short",
2780 "java/lang/Byte",
2781 "java/lang/Character",
2782 "java/lang/Float",
2783 "java/lang/Double",
2784 ];
2785 for (i, evs) in evidence.iter().enumerate() {
2786 let info = &vt.vars[i];
2787 if info.is_param {
2788 continue;
2789 }
2790 let strong: Vec<JavaType> = evs
2791 .iter()
2792 .filter(|(_, s)| *s)
2793 .map(|(t, _)| t.clone())
2794 .collect();
2795 let all: Vec<JavaType> = evs.iter().map(|(t, _)| t.clone()).collect();
2796 if info.ty.erased().is_reference() {
2797 if strong.is_empty() {
2803 let refs: std::collections::HashSet<&str> = all
2804 .iter()
2805 .filter_map(|t| match t {
2806 JavaType::Object(n) if n.as_ref() != "java/lang/Object" => {
2807 Some(n.as_ref())
2808 }
2809 _ => None,
2810 })
2811 .collect();
2812 let mixed = all.iter().any(|t| t.is_numeric());
2813 if refs.len() >= 2 || (mixed && !refs.is_empty()) || (mixed && all.iter().any(|t| matches!(t, JavaType::Array(_)))) {
2814 vt.vars[i].ty =
2815 TypeRef::J(JavaType::Object("java/lang/Object".into()));
2816 continue;
2817 }
2818 }
2819 if arr_derefed[i] {
2822 let arr = strong
2823 .iter()
2824 .chain(all.iter())
2825 .find(|t| matches!(t, JavaType::Array(_)))
2826 .cloned();
2827 if let Some(t) = arr {
2828 vt.vars[i].ty = TypeRef::J(t);
2829 continue;
2830 }
2831 }
2832 let deref_pick = || {
2836 if !derefed[i] {
2837 return None;
2838 }
2839 strong
2840 .iter()
2841 .find(|t| {
2842 matches!(t, JavaType::Object(n)
2843 if !n.is_empty()
2844 && n.as_ref() != "java/lang/Object"
2845 && !FINAL_JDK.contains(&n.as_ref()))
2846 })
2847 .cloned()
2848 };
2849 if let Some(t) = deref_pick()
2850 .or_else(|| pick_object(&strong))
2851 .or_else(|| pick_object(&all))
2852 {
2853 vt.vars[i].ty = TypeRef::J(t);
2854 }
2855 } else if let Some(t) = pick_object(&strong) {
2856 vt.vars[i].ty = TypeRef::J(t);
2860 } else if strong.contains(&JavaType::Boolean) {
2861 vt.vars[i].ty = TypeRef::J(JavaType::Boolean);
2865 } else if !all.is_empty() && all.iter().all(|t| t.is_numeric()) {
2866 if let Some(t) = pick_numeric(&all) {
2867 vt.vars[i].ty = TypeRef::J(t);
2868 }
2869 } else {
2870 let refs = all.iter().filter(|t| t.is_reference()).count();
2874 let nums = all.iter().filter(|t| t.is_numeric()).count();
2875 if refs > 0 && nums > 0 {
2876 vt.vars[i].ty =
2877 TypeRef::J(JavaType::Object("java/lang/Object".into()));
2878 } else if let Some(t) = pick_object(&strong).or_else(|| pick_object(&all)) {
2879 vt.vars[i].ty = TypeRef::J(t);
2880 }
2881 }
2882 }
2883
2884 let types: Vec<&TypeRef> = vt.vars.iter().map(|v| &v.ty).collect();
2889 rewrite_exprs(body, &mut |e| {
2890 deep_rewrite(e, &mut |x| {
2891 if let Expr::Local { var, ty } = x {
2892 if let Some(want) = types.get(*var as usize) {
2893 if ty != *want {
2894 *ty = (*want).clone();
2895 }
2896 }
2897 }
2898 });
2899 });
2900
2901 coerce_num_consts(body, &types);
2904}
2905
2906fn coerce_num_consts(body: &mut Stmt, types: &[&TypeRef]) {
2907 walk_mut_deep(body, &mut |st| {
2908 let (var, val): (u32, &mut Expr) = match st {
2909 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &mut **target {
2910 Expr::Local { var, .. } => (*var, value),
2911 _ => return,
2912 },
2913 Stmt::LocalDef {
2914 var, init: Some(e), ..
2915 } => (*var, e),
2916 _ => return,
2917 };
2918 let want = match types.get(var as usize) {
2919 Some(TypeRef::J(t)) => t.clone(),
2920 _ => return,
2921 };
2922 let rewritten = match &*val {
2923 Expr::Const(ConstVal::Long(l)) => match &want {
2924 JavaType::Double => Some(Expr::Const(ConstVal::Double(*l as f64))),
2925 JavaType::Float => Some(Expr::Const(ConstVal::Float(*l as f32))),
2926 _ => None,
2927 },
2928 Expr::Const(ConstVal::Int(i)) => match &want {
2929 JavaType::Double => Some(Expr::Const(ConstVal::Double(*i as f64))),
2930 JavaType::Float => Some(Expr::Const(ConstVal::Float(*i as f32))),
2931 _ => None,
2932 },
2933 _ => None,
2934 };
2935 if let Some(e) = rewritten {
2936 *val = e;
2937 }
2938 });
2939}
2940
2941fn pick_object(evs: &[JavaType]) -> Option<JavaType> {
2942 evs.iter()
2943 .find(|t| {
2944 t.is_reference()
2945 && !matches!(t, JavaType::Object(n) if n.is_empty() || n.as_ref() == "java/lang/Object")
2946 })
2947 .cloned()
2948}
2949
2950fn pick_numeric(evs: &[JavaType]) -> Option<JavaType> {
2951 if evs.is_empty() {
2952 return None;
2953 }
2954 let mut t = evs[0].clone();
2955 for x in &evs[1..] {
2956 t = join_numeric(&t, x);
2957 }
2958 Some(t)
2959}
2960
2961fn join_numeric(a: &JavaType, b: &JavaType) -> JavaType {
2962 if a == b {
2963 return a.clone();
2964 }
2965 match (a, b) {
2966 (JavaType::Double, _) | (_, JavaType::Double) => JavaType::Double,
2967 (JavaType::Float, _) | (_, JavaType::Float) => JavaType::Float,
2968 (JavaType::Long, _) | (_, JavaType::Long) => JavaType::Long,
2969 (JavaType::Int, x) if x.is_integral() => JavaType::Int,
2970 (x, JavaType::Int) if x.is_integral() => JavaType::Int,
2971 (x, y) if x.is_integral() && y.is_integral() => JavaType::Int,
2972 _ => JavaType::Int,
2973 }
2974}
2975
2976fn expr_evidence<F: FnMut(u32, JavaType)>(e: &Expr, f: &mut F) {
2978 visit_exprs(e, &mut |x| match x {
2979 Expr::Method {
2980 owner,
2981 args,
2982 desc,
2983 is_static,
2984 cls,
2985 ..
2986 } => {
2987 if !*is_static {
2988 if let Some(o) = owner {
2989 if let Expr::Local { var, .. } = &**o {
2990 f(*var, JavaType::Object(cls.clone()));
2991 }
2992 }
2993 }
2994 for (i, a) in args.iter().enumerate() {
2995 if let Expr::Local { var, .. } = a {
2996 if let Some(t) = desc.args.get(i) {
2997 f(*var, t.clone());
2998 }
2999 }
3000 }
3001 }
3002 Expr::Field {
3003 owner,
3004 cls,
3005 ty,
3006 is_static,
3007 ..
3008 } => {
3009 if !*is_static && !cls.is_empty() {
3010 if let Some(o) = owner {
3011 if let Expr::Local { var, .. } = &**o {
3012 f(*var, JavaType::Object(cls.clone()));
3022 }
3023 }
3024 }
3025 let _ = ty;
3026 }
3027 Expr::Bin { op, l, r, .. }
3032 if matches!(op, BinOp::Eq | BinOp::Ne)
3033 && (matches!(&**r, Expr::Const(ConstVal::Null))
3034 || matches!(&**l, Expr::Const(ConstVal::Null))) =>
3035 {
3036 for side in [l, r] {
3037 if let Expr::Local { var, .. } = &**side {
3038 f(*var, JavaType::Object("java/lang/Object".into()));
3039 }
3040 }
3041 }
3042 Expr::ArrayIndex { array, .. } => {
3043 if let Expr::Local { var, ty, .. } = &**array {
3044 let t = ty.erased();
3049 if matches!(t, JavaType::Array(_)) {
3050 f(*var, t);
3051 } else {
3052 f(*var, JavaType::Array(Box::new(JavaType::Int)));
3053 }
3054 }
3055 }
3056 Expr::Assign { .. } => {
3057 }
3060 Expr::Cast { e: inner, .. } => {
3061 let _ = inner;
3067 }
3068 Expr::InstanceOf { e: inner, .. } => {
3069 let _ = inner;
3070 }
3071 _ => {}
3072 });
3073}
3074
3075pub fn mark_field_owner_concrete(vt: &mut VarTable, body: &mut Stmt) {
3087 let mut owners: HashSet<u32> = HashSet::default();
3088 rewrite_exprs(body, &mut |e| {
3089 visit_exprs(e, &mut |x| {
3090 if let Expr::Field {
3091 owner: Some(o),
3092 is_static: false,
3093 ..
3094 } = x
3095 {
3096 if let Expr::Local { var, .. } = &**o {
3097 owners.insert(*var);
3098 }
3099 }
3100 });
3101 });
3102 for var in owners {
3103 let i = var as usize;
3104 if i >= vt.vars.len() {
3105 continue;
3106 }
3107 if let JavaType::Object(cls) = vt.vars[i].ty.erased() {
3108 if let Some(last) = cls.rsplit('$').next() {
3109 if !last.is_empty() && last.chars().all(|c| c.is_ascii_digit()) {
3110 vt.force_concrete_vars.insert(var);
3111 }
3112 }
3113 }
3114 }
3115 let concrete: Vec<u32> = vt
3124 .vars
3125 .iter()
3126 .filter(|v| {
3127 if let JavaType::Object(cls) = v.ty.erased() {
3128 cls.rsplit('$')
3129 .next()
3130 .is_some_and(|l| !l.is_empty() && l.chars().all(|c| c.is_ascii_digit()))
3131 } else {
3132 false
3133 }
3134 })
3135 .map(|v| v.id)
3136 .collect();
3137 for var in concrete {
3138 vt.force_concrete_vars.insert(var);
3139 }
3140}
3141
3142pub fn insert_object_narrowing_casts(
3154 vt: &VarTable,
3155 body: &mut Stmt,
3156 pool: &DexPool,
3157 ret: &JavaType,
3158) {
3159 let value_ty = |e: &Expr| -> JavaType {
3162 match e {
3163 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
3164 other => other.type_ref().erased(),
3165 }
3166 };
3167 let is_top_object = |e: &Expr| -> bool {
3168 value_ty(e) == JavaType::Object("java/lang/Object".into())
3169 };
3170 let specific_ref = |ty: &TypeRef| -> Option<TypeRef> {
3174 match ty.erased() {
3175 JavaType::Object(c) if c.as_ref() != "java/lang/Object" => Some(ty.clone()),
3176 JavaType::Array(_) => Some(ty.clone()),
3177 _ => None,
3178 }
3179 };
3180 let castable = |e: &Expr| -> bool {
3181 is_top_object(e)
3182 && !matches!(e, Expr::Const(_) | Expr::Cast { .. } | Expr::InstanceOf { .. })
3183 };
3184 let needs_downcast = |e: &Expr, tt: &JavaType| -> bool {
3194 if matches!(e, Expr::Const(_) | Expr::Cast { .. } | Expr::InstanceOf { .. }) {
3195 return false;
3196 }
3197 let v = value_ty(e);
3198 let (JavaType::Object(vn), JavaType::Object(tn)) = (&v, tt) else {
3199 return false;
3200 };
3201 if vn.as_ref() == "java/lang/Object" || tn.as_ref() == "java/lang/Object" {
3202 return false;
3203 }
3204 vn != tn && !pool.is_subtype(vn, tn)
3205 };
3206 walk_mut_deep(body, &mut |st| match st {
3207 Stmt::ExprStmt(Expr::Assign {
3208 target,
3209 value,
3210 op: AssignOp::Plain,
3211 ..
3212 }) => {
3213 let tgt = match &**target {
3214 Expr::Local { var, .. } => vt.var(*var).ty.clone(),
3215 Expr::Field { ty, .. } => ty.clone(),
3216 Expr::ArrayIndex { array, .. } => {
3223 let aty = match &**array {
3224 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
3225 other => other.type_ref().erased(),
3226 };
3227 match aty {
3228 JavaType::Array(inner) => TypeRef::J(*inner),
3229 _ => return,
3230 }
3231 }
3232 _ => return,
3233 };
3234 if let Some(t) = specific_ref(&tgt) {
3235 if castable(value) || needs_downcast(value, &tgt.erased()) {
3236 let v = std::mem::replace(value, Box::new(Expr::This));
3237 **value = Expr::Cast { ty: t, e: v };
3238 }
3239 }
3240 }
3241 Stmt::LocalDef { var, init: Some(value), .. } => {
3242 if let Some(t) = specific_ref(&vt.var(*var).ty) {
3243 let tt = vt.var(*var).ty.erased();
3244 if castable(value) || needs_downcast(value, &tt) {
3245 let v = std::mem::replace(value, Expr::This);
3246 *value = Expr::Cast {
3247 ty: t,
3248 e: Box::new(v),
3249 };
3250 }
3251 }
3252 }
3253 Stmt::Return(Some(value)) => {
3254 if let Some(t) = specific_ref(&TypeRef::J(ret.clone())) {
3255 if castable(value) || needs_downcast(value, ret) {
3256 let v = std::mem::replace(value, Expr::This);
3257 *value = Expr::Cast { ty: t, e: Box::new(v) };
3258 }
3259 }
3260 }
3261 _ => {}
3262 });
3263 walk_stmt_exprs(body, &mut |e| {
3270 deep_rewrite(e, &mut |x| {
3271 let (args, arg_tys): (&mut Vec<Expr>, &[JavaType]) = match x {
3272 Expr::Method { desc, args, is_dynamic: false, .. } => {
3273 (args, desc.args.as_slice())
3274 }
3275 Expr::New { arg_tys, args, .. } => (args, arg_tys.as_slice()),
3279 _ => return,
3280 };
3281 for (i, a) in args.iter_mut().enumerate() {
3282 let Some(formal) = arg_tys.get(i) else {
3283 continue;
3284 };
3285 let Some(t) = specific_ref(&TypeRef::J(formal.clone())) else {
3286 continue;
3287 };
3288 if !(castable(a) || needs_downcast(a, formal)) {
3289 continue;
3290 }
3291 let v = std::mem::replace(a, Expr::Const(ConstVal::Null));
3292 *a = Expr::Cast { ty: t, e: Box::new(v) };
3293 }
3294 });
3295 });
3296}
3297
3298pub fn fix_primitive_assign_casts(vt: &VarTable, body: &mut Stmt, ret_ty: &JavaType) {
3307 fn numlike(t: &JavaType) -> bool {
3308 matches!(
3309 t,
3310 JavaType::Byte
3311 | JavaType::Short
3312 | JavaType::Int
3313 | JavaType::Char
3314 | JavaType::Long
3315 | JavaType::Float
3316 | JavaType::Double
3317 )
3318 }
3319 fn val_ty(e: &Expr, vt: &VarTable) -> JavaType {
3320 match e {
3321 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
3322 vt.var(*var).ty.erased()
3323 }
3324 other => other.type_ref().erased(),
3325 }
3326 }
3327 fn wrap(ty: &JavaType, value: &mut Expr) {
3328 if matches!(value, Expr::Cast { .. }) {
3329 return;
3330 }
3331 if val_ty_static(value) == *ty {
3332 return;
3333 }
3334 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
3335 *value = Expr::Cast {
3336 ty: TypeRef::J(ty.clone()),
3337 e: Box::new(taken),
3338 };
3339 }
3340 fn val_ty_static(e: &Expr) -> JavaType {
3341 e.type_ref().erased()
3342 }
3343 fn coerce(tt: &JavaType, value: &mut Expr, vt: &VarTable) {
3351 match (&*value, tt) {
3356 (Expr::Const(ConstVal::Int(b)), JavaType::Float) => {
3357 *value = Expr::Const(ConstVal::Float(f32::from_bits(*b as u32)));
3358 return;
3359 }
3360 (Expr::Const(ConstVal::Long(b)), JavaType::Double) => {
3361 *value = Expr::Const(ConstVal::Double(f64::from_bits(*b as u64)));
3362 return;
3363 }
3364 (Expr::Const(ConstVal::Float(f)), JavaType::Int) => {
3365 *value = Expr::Const(ConstVal::Int(f.to_bits() as i32));
3366 return;
3367 }
3368 (Expr::Const(ConstVal::Double(d)), JavaType::Long) => {
3369 *value = Expr::Const(ConstVal::Long(d.to_bits() as i64));
3370 return;
3371 }
3372 _ => {}
3373 }
3374 let vt_val = val_ty(value, vt);
3375 let tt_bool = matches!(tt, JavaType::Boolean);
3376 let val_bool = matches!(vt_val, JavaType::Boolean);
3377 if tt_bool && !val_bool && numlike(&vt_val) {
3378 if matches!(
3379 value,
3380 Expr::Const(_)
3381 | Expr::Bin { op: BinOp::Eq | BinOp::Ne, .. }
3382 | Expr::Un { op: UnOp::Not, .. }
3383 ) {
3384 return;
3389 }
3390 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
3391 *value = Expr::Bin {
3392 op: BinOp::Ne,
3393 l: Box::new(taken),
3394 r: Box::new(Expr::Const(ConstVal::Int(0))),
3395 ty: Some(TypeRef::J(JavaType::Boolean)),
3396 };
3397 } else if !tt_bool && val_bool && numlike(tt) {
3398 if matches!(value, Expr::Cast { .. }) {
3399 return;
3400 }
3401 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
3402 *value = Expr::Cast {
3403 ty: TypeRef::J(tt.clone()),
3404 e: Box::new(Expr::Cond {
3405 c: Box::new(taken),
3406 t: Box::new(Expr::Const(ConstVal::Int(1))),
3407 f: Box::new(Expr::Const(ConstVal::Int(0))),
3408 }),
3409 };
3410 } else if numlike(tt) && numlike(&vt_val) {
3411 wrap(tt, value);
3412 }
3413 }
3414 walk_mut_deep(body, &mut |st| match st {
3415 Stmt::ExprStmt(Expr::Assign {
3416 target,
3417 op: AssignOp::Plain,
3418 value,
3419 }) => {
3420 let tt = match &**target {
3421 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
3422 vt.var(*var).ty.erased()
3423 }
3424 Expr::Field { ty, .. } => ty.erased(),
3425 Expr::ArrayIndex { array, .. } => match array.type_ref().erased() {
3429 JavaType::Array(inner) => *inner,
3430 _ => return,
3431 },
3432 _ => return,
3433 };
3434 coerce(&tt, value, vt);
3435 }
3436 Stmt::LocalDef {
3437 var,
3438 init: Some(value),
3439 ..
3440 } => {
3441 if (*var as usize) >= vt.vars.len() {
3442 return;
3443 }
3444 let tt = vt.var(*var).ty.erased();
3445 coerce(&tt, value, vt);
3446 }
3447 Stmt::Return(Some(e)) => {
3448 if !numlike(ret_ty) || !numlike(&val_ty(e, vt)) {
3449 return;
3450 }
3451 wrap(ret_ty, e);
3452 }
3453 _ => {}
3454 });
3455}
3456
3457pub fn fix_shadowed_super_fields(
3481 body: &mut Stmt,
3482 vt: &VarTable,
3483 pool: &DexPool,
3484 class_name: &str,
3485) {
3486 fn display(pool: &DexPool, cls: &str, name: &str) -> Option<String> {
3490 let pc = pool.get(cls)?;
3491 let f = pc
3492 .static_fields
3493 .iter()
3494 .chain(pc.instance_fields.iter())
3495 .find(|f| f.name.as_str() == name)?;
3496 Some(
3497 jdc_core::rename::field_display(cls, name, &f.desc)
3498 .map(|d| d.to_string())
3499 .unwrap_or_else(|| name.to_string()),
3500 )
3501 }
3502 fn owner_ct(
3503 o: &Option<Box<Expr>>,
3504 vt: &VarTable,
3505 class_name: &str,
3506 ) -> Option<std::sync::Arc<str>> {
3507 match o.as_deref() {
3508 None | Some(Expr::This) => Some(std::sync::Arc::from(class_name)),
3509 Some(Expr::Local { var, .. }) => match vt.var(*var).ty.erased() {
3510 JavaType::Object(n) => Some(n),
3511 _ => None,
3512 },
3513 Some(other) => match other.type_ref().erased() {
3514 JavaType::Object(n) => Some(n),
3515 _ => None,
3516 },
3517 }
3518 }
3519 walk_stmt_exprs(body, &mut |e| {
3520 deep_rewrite(e, &mut |x| {
3521 let Expr::Field {
3522 owner,
3523 cls,
3524 name,
3525 is_static: false,
3526 ..
3527 } = x
3528 else {
3529 return;
3530 };
3531 let Some(ot) = owner_ct(owner, vt, class_name) else {
3532 return;
3533 };
3534 if cls.as_ref() == ot.as_ref() {
3535 return;
3536 }
3537 let Some(ref_display) = display(pool, cls, name) else {
3538 return;
3539 };
3540 let mut cur = ot.to_string();
3544 let mut shadowed = false;
3545 let mut found = false;
3546 for _ in 0..64 {
3547 if display(pool, &cur, name).as_deref() == Some(ref_display.as_str()) {
3548 shadowed = true;
3549 }
3550 let Some(pc) = pool.get(&cur) else {
3551 return;
3552 };
3553 let Some(sup) = pc.super_name.clone() else {
3554 return;
3555 };
3556 if sup.as_str() == cls.as_ref() {
3557 found = true;
3558 break;
3559 }
3560 cur = sup;
3561 }
3562 if !found || !shadowed {
3563 return;
3564 }
3565 let ty = TypeRef::J(JavaType::Object(cls.clone()));
3566 match owner {
3567 Some(o) => {
3568 if let Expr::Cast { ty: ct, .. } = &mut **o {
3569 *ct = ty;
3570 } else {
3571 let taken = std::mem::replace(&mut **o, Expr::This);
3572 **o = Expr::Cast {
3573 ty,
3574 e: Box::new(taken),
3575 };
3576 }
3577 }
3578 None => {
3579 *owner = Some(Box::new(Expr::Cast {
3580 ty,
3581 e: Box::new(Expr::This),
3582 }))
3583 }
3584 }
3585 });
3586 });
3587}
3588
3589pub fn fix_field_owner_downcasts(body: &mut Stmt, vt: &VarTable, pool: &DexPool) {
3608 fn owner_internal(o: &Expr, vt: &VarTable) -> Option<std::sync::Arc<str>> {
3609 let t = match o {
3610 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
3611 other => other.type_ref().erased(),
3612 };
3613 match t {
3614 JavaType::Object(n) => Some(n),
3615 _ => None,
3616 }
3617 }
3618 fn declares_up(pool: &DexPool, class: &str, name: &str) -> bool {
3619 let mut cur = Some(class.to_string());
3620 let mut hops = 0;
3621 while let Some(c) = cur {
3622 hops += 1;
3623 if hops > 64 {
3624 return false;
3625 }
3626 let Some(pc) = pool.get(&c) else { return false };
3627 if pc
3628 .instance_fields
3629 .iter()
3630 .chain(pc.static_fields.iter())
3631 .any(|f| f.name.as_str() == name)
3632 {
3633 return true;
3634 }
3635 cur = pc.super_name.clone();
3636 }
3637 false
3638 }
3639 walk_stmt_exprs(body, &mut |e| {
3640 deep_rewrite(e, &mut |x| {
3641 if let Expr::Field {
3642 owner: Some(o),
3643 cls,
3644 name,
3645 is_static: false,
3646 ..
3647 } = x
3648 {
3649 let Some(ot) = owner_internal(o, vt) else {
3650 return;
3651 };
3652 if ot.as_ref() == cls.as_ref() || !pool.is_subtype(cls, &ot) {
3653 return;
3654 }
3655 if declares_up(pool, &ot, name) || !declares_up(pool, cls, name) {
3656 return;
3657 }
3658 let ty = TypeRef::J(JavaType::Object(cls.clone()));
3659 if let Expr::Cast { ty: ct, .. } = &mut **o {
3660 *ct = ty;
3661 } else {
3662 let taken = std::mem::replace(&mut **o, Expr::This);
3663 **o = Expr::Cast { ty, e: Box::new(taken) };
3664 }
3665 }
3666 let (tgt_ty, value) = match x {
3668 Expr::Assign { target, op: AssignOp::Plain, value } => {
3669 let t = match &**target {
3670 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
3671 Expr::Field { ty, .. } => ty.erased(),
3672 _ => return,
3673 };
3674 (t, value)
3675 }
3676 _ => return,
3677 };
3678 let JavaType::Object(sn) = tgt_ty else { return };
3679 if let Expr::Cast { ty, .. } = &mut **value {
3680 if let TypeRef::J(JavaType::Object(tn)) = ty {
3681 if tn.as_ref() != sn.as_ref() && pool.is_subtype(&sn, tn) {
3682 *ty = TypeRef::J(JavaType::Object(sn.clone()));
3683 }
3684 }
3685 }
3686 });
3687 });
3688 walk_mut_deep(body, &mut |st| {
3690 if let Stmt::LocalDef { var, init: Some(value), .. } = st {
3691 let tt = if (*var as usize) < vt.vars.len() {
3692 vt.vars[*var as usize].ty.erased()
3693 } else {
3694 return;
3695 };
3696 let JavaType::Object(sn) = tt else { return };
3697 if let Expr::Cast { ty, .. } = value {
3698 if let TypeRef::J(JavaType::Object(tn)) = ty {
3699 if tn.as_ref() != sn.as_ref() && pool.is_subtype(&sn, tn) {
3700 *ty = TypeRef::J(JavaType::Object(sn.clone()));
3701 }
3702 }
3703 }
3704 }
3705 });
3706}
3707
3708pub fn fix_incomparable_equality(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
3715 fn side_ty(e: &Expr, vt: &VarTable) -> JavaType {
3716 match e {
3717 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
3718 vt.var(*var).ty.erased()
3719 }
3720 other => other.type_ref().erased(),
3721 }
3722 }
3723 let incomparable = |a: &JavaType, b: &JavaType| -> bool {
3724 use JavaType::{Array, Object};
3725 match (a, b) {
3726 (Object(x), Object(y)) => {
3727 if x == y || x.as_ref() == "java/lang/Object" || y.as_ref() == "java/lang/Object"
3728 {
3729 return false;
3730 }
3731 let xi = pool.get(x).map(|c| c.is_interface()).unwrap_or(true);
3732 let yi = pool.get(y).map(|c| c.is_interface()).unwrap_or(true);
3733 if xi || yi {
3737 return false;
3738 }
3739 !pool.is_subtype(x, y) && !pool.is_subtype(y, x)
3740 }
3741 (Array(_), Object(y)) | (Object(y), Array(_)) => {
3742 y.as_ref() != "java/lang/Object"
3743 && y.as_ref() != "java/lang/Cloneable"
3744 && y.as_ref() != "java/io/Serializable"
3745 && !pool.get(y).map(|c| c.is_interface()).unwrap_or(false)
3746 }
3747 _ => false,
3748 }
3749 };
3750 let is_ref = |t: &JavaType| matches!(t, JavaType::Object(_) | JavaType::Array(_));
3751 walk_stmt_exprs(body, &mut |e| {
3752 deep_rewrite(e, &mut |x| {
3753 if let Expr::Bin {
3754 op: BinOp::Eq | BinOp::Ne,
3755 l,
3756 r,
3757 ..
3758 } = x
3759 {
3760 let lt = side_ty(l, vt);
3761 let rt = side_ty(r, vt);
3762 if is_ref(<) && matches!(&**r, Expr::Const(ConstVal::Int(0))) {
3768 **r = Expr::Const(ConstVal::Null);
3769 return;
3770 }
3771 if is_ref(&rt) && matches!(&**l, Expr::Const(ConstVal::Int(0))) {
3772 **l = Expr::Const(ConstVal::Null);
3773 return;
3774 }
3775 if incomparable(<, &rt) {
3776 let taken = std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
3777 **l = Expr::Cast {
3778 ty: TypeRef::J(JavaType::Object("java/lang/Object".into())),
3779 e: taken,
3780 };
3781 }
3782 }
3783 });
3784 });
3785}
3786
3787pub fn fix_primitive_arg_bridges(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
3801 fn val_ty(e: &Expr, vt: &VarTable) -> JavaType {
3802 match e {
3803 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
3804 other => other.type_ref().erased(),
3805 }
3806 }
3807 #[allow(clippy::type_complexity)]
3808 let is_bool = |t: &JavaType| matches!(t, JavaType::Boolean);
3809 let is_num = |t: &JavaType| {
3810 matches!(
3811 t,
3812 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
3813 | JavaType::Long | JavaType::Float | JavaType::Double
3814 )
3815 };
3816 fn bridge(actual: &JavaType, formal: &JavaType, arg: &Expr) -> Option<Expr> {
3821 match (arg, formal) {
3831 (Expr::Const(ConstVal::Int(b)), JavaType::Float) => {
3832 return Some(Expr::Const(ConstVal::Float(f32::from_bits(*b as u32))));
3833 }
3834 (Expr::Const(ConstVal::Long(b)), JavaType::Double) => {
3835 return Some(Expr::Const(ConstVal::Double(f64::from_bits(*b as u64))));
3836 }
3837 (Expr::Const(ConstVal::Float(f)), JavaType::Int) => {
3838 return Some(Expr::Const(ConstVal::Int(f.to_bits() as i32)));
3839 }
3840 (Expr::Const(ConstVal::Double(d)), JavaType::Long) => {
3841 return Some(Expr::Const(ConstVal::Long(d.to_bits() as i64)));
3842 }
3843 _ => {}
3844 }
3845 if matches!(actual, JavaType::Boolean)
3846 && matches!(
3847 formal,
3848 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
3849 | JavaType::Long | JavaType::Float | JavaType::Double
3850 )
3851 {
3852 Some(Expr::Cast {
3853 ty: TypeRef::J(formal.clone()),
3854 e: Box::new(Expr::Cond {
3855 c: Box::new(arg.clone()),
3856 t: Box::new(Expr::Const(ConstVal::Int(1))),
3857 f: Box::new(Expr::Const(ConstVal::Int(0))),
3858 }),
3859 })
3860 } else if matches!(formal, JavaType::Boolean)
3861 && matches!(
3862 actual,
3863 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
3864 | JavaType::Long | JavaType::Float | JavaType::Double
3865 )
3866 {
3867 Some(Expr::Bin {
3868 op: BinOp::Ne,
3869 l: Box::new(arg.clone()),
3870 r: Box::new(Expr::Const(ConstVal::Int(0))),
3871 ty: Some(TypeRef::J(JavaType::Boolean)),
3872 })
3873 } else if matches!(
3874 actual,
3875 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
3876 | JavaType::Long | JavaType::Float | JavaType::Double
3877 ) && matches!(
3878 formal,
3879 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
3880 | JavaType::Long | JavaType::Float | JavaType::Double
3881 ) && !implicit_widening(actual, formal)
3882 {
3883 Some(Expr::Cast {
3892 ty: TypeRef::J(formal.clone()),
3893 e: Box::new(arg.clone()),
3894 })
3895 } else {
3896 None
3897 }
3898 }
3899 fn fw_resolvable(n: &str) -> bool {
3915 crate::fwdb::exists(n)
3916 }
3917 fn null_arg_cast(a: &mut Expr, f: &JavaType, pool: &DexPool) -> bool {
3918 if !matches!(&*a, Expr::Const(ConstVal::Null)) {
3919 return false;
3920 }
3921 if let JavaType::Object(n) = f {
3922 let resolvable = pool.get(n).is_some() || fw_resolvable(n);
3928 if n.as_ref() != "java/lang/Object" && resolvable {
3929 *a = Expr::Cast {
3930 ty: TypeRef::J(f.clone()),
3931 e: Box::new(Expr::Const(ConstVal::Null)),
3932 };
3933 return true;
3934 }
3935 } else if let JavaType::Array(_) = f {
3936 let mut e: &JavaType = f;
3943 while let JavaType::Array(inner) = e {
3944 e = &**inner;
3945 }
3946 let resolvable = match e {
3947 JavaType::Object(n) => pool.get(n).is_some() || fw_resolvable(n),
3948 _ => true, };
3950 if resolvable {
3951 *a = Expr::Cast {
3952 ty: TypeRef::J(f.clone()),
3953 e: Box::new(Expr::Const(ConstVal::Null)),
3954 };
3955 return true;
3956 }
3957 }
3958 false
3961 }
3962 fn implicit_widening(a: &JavaType, f: &JavaType) -> bool {
3965 use JavaType::*;
3966 match (a, f) {
3967 (Byte, Short) | (Byte, Int) | (Byte, Long) | (Byte, Float) | (Byte, Double) => true,
3968 (Short, Int) | (Short, Long) | (Short, Float) | (Short, Double) => true,
3969 (Char, Int) | (Char, Long) | (Char, Float) | (Char, Double) => true,
3970 (Int, Long) | (Int, Float) | (Int, Double) => true,
3971 (Long, Float) | (Long, Double) => true,
3972 (Float, Double) => true,
3973 (x, y) => x == y,
3974 }
3975 }
3976 let overload_competes = |cls: &str, name: &str, args: &[JavaType]| -> bool {
3985 if let Some(c) = pool.get(cls) {
3986 return c.all_methods().any(|m| {
3987 &*m.name == name
3988 && m.access & (crate::access::ACC_BRIDGE | crate::access::ACC_SYNTHETIC) == 0
3994 && m.parsed_desc()
3995 .is_some_and(|d| d.args.len() == args.len() && d.args != args)
3996 });
3997 }
3998 let _ = args;
4007 false
4008 };
4009 let ctor_formals = |cls: &str, n: usize| -> Option<Vec<JavaType>> {
4012 let c = pool.get(cls)?;
4013 let mut out: Option<Vec<JavaType>> = None;
4014 for m in c.all_methods() {
4015 if &*m.name != "<init>" {
4016 continue;
4017 }
4018 let Some(d) = m.parsed_desc() else { continue };
4019 if d.args.len() != n {
4020 continue;
4021 }
4022 match &out {
4023 None => out = Some(d.args.clone()),
4024 Some(prev) if *prev != d.args => return None,
4025 Some(_) => {}
4026 }
4027 }
4028 out
4029 };
4030 walk_stmt_exprs(body, &mut |e| {
4031 deep_rewrite(e, &mut |x| match x {
4032 Expr::Method {
4033 cls,
4034 name,
4035 desc,
4036 args,
4037 is_static,
4038 ..
4039 } => {
4040 if args.len() != desc.args.len() {
4041 return;
4042 }
4043 let _ = is_static;
4044 let mut competes: Option<bool> = None;
4045 for (a, f) in args.iter_mut().zip(desc.args.iter()) {
4046 if null_arg_cast(a, f, pool) {
4047 continue;
4048 }
4049 if matches!(a, Expr::Cast { .. }) {
4050 continue;
4051 }
4052 let subtype_arg = match (&val_ty(a, vt), f) {
4063 (JavaType::Object(an), JavaType::Object(fn_)) => {
4064 (an != fn_ && pool.is_subtype(an, fn_))
4069 || (fn_.as_ref() == "java/lang/Object"
4070 && an.as_ref() != "java/lang/Object")
4071 }
4072 (JavaType::Array(_), JavaType::Object(fn_))
4073 if fn_.as_ref() == "java/lang/Object" =>
4074 {
4075 true
4076 }
4077 _ => false,
4078 };
4079 let null_obj =
4093 matches!(a, Expr::Const(ConstVal::Null)) && matches!(f, JavaType::Object(_));
4094 let framework_owner = pool.get(cls.as_ref()).is_none();
4095 let fw_null_ambiguous = framework_owner
4096 && matches!(
4097 (cls.as_ref(), name.as_ref()),
4098 ("java/lang/StringBuilder", "append")
4099 | ("java/lang/StringBuffer", "append")
4100 | ("java/io/PrintStream", "println")
4101 | ("java/io/PrintStream", "print")
4102 | ("java/io/PrintWriter", "println")
4103 | ("java/io/PrintWriter", "print")
4104 | ("java/lang/String", "valueOf")
4105 );
4106 let fw_subtype_pinnable = framework_owner
4117 && subtype_arg
4118 && !matches!(f, JavaType::Object(fn_) if fn_.as_ref() == "java/lang/Object")
4119 && matches!(
4120 (cls.as_ref(), name.as_ref()),
4121 ("android/content/Intent", "putExtra")
4122 | ("java/util/concurrent/ScheduledExecutorService", "schedule")
4123 | ("java/util/concurrent/ExecutorService", "submit")
4124 | ("java/util/concurrent/ExecutorService", "invokeAll")
4125 );
4126 if fw_subtype_pinnable
4127 || (subtype_arg
4128 && *competes.get_or_insert_with(|| {
4129 overload_competes(cls, name, &desc.args)
4130 }))
4131 || (null_obj
4132 && (fw_null_ambiguous
4133 || *competes.get_or_insert_with(|| {
4134 overload_competes(cls, name, &desc.args)
4135 })))
4136 {
4137 let old =
4138 std::mem::replace(a, Expr::Const(ConstVal::Null));
4139 *a = Expr::Cast {
4140 ty: TypeRef::J(f.clone()),
4141 e: Box::new(old),
4142 };
4143 continue;
4144 }
4145 if !is_bool(&val_ty(a, vt)) && !is_num(&val_ty(a, vt)) {
4146 continue;
4147 }
4148 if !is_bool(f) && !is_num(f) {
4149 continue;
4150 }
4151 if let Some(b) = bridge(&val_ty(a, vt), f, a) {
4152 *a = b;
4153 }
4154 }
4155 }
4156 Expr::New { cls, args, arg_tys, .. } => {
4157 let formals_owned = if arg_tys.len() == args.len() && !arg_tys.is_empty() {
4158 Some(arg_tys.clone())
4159 } else {
4160 ctor_formals(cls, args.len())
4161 };
4162 if let Some(formals) = formals_owned {
4163 for (a, f) in args.iter_mut().zip(formals.iter()) {
4164 if null_arg_cast(a, f, pool) {
4165 continue;
4166 }
4167 if matches!(a, Expr::Cast { .. }) {
4168 continue;
4169 }
4170 if !is_bool(f) && !is_num(f) {
4171 continue;
4172 }
4173 if let Some(b) = bridge(&val_ty(a, vt), f, a) {
4174 *a = b;
4175 }
4176 }
4177 }
4178 }
4179 _ => {}
4180 });
4181 });
4182}
4183
4184pub fn deshadow_locals(vt: &mut VarTable, pool: &crate::DexPool, class_name: &str, body: &Stmt) {
4196 let segs = pool.root_pkg_segs();
4197 let obscured = crate::classdec::obscured_simples_snapshot();
4201 let self_pkg = class_name.rfind('/').map(|i| &class_name[..i]).unwrap_or("");
4208 let mut same_pkg_simples: jdc_core::FxHashSet<String> = jdc_core::FxHashSet::default();
4209 visit_all_exprs(body, &mut |x| {
4210 let cls = match x {
4211 Expr::Field { cls, is_static: true, .. } => Some(cls.as_ref()),
4212 Expr::Method { cls, is_static: true, .. } => Some(cls.as_ref()),
4213 _ => None,
4214 };
4215 if let Some(c) = cls {
4216 let pkg = c.rfind('/').map(|i| &c[..i]).unwrap_or("");
4217 if pkg == self_pkg {
4218 let simple = &c[c.rfind('/').map(|i| i + 1).unwrap_or(0)..];
4219 let head = simple.split('$').next().unwrap_or(simple);
4220 if !head.is_empty() {
4221 same_pkg_simples.insert(head.to_string());
4222 }
4223 }
4224 }
4225 });
4226 let hit = |name: &str| {
4227 segs.contains(name) || obscured.contains(name) || same_pkg_simples.contains(name)
4228 };
4229 if (segs.is_empty() && obscured.is_empty() && same_pkg_simples.is_empty())
4230 || !vt.vars.iter().any(|v| hit(&v.name))
4231 {
4232 return;
4233 }
4234 let mut taken: jdc_core::FxHashSet<String> =
4235 vt.vars.iter().map(|v| v.name.clone()).collect();
4236 for v in vt.vars.iter_mut() {
4237 if !hit(&v.name) {
4238 continue;
4239 }
4240 let mut k = 0u32;
4241 let cand = loop {
4242 k += 1;
4243 let c = if k == 1 {
4244 format!("{}x", v.name)
4245 } else {
4246 format!("{}x{}", v.name, k)
4247 };
4248 if !taken.contains(&c) {
4249 break c;
4250 }
4251 };
4252 taken.insert(cand.clone());
4253 v.name = cand;
4254 }
4255}
4256
4257pub fn fix_ctor_this_aliases(body: &mut Stmt, vt: &VarTable) {
4283 let Some(this_var) = vt
4284 .vars
4285 .iter()
4286 .find(|v| v.is_param && v.name == "this")
4287 .map(|v| v.id)
4288 else {
4289 return;
4290 };
4291 let mut analysis = VarAnalysis {
4292 assigns: Vec::new(),
4293 reads: Vec::new(),
4294 values: Vec::new(),
4295 };
4296 analyze_vars(body, &mut analysis);
4297 let n = vt.vars.len().max(analysis.assigns.len());
4298 let mut alias = vec![false; n];
4299 for _ in 0..4 {
4300 let mut changed = false;
4301 for v in 0..n as u32 {
4302 if alias[v as usize] {
4303 continue;
4304 }
4305 if analysis.assigns.get(v as usize).copied().unwrap_or(0) != 1 {
4306 continue;
4307 }
4308 let Some(Expr::Local { var: src, .. }) = analysis.values.get(v as usize).and_then(|o| o.as_ref()) else {
4309 continue;
4310 };
4311 if *src == this_var
4312 || (*src < n as u32 && alias[*src as usize])
4313 {
4314 alias[v as usize] = true;
4315 changed = true;
4316 }
4317 }
4318 if !changed {
4319 break;
4320 }
4321 }
4322 if !alias.iter().any(|&b| b) {
4323 return;
4324 }
4325 walk_stmt_exprs(body, &mut |e| {
4326 deep_rewrite_reads(e, &mut |x| {
4327 if let Expr::Local { var, .. } = x {
4328 if (*var as usize) < n && alias[*var as usize] {
4329 *x = Expr::This;
4330 }
4331 }
4332 });
4333 });
4334}
4335
4336pub fn fix_this0_owners(body: &mut Stmt, class_name: &str) {
4337 walk_stmt_exprs(body, &mut |e| {
4338 deep_rewrite(e, &mut |x| {
4339 if let Expr::Field { owner: Some(_), cls, name, .. } = x {
4340 if name.as_ref() == "this$0" && cls.as_ref() == class_name {
4341 if let Expr::Field { owner, .. } = x {
4342 *owner = None;
4343 }
4344 }
4345 }
4346 });
4347 });
4348}
4349
4350pub fn rescue_primitive_receivers(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
4351 fn prim(t: &JavaType) -> bool {
4352 match t {
4353 JavaType::Int
4354 | JavaType::Long
4355 | JavaType::Short
4356 | JavaType::Byte
4357 | JavaType::Char
4358 | JavaType::Float
4359 | JavaType::Double
4360 | JavaType::Boolean => true,
4361 JavaType::Object(n) => matches!(
4365 n.as_ref(),
4366 "java/lang/Boolean"
4367 | "java/lang/String"
4368 | "java/lang/Integer"
4369 | "java/lang/Long"
4370 | "java/lang/Short"
4371 | "java/lang/Byte"
4372 | "java/lang/Character"
4373 | "java/lang/Float"
4374 | "java/lang/Double"
4375 ),
4376 _ => false,
4377 }
4378 }
4379 walk_stmt_exprs(body, &mut |e| {
4380 deep_rewrite(e, &mut |x| {
4381 let (recv_var, recv_ty) = {
4383 let o: &Expr = match &*x {
4384 Expr::Method { owner: Some(o), .. } => o,
4385 Expr::Field { owner: Some(o), is_static: false, .. } => o,
4386 _ => return,
4387 };
4388 let Expr::Local { var, ty } = o else {
4389 return;
4390 };
4391 let vt_ty = if (*var as usize) < vt.vars.len() {
4392 vt.var(*var).ty.erased()
4393 } else {
4394 ty.erased()
4395 };
4396 if !prim(&vt_ty) {
4397 return;
4398 }
4399 (*var, vt_ty)
4400 };
4401 let cls: std::sync::Arc<str> = match &*x {
4402 Expr::Method { cls, .. } | Expr::Field { cls, .. } => cls.clone(),
4403 _ => return,
4404 };
4405 if let JavaType::Object(n) = &recv_ty {
4414 if n.as_ref() == cls.as_ref() || pool.is_subtype(n.as_ref(), cls.as_ref()) {
4415 return;
4416 }
4417 }
4418 let mut cands: Vec<u32> = Vec::new();
4420 for v in &vt.vars {
4421 if let JavaType::Object(n) = v.ty.erased() {
4422 if n.as_ref() == cls.as_ref()
4423 || pool.is_subtype(n.as_ref(), cls.as_ref())
4424 {
4425 cands.push(v.id);
4426 }
4427 }
4428 }
4429 let id = if cands.len() == 1 {
4430 cands[0]
4431 } else if cands.len() > 1 {
4432 let slot = if (recv_var as usize) < vt.vars.len() {
4437 Some(vt.var(recv_var).slot)
4438 } else {
4439 None
4440 };
4441 let mut hits: Vec<u32> = match slot {
4442 Some(sl) => cands
4443 .iter()
4444 .copied()
4445 .filter(|&c| {
4446 c != recv_var
4447 && (c as usize) < vt.vars.len()
4448 && vt.var(c).slot == sl
4449 })
4450 .collect(),
4451 None => Vec::new(),
4452 };
4453 if hits.is_empty() {
4454 hits = cands
4457 .iter()
4458 .copied()
4459 .filter(|&c| c != recv_var)
4460 .collect();
4461 }
4462 hits.sort_unstable();
4463 hits.dedup();
4464 if hits.is_empty() {
4465 return;
4466 }
4467 *hits.last().unwrap()
4468 } else {
4469 if cands.is_empty() {
4476 return;
4477 }
4478 let slot = if (recv_var as usize) < vt.vars.len() {
4479 vt.var(recv_var).slot
4480 } else {
4481 return;
4482 };
4483 let mut hits: Vec<u32> = cands
4484 .into_iter()
4485 .filter(|&c| {
4486 c != recv_var
4487 && (c as usize) < vt.vars.len()
4488 && vt.var(c).slot == slot
4489 })
4490 .collect();
4491 hits.sort_unstable();
4492 hits.dedup();
4493 if hits.len() != 1 {
4494 return;
4495 }
4496 hits[0]
4497 };
4498 let o: &mut Box<Expr> = match x {
4500 Expr::Method { owner: Some(o), .. } => o,
4501 Expr::Field { owner: Some(o), is_static: false, .. } => o,
4502 _ => return,
4503 };
4504 if let Expr::Local { var, ty } = &mut **o {
4505 if *var == recv_var {
4506 *var = id;
4507 *ty = vt.vars[id as usize].ty.clone();
4508 }
4509 }
4510 });
4511 });
4512
4513}
4514
4515pub fn rescue_arg_return_swaps(
4521 body: &mut Stmt,
4522 vt: &VarTable,
4523 pool: &crate::DexPool,
4524 ret_ty: &JavaType,
4525) {
4526 let mut top_ids: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
4534 for v in &vt.vars {
4535 if v.is_param {
4536 top_ids.insert(v.id);
4537 }
4538 }
4539 if let Stmt::Block(stmts) = body {
4540 for st in stmts.iter() {
4541 if let Stmt::LocalDef { var, .. } = st {
4542 top_ids.insert(*var);
4543 }
4544 }
4545 }
4546 let ret_wants_ref = match ret_ty {
4547 JavaType::Object(n) => n.as_ref() != "java/lang/Object",
4548 JavaType::Array(_) => true,
4549 _ => false,
4550 };
4551 let try_swap = |a: &mut Expr, want: &JavaType| {
4552 let Expr::Local { var, ty } = &mut *a else {
4553 return;
4554 };
4555 if (*var as usize) >= vt.vars.len() {
4556 return;
4557 }
4558 let avt = vt.var(*var).ty.erased();
4559 let avt_ref = matches!(avt, JavaType::Object(_) | JavaType::Array(_));
4560 let want_arr = matches!(want, JavaType::Array(_));
4561 if avt_ref && !(avt == JavaType::Object("java/lang/Object".into())) {
4564 return;
4565 }
4566 if avt_ref && !matches!(want, JavaType::Object(_)) {
4567 return;
4568 }
4569 let slot = vt.var(*var).slot;
4570 let mut hits: Vec<u32> = Vec::new();
4571 for v in &vt.vars {
4572 if v.slot != slot || !top_ids.contains(&v.id) || v.id == *var {
4573 continue;
4574 }
4575 let t = v.ty.erased();
4576 let ok = match (&t, want) {
4577 (JavaType::Object(n), JavaType::Object(w)) => {
4578 n.as_ref() == w.as_ref()
4579 || pool.is_subtype(n.as_ref(), w.as_ref())
4580 }
4581 (JavaType::Array(_), JavaType::Array(_)) => want_arr,
4582 _ => false,
4583 };
4584 if ok {
4585 hits.push(v.id);
4586 }
4587 }
4588 hits.sort_unstable();
4589 hits.dedup();
4590 if hits.is_empty() {
4591 return;
4592 }
4593 let id = *hits.last().unwrap();
4599 *var = id;
4600 *ty = vt.vars[id as usize].ty.clone();
4601 };
4602 walk_stmt_exprs(body, &mut |e| {
4603 deep_rewrite(e, &mut |x| {
4604 match x {
4605 Expr::Method { desc, args, is_dynamic: false, .. } => {
4606 for (i, a) in args.iter_mut().enumerate() {
4607 if let Some(f) = desc.args.get(i) {
4608 if matches!(f, JavaType::Object(n) if n.as_ref() != "java/lang/Object")
4609 || matches!(f, JavaType::Array(_))
4610 {
4611 try_swap(a, f);
4612 }
4613 }
4614 }
4615 }
4616 Expr::New { arg_tys, args, .. } => {
4617 for (i, a) in args.iter_mut().enumerate() {
4618 if let Some(f) = arg_tys.get(i) {
4619 if matches!(f, JavaType::Object(n) if n.as_ref() != "java/lang/Object")
4620 || matches!(f, JavaType::Array(_))
4621 {
4622 try_swap(a, f);
4623 }
4624 }
4625 }
4626 }
4627 _ => {}
4628 }
4629 });
4630 });
4631 if ret_wants_ref {
4632 walk_mut_deep(body, &mut |st| {
4633 if let Stmt::Return(Some(e)) = st {
4634 try_swap(e, ret_ty);
4635 }
4636 });
4637 }
4638}
4639
4640pub fn fix_ref_null_assigns(vt: &VarTable, body: &mut Stmt) {
4655 let is_ref_var = |var: u32| -> bool {
4656 (var as usize) < vt.vars.len()
4657 && matches!(
4658 vt.vars[var as usize].ty.erased(),
4659 JavaType::Object(_) | JavaType::Array(_)
4660 )
4661 };
4662 walk_mut_deep(body, &mut |st| match st {
4663 Stmt::ExprStmt(Expr::Assign {
4664 target,
4665 value,
4666 op: AssignOp::Plain,
4667 ..
4668 }) => {
4669 if let Expr::Local { var, .. } = &**target {
4670 if is_ref_var(*var) && matches!(&**value, Expr::Const(ConstVal::Int(0))) {
4671 **value = Expr::Const(ConstVal::Null);
4672 }
4673 }
4674 }
4675 Stmt::LocalDef {
4676 var,
4677 init: Some(e),
4678 ..
4679 } => {
4680 if is_ref_var(*var) && matches!(e, Expr::Const(ConstVal::Int(0))) {
4681 *e = Expr::Const(ConstVal::Null);
4682 }
4683 }
4684 _ => {}
4685 });
4686}
4687
4688pub fn fix_ref_array_null_consts(body: &mut Stmt) {
4689 walk_stmt_exprs(body, &mut |e| {
4690 deep_rewrite(e, &mut |x| {
4691 if let Expr::NewArray { elem, init: Some(list), .. } = x {
4692 if matches!(elem.erased(), JavaType::Object(_) | JavaType::Array(_)) {
4693 for slot in list.iter_mut() {
4694 if matches!(&*slot, Expr::Const(ConstVal::Int(0))) {
4695 *slot = Expr::Const(ConstVal::Null);
4696 }
4697 }
4698 }
4699 }
4700 });
4701 });
4702}
4703
4704pub fn fix_null_sentinels(body: &mut Stmt, vt: &VarTable, ret: &JavaType) {
4743 #[derive(Clone, Copy, PartialEq)]
4744 enum WK {
4745 Zero,
4746 Copy(u32),
4747 Other,
4748 }
4749 fn is_ref_jt(t: &JavaType) -> bool {
4750 matches!(t, JavaType::Object(_) | JavaType::Array(_))
4751 }
4752 fn cls_write(e: &Expr) -> WK {
4753 match e {
4754 Expr::Const(ConstVal::Int(0)) => WK::Zero,
4755 Expr::Local { var, .. } => WK::Copy(*var),
4756 _ => WK::Other,
4757 }
4758 }
4759 fn expr_writes(e: &Expr, writes: &mut std::collections::HashMap<u32, Vec<WK>>) {
4760 visit_exprs(e, &mut |x| match x {
4761 Expr::Assign { target, op, value } => {
4762 if let Expr::Local { var, .. } = &**target {
4763 let k = if matches!(op, AssignOp::Plain) {
4764 cls_write(value)
4765 } else {
4766 WK::Other
4767 };
4768 writes.entry(*var).or_default().push(k);
4769 }
4770 }
4771 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
4772 if let Expr::Local { var, .. } = &**inner {
4773 writes.entry(*var).or_default().push(WK::Other);
4774 }
4775 }
4776 _ => {}
4777 });
4778 }
4779 fn collect_writes(
4780 s: &Stmt,
4781 writes: &mut std::collections::HashMap<u32, Vec<WK>>,
4782 has_zero: &mut bool,
4783 ) {
4784 fn note(e: &Expr, has_zero: &mut bool, writes: &mut std::collections::HashMap<u32, Vec<WK>>) {
4785 visit_exprs(e, &mut |x| {
4786 if let Expr::Const(ConstVal::Int(0)) = x {
4787 *has_zero = true;
4788 }
4789 });
4790 expr_writes(e, writes);
4791 }
4792 match s {
4793 Stmt::Block(v) => {
4794 for x in v {
4795 collect_writes(x, writes, has_zero);
4796 }
4797 }
4798 Stmt::LocalDef { var, init, .. } => {
4799 if let Some(e) = init {
4800 if matches!(e, Expr::Const(ConstVal::Int(0))) {
4801 *has_zero = true;
4802 }
4803 writes.entry(*var).or_default().push(cls_write(e));
4804 }
4805 }
4806 Stmt::ExprStmt(e) => note(e, has_zero, writes),
4807 Stmt::Return(e) => {
4808 if let Some(x) = e {
4809 note(x, has_zero, writes);
4810 }
4811 }
4812 Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
4813 note(e, has_zero, writes);
4814 }
4815 Stmt::TernaryValue { e } => note(e, has_zero, writes),
4816 Stmt::If { cond, then_stmt, else_stmt } => {
4817 note(cond, has_zero, writes);
4818 collect_writes(then_stmt, writes, has_zero);
4819 if let Some(b) = else_stmt {
4820 collect_writes(b, writes, has_zero);
4821 }
4822 }
4823 Stmt::While { cond, body } | Stmt::DoWhile { body, cond } => {
4824 note(cond, has_zero, writes);
4825 collect_writes(body, writes, has_zero);
4826 }
4827 Stmt::For { init, cond, update, body } => {
4828 for x in init {
4829 collect_writes(x, writes, has_zero);
4830 }
4831 if let Some(c) = cond {
4832 note(c, has_zero, writes);
4833 }
4834 for u in update {
4835 note(u, has_zero, writes);
4836 }
4837 collect_writes(body, writes, has_zero);
4838 }
4839 Stmt::ForEach { var, iterable, body, .. } => {
4840 writes.entry(*var).or_default().push(WK::Other);
4841 note(iterable, has_zero, writes);
4842 collect_writes(body, writes, has_zero);
4843 }
4844 Stmt::Switch { selector, cases, default, .. } => {
4845 note(selector, has_zero, writes);
4846 for c in cases {
4847 if let Some(g) = &c.guard {
4848 note(g, has_zero, writes);
4849 }
4850 for x in &c.body {
4851 collect_writes(x, writes, has_zero);
4852 }
4853 }
4854 if let Some(d) = default {
4855 collect_writes(d, writes, has_zero);
4856 }
4857 }
4858 Stmt::Try { body, catches, finally } => {
4859 collect_writes(body, writes, has_zero);
4860 for c in catches {
4861 writes.entry(c.var).or_default().push(WK::Other);
4862 collect_writes(&c.body, writes, has_zero);
4863 }
4864 if let Some(f) = finally {
4865 collect_writes(f, writes, has_zero);
4866 }
4867 }
4868 Stmt::TryWithResources { resources, body, catches, finally, .. } => {
4869 for r in resources {
4870 collect_writes(r, writes, has_zero);
4871 }
4872 collect_writes(body, writes, has_zero);
4873 for c in catches {
4874 writes.entry(c.var).or_default().push(WK::Other);
4875 collect_writes(&c.body, writes, has_zero);
4876 }
4877 if let Some(f) = finally {
4878 collect_writes(f, writes, has_zero);
4879 }
4880 }
4881 Stmt::Assert { cond, msg } => {
4882 note(cond, has_zero, writes);
4883 if let Some(m) = msg {
4884 note(m, has_zero, writes);
4885 }
4886 }
4887 Stmt::Synchronized { lock, body } => {
4888 note(lock, has_zero, writes);
4889 collect_writes(body, writes, has_zero);
4890 }
4891 Stmt::Labeled { body, .. } => collect_writes(body, writes, has_zero),
4892 _ => {}
4893 }
4894 }
4895
4896 let mut writes: std::collections::HashMap<u32, Vec<WK>> =
4897 std::collections::HashMap::new();
4898 let mut has_zero = false;
4899 collect_writes(body, &mut writes, &mut has_zero);
4900
4901 let mut eligible: HashSet<u32> = HashSet::default();
4903 let int_typed = |v: u32| {
4904 (v as usize) < vt.vars.len() && vt.vars[v as usize].ty.erased() == JavaType::Int
4905 };
4906 loop {
4907 let mut changed = false;
4908 for (&v, ws) in writes.iter() {
4909 if eligible.contains(&v) || ws.is_empty() || !int_typed(v) {
4910 continue;
4911 }
4912 if ws.iter().all(|k| match k {
4913 WK::Zero => true,
4914 WK::Copy(u) => *u != v && eligible.contains(u),
4915 WK::Other => false,
4916 }) {
4917 eligible.insert(v);
4918 changed = true;
4919 }
4920 }
4921 if !changed {
4922 break;
4923 }
4924 }
4925 if eligible.is_empty() && !has_zero {
4926 return; }
4928
4929 #[derive(Clone, Copy, PartialEq)]
4930 enum Mode {
4931 Scan,
4932 Rewrite,
4933 }
4934 struct St<'a> {
4935 mode: Mode,
4936 vt: &'a VarTable,
4937 eligible: &'a HashSet<u32>,
4938 ref_read: HashSet<u32>,
4939 dead: HashSet<u32>,
4940 sentinels: HashSet<u32>,
4941 n_lit: usize,
4942 n_var: usize,
4943 }
4944 fn arg_ctx(t: &JavaType) -> Option<JavaType> {
4948 match t {
4949 JavaType::Object(n) if n.as_ref() == "java/lang/Object" => None,
4950 t if is_ref_jt(t) => Some(t.clone()),
4951 _ => None,
4952 }
4953 }
4954 fn walk_expr(e: &mut Expr, ctx: Option<JavaType>, st: &mut St) {
4955 walk_expr_c(e, ctx, false, st);
4956 }
4957 fn walk_expr_c(e: &mut Expr, ctx: Option<JavaType>, cast_arg: bool, st: &mut St) {
4963 let slot_ref = ctx.as_ref().is_some_and(is_ref_jt);
4964 fn mk_null(ctx: &Option<JavaType>, cast_arg: bool) -> Expr {
4965 match (cast_arg, ctx) {
4966 (true, Some(t)) => Expr::Cast {
4967 ty: TypeRef::J(t.clone()),
4968 e: Box::new(Expr::Const(ConstVal::Null)),
4969 },
4970 _ => Expr::Const(ConstVal::Null),
4971 }
4972 }
4973 match e {
4974 Expr::Const(ConstVal::Int(0)) if slot_ref => {
4975 if st.mode == Mode::Rewrite {
4976 *e = mk_null(&ctx, cast_arg);
4977 st.n_lit += 1;
4978 }
4979 return;
4980 }
4981 Expr::Local { var, .. } => {
4982 if st.eligible.contains(var) {
4983 if slot_ref {
4984 st.ref_read.insert(*var);
4985 } else {
4986 st.dead.insert(*var);
4987 }
4988 }
4989 if st.mode == Mode::Rewrite && slot_ref && st.sentinels.contains(var) {
4990 *e = mk_null(&ctx, cast_arg);
4991 st.n_var += 1;
4992 }
4993 return;
4994 }
4995 _ => {}
4996 }
4997 match e {
4998 Expr::Method { owner, desc, args, .. } => {
4999 if let Some(o) = owner {
5000 walk_expr(o, None, st);
5001 }
5002 for (i, a) in args.iter_mut().enumerate() {
5003 let c = desc.args.get(i).and_then(arg_ctx);
5004 walk_expr_c(a, c, true, st);
5005 }
5006 }
5007 Expr::Invokedynamic { desc, args, .. } => {
5008 for (i, a) in args.iter_mut().enumerate() {
5009 let c = desc.args.get(i).and_then(arg_ctx);
5010 walk_expr_c(a, c, true, st);
5011 }
5012 }
5013 Expr::New { args, arg_tys, .. } => {
5014 for (i, a) in args.iter_mut().enumerate() {
5015 let c = arg_tys.get(i).and_then(arg_ctx);
5016 walk_expr_c(a, c, true, st);
5017 }
5018 }
5019 Expr::AnonNew { args, .. } => {
5020 for a in args.iter_mut() {
5021 walk_expr(a, None, st);
5022 }
5023 }
5024 Expr::Assign { target, value, .. } => {
5025 if let Expr::Local { var: u, .. } = &**target {
5029 if st.eligible.contains(u) {
5030 return;
5031 }
5032 }
5033 let vctx = match &**target {
5034 Expr::Field { ty, .. } => {
5035 let t = ty.erased();
5036 is_ref_jt(&t).then_some(t)
5037 }
5038 Expr::ArrayIndex { array, .. } => match array.type_ref().erased() {
5039 JavaType::Array(inner) if is_ref_jt(&inner) => Some(*inner),
5040 _ => None,
5041 },
5042 _ => None,
5043 };
5044 walk_expr(target, None, st);
5045 walk_expr(value, vctx, st);
5046 }
5047 Expr::Cast { ty, e: inner } => {
5048 let t = ty.erased();
5049 let c = is_ref_jt(&t).then_some(t);
5050 walk_expr(inner, c, st);
5051 }
5052 Expr::InstanceOf { e: inner, .. } => {
5053 walk_expr(inner, Some(JavaType::Object("java/lang/Object".into())), st);
5054 }
5055 Expr::Cond { c, t, f } => {
5056 walk_expr(c, None, st);
5057 let tc = ctx.clone();
5058 walk_expr(t, tc, st);
5059 walk_expr(f, ctx, st);
5060 }
5061 Expr::NewArray { elem, dims, init, .. } => {
5062 for d in dims.iter_mut() {
5063 walk_expr(d, None, st);
5064 }
5065 if let Some(list) = init {
5066 let et = elem.erased();
5067 let c = is_ref_jt(&et).then_some(et);
5068 for slot in list.iter_mut() {
5069 walk_expr(slot, c.clone(), st);
5070 }
5071 }
5072 }
5073 Expr::NewMultiArray { dims, .. } => {
5074 for d in dims.iter_mut() {
5075 walk_expr(d, None, st);
5076 }
5077 }
5078 Expr::ArrayIndex { array, index } => {
5079 walk_expr(array, None, st);
5080 walk_expr(index, None, st);
5081 }
5082 Expr::Field { owner, .. } => {
5083 if let Some(o) = owner {
5084 walk_expr(o, None, st);
5085 }
5086 }
5087 Expr::Bin { l, r, .. } => {
5088 walk_expr(l, None, st);
5089 walk_expr(r, None, st);
5090 }
5091 Expr::Un { e: inner, .. } => walk_expr(inner, None, st),
5092 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
5093 walk_expr(inner, None, st);
5094 }
5095 Expr::StringConcat(parts) => {
5096 for p in parts.iter_mut() {
5097 if let ConcatPart::Str(x) = p {
5098 walk_expr(x, None, st);
5099 }
5100 }
5101 }
5102 Expr::Lambda(l) => {
5103 for c in l.captures.iter_mut() {
5104 walk_expr(c, None, st);
5105 }
5106 }
5107 _ => {}
5108 }
5109 }
5110 fn walk_stmt(s: &mut Stmt, st: &mut St, ret: &JavaType) {
5111 match s {
5112 Stmt::Block(v) => {
5113 for x in v.iter_mut() {
5114 walk_stmt(x, st, ret);
5115 }
5116 }
5117 Stmt::ExprStmt(e) => walk_expr(e, None, st),
5118 Stmt::LocalDef { var, init, .. } => {
5119 if let Some(e) = init {
5120 let ctx = if (*var as usize) < st.vt.vars.len() {
5121 let t = st.vt.vars[*var as usize].ty.erased();
5122 is_ref_jt(&t).then_some(t)
5123 } else {
5124 None
5125 };
5126 walk_expr(e, ctx, st);
5127 }
5128 }
5129 Stmt::Return(e) => {
5130 if let Some(x) = e {
5131 let c = is_ref_jt(ret).then(|| ret.clone());
5132 walk_expr(x, c, st);
5133 }
5134 }
5135 Stmt::Throw(e) => {
5136 walk_expr(e, Some(JavaType::Object("java/lang/Throwable".into())), st);
5137 }
5138 Stmt::If { cond, then_stmt, else_stmt } => {
5139 walk_expr(cond, None, st);
5140 walk_stmt(then_stmt, st, ret);
5141 if let Some(b) = else_stmt {
5142 walk_stmt(b, st, ret);
5143 }
5144 }
5145 Stmt::While { cond, body } => {
5146 walk_expr(cond, None, st);
5147 walk_stmt(body, st, ret);
5148 }
5149 Stmt::DoWhile { body, cond } => {
5150 walk_stmt(body, st, ret);
5151 walk_expr(cond, None, st);
5152 }
5153 Stmt::For { init, cond, update, body } => {
5154 for x in init.iter_mut() {
5155 walk_stmt(x, st, ret);
5156 }
5157 if let Some(c) = cond {
5158 walk_expr(c, None, st);
5159 }
5160 for u in update.iter_mut() {
5161 walk_expr(u, None, st);
5162 }
5163 walk_stmt(body, st, ret);
5164 }
5165 Stmt::ForEach { iterable, body, .. } => {
5166 walk_expr(iterable, None, st);
5167 walk_stmt(body, st, ret);
5168 }
5169 Stmt::Switch { selector, cases, default, .. } => {
5170 walk_expr(selector, None, st);
5171 for c in cases.iter_mut() {
5172 if let Some(g) = &mut c.guard {
5173 walk_expr(g, None, st);
5174 }
5175 for x in c.body.iter_mut() {
5176 walk_stmt(x, st, ret);
5177 }
5178 }
5179 if let Some(d) = default {
5180 walk_stmt(d, st, ret);
5181 }
5182 }
5183 Stmt::Try { body, catches, finally } => {
5184 walk_stmt(body, st, ret);
5185 for c in catches.iter_mut() {
5186 walk_stmt(&mut c.body, st, ret);
5187 }
5188 if let Some(f) = finally {
5189 walk_stmt(f, st, ret);
5190 }
5191 }
5192 Stmt::TryWithResources { resources, body, catches, finally, .. } => {
5193 for r in resources.iter_mut() {
5194 walk_stmt(r, st, ret);
5195 }
5196 walk_stmt(body, st, ret);
5197 for c in catches.iter_mut() {
5198 walk_stmt(&mut c.body, st, ret);
5199 }
5200 if let Some(f) = finally {
5201 walk_stmt(f, st, ret);
5202 }
5203 }
5204 Stmt::Assert { cond, msg } => {
5205 walk_expr(cond, None, st);
5206 if let Some(m) = msg {
5207 walk_expr(m, None, st);
5208 }
5209 }
5210 Stmt::Synchronized { lock, body } => {
5211 walk_expr(lock, Some(JavaType::Object("java/lang/Object".into())), st);
5212 walk_stmt(body, st, ret);
5213 }
5214 Stmt::TernaryValue { e } => walk_expr(e, None, st),
5215 Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
5216 walk_expr(e, Some(JavaType::Object("java/lang/Object".into())), st);
5217 }
5218 Stmt::Labeled { body, .. } => walk_stmt(body, st, ret),
5219 _ => {}
5220 }
5221 }
5222
5223 let mut st = St {
5224 mode: Mode::Scan,
5225 vt,
5226 eligible: &eligible,
5227 ref_read: HashSet::default(),
5228 dead: HashSet::default(),
5229 sentinels: HashSet::default(),
5230 n_lit: 0,
5231 n_var: 0,
5232 };
5233 if !eligible.is_empty() {
5234 walk_stmt(body, &mut st, ret);
5235 st.sentinels = eligible
5236 .iter()
5237 .copied()
5238 .filter(|v| st.ref_read.contains(v) && !st.dead.contains(v))
5239 .collect();
5240 }
5241 if st.sentinels.is_empty() && !has_zero {
5242 return;
5243 }
5244 st.mode = Mode::Rewrite;
5245 walk_stmt(body, &mut st, ret);
5246 if std::env::var_os("DDC_SENT").is_some() {
5247 eprintln!(
5248 "[sent] elig={} confirmed={} lit={} var={}",
5249 eligible.len(),
5250 st.sentinels.len(),
5251 st.n_lit,
5252 st.n_var
5253 );
5254 }
5255}
5256
5257pub fn fix_int_operand_bridges(body: &mut Stmt, vt: &VarTable) {
5265 fn side_bool(e: &Expr, vt: &VarTable) -> bool {
5266 matches!(
5267 match e {
5268 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
5269 other => other.type_ref().erased(),
5270 },
5271 JavaType::Boolean
5272 )
5273 }
5274 fn side_int(e: &Expr, vt: &VarTable) -> bool {
5275 matches!(
5276 match e {
5277 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
5278 other => other.type_ref().erased(),
5279 },
5280 JavaType::Int | JavaType::Short | JavaType::Byte | JavaType::Char | JavaType::Long
5281 )
5282 }
5283 fn wrap(e: Box<Expr>) -> Box<Expr> {
5284 Box::new(Expr::Cond {
5285 c: e,
5286 t: Box::new(Expr::Const(ConstVal::Int(1))),
5287 f: Box::new(Expr::Const(ConstVal::Int(0))),
5288 })
5289 }
5290 walk_stmt_exprs(body, &mut |e| {
5291 deep_rewrite(e, &mut |x| {
5292 if let Expr::Assign { target, value, op: AssignOp::Plain } = x {
5299 let tgt_ty: Option<JavaType> = match &**target {
5300 Expr::Field { ty, .. } => Some(ty.erased()),
5301 Expr::ArrayIndex { array, .. } => {
5302 match array.type_ref().erased() {
5303 JavaType::Array(el) => Some(el.as_ref().clone()),
5304 _ => None,
5305 }
5306 }
5307 Expr::Local { var, .. } => {
5312 if (*var as usize) < vt.vars.len() {
5313 Some(vt.var(*var).ty.erased())
5314 } else {
5315 None
5316 }
5317 }
5318 _ => None,
5319 };
5320 if let Some(t) = tgt_ty {
5321 let tgt_int = matches!(
5322 t,
5323 JavaType::Int
5324 | JavaType::Short
5325 | JavaType::Byte
5326 | JavaType::Char
5327 | JavaType::Long
5328 );
5329 if tgt_int && side_bool(value, vt) && !side_int(value, vt) {
5330 let taken = std::mem::replace(
5331 value,
5332 Box::new(Expr::Const(ConstVal::Null)),
5333 );
5334 let mut wrapped = wrap(taken);
5335 if !matches!(t, JavaType::Int | JavaType::Long) {
5336 wrapped = Box::new(Expr::Cast {
5337 ty: TypeRef::J(t.clone()),
5338 e: wrapped,
5339 });
5340 }
5341 *value = wrapped;
5342 } else if matches!(t, JavaType::Boolean)
5343 && side_int(value, vt)
5344 && !side_bool(value, vt)
5345 {
5346 let taken = std::mem::replace(
5347 value,
5348 Box::new(Expr::Const(ConstVal::Null)),
5349 );
5350 **value = Expr::Bin {
5351 op: BinOp::Ne,
5352 l: taken,
5353 r: Box::new(Expr::Const(ConstVal::Int(0))),
5354 ty: Some(TypeRef::J(JavaType::Boolean)),
5355 };
5356 }
5357 }
5358 }
5359 if let Expr::Cast { ty, e } = x {
5365 let t = ty.erased();
5366 let t_num = matches!(
5367 t,
5368 JavaType::Int
5369 | JavaType::Short
5370 | JavaType::Byte
5371 | JavaType::Char
5372 | JavaType::Long
5373 | JavaType::Float
5374 | JavaType::Double
5375 );
5376 if t_num && side_bool(e, vt) && !side_int(e, vt) {
5377 let taken = std::mem::replace(e, Box::new(Expr::Const(ConstVal::Null)));
5378 **e = Expr::Cond {
5379 c: taken,
5380 t: Box::new(Expr::Const(ConstVal::Int(1))),
5381 f: Box::new(Expr::Const(ConstVal::Int(0))),
5382 };
5383 } else if matches!(t, JavaType::Boolean)
5384 && side_int(e, vt)
5385 && !side_bool(e, vt)
5386 {
5387 let taken = std::mem::replace(e, Box::new(Expr::Const(ConstVal::Null)));
5388 *x = Expr::Bin {
5389 op: BinOp::Ne,
5390 l: taken,
5391 r: Box::new(Expr::Const(ConstVal::Int(0))),
5392 ty: Some(TypeRef::J(JavaType::Boolean)),
5393 };
5394 }
5395 }
5396 if let Expr::Bin { op, l, r, .. } = x {
5402 if matches!(op, BinOp::Eq | BinOp::Ne) {
5403 if matches!(&**r, Expr::Const(ConstVal::Null))
5404 && side_int(l, vt)
5405 && !side_bool(l, vt)
5406 {
5407 **r = Expr::Const(ConstVal::Int(0));
5408 } else if matches!(&**l, Expr::Const(ConstVal::Null))
5409 && side_int(r, vt)
5410 && !side_bool(r, vt)
5411 {
5412 **l = Expr::Const(ConstVal::Int(0));
5413 }
5414 }
5415 }
5416 if let Expr::ArrayIndex { index, .. } = x {
5420 if side_bool(index, vt) && !side_int(index, vt) {
5421 let taken =
5422 std::mem::replace(index, Box::new(Expr::Const(ConstVal::Null)));
5423 *index = wrap(taken);
5424 }
5425 }
5426 if let Expr::NewArray { dims, .. } = x {
5432 for d in dims.iter_mut() {
5433 if side_bool(d, vt) && !side_int(d, vt) {
5434 let taken =
5435 std::mem::replace(d, Expr::Const(ConstVal::Null));
5436 *d = *wrap(Box::new(taken));
5437 }
5438 }
5439 }
5440 if let Expr::Bin { op, l, r, .. } = x {
5441 let int_kind = matches!(
5442 op,
5443 BinOp::Or
5444 | BinOp::And
5445 | BinOp::Xor
5446 | BinOp::Add
5447 | BinOp::Sub
5448 | BinOp::Mul
5449 | BinOp::Div
5450 | BinOp::Rem
5451 | BinOp::Shl
5452 | BinOp::Shr
5453 | BinOp::Ushr
5454 | BinOp::Eq
5455 | BinOp::Ne
5456 | BinOp::Lt
5457 | BinOp::Ge
5458 | BinOp::Gt
5459 | BinOp::Le
5460 );
5461 if !int_kind {
5462 return;
5463 }
5464 if side_bool(l, vt) && side_int(r, vt) && !side_bool(r, vt) {
5467 let taken = std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
5468 *l = wrap(taken);
5469 } else if side_bool(r, vt) && side_int(l, vt) && !side_bool(l, vt) {
5470 let taken = std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
5471 *r = wrap(taken);
5472 }
5473 }
5474 });
5475 });
5476}
5477
5478pub fn fold_value_diamonds(body: &mut Stmt, vt: &VarTable) {
5516 let Stmt::Block(stmts) = body else { return };
5517 fold_diamonds_in(stmts, vt);
5518}
5519
5520fn diamond_types_ok(a: &Expr, b: &Expr) -> bool {
5521 fn num(t: &JavaType) -> bool {
5522 matches!(
5523 t,
5524 JavaType::Byte
5525 | JavaType::Short
5526 | JavaType::Int
5527 | JavaType::Char
5528 | JavaType::Long
5529 | JavaType::Float
5530 | JavaType::Double
5531 )
5532 }
5533 if let Some(other) = if matches!(a, Expr::Const(ConstVal::Null)) {
5541 Some(b)
5542 } else if matches!(b, Expr::Const(ConstVal::Null)) {
5543 Some(a)
5544 } else {
5545 None
5546 } {
5547 return !num(&other.type_ref().erased());
5548 }
5549 let ta = a.type_ref().erased();
5550 let tb = b.type_ref().erased();
5551 ta == tb || (num(&ta) && num(&tb))
5552}
5553
5554fn single_local_assign(st: &Stmt) -> Option<(u32, Expr, Expr)> {
5562 let mut inner = st;
5563 while let Stmt::Block(v) = inner {
5564 if v.len() != 1 {
5565 return None;
5566 }
5567 inner = &v[0];
5568 }
5569 match inner {
5570 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain }) => {
5571 match &**target {
5572 Expr::Local { var, .. } => Some((*var, *target.clone(), *value.clone())),
5573 _ => None,
5574 }
5575 }
5576 _ => None,
5577 }
5578}
5579
5580fn arm_assign_inlined(st: &Stmt) -> Option<(u32, Expr, Expr)> {
5589 if let Some(r) = single_local_assign(st) {
5590 return Some(r);
5591 }
5592 let mut inner = st;
5593 while let Stmt::Block(v) = inner {
5594 if v.len() == 1 {
5595 inner = &v[0];
5596 } else {
5597 break;
5598 }
5599 }
5600 let Stmt::Block(v) = inner else { return None };
5601 if v.len() != 2 {
5602 return None;
5603 }
5604 let Stmt::LocalDef {
5605 var: x,
5606 init: Some(init),
5607 ..
5608 } = &v[0]
5609 else {
5610 return None;
5611 };
5612 let (var, tgt, val) = single_local_assign(&v[1])?;
5613 let mut count = 0usize;
5614 let mut c = val.clone();
5615 deep_rewrite(&mut c, &mut |e| {
5616 if let Expr::Local { var: lv, .. } = e {
5617 if lv == x {
5618 count += 1;
5619 }
5620 }
5621 });
5622 if count != 1 {
5623 return None;
5624 }
5625 let mut out = val.clone();
5626 deep_rewrite(&mut out, &mut |e| {
5627 if let Expr::Local { var: lv, .. } = e {
5628 if lv == x {
5629 *e = init.clone();
5630 }
5631 }
5632 });
5633 Some((var, tgt, out))
5634}
5635
5636fn mk_ternary_assign(tgt: Expr, c: Expr, a: Expr, b: Expr) -> Stmt {
5637 Stmt::ExprStmt(Expr::Assign {
5638 target: Box::new(tgt),
5639 op: AssignOp::Plain,
5640 value: Box::new(Expr::Cond {
5641 c: Box::new(c),
5642 t: Box::new(a),
5643 f: Box::new(b),
5644 }),
5645 })
5646}
5647
5648fn same_render_var(v1: u32, v2: u32, vt: &VarTable) -> bool {
5653 let (a, b) = (vt.vars.get(v1 as usize), vt.vars.get(v2 as usize));
5654 match (a, b) {
5655 (Some(x), Some(y)) => x.name == y.name && x.ty.erased() == y.ty.erased(),
5656 _ => false,
5657 }
5658}
5659
5660fn canon_stmts_eq(a: &[Stmt], b: &[Stmt], vt: &VarTable) -> bool {
5667 if a.len() != b.len() {
5668 return false;
5669 }
5670 let mut map: std::collections::HashMap<String, u32> = std::collections::HashMap::default();
5671 let canon = |s: &Stmt, map: &mut std::collections::HashMap<String, u32>| -> Stmt {
5672 let mut c = s.clone();
5673 walk_stmt_exprs(&mut c, &mut |e| {
5674 deep_rewrite(e, &mut |x| {
5675 if let Expr::Local { var, .. } = x {
5676 let key = match vt.vars.get(*var as usize) {
5677 Some(vi) => format!("{}#{:?}", vi.name, vi.ty.erased()),
5678 None => format!("v{}", var),
5679 };
5680 let id = *map.entry(key).or_insert(*var);
5681 *x = Expr::Local {
5682 var: id,
5683 ty: vt.var(id).ty.clone(),
5684 };
5685 }
5686 });
5687 });
5688 c
5689 };
5690 a.iter()
5691 .zip(b.iter())
5692 .all(|(x, y)| canon(x, &mut map) == canon(y, &mut map))
5693}
5694
5695fn fold_diamonds_in(stmts: &mut Vec<Stmt>, vt: &VarTable) {
5696 let mut i = 0usize;
5697 while i < stmts.len() {
5698 match &mut stmts[i] {
5699 Stmt::Block(v) => fold_diamonds_in(v, vt),
5700 Stmt::If { then_stmt, else_stmt, .. } => {
5701 if let Stmt::Block(v) = &mut **then_stmt {
5702 fold_diamonds_in(v, vt);
5703 }
5704 if let Some(e) = else_stmt {
5705 if let Stmt::Block(v) = &mut **e {
5706 fold_diamonds_in(v, vt);
5707 }
5708 }
5709 }
5710 _ => {}
5711 }
5712 let shape1 = if let Stmt::If { cond, then_stmt, else_stmt: Some(el), .. } = &stmts[i] {
5714 match (arm_assign_inlined(then_stmt), arm_assign_inlined(el)) {
5715 (Some((v1, tgt, a)), Some((v2, _, b)))
5716 if v1 == v2 && diamond_types_ok(&a, &b) =>
5717 {
5718 Some(mk_ternary_assign(tgt, cond.clone(), a, b))
5719 }
5720 _ => None,
5721 }
5722 } else {
5723 None
5724 };
5725 if let Some(f) = shape1 {
5726 stmts[i] = f;
5727 i += 1;
5728 continue;
5729 }
5730 let shape3 = if let Stmt::If {
5743 cond,
5744 then_stmt,
5745 else_stmt: Some(el),
5746 ..
5747 } = &stmts[i]
5748 {
5749 let arm = |st: &Stmt| -> Option<((u32, Expr, Expr), Vec<Stmt>)> {
5750 let Stmt::Block(v) = st else { return None };
5751 if v.len() < 2 {
5752 return None;
5753 }
5754 let first = single_local_assign(&v[0])?;
5755 Some((first, v[1..].to_vec()))
5756 };
5757 match (arm(then_stmt), arm(el)) {
5758 (Some(((v1, tgt1, a), mut rest_t)), Some(((v2, _t2, b), rest_e)))
5759 if !rest_t.is_empty()
5760 && is_bare_ctor_call(first_leaf_stmt(&rest_t[0]))
5761 && stmts_count_writes(&rest_t, v1) == 0
5762 && stmts_count_writes(&rest_t, v2) == 0
5763 && diamond_types_ok(&a, &b) =>
5764 {
5765 if v1 == v2 || same_render_var(v1, v2, vt) {
5766 if canon_stmts_eq(&rest_t, &rest_e, vt) {
5768 Some((mk_ternary_assign(tgt1, cond.clone(), a, b), rest_t))
5769 } else {
5770 None
5771 }
5772 } else if stmts_read_var(&rest_t, v1) == 1
5773 && stmts_read_var(&rest_e, v2) == 1
5774 && stmts_read_var(&rest_t, v2) == 0
5775 && stmts_read_var(&rest_e, v1) == 0
5776 {
5777 let mut unified = rest_e.clone();
5785 for st in unified.iter_mut() {
5786 let mut c2 = st.clone();
5787 walk_stmt_exprs(&mut c2, &mut |e| {
5788 deep_rewrite(e, &mut |x| {
5789 if let Expr::Local { var, .. } = x {
5790 if *var == v2 {
5791 *x = Expr::Local {
5792 var: v1,
5793 ty: vt.var(v1).ty.clone(),
5794 };
5795 }
5796 }
5797 });
5798 });
5799 *st = c2;
5800 }
5801 if unified != rest_t {
5802 None
5803 } else {
5804 let tern = Expr::Cond {
5805 c: Box::new(cond.clone()),
5806 t: Box::new(a.clone()),
5807 f: Box::new(b.clone()),
5808 };
5809 for st in rest_t.iter_mut() {
5810 let mut c2 = st.clone();
5811 walk_stmt_exprs(&mut c2, &mut |e| {
5812 deep_rewrite(e, &mut |x| {
5813 if let Expr::Local { var, .. } = x {
5814 if *var == v1 {
5815 *x = tern.clone();
5816 }
5817 }
5818 });
5819 });
5820 *st = c2;
5821 }
5822 let first = rest_t.remove(0);
5826 Some((first, rest_t))
5827 }
5828 } else {
5829 None
5830 }
5831 }
5832 _ => None,
5833 }
5834 } else {
5835 None
5836 };
5837 if let Some((folded, rest)) = shape3 {
5838 stmts[i] = folded;
5839 stmts.splice(i + 1..i + 1, rest);
5840 i += 1;
5841 continue;
5842 }
5843 if i + 1 < stmts.len() {
5853 let shape2 = {
5873 let s_i = single_local_assign(&stmts[i]);
5874 if let Stmt::If {
5875 cond,
5876 then_stmt,
5877 else_stmt,
5878 ..
5879 } = &stmts[i + 1]
5880 {
5881 let then_a = single_local_assign(then_stmt);
5882 let else_a = else_stmt.as_deref().and_then(single_local_assign);
5883 let then_empty =
5884 matches!(&**then_stmt, Stmt::Block(v) if v.is_empty());
5885 let else_empty = else_stmt
5886 .as_deref()
5887 .map(|e| matches!(e, Stmt::Block(v) if v.is_empty()))
5888 .unwrap_or(true);
5889 let try_fold = |over: Option<(u32, Expr, Expr)>,
5890 keep_over: bool|
5891 -> Option<Stmt> {
5892 let (v2, tgt2, a) = over?;
5893 let (v1, _t1, b) = s_i.as_ref()?;
5894 if !(*v1 == v2 || same_render_var(*v1, v2, vt)) {
5895 return None;
5896 }
5897 let keep = if *v1 == v2 {
5898 b.clone()
5899 } else {
5900 Expr::Local {
5901 var: *v1,
5902 ty: vt.var(*v1).ty.clone(),
5903 }
5904 };
5905 if !diamond_types_ok(&a, &keep) {
5906 return None;
5907 }
5908 if keep_over {
5909 Some(mk_ternary_assign(tgt2, cond.clone(), keep, a))
5910 } else {
5911 Some(mk_ternary_assign(tgt2, cond.clone(), a, keep))
5912 }
5913 };
5914 if then_a.is_some() && else_empty {
5915 try_fold(then_a, false)
5916 } else if else_a.is_some() && then_empty {
5917 try_fold(else_a, true)
5918 } else {
5919 None
5920 }
5921 } else {
5922 None
5923 }
5924 };
5925 let mut lead_fold: Option<(Stmt, Stmt)> = None;
5934 if shape2.is_none() && i + 1 < stmts.len() {
5935 let s_i2 = single_local_assign(&stmts[i]);
5936 let leading = |st: &Stmt| -> Option<((u32, Expr, Expr), Vec<Stmt>)> {
5937 let Stmt::Block(v) = st else { return None };
5938 if v.len() < 2 {
5939 return None;
5940 }
5941 let first = single_local_assign(&v[0])?;
5942 Some((first, v[1..].to_vec()))
5943 };
5944 if let (Some((v1, _t1, b)), Stmt::If { cond, then_stmt, else_stmt, .. }) =
5945 (&s_i2, &stmts[i + 1])
5946 {
5947 let else_absent = else_stmt.is_none()
5948 || else_stmt
5949 .as_deref()
5950 .is_some_and(|e| matches!(e, Stmt::Block(v) if v.is_empty()));
5951 let then_absent = matches!(&**then_stmt, Stmt::Block(v) if v.is_empty());
5952 let try_lead = |over: Option<((u32, Expr, Expr), Vec<Stmt>)>,
5953 keep_over: bool|
5954 -> Option<(Stmt, Stmt)> {
5955 let ((v2, tgt2, a), rest) = over?;
5956 if !(*v1 == v2 || same_render_var(*v1, v2, vt)) {
5957 return None;
5958 }
5959 if stmts_count_writes(&rest, *v1) != 0
5960 || stmts_count_writes(&rest, v2) != 0
5961 {
5962 return None;
5963 }
5964 let keep = if *v1 == v2 {
5965 b.clone()
5966 } else {
5967 Expr::Local {
5968 var: *v1,
5969 ty: vt.var(*v1).ty.clone(),
5970 }
5971 };
5972 if !diamond_types_ok(&a, &keep) {
5973 return None;
5974 }
5975 let t = if keep_over {
5976 mk_ternary_assign(tgt2, cond.clone(), keep, a)
5977 } else {
5978 mk_ternary_assign(tgt2, cond.clone(), a, keep)
5979 };
5980 let mut new_if = stmts[i + 1].clone();
5981 if let Stmt::If { then_stmt, else_stmt, .. } = &mut new_if {
5982 if keep_over {
5983 *else_stmt = Some(Box::new(Stmt::Block(rest)));
5984 } else {
5985 **then_stmt = Stmt::Block(rest);
5986 }
5987 }
5988 Some((t, new_if))
5989 };
5990 if else_absent {
5991 lead_fold = try_lead(leading(then_stmt), false);
5992 } else if then_absent {
5993 if let Some(e) = else_stmt {
5994 lead_fold = try_lead(leading(e), true);
5995 }
5996 }
5997 }
5998 }
5999 if let Some((folded, new_if)) = lead_fold {
6000 stmts[i] = folded;
6001 stmts[i + 1] = new_if;
6002 continue;
6003 }
6004 if let Some(f) = shape2 {
6005 stmts[i] = f;
6006 stmts.remove(i + 1);
6007 continue; }
6009 }
6010 i += 1;
6011 }
6012}
6013
6014pub fn fold_bool_value_diamonds(body: &mut Stmt, vt: &VarTable) {
6015 fn is_bool_var(v: u32, vt: &VarTable) -> bool {
6016 (v as usize) < vt.vars.len()
6017 && matches!(vt.vars[v as usize].ty.erased(), JavaType::Boolean)
6018 }
6019 fn bool_const(e: &Expr) -> Option<bool> {
6020 fn cint(e: &Expr) -> Option<i32> {
6021 match e {
6022 Expr::Const(ConstVal::Int(n)) => Some(*n),
6023 _ => None,
6024 }
6025 }
6026 match e {
6027 Expr::Const(ConstVal::Int(0)) => Some(false),
6028 Expr::Const(ConstVal::Int(1)) => Some(true),
6029 Expr::Bin { op: op @ (BinOp::Eq | BinOp::Ne), l, r, .. } => {
6033 let (a, b) = (cint(l)?, cint(r)?);
6034 let eq = a == b;
6035 Some(if matches!(op, BinOp::Eq) { eq } else { !eq })
6036 }
6037 _ => None,
6038 }
6039 }
6040 fn reads_var(e: &Expr, v: u32) -> bool {
6041 let mut hit = false;
6042 visit_exprs(e, &mut |x| {
6043 if let Expr::Local { var, .. } = x {
6044 if *var == v {
6045 hit = true;
6046 }
6047 }
6048 });
6049 hit
6050 }
6051 fn bool_valued(e: &Expr, vt: &VarTable) -> bool {
6055 fn side(e: &Expr, vt: &VarTable) -> bool {
6056 bool_const(e).is_some() || bool_valued(e, vt)
6057 }
6058 match e {
6059 Expr::Local { var, .. } => is_bool_var(*var, vt),
6060 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
6061 Expr::InstanceOf { .. } => true,
6062 Expr::Un { op: UnOp::Not, e: x, .. } => bool_valued(x, vt) || bool_const(x).is_some(),
6063 Expr::Bin { op, l, r, .. } => match op {
6064 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le => true,
6065 BinOp::LogAnd | BinOp::LogOr => {
6068 side(l, vt) && side(r, vt)
6069 }
6070 _ => false,
6071 },
6072 Expr::Cond { t, f, .. } => side(t, vt) && side(f, vt),
6073 _ => false,
6074 }
6075 }
6076 fn lone_assign(
6085 b: &Stmt,
6086 v: u32,
6087 vt: &VarTable,
6088 an: &VarAnalysis,
6089 consumed: &mut HashSet<u32>,
6090 ) -> Option<Expr> {
6091 let l = flat_list(b);
6092 if l.len() == 2 {
6093 let (u, e): (u32, &Expr) = match l[0] {
6094 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e),
6095 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
6096 match &**target {
6097 Expr::Local { var, .. } => (*var, &**value),
6098 _ => return None,
6099 }
6100 }
6101 _ => return None,
6102 };
6103 if u == v || consumed.contains(&u) {
6104 return None;
6105 }
6106 if an.assigns.get(u as usize).copied().unwrap_or(0) != 1
6113 || an.reads.get(u as usize).copied().unwrap_or(0) != 1
6114 {
6115 return None;
6116 }
6117 let (tv, rv) = match l[1] {
6118 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
6119 match (&**target, &**value) {
6120 (Expr::Local { var: t, .. }, Expr::Local { var: r, .. }) => (*t, *r),
6121 _ => return None,
6122 }
6123 }
6124 Stmt::LocalDef { var, init: Some(Expr::Local { var: r, .. }), .. } => (*var, *r),
6125 _ => return None,
6126 };
6127 if tv != v || rv != u {
6128 return None;
6129 }
6130 if reads_var(e, v) || !(bool_valued(e, vt) || bool_const(e).is_some()) {
6131 return None;
6132 }
6133 consumed.insert(u);
6134 return Some(e.clone());
6135 }
6136 if l.len() != 1 {
6137 return None;
6138 }
6139 let (target, value) = match l[0] {
6140 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => (target, value),
6141 Stmt::LocalDef { var, init: Some(value), .. } if *var == v => {
6142 if reads_var(value, v) || !(bool_valued(value, vt) || bool_const(value).is_some()) {
6143 return None;
6144 }
6145 return Some(value.clone());
6146 }
6147 _ => return None,
6148 };
6149 let Expr::Local { var: tv, .. } = &**target else { return None };
6150 if *tv != v || reads_var(value, v) {
6151 return None;
6152 }
6153 if !(bool_valued(value, vt) || bool_const(value).is_some()) {
6154 return None;
6155 }
6156 Some((**value).clone())
6157 }
6158 fn not(e: Expr) -> Expr {
6159 Expr::Un { op: UnOp::Not, e: Box::new(e) }
6160 }
6161 fn simplify_bool(e: Expr) -> Expr {
6166 match e {
6167 Expr::Un { op: UnOp::Not, e: inner, .. } => match *inner {
6168 Expr::Bin { op: BinOp::Ne, l, r, ty } => {
6173 simplify_bool(Expr::Bin { op: BinOp::Eq, l, r, ty })
6174 }
6175 Expr::Bin { op: BinOp::Eq, l, r, ty } => {
6176 simplify_bool(Expr::Bin { op: BinOp::Ne, l, r, ty })
6177 }
6178 other => Expr::Un { op: UnOp::Not, e: Box::new(simplify_bool(other)) },
6179 },
6180 Expr::Bin { op: op @ (BinOp::Eq | BinOp::Ne), l, r, ty } => {
6181 fn cond01(e: &Expr) -> Option<Expr> {
6183 if let Expr::Cond { c, t, f } = e {
6184 if matches!(&**t, Expr::Const(ConstVal::Int(1)))
6185 && matches!(&**f, Expr::Const(ConstVal::Int(0)))
6186 {
6187 return Some((**c).clone());
6188 }
6189 }
6190 None
6191 }
6192 let (lc, rc) = (cond01(&l), cond01(&r));
6193 match (lc, rc) {
6194 (Some(c), None) => match (&*r, op) {
6195 (Expr::Const(ConstVal::Int(1)), BinOp::Eq) => c,
6196 (Expr::Const(ConstVal::Int(1)), BinOp::Ne) => not(c),
6197 (Expr::Const(ConstVal::Int(0)), BinOp::Eq) => not(c),
6198 (Expr::Const(ConstVal::Int(0)), BinOp::Ne) => c,
6199 _ => Expr::Bin { op, l: Box::new(c), r, ty },
6200 },
6201 (None, Some(c)) => match (&*l, op) {
6202 (Expr::Const(ConstVal::Int(1)), BinOp::Eq) => c,
6203 (Expr::Const(ConstVal::Int(1)), BinOp::Ne) => not(c),
6204 (Expr::Const(ConstVal::Int(0)), BinOp::Eq) => not(c),
6205 (Expr::Const(ConstVal::Int(0)), BinOp::Ne) => c,
6206 _ => Expr::Bin { op, l, r: Box::new(c), ty },
6207 },
6208 _ => Expr::Bin { op, l, r, ty },
6209 }
6210 }
6211 other => other,
6212 }
6213 }
6214 fn bin(op: BinOp, l: Expr, r: Expr) -> Expr {
6215 Expr::Bin { op, l: Box::new(l), r: Box::new(r), ty: None }
6216 }
6217
6218 for _round in 0..4 {
6219 let mut changed = false;
6220 let mut analysis = VarAnalysis {
6221 assigns: Vec::new(),
6222 reads: Vec::new(),
6223 values: Vec::new(),
6224 };
6225 analyze_vars(body, &mut analysis);
6226 let mut consumed: HashSet<u32> = HashSet::default();
6227 walk_mut_deep(body, &mut |st| {
6228 let Stmt::Block(list) = st else { return };
6229 let mut i = 0usize;
6230 while i + 1 < list.len() {
6231 let folded = try_at(list, i, vt, &is_bool_var, &bool_const, &reads_var, &lone_assign, &analysis, &mut consumed);
6232 match folded {
6233 Some(expr) => {
6234 let is_def = matches!(&list[i], Stmt::LocalDef { .. });
6235 let var = match &list[i] {
6236 Stmt::LocalDef { var, .. } => *var,
6237 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
6238 match &**target { Expr::Local { var, .. } => *var, _ => unreachable!() }
6239 }
6240 _ => unreachable!(),
6241 };
6242 let (is_final, force_type) = match &list[i] {
6243 Stmt::LocalDef { is_final, force_type, .. } => (*is_final, *force_type),
6244 _ => (false, false),
6245 };
6246 list[i] = if is_def {
6247 Stmt::LocalDef { var, init: Some(expr), is_final, force_type }
6248 } else {
6249 Stmt::ExprStmt(Expr::Assign {
6250 target: Box::new(Expr::Local {
6251 var,
6252 ty: TypeRef::J(JavaType::Boolean),
6253 }),
6254 op: AssignOp::Plain,
6255 value: Box::new(expr),
6256 })
6257 };
6258 list.remove(i + 1);
6259 changed = true;
6260 }
6263 None => i += 1,
6264 }
6265 }
6266 });
6267 if !changed {
6268 break;
6269 }
6270 }
6271
6272 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
6273 fn try_at(
6274 list: &[Stmt],
6275 i: usize,
6276 vt: &VarTable,
6277 is_bool_var: &dyn Fn(u32, &VarTable) -> bool,
6278 bool_const: &dyn Fn(&Expr) -> Option<bool>,
6279 reads_var: &dyn Fn(&Expr, u32) -> bool,
6280 lone_assign: &dyn Fn(&Stmt, u32, &VarTable, &VarAnalysis, &mut HashSet<u32>) -> Option<Expr>,
6281 an: &VarAnalysis,
6282 consumed: &mut HashSet<u32>,
6283 ) -> Option<Expr> {
6284 let (v, base) = match &list[i] {
6286 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
6287 match &**target {
6288 Expr::Local { var, .. } => {
6289 let b = bool_const(value)?;
6292 if !is_bool_var(*var, vt) { return None; }
6293 (*var, b)
6294 }
6295 _ => return None,
6296 }
6297 }
6298 Stmt::LocalDef { var, init: Some(e), .. } => {
6299 let b = bool_const(e)?;
6300 if !is_bool_var(*var, vt) { return None; }
6301 (*var, b)
6302 }
6303 _ => return None,
6304 };
6305 let Stmt::If { cond, then_stmt, else_stmt } = &list[i + 1] else {
6306 return None;
6307 };
6308 if reads_var(cond, v) {
6309 return None;
6310 }
6311 let mut touches_v = false;
6312 visit_exprs(cond, &mut |x| {
6313 if let Expr::Assign { target, .. } = x {
6314 if let Expr::Local { var, .. } = &**target {
6315 if *var == v { touches_v = true; }
6316 }
6317 }
6318 });
6319 if touches_v {
6320 return None;
6321 }
6322 let (cond_n, then_arm, else_arm): (Expr, &Stmt, Option<&Stmt>) =
6325 if flat_list(then_stmt).is_empty() && else_stmt.is_some() {
6326 (not(cond.clone()), else_stmt.as_ref().unwrap(), None)
6327 } else {
6328 (cond.clone(), then_stmt.as_ref(), else_stmt.as_deref())
6329 };
6330 let e1 = lone_assign(then_arm, v, vt, an, consumed)?;
6331 let e2 = match else_arm {
6332 None => None,
6333 Some(b) => Some(lone_assign(b, v, vt, an, consumed)?),
6334 };
6335 let e1c = bool_const(&e1);
6336 let c = simplify_bool(cond_n);
6337 Some(match e2 {
6338 None => {
6339 if !base {
6340 match e1c {
6341 Some(true) => c,
6342 Some(false) => return None, None => bin(BinOp::LogAnd, c, e1),
6344 }
6345 } else {
6346 match e1c {
6347 Some(false) => not(c),
6348 Some(true) => return None,
6349 None => bin(BinOp::LogOr, not(c), e1),
6350 }
6351 }
6352 }
6353 Some(e2) => {
6354 if e1 == e2 {
6356 return None; }
6358 Expr::Cond { c: Box::new(c), t: Box::new(e1), f: Box::new(e2) }
6359 }
6360 })
6361 }
6362}
6363
6364pub fn idiom_compounds(body: &mut Stmt, vt: &VarTable) {
6365 walk_mut_deep(body, &mut |st| {
6366 let Stmt::ExprStmt(e) = st else { return };
6367 let Expr::Assign { target, op: AssignOp::Plain, value } = &mut *e else {
6368 return;
6369 };
6370 enum Tgt {
6373 Loc(u32),
6374 Fld,
6375 }
6376 let tgt = match &**target {
6377 Expr::Local { var, .. } => Tgt::Loc(*var),
6378 Expr::Field { owner, .. }
6379 if owner.is_none()
6380 || matches!(&**owner.as_ref().unwrap(), Expr::This) =>
6381 {
6382 Tgt::Fld
6383 }
6384 _ => return,
6385 };
6386 let Expr::Bin { op, l, r, .. } = &mut **value else {
6387 return;
6388 };
6389 match (&tgt, &**l) {
6391 (Tgt::Loc(v), Expr::Local { var, .. }) if *var == *v => {}
6392 (Tgt::Fld, Expr::Field { cls: c2, name: n2, .. }) => {
6393 let Expr::Field { cls: c1, name: n1, .. } = &**target else {
6394 return;
6395 };
6396 if c1 != c2 || n1 != n2 {
6397 return;
6398 }
6399 }
6400 _ => return,
6401 }
6402 let one = match &**r {
6404 Expr::Const(ConstVal::Int(1)) => Some(1i64),
6405 Expr::Const(ConstVal::Int(-1)) => Some(-1),
6406 Expr::Const(ConstVal::Long(1)) => Some(1),
6407 Expr::Const(ConstVal::Long(-1)) => Some(-1),
6408 _ => None,
6409 };
6410 if *op == BinOp::Add || *op == BinOp::Sub {
6411 if let Some(mut d) = one {
6412 if *op == BinOp::Sub {
6413 d = -d;
6414 }
6415 let tgt_ty = match &tgt {
6416 Tgt::Loc(v) => vt.var(*v).ty.erased(),
6417 Tgt::Fld => {
6418 let Expr::Field { ty, .. } = &**target else {
6419 return;
6420 };
6421 ty.erased()
6422 }
6423 };
6424 let numeric = matches!(
6425 tgt_ty,
6426 JavaType::Int
6427 | JavaType::Long
6428 | JavaType::Short
6429 | JavaType::Byte
6430 | JavaType::Char
6431 | JavaType::Float
6432 | JavaType::Double
6433 );
6434 if numeric {
6435 let wide = matches!(tgt_ty, JavaType::Long);
6436 let inner = match &tgt {
6437 Tgt::Loc(v) => Expr::Local {
6438 var: *v,
6439 ty: vt.var(*v).ty.clone(),
6440 },
6441 Tgt::Fld => (**target).clone(),
6442 };
6443 *e = Expr::PostIncDec {
6444 e: Box::new(inner),
6445 delta: d,
6446 wide,
6447 };
6448 return;
6449 }
6450 }
6451 }
6452 let cop = match op {
6453 BinOp::Add => AssignOp::Add,
6454 BinOp::Sub => AssignOp::Sub,
6455 BinOp::Mul => AssignOp::Mul,
6456 BinOp::Div => AssignOp::Div,
6457 BinOp::Rem => AssignOp::Rem,
6458 BinOp::Shl => AssignOp::Shl,
6459 BinOp::Shr => AssignOp::Shr,
6460 BinOp::Ushr => AssignOp::Ushr,
6461 BinOp::And => AssignOp::And,
6462 BinOp::Or => AssignOp::Or,
6463 BinOp::Xor => AssignOp::Xor,
6464 _ => return,
6465 };
6466 let rhs = std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
6467 let new_target = match &tgt {
6468 Tgt::Loc(v) => Box::new(Expr::Local {
6469 var: *v,
6470 ty: vt.var(*v).ty.clone(),
6471 }),
6472 Tgt::Fld => target.clone(),
6473 };
6474 *e = Expr::Assign {
6475 target: new_target,
6476 op: cop,
6477 value: rhs,
6478 };
6479 });
6480}
6481
6482pub fn fix_bool_xor(body: &mut Stmt, vt: &VarTable, ret_bool: bool) {
6483 fn is_one(e: &Expr) -> bool {
6484 matches!(e, Expr::Const(ConstVal::Int(1)))
6485 }
6486 fn bty(e: &Expr, vt: &VarTable) -> bool {
6487 matches!(
6488 match e {
6489 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
6490 other => other.type_ref().erased(),
6491 },
6492 JavaType::Boolean
6493 )
6494 }
6495 fn inv_of_xor(x: &Expr, vt: &VarTable) -> Option<Expr> {
6500 if let Expr::Bin { op: BinOp::Xor, l, r, .. } = x {
6501 let (int_side, one_side) = if is_one(r) {
6502 (l, r)
6503 } else if is_one(l) {
6504 (r, l)
6505 } else {
6506 return None;
6507 };
6508 let _ = one_side;
6509 if !bty(int_side, vt) {
6510 return Some(Expr::Bin {
6511 op: BinOp::Eq,
6512 l: int_side.clone(),
6513 r: Box::new(Expr::Const(ConstVal::Int(0))),
6514 ty: Some(TypeRef::J(JavaType::Boolean)),
6515 });
6516 }
6517 }
6518 None
6519 }
6520 let tgt_bool = |target: &Expr, vt: &VarTable| -> bool {
6521 match target {
6522 Expr::Local { var, .. } => matches!(vt.var(*var).ty.erased(), JavaType::Boolean),
6523 Expr::Field { ty, .. } => matches!(ty.erased(), JavaType::Boolean),
6524 _ => false,
6525 }
6526 };
6527 walk_mut_deep(body, &mut |st| match st {
6528 Stmt::Return(Some(e)) if ret_bool => {
6529 if let Some(r) = inv_of_xor(e, vt) {
6530 *e = r;
6531 }
6532 }
6533 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain, .. }) => {
6534 if tgt_bool(target, vt) {
6535 if let Some(r) = inv_of_xor(value, vt) {
6536 **value = r;
6537 }
6538 }
6539 }
6540 Stmt::LocalDef { var, init: Some(value), .. }
6541 if matches!(vt.var(*var).ty.erased(), JavaType::Boolean) =>
6542 {
6543 if let Some(r) = inv_of_xor(value, vt) {
6544 *value = r;
6545 }
6546 }
6547 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
6548 if let Some(r) = inv_of_xor(cond, vt) {
6549 *cond = r;
6550 }
6551 }
6552 _ => {}
6553 });
6554 fn ity(e: &Expr, vt: &VarTable) -> bool {
6555 matches!(
6556 match e {
6557 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
6558 other => other.type_ref().erased(),
6559 },
6560 JavaType::Int | JavaType::Short | JavaType::Byte | JavaType::Char
6561 )
6562 }
6563 fn mixed_rewrite(e: &mut Expr, vt: &VarTable) {
6572 deep_rewrite(e, &mut |x| {
6573 if let Expr::Bin { op, l, r, ty } = x {
6574 match op {
6575 BinOp::Xor => {
6576 if is_one(r) && bty(l, vt) {
6577 let taken =
6578 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
6579 *x = Expr::Un { op: UnOp::Not, e: taken };
6580 } else if is_one(l) && bty(r, vt) {
6581 let taken =
6582 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
6583 *x = Expr::Un { op: UnOp::Not, e: taken };
6584 }
6585 }
6586 BinOp::Or | BinOp::And => {
6587 if bty(l, vt) && ity(r, vt) {
6588 let taken =
6589 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
6590 **r = Expr::Bin {
6591 op: BinOp::Ne,
6592 l: taken,
6593 r: Box::new(Expr::Const(ConstVal::Int(0))),
6594 ty: Some(TypeRef::J(JavaType::Boolean)),
6595 };
6596 *ty = Some(TypeRef::J(JavaType::Boolean));
6597 } else if ity(l, vt) && bty(r, vt) {
6598 let taken =
6599 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
6600 **l = Expr::Bin {
6601 op: BinOp::Ne,
6602 l: taken,
6603 r: Box::new(Expr::Const(ConstVal::Int(0))),
6604 ty: Some(TypeRef::J(JavaType::Boolean)),
6605 };
6606 *ty = Some(TypeRef::J(JavaType::Boolean));
6607 }
6608 }
6609 _ => {}
6610 }
6611 }
6612 });
6613 }
6614 walk_mut_deep(body, &mut |st| match st {
6617 Stmt::Return(Some(e)) if ret_bool => mixed_rewrite(e, vt),
6618 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain, .. }) => {
6619 if tgt_bool(target, vt) {
6620 mixed_rewrite(value, vt);
6621 }
6622 }
6623 Stmt::LocalDef { var, init: Some(e), .. }
6624 if matches!(vt.var(*var).ty.erased(), JavaType::Boolean) =>
6625 {
6626 mixed_rewrite(e, vt);
6627 }
6628 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
6629 mixed_rewrite(cond, vt);
6630 }
6631 _ => {}
6632 });
6633 walk_stmt_exprs(body, &mut |e| {
6636 deep_rewrite(e, &mut |x| {
6637 if let Expr::Cond { c, .. } = x {
6638 mixed_rewrite(c, vt);
6639 }
6640 });
6641 });
6642}
6643
6644pub fn booleanize(vt: &mut VarTable, body: &mut Stmt, ret_bool: bool) -> usize {
6650 let n0 = vt.vars.len();
6657 let mut derefed = vec![false; n0];
6658 mark_derefs(body, &mut derefed);
6659 let mut total = 0usize;
6660 for _ in 0..4 {
6661 let n = booleanize_round(vt, body, ret_bool, &derefed);
6662 if n == 0 {
6663 break;
6664 }
6665 total += n;
6666 }
6667 total
6668}
6669
6670fn mark_deref_expr(e: &Expr, derefed: &mut [bool]) {
6674 match e {
6675 Expr::Field { owner: Some(o), is_static: false, .. }
6676 | Expr::Method { owner: Some(o), .. } => {
6677 if let Expr::Local { var, .. } = &**o {
6678 if (*var as usize) < derefed.len() {
6679 derefed[*var as usize] = true;
6680 }
6681 }
6682 }
6683 Expr::ArrayIndex { array, .. } => {
6684 if let Expr::Local { var, .. } = &**array {
6685 if (*var as usize) < derefed.len() {
6686 derefed[*var as usize] = true;
6687 }
6688 }
6689 }
6690 _ => {}
6691 }
6692 for_each_child(e, &mut |c| mark_deref_expr(c, derefed));
6693}
6694
6695fn mark_derefs(body: &Stmt, derefed: &mut [bool]) {
6696 walk_all(body, &mut |st| {
6697 match st {
6698 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
6699 mark_deref_expr(e, derefed);
6700 }
6701 Stmt::Return(Some(e)) => mark_deref_expr(e, derefed),
6702 Stmt::LocalDef { init: Some(e), .. } => mark_deref_expr(e, derefed),
6703 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
6704 mark_deref_expr(cond, derefed);
6705 }
6706 Stmt::For { init, .. } => {
6707 for x in init {
6708 mark_derefs(x, derefed);
6709 }
6710 }
6711 Stmt::Switch { selector, .. } => mark_deref_expr(selector, derefed),
6712 Stmt::ForEach { iterable, .. } => mark_deref_expr(iterable, derefed),
6713 _ => {}
6714 }
6715 });
6716}
6717
6718fn booleanize_round(
6719 vt: &mut VarTable,
6720 body: &mut Stmt,
6721 ret_bool: bool,
6722 derefed: &[bool],
6723) -> usize {
6724 let n = vt.vars.len();
6725 let mut in_cond = vec![false; n];
6726
6727 walk_all(body, &mut |st| {
6730 let cond: Option<&Expr> = match st {
6731 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
6732 Some(cond)
6733 }
6734 _ => None,
6735 };
6736 if let Some(c) = cond {
6737 if let Expr::Bin {
6738 op: BinOp::Eq | BinOp::Ne,
6739 l,
6740 r,
6741 ..
6742 } = c
6743 {
6744 for (x, other) in [(l, r), (r, l)] {
6745 if let (Expr::Local { var, .. }, Expr::Const(ConstVal::Int(0))) =
6746 (&**x, &**other)
6747 {
6748 if (*var as usize) < n {
6749 in_cond[*var as usize] = true;
6750 }
6751 }
6752 }
6753 }
6754 }
6755 if ret_bool {
6760 if let Stmt::Return(Some(Expr::Local { var, .. })) = st {
6761 if (*var as usize) < n {
6762 in_cond[*var as usize] = true;
6763 }
6764 }
6765 }
6766 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = st {
6773 if let Expr::Field { ty, .. } = &**target {
6774 if ty.erased() == JavaType::Boolean {
6775 if let Expr::Local { var, .. } = &**value {
6776 if (*var as usize) < n {
6777 in_cond[*var as usize] = true;
6778 }
6779 }
6780 }
6781 }
6782 }
6783 let val: Option<&Expr> = match st {
6784 Stmt::ExprStmt(Expr::Assign { value, .. }) => Some(value),
6785 Stmt::ExprStmt(e) => Some(e),
6788 Stmt::LocalDef { init: Some(e), .. } => Some(e),
6789 Stmt::Return(Some(e)) => Some(e),
6790 _ => None,
6791 };
6792 if let Some(v) = val {
6793 visit_exprs(v, &mut |x| {
6794 if let Expr::Method { desc, args, .. } = x {
6800 for (i, a) in args.iter().enumerate() {
6801 if desc.args.get(i) == Some(&JavaType::Boolean) {
6802 if let Expr::Local { var, .. } = a {
6803 if (*var as usize) < n {
6804 in_cond[*var as usize] = true;
6805 }
6806 }
6807 }
6808 }
6809 }
6810 if let Expr::Bin { op, l, r, .. } = x {
6811 if matches!(
6812 op,
6813 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
6814 ) {
6815 for side in [l, r] {
6816 if let Expr::Local { var, .. } = &**side {
6817 if (*var as usize) < n {
6818 in_cond[*var as usize] = true;
6819 }
6820 }
6821 }
6822 }
6823 if matches!(op, BinOp::Or | BinOp::And) {
6829 let l_bool = l.type_ref().erased() == JavaType::Boolean;
6830 let r_bool = r.type_ref().erased() == JavaType::Boolean;
6831 if l_bool != r_bool {
6832 let side = if l_bool { r } else { l };
6833 if let Expr::Local { var, .. } = &**side {
6834 if (*var as usize) < n {
6835 in_cond[*var as usize] = true;
6836 }
6837 }
6838 }
6839 }
6840 }
6841 });
6842 }
6843 });
6844
6845 let mut assigned_any = vec![false; n];
6852 let mut all_bool = vec![true; n];
6853 let mut edges: Vec<(usize, usize)> = Vec::new();
6854 walk_all(body, &mut |st| {
6855 let (var, value) = match st {
6856 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
6857 Expr::Local { var, .. } => (var, &**value),
6858 _ => return,
6859 },
6860 Stmt::LocalDef {
6861 var, init: Some(e), ..
6862 } => (var, e),
6863 _ => return,
6864 };
6865 let i = *var as usize;
6866 if i < n {
6867 assigned_any[i] = true;
6868 if !is_boolean_valued(value, vt) {
6869 all_bool[i] = false;
6870 }
6871 if let Expr::Local { var: src, .. } = value {
6877 if (*src as usize) < n {
6878 edges.push((i, *src as usize));
6879 }
6880 }
6881 }
6882 });
6883 loop {
6888 let mut changed = false;
6889 for &(tgt, src) in &edges {
6890 if in_cond[tgt] && !in_cond[src] && all_bool[src] {
6891 in_cond[src] = true;
6892 changed = true;
6893 }
6894 if in_cond[src] && !in_cond[tgt] && all_bool[tgt] {
6900 in_cond[tgt] = true;
6901 changed = true;
6902 }
6903 }
6904 if !changed {
6905 break;
6906 }
6907 }
6908 let reads = count_locals_stmts(std::slice::from_ref(body));
6909 let mut boolean_vars: HashSet<u32> = HashSet::default();
6910 for i in 0..n {
6911 if vt.vars[i].is_param {
6912 continue;
6913 }
6914 if derefed[i] {
6915 continue;
6916 }
6917 if !in_cond[i] && !(reads.get(&(i as u32)).copied().unwrap_or(0) == 0) {
6918 continue;
6919 }
6920 if assigned_any[i] && all_bool[i] {
6921 boolean_vars.insert(i as u32);
6922 }
6923 }
6924 if boolean_vars.is_empty() {
6925 return 0;
6926 }
6927 for v in &boolean_vars {
6928 vt.vars[*v as usize].ty = TypeRef::J(JavaType::Boolean);
6929 }
6930 let types: Vec<&TypeRef> = vt.vars.iter().map(|v| &v.ty).collect();
6931 rewrite_exprs(body, &mut |e| {
6932 deep_rewrite(e, &mut |x| {
6933 if let Expr::Local { var, ty } = x {
6934 if let Some(want) = types.get(*var as usize) {
6935 if ty != *want {
6936 *ty = (*want).clone();
6937 }
6938 }
6939 }
6940 });
6941 });
6942
6943 fold_bool_conditions(body, &boolean_vars);
6945 boolean_vars.len()
6946}
6947
6948fn is_boolean_valued(e: &Expr, vt: &VarTable) -> bool {
6949 match e {
6950 Expr::Const(ConstVal::Int(0)) | Expr::Const(ConstVal::Int(1)) => true,
6951 Expr::Bin { op: BinOp::Or | BinOp::And, l, r, .. } => {
6958 is_boolean_valued(l, vt) || is_boolean_valued(r, vt)
6959 }
6960 Expr::Bin {
6968 op: BinOp::Xor,
6969 l,
6970 r,
6971 ..
6972 } => is_boolean_valued(l, vt) && is_boolean_valued(r, vt),
6973 Expr::Bin { op, .. } => matches!(
6974 op,
6975 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
6976 ),
6977 Expr::InstanceOf { .. } => true,
6978 Expr::Local { var, ty } => {
6984 let t = if (*var as usize) < vt.vars.len() {
6985 vt.var(*var).ty.erased()
6986 } else {
6987 ty.erased()
6988 };
6989 t == JavaType::Boolean
6990 }
6991 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
6992 Expr::Cond { t, f, .. } => is_boolean_valued(t, vt) && is_boolean_valued(f, vt),
6993 Expr::Un { op: UnOp::Not, .. } => true,
6994 Expr::Field { ty, .. } => ty.erased() == JavaType::Boolean,
6998 _ => false,
6999 }
7000}
7001
7002fn fold_bool_conditions(s: &mut Stmt, bv: &HashSet<u32>) {
7003 let fold = |e: &mut Expr| {
7004 deep_rewrite(e, &mut |x| {
7005 if let Expr::Bin { op, l, r, .. } = x {
7006 let (var_side, const_side) = match (&**l, &**r) {
7007 (Expr::Local { .. }, Expr::Const(ConstVal::Int(_))) => (l, 0),
7008 (Expr::Const(ConstVal::Int(_)), Expr::Local { .. }) => (r, 1),
7009 _ => return,
7010 };
7011 let var = match &**var_side {
7012 Expr::Local { var, .. } => *var,
7013 _ => return,
7014 };
7015 if !bv.contains(&var) {
7016 return;
7017 }
7018 let _ = const_side;
7019 match op {
7020 BinOp::Ne => {
7021 let inner = var_side.clone();
7022 *x = *inner;
7023 }
7024 BinOp::Eq => {
7025 let inner = var_side.clone();
7026 *x = Expr::Un {
7027 op: UnOp::Not,
7028 e: Box::new(*inner),
7029 };
7030 }
7031 _ => {}
7032 }
7033 }
7034 });
7035 };
7036 match s {
7037 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => fold(cond),
7038 _ => {}
7039 }
7040 walk_mut_deep(s, &mut |st| {
7041 let cond = match st {
7042 Stmt::If { cond, .. } => Some(cond),
7043 Stmt::While { cond, .. } => Some(cond),
7044 Stmt::DoWhile { cond, .. } => Some(cond),
7045 _ => None,
7046 };
7047 if let Some(c) = cond {
7048 fold(c);
7049 }
7050 });
7051}
7052
7053pub fn flip_const_compares(s: &mut Stmt) {
7055 rewrite_exprs(s, &mut |e| {
7056 deep_rewrite(e, &mut |x| {
7057 if let Expr::Bin { op, l, r, .. } = x {
7058 if matches!(
7059 op,
7060 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
7061 ) {
7062 let l_const = matches!(&**l, Expr::Const(_));
7063 let r_var = matches!(&**r, Expr::Local { .. });
7064 if l_const && r_var {
7065 std::mem::swap(l, r);
7066 }
7067 }
7068 }
7069 });
7070 });
7071}
7072
7073pub fn null_compares(vt: &VarTable, body: &mut Stmt) {
7075 let obj_vars: HashSet<u32> = vt
7076 .vars
7077 .iter()
7078 .filter(|v| v.ty.erased().is_reference())
7079 .map(|v| v.id)
7080 .collect();
7081 rewrite_exprs(body, &mut |e| {
7082 deep_rewrite(e, &mut |x| {
7083 null_side_rewrite(x, &|v| obj_vars.contains(&v));
7084 });
7085 });
7086}
7087
7088pub fn drop_dead_locals(body: &mut Stmt) {
7097 loop {
7098 let mut reads: Vec<usize> = Vec::new();
7099 count_reads(body, &mut reads);
7100 let mut dropped = 0usize;
7101 walk_mut_deep(body, &mut |st| match st {
7102 Stmt::Block(items) => prune_dead_items(items, &reads, &mut dropped),
7103 Stmt::Switch { cases, .. } => {
7113 for c in cases {
7114 prune_dead_items(&mut c.body, &reads, &mut dropped);
7115 }
7116 }
7117 Stmt::For { init, .. } => prune_dead_items(init, &reads, &mut dropped),
7118 _ => {}
7119 });
7120 if dropped == 0 {
7121 break;
7122 }
7123 }
7124}
7125
7126fn prune_dead_items(items: &mut Vec<Stmt>, reads: &[usize], dropped: &mut usize) {
7131 let dead = |v: u32| reads.get(v as usize).copied().unwrap_or(0) == 0;
7132 items.retain_mut(|x| match x {
7133 Stmt::ExprStmt(Expr::Assign { target, value, op, .. }) => {
7134 let dead_target = matches!(&**target, Expr::Local { var, .. } if dead(*var));
7135 if dead_target && matches!(*op, AssignOp::Plain) {
7136 *dropped += 1;
7137 if has_side_effects(value) {
7138 let e = std::mem::replace(value, Box::new(Expr::This));
7139 *x = Stmt::ExprStmt(*e);
7140 true
7141 } else {
7142 false
7143 }
7144 } else {
7145 true
7146 }
7147 }
7148 Stmt::LocalDef { var, init, .. } if dead(*var) => {
7149 *dropped += 1;
7150 if let Some(e) = init.take() {
7151 if has_side_effects(&e) {
7152 *x = Stmt::ExprStmt(e);
7153 return true;
7154 }
7155 }
7156 false
7157 }
7158 _ => true,
7159 });
7160}
7161
7162
7163fn stmt_shallow_vars<F: FnMut(u32)>(st: &Stmt, out: &mut F) {
7168 fn ex<F: FnMut(u32)>(e: &Expr, out: &mut F) {
7169 visit_exprs(e, &mut |x| {
7170 if let Expr::Local { var, .. } = x {
7171 out(*var);
7172 }
7173 });
7174 }
7175 match st {
7176 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
7177 ex(e, out)
7178 }
7179 Stmt::TernaryValue { e } => ex(e, out),
7180 Stmt::Return(Some(e)) => ex(e, out),
7181 Stmt::LocalDef { var, init, .. } => {
7182 out(*var);
7183 if let Some(e) = init {
7184 ex(e, out);
7185 }
7186 }
7187 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
7188 ex(cond, out)
7189 }
7190 Stmt::For { cond, update, .. } => {
7191 if let Some(c) = cond {
7192 ex(c, out);
7193 }
7194 for u in update {
7195 ex(u, out);
7196 }
7197 }
7198 Stmt::ForEach { iterable, .. } => ex(iterable, out),
7199 Stmt::Switch { selector, cases, .. } => {
7200 ex(selector, out);
7201 for cg in cases {
7202 if let Some(g) = &cg.guard {
7203 ex(g, out);
7204 }
7205 }
7206 }
7207 Stmt::Assert { cond, msg } => {
7208 ex(cond, out);
7209 if let Some(m) = msg {
7210 ex(m, out);
7211 }
7212 }
7213 Stmt::Synchronized { lock, .. } => ex(lock, out),
7214 _ => {}
7215 }
7216}
7217
7218fn scope_pass_a(st: &Stmt, cur: u32, next: &mut u32, def_block: &mut [u32], sizes: &mut Vec<u32>) {
7230 if let Stmt::LocalDef { var, .. } = st {
7231 let i = *var as usize;
7232 if i < def_block.len() && def_block[i] == u32::MAX {
7233 def_block[i] = cur;
7234 }
7235 }
7236 fn scoped(c: &Stmt, next: &mut u32, def_block: &mut [u32], sizes: &mut Vec<u32>) {
7237 let id = *next;
7238 *next += 1;
7239 sizes.push(0);
7240 scope_pass_a(c, id, next, def_block, sizes);
7241 sizes[id as usize] = *next - id;
7242 }
7243 match st {
7244 Stmt::Block(v) => {
7245 for x in v {
7246 scope_pass_a(x, cur, next, def_block, sizes);
7247 }
7248 }
7249 Stmt::If {
7250 then_stmt,
7251 else_stmt,
7252 ..
7253 } => {
7254 scoped(then_stmt, next, def_block, sizes);
7255 if let Some(e) = else_stmt {
7256 scoped(e, next, def_block, sizes);
7257 }
7258 }
7259 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => {
7260 scoped(body, next, def_block, sizes)
7261 }
7262 Stmt::For { init, body, .. } => {
7263 let id = *next;
7266 *next += 1;
7267 sizes.push(0);
7268 for x in init {
7269 scope_pass_a(x, id, next, def_block, sizes);
7270 }
7271 scoped(body, next, def_block, sizes);
7272 sizes[id as usize] = *next - id;
7273 }
7274 Stmt::ForEach { body, .. } => scoped(body, next, def_block, sizes),
7275 Stmt::Switch { cases, default, .. } => {
7276 for c in cases {
7277 let id = *next;
7282 *next += 1;
7283 sizes.push(0);
7284 for x in &c.body {
7285 if matches!(x, Stmt::Block(_)) {
7286 scoped(x, next, def_block, sizes);
7287 } else {
7288 scope_pass_a(x, id, next, def_block, sizes);
7289 }
7290 }
7291 sizes[id as usize] = *next - id;
7292 }
7293 if let Some(d) = default {
7294 scoped(d, next, def_block, sizes);
7295 }
7296 }
7297 Stmt::Try {
7298 body,
7299 catches,
7300 finally,
7301 } => {
7302 scoped(body, next, def_block, sizes);
7303 for c in catches {
7304 scoped(&c.body, next, def_block, sizes);
7305 }
7306 if let Some(f) = finally {
7307 scoped(f, next, def_block, sizes);
7308 }
7309 }
7310 Stmt::TryWithResources {
7311 resources,
7312 body,
7313 catches,
7314 finally,
7315 } => {
7316 let id = *next;
7318 *next += 1;
7319 sizes.push(0);
7320 for r in resources {
7321 scope_pass_a(r, id, next, def_block, sizes);
7322 }
7323 scoped(body, next, def_block, sizes);
7324 for c in catches {
7325 scoped(&c.body, next, def_block, sizes);
7326 }
7327 if let Some(f) = finally {
7328 scoped(f, next, def_block, sizes);
7329 }
7330 sizes[id as usize] = *next - id;
7331 }
7332 Stmt::Synchronized { body, .. } => scoped(body, next, def_block, sizes),
7333 Stmt::Labeled { body, .. } => scope_pass_a(body, cur, next, def_block, sizes),
7334 _ => {}
7335 }
7336}
7337
7338fn scope_pass_b(
7343 st: &Stmt,
7344 cur: u32,
7345 next: &mut u32,
7346 def_block: &[u32],
7347 sizes: &[u32],
7348 within: &mut [u32],
7349 total: &mut [u32],
7350) {
7351 fn cnt(
7352 v: u32,
7353 at: u32,
7354 def_block: &[u32],
7355 sizes: &[u32],
7356 within: &mut [u32],
7357 total: &mut [u32],
7358 ) {
7359 let i = v as usize;
7360 if i < total.len() {
7361 total[i] += 1;
7362 let d = def_block[i];
7363 if d != u32::MAX && d <= at && at < d + sizes[d as usize] {
7364 within[i] += 1;
7365 }
7366 }
7367 }
7368 fn scoped(
7369 c: &Stmt,
7370 next: &mut u32,
7371 def_block: &[u32],
7372 sizes: &[u32],
7373 within: &mut [u32],
7374 total: &mut [u32],
7375 ) {
7376 let id = *next;
7377 *next += 1;
7378 scope_pass_b(c, id, next, def_block, sizes, within, total);
7379 }
7380 fn ex_at(
7381 e: &Expr,
7382 at: u32,
7383 def_block: &[u32],
7384 sizes: &[u32],
7385 within: &mut [u32],
7386 total: &mut [u32],
7387 ) {
7388 visit_exprs(e, &mut |x| {
7389 if let Expr::Local { var, .. } = x {
7390 cnt(*var, at, def_block, sizes, within, total);
7391 }
7392 });
7393 }
7394 match st {
7395 Stmt::For {
7396 init,
7397 cond,
7398 update,
7399 body,
7400 } => {
7401 let id = *next;
7402 *next += 1;
7403 for x in init {
7404 scope_pass_b(x, id, next, def_block, sizes, within, total);
7405 }
7406 if let Some(c) = cond {
7407 ex_at(c, id, def_block, sizes, within, total);
7408 }
7409 for u in update {
7410 ex_at(u, id, def_block, sizes, within, total);
7411 }
7412 scoped(body, next, def_block, sizes, within, total);
7413 }
7414 Stmt::TryWithResources {
7415 resources,
7416 body,
7417 catches,
7418 finally,
7419 } => {
7420 let id = *next;
7421 *next += 1;
7422 for r in resources {
7423 scope_pass_b(r, id, next, def_block, sizes, within, total);
7424 }
7425 scoped(body, next, def_block, sizes, within, total);
7426 for c in catches {
7427 scoped(&c.body, next, def_block, sizes, within, total);
7428 }
7429 if let Some(f) = finally {
7430 scoped(f, next, def_block, sizes, within, total);
7431 }
7432 }
7433 _ => {
7434 stmt_shallow_vars(st, &mut |v| cnt(v, cur, def_block, sizes, within, total));
7435 match st {
7436 Stmt::Block(v) => {
7437 for x in v {
7438 scope_pass_b(x, cur, next, def_block, sizes, within, total);
7439 }
7440 }
7441 Stmt::If {
7442 then_stmt,
7443 else_stmt,
7444 ..
7445 } => {
7446 scoped(then_stmt, next, def_block, sizes, within, total);
7447 if let Some(e) = else_stmt {
7448 scoped(e, next, def_block, sizes, within, total);
7449 }
7450 }
7451 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => {
7452 scoped(body, next, def_block, sizes, within, total)
7453 }
7454 Stmt::ForEach { body, .. } => {
7455 scoped(body, next, def_block, sizes, within, total)
7456 }
7457 Stmt::Switch { cases, default, .. } => {
7458 for c in cases {
7459 let id = *next;
7460 *next += 1;
7461 for x in &c.body {
7462 if matches!(x, Stmt::Block(_)) {
7463 scoped(x, next, def_block, sizes, within, total);
7464 } else {
7465 scope_pass_b(x, id, next, def_block, sizes, within, total);
7466 }
7467 }
7468 }
7469 if let Some(d) = default {
7470 scoped(d, next, def_block, sizes, within, total);
7471 }
7472 }
7473 Stmt::Try {
7474 body,
7475 catches,
7476 finally,
7477 } => {
7478 scoped(body, next, def_block, sizes, within, total);
7479 for c in catches {
7480 scoped(&c.body, next, def_block, sizes, within, total);
7481 }
7482 if let Some(f) = finally {
7483 scoped(f, next, def_block, sizes, within, total);
7484 }
7485 }
7486 Stmt::Synchronized { body, .. } => {
7487 scoped(body, next, def_block, sizes, within, total)
7488 }
7489 Stmt::Labeled { body, .. } => {
7490 scope_pass_b(body, cur, next, def_block, sizes, within, total)
7491 }
7492 _ => {}
7493 }
7494 }
7495 }
7496}
7497
7498pub fn strip_enum_const_inits(s: &mut Stmt, class: &crate::PoolClass) {
7507 let const_fields: jdc_core::FxHashSet<&str> = class
7508 .static_fields
7509 .iter()
7510 .filter(|f| f.access & crate::access::ACC_ENUM != 0)
7511 .map(|f| f.name.as_str())
7512 .collect();
7513 if const_fields.is_empty() {
7514 return;
7515 }
7516 if let Stmt::Block(stmts) = s {
7517 stmts.retain(|st| {
7518 !matches!(
7519 st,
7520 Stmt::ExprStmt(Expr::Assign { target, value, .. })
7521 if matches!(&**target,
7522 Expr::Field { cls, name, is_static: true, .. }
7523 if cls.as_ref() == class.name
7524 && const_fields.contains(&**name))
7525 && matches!(&**value, Expr::New { cls: ncls, .. } if ncls.as_ref() == class.name)
7526 )
7527 });
7528 }
7529}
7530
7531fn contains_this_access(e: &Expr) -> bool {
7540 let mut hit = false;
7541 visit_exprs(e, &mut |x| {
7542 if hit {
7543 return;
7544 }
7545 match x {
7546 Expr::This => hit = true,
7547 Expr::Field { owner, is_static: false, .. } => {
7548 if owner.is_none() || matches!(&**owner.as_ref().unwrap(), Expr::This) {
7549 hit = true;
7550 }
7551 }
7552 Expr::Method { owner, is_static: false, .. } => {
7553 if owner.is_none() || matches!(&**owner.as_ref().unwrap(), Expr::This) {
7554 hit = true;
7555 }
7556 }
7557 _ => {}
7558 }
7559 });
7560 hit
7561}
7562
7563fn var_occurs_in(stmts: &[Stmt], var: u32) -> bool {
7578 stmts.iter().any(|st| {
7579 let mut hit = false;
7580 visit_stmt_exprs_ro(st, &mut |e| {
7581 if !hit {
7582 visit_exprs(e, &mut |x| {
7583 if let Expr::Local { var: v, .. } = x {
7584 if *v == var {
7585 hit = true;
7586 }
7587 }
7588 });
7589 }
7590 });
7591 hit
7592 })
7593}
7594
7595pub struct CtorHelper {
7605 pub name: String,
7606 pub ret: JavaType,
7607 pub params: Vec<(JavaType, String)>,
7608 pub body: Stmt,
7609 pub vt: VarTable,
7610}
7611
7612fn ctor_helpers() -> &'static std::sync::Mutex<std::collections::HashMap<String, Vec<CtorHelper>>> {
7613 static H: std::sync::OnceLock<std::sync::Mutex<std::collections::HashMap<String, Vec<CtorHelper>>>> =
7614 std::sync::OnceLock::new();
7615 H.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::default()))
7616}
7617
7618pub struct CtorRelay {
7623 pub formals: Vec<JavaType>,
7625 pub is_super: bool,
7626}
7627
7628type RelayMap = std::collections::HashMap<String, Vec<(String, CtorRelay)>>;
7629
7630fn ctor_relays() -> &'static std::sync::Mutex<RelayMap> {
7631 static H: std::sync::OnceLock<std::sync::Mutex<RelayMap>> = std::sync::OnceLock::new();
7632 H.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::default()))
7633}
7634
7635pub fn take_ctor_relays(class: &str) -> Vec<(String, CtorRelay)> {
7638 let mut m = ctor_relays().lock().unwrap_or_else(|e| e.into_inner());
7639 let mut v = m.remove(class).unwrap_or_default();
7640 v.sort_by(|a, b| a.0.cmp(&b.0));
7641 v
7642}
7643
7644fn array_init(args: Vec<Expr>) -> Expr {
7645 Expr::NewArray {
7646 elem: TypeRef::J(JavaType::Object(std::sync::Arc::from("java/lang/Object"))),
7647 dims: Vec::new(),
7648 trailing_dims: 0,
7649 init: Some(args),
7650 }
7651}
7652
7653fn helper_name(class: &str, desc: &str) -> String {
7656 let mut h: u64 = 0xcbf29ce484222325;
7657 for b in class.as_bytes().iter().chain(b"::").chain(desc.as_bytes()) {
7658 h ^= *b as u64;
7659 h = h.wrapping_mul(0x100000001b3);
7660 }
7661 format!("resolve${:x}", (h >> 16) & 0xffff_ffff)
7662}
7663
7664pub fn take_ctor_helpers(class: &str) -> Vec<CtorHelper> {
7667 let mut m = ctor_helpers().lock().unwrap_or_else(|e| e.into_inner());
7668 let mut v = m.remove(class).unwrap_or_default();
7669 v.sort_by(|a, b| a.name.cmp(&b.name));
7670 v
7671}
7672
7673fn strip_delegations_deep(stmts: &mut Vec<Stmt>) {
7674 stmts.retain(|s| !is_bare_ctor_call(s));
7675 for s in stmts.iter_mut() {
7676 strip_delegation_one(s);
7677 }
7678}
7679
7680fn strip_delegation_one(st: &mut Stmt) {
7681 if is_bare_ctor_call(st) {
7682 *st = Stmt::Block(Vec::new());
7683 return;
7684 }
7685 match st {
7686 Stmt::Block(v) => strip_delegations_deep(v),
7687 Stmt::If { then_stmt, else_stmt, .. } => {
7688 strip_delegation_one(then_stmt);
7689 if let Some(e) = else_stmt {
7690 strip_delegation_one(e);
7691 }
7692 }
7693 Stmt::While { body, .. }
7694 | Stmt::DoWhile { body, .. }
7695 | Stmt::ForEach { body, .. }
7696 | Stmt::Synchronized { body, .. }
7697 | Stmt::Labeled { body, .. } => strip_delegation_one(body),
7698 Stmt::For { init, body, .. } => {
7699 strip_delegations_deep(init);
7700 strip_delegation_one(body);
7701 }
7702 Stmt::Switch { cases, default, .. } => {
7703 for c in cases.iter_mut() {
7704 strip_delegations_deep(&mut c.body);
7705 }
7706 if let Some(d) = default {
7707 strip_delegation_one(d);
7708 }
7709 }
7710 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
7711 strip_delegation_one(body);
7712 for c in catches.iter_mut() {
7713 strip_delegation_one(&mut c.body);
7714 }
7715 if let Some(f) = finally {
7716 strip_delegation_one(f);
7717 }
7718 }
7719 _ => {}
7720 }
7721}
7722
7723#[allow(clippy::ptr_arg)]
7724fn rewrite_bare_returns(stmts: &mut Vec<Stmt>, val: &Expr) {
7725 for s in stmts.iter_mut() {
7726 rewrite_bare_returns_one(s, val);
7727 }
7728}
7729
7730fn rewrite_bare_returns_one(st: &mut Stmt, val: &Expr) {
7731 match st {
7732 Stmt::Return(None) => *st = Stmt::Return(Some(val.clone())),
7733 Stmt::Block(v) => rewrite_bare_returns(v, val),
7734 Stmt::If { then_stmt, else_stmt, .. } => {
7735 rewrite_bare_returns_one(then_stmt, val);
7736 if let Some(e) = else_stmt {
7737 rewrite_bare_returns_one(e, val);
7738 }
7739 }
7740 Stmt::While { body, .. }
7741 | Stmt::DoWhile { body, .. }
7742 | Stmt::ForEach { body, .. }
7743 | Stmt::Synchronized { body, .. }
7744 | Stmt::Labeled { body, .. } => rewrite_bare_returns_one(body, val),
7745 Stmt::For { init, body, .. } => {
7746 rewrite_bare_returns(init, val);
7747 rewrite_bare_returns_one(body, val);
7748 }
7749 Stmt::Switch { cases, default, .. } => {
7750 for c in cases.iter_mut() {
7751 rewrite_bare_returns(&mut c.body, val);
7752 }
7753 if let Some(d) = default {
7754 rewrite_bare_returns_one(d, val);
7755 }
7756 }
7757 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
7758 rewrite_bare_returns_one(body, val);
7759 for c in catches.iter_mut() {
7760 rewrite_bare_returns_one(&mut c.body, val);
7761 }
7762 if let Some(f) = finally {
7763 rewrite_bare_returns_one(f, val);
7764 }
7765 }
7766 _ => {}
7767 }
7768}
7769
7770fn collect_def_vars(stmts: &[Stmt], out: &mut jdc_core::FxHashSet<u32>) {
7771 for st in stmts {
7772 match st {
7773 Stmt::LocalDef { var, .. } => {
7774 out.insert(*var);
7775 }
7776 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
7777 if let Expr::Local { var, .. } = &**target {
7778 out.insert(*var);
7779 }
7780 }
7781 Stmt::Block(v) => collect_def_vars(v, out),
7782 Stmt::If { then_stmt, else_stmt, .. } => {
7783 collect_def_vars(std::slice::from_ref(then_stmt.as_ref()), out);
7784 if let Some(e) = else_stmt {
7785 collect_def_vars(std::slice::from_ref(e.as_ref()), out);
7786 }
7787 }
7788 Stmt::While { body, .. }
7789 | Stmt::DoWhile { body, .. }
7790 | Stmt::Synchronized { body, .. }
7791 | Stmt::Labeled { body, .. } => {
7792 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
7793 }
7794 Stmt::ForEach { var, body, .. } => {
7795 out.insert(*var);
7796 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
7797 }
7798 Stmt::For { init, body, .. } => {
7799 collect_def_vars(init, out);
7800 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
7801 }
7802 Stmt::Switch { cases, default, .. } => {
7803 for c in cases {
7804 collect_def_vars(&c.body, out);
7805 }
7806 if let Some(d) = default {
7807 collect_def_vars(std::slice::from_ref(d.as_ref()), out);
7808 }
7809 }
7810 Stmt::Try { body, catches, finally }
7811 | Stmt::TryWithResources { body, catches, finally, .. } => {
7812 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
7813 for c in catches {
7814 out.insert(c.var);
7815 collect_def_vars(std::slice::from_ref(c.body.as_ref()), out);
7816 }
7817 if let Some(f) = finally {
7818 collect_def_vars(std::slice::from_ref(f.as_ref()), out);
7819 }
7820 }
7821 _ => {}
7822 }
7823 }
7824}
7825
7826fn extraction_shape_ok(stmts: &[Stmt]) -> bool {
7829 stmts.iter().all(|st| match st {
7830 Stmt::Block(v) => extraction_shape_ok(v),
7831 Stmt::LocalDef { .. }
7832 | Stmt::ExprStmt(_)
7833 | Stmt::Return(_)
7834 | Stmt::Throw(_)
7835 | Stmt::Assert { .. }
7836 | Stmt::Break(_)
7837 | Stmt::Continue(_) => true,
7838 Stmt::If { then_stmt, else_stmt, .. } => {
7839 ok_one(then_stmt)
7840 && else_stmt.as_deref().map(ok_one).unwrap_or(true)
7841 }
7842 Stmt::While { body, .. }
7843 | Stmt::DoWhile { body, .. }
7844 | Stmt::ForEach { body, .. }
7845 | Stmt::Synchronized { body, .. } => ok_one(body),
7846 Stmt::Labeled { body, .. } => ok_one(body),
7847 Stmt::For { init, body, .. } => extraction_shape_ok(init) && ok_one(body),
7848 Stmt::Switch { cases, default, .. } => {
7849 cases.iter().all(|c| extraction_shape_ok(&c.body))
7850 && default.as_deref().map(ok_one).unwrap_or(true)
7851 }
7852 Stmt::Try { body, catches, finally }
7853 | Stmt::TryWithResources { body, catches, finally, .. } => {
7854 ok_one(body)
7855 && catches.iter().all(|c| ok_one(&c.body))
7856 && finally.as_deref().map(ok_one).unwrap_or(true)
7857 }
7858 _ => false,
7861 })
7862}
7863
7864fn ok_one(st: &Stmt) -> bool {
7865 extraction_shape_ok(std::slice::from_ref(st))
7866}
7867
7868fn static_safe(stmts: &[Stmt], this_id: Option<u32>) -> bool {
7869 let mut ok = true;
7870 for st in stmts {
7871 visit_stmt_exprs_ro(st, &mut |e| {
7872 visit_exprs(e, &mut |x| {
7873 match x {
7874 Expr::This => ok = false,
7875 Expr::Local { var, .. } => {
7881 if Some(*var) == this_id {
7882 ok = false;
7883 }
7884 }
7885 Expr::Field { owner: None, is_static: false, .. } => ok = false,
7887 Expr::Method { owner: None, is_static: false, .. } => ok = false,
7888 _ => {}
7889 }
7890 });
7891 });
7892 }
7893 ok
7894}
7895
7896pub fn strip_clinit_hotfix_guard(body: &mut Stmt) {
7907 if let Stmt::Block(vs) = body {
7911 flatten_top_blocks(vs);
7912 }
7913 let Stmt::Block(stmts) = body else { return };
7914 let mut gi = 0usize;
7917 while gi < stmts.len() && matches!(&stmts[gi], Stmt::LocalDef { init: None, .. }) {
7918 gi += 1;
7919 }
7920 let Some(first) = stmts.get(gi) else { return };
7921 let Stmt::If { then_stmt, else_stmt, .. } = first else {
7922 return;
7923 };
7924 if else_stmt.is_some() {
7925 return;
7926 }
7927 let mut has_ret = false;
7928 let mut has_proxy = false;
7929 let c = then_stmt.as_ref();
7930 walk_all(c, &mut |st| {
7931 if matches!(st, Stmt::Return(None)) {
7932 has_ret = true;
7933 }
7934 });
7935 let mut cc = c.clone();
7936 walk_stmt_exprs(&mut cc, &mut |e| {
7937 deep_rewrite(e, &mut |x| {
7938 if let Expr::Method { cls, .. } = x {
7939 if cls.contains("PatchProxy") {
7940 has_proxy = true;
7941 }
7942 }
7943 });
7944 });
7945 if has_ret && has_proxy {
7946 stmts.remove(gi);
7947 }
7948}
7949
7950pub(crate) fn first_leaf_stmt(s: &Stmt) -> &Stmt {
7971 match s {
7972 Stmt::Block(v) if !v.is_empty() => first_leaf_stmt(&v[0]),
7973 other => other,
7974 }
7975}
7976
7977pub fn strip_ctor_hotfix_guards(body: &mut Stmt) {
7996 fn marker_hit(e: &Expr) -> bool {
7997 let has = |n: &str| {
7998 let l = n.to_ascii_lowercase();
7999 l.contains("hotfix")
8000 || l.contains("instantrun")
8001 || l.contains("robust")
8002 || l.contains("titan")
8003 || l.contains("patchproxy")
8004 || l.contains("changequickredirect")
8005 || l.contains("constructorcode")
8006 };
8007 let mut hit = false;
8008 visit_exprs(e, &mut |x| {
8009 match x {
8010 Expr::Method { cls, .. } | Expr::New { cls, .. } | Expr::Field { cls, .. } => {
8011 if has(cls) {
8012 hit = true;
8013 }
8014 }
8015 _ => {}
8016 }
8017 if let Expr::Field { ty, .. } = x {
8018 if let TypeRef::J(JavaType::Object(n)) = ty {
8019 if has(n) {
8020 hit = true;
8021 }
8022 }
8023 }
8024 });
8025 hit
8026 }
8027 fn is_guard(st: &Stmt) -> bool {
8031 let mut cur = st;
8032 loop {
8033 match cur {
8034 Stmt::Block(v) if v.len() == 1 => cur = &v[0],
8035 Stmt::If { .. } => break,
8036 _ => return false,
8037 }
8038 }
8039 let mut has_del = false;
8040 walk_all(cur, &mut |s2| {
8041 if is_bare_ctor_call(s2) {
8042 has_del = true;
8043 }
8044 });
8045 if !has_del {
8046 return false;
8047 }
8048 let mut has_marker = false;
8049 visit_stmt_exprs_ro(cur, &mut |e| {
8050 if marker_hit(e) {
8051 has_marker = true;
8052 }
8053 });
8054 has_marker
8055 }
8056 fn strip_in(stmts: &mut Vec<Stmt>) {
8067 stmts.retain(|st| !is_guard(st));
8068 for st in stmts.iter_mut() {
8069 if let Stmt::Block(v) = st {
8070 strip_in(v);
8071 }
8072 }
8073 }
8074 let Stmt::Block(stmts) = body else { return };
8075 strip_in(stmts);
8076}
8077
8078pub fn hoist_branch_delegations(body: &mut Stmt, vt: &VarTable) {
8091 let Stmt::Block(stmts) = body else { return };
8092 if stmts.first().is_some_and(is_bare_ctor_call) {
8093 return; }
8095 let mut dels: Vec<Expr> = Vec::new();
8096 for st in stmts.iter() {
8097 visit_stmt_exprs_ro(st, &mut |e| {
8098 if is_delegation_expr(e) {
8099 dels.push(e.clone());
8100 }
8101 });
8102 }
8103 if dels.is_empty() {
8104 return;
8105 }
8106 if !dels.iter().all(|d| *d == dels[0]) {
8107 return;
8108 }
8109 let mut bad = false;
8110 visit_exprs(&dels[0], &mut |x| {
8111 if let Expr::Local { var, .. } = x {
8112 if (*var as usize) >= vt.vars.len() || !vt.vars[*var as usize].is_param {
8113 bad = true;
8114 }
8115 }
8116 });
8117 if bad {
8118 return;
8119 }
8120 let del = dels[0].clone();
8121 strip_delegations_deep(stmts);
8122 stmts.insert(0, Stmt::ExprStmt(del));
8123}
8124
8125pub fn extract_branched_delegation_helper(
8159 body: &mut Stmt,
8160 vt: &VarTable,
8161 pool: &DexPool,
8162 class: &crate::PoolClass,
8163 desc_str: &str,
8164) {
8165 if class.is_enum() {
8166 return;
8167 }
8168 if class.nesting.enclosing_class.is_some() && !crate::ctx::nested_is_static(pool, class) {
8173 return;
8174 }
8175 let Stmt::Block(stmts) = body else { return };
8176 let mut dels: Vec<Expr> = Vec::new();
8177 for st in stmts.iter() {
8178 visit_stmt_exprs_ro(st, &mut |e| {
8179 if is_delegation_expr(e) {
8180 dels.push(e.clone());
8181 }
8182 });
8183 }
8184 if dels.is_empty() {
8185 return;
8186 }
8187 if dels.len() == 1 && stmts.first().is_some_and(is_bare_ctor_call) {
8188 return; }
8190 let template = dels[0].clone();
8191 let Expr::Method { args: t_args, .. } = &template else {
8192 return;
8193 };
8194 enum Mode {
8198 Single(u32),
8199 Position(usize),
8200 Full,
8207 }
8208 let full_gate = (|| -> Option<()> {
8215 let Expr::Method {
8216 args: a0,
8217 desc: d0,
8218 is_super: s0,
8219 cls: c0,
8220 ..
8221 } = &dels[0]
8222 else {
8223 return None;
8224 };
8225 if a0.is_empty() {
8226 return None;
8227 }
8228 for d in &dels[1..] {
8229 match d {
8230 Expr::Method {
8231 args,
8232 desc,
8233 is_super,
8234 cls,
8235 ..
8236 } if args.len() == a0.len()
8237 && **desc == **d0
8238 && is_super == s0
8239 && cls == c0 => {}
8240 _ => return None,
8241 }
8242 }
8243 if class
8244 .all_methods()
8245 .any(|m| &*m.name == "<init>" && &*m.desc == "([Ljava/lang/Object;)V")
8246 {
8247 return None;
8248 }
8249 Some(())
8250 })()
8251 .is_some();
8252 let mode = if dels.iter().all(|d| *d == dels[0]) {
8253 let mut carrier: Option<u32> = None;
8254 let mut reads = 0usize;
8255 let mut multi = false;
8256 for a in t_args.iter() {
8257 let c = a.clone();
8258 visit_exprs(&c, &mut |x| {
8259 if let Expr::Local { var, .. } = x {
8260 if (*var as usize) < vt.vars.len() && !vt.vars[*var as usize].is_param {
8261 reads += 1;
8262 match carrier {
8263 Some(v) if v == *var => {}
8264 Some(_) => multi = true,
8265 None => carrier = Some(*var),
8266 }
8267 }
8268 }
8269 });
8270 }
8271 match carrier {
8272 Some(cv) if !multi && reads == 1 => Mode::Single(cv),
8273 _ if full_gate => Mode::Full,
8274 _ => return,
8275 }
8276 } else {
8277 let positioned: Option<Mode> = (|| {
8278 let arity = t_args.len();
8279 if dels
8283 .iter()
8284 .any(|d| !matches!(d, Expr::Method { args, .. } if args.len() == arity))
8285 {
8286 return None;
8287 }
8288 let mut diff: Vec<usize> = Vec::new();
8289 for j in 0..arity {
8290 if dels.iter().any(|d| match d {
8291 Expr::Method { args, .. } => args.get(j) != t_args.get(j),
8292 _ => true,
8293 }) {
8294 diff.push(j);
8295 }
8296 }
8297 if diff.len() != 1 {
8298 return None;
8299 }
8300 let k = diff[0];
8301 let mut base0 = dels[0].clone();
8305 if let Expr::Method { args, .. } = &mut base0 {
8306 args[k] = Expr::Const(ConstVal::Null);
8307 }
8308 for d in dels.iter() {
8309 let mut c = d.clone();
8310 if let Expr::Method { args, .. } = &mut c {
8311 args[k] = Expr::Const(ConstVal::Null);
8312 }
8313 if c != base0 {
8314 return None;
8315 }
8316 }
8317 let mut carrier_ty: Option<JavaType> = None;
8320 for d in dels.iter() {
8321 let Expr::Method { args, .. } = d else {
8322 return None;
8323 };
8324 let Expr::Local { var, .. } = &args[k] else {
8325 return None;
8326 };
8327 if (*var as usize) >= vt.vars.len() || vt.vars[*var as usize].is_param {
8328 return None;
8329 }
8330 let ty = vt.vars[*var as usize].ty.erased();
8331 match &carrier_ty {
8332 Some(t) if *t == ty => {}
8333 Some(_) => return None,
8334 None => carrier_ty = Some(ty),
8335 }
8336 for (j, a) in args.iter().enumerate() {
8342 if j == k {
8343 continue;
8344 }
8345 let mut bad = false;
8346 visit_exprs(a, &mut |x| {
8347 if let Expr::Local { var: v2, .. } = x {
8348 if (*v2 as usize) >= vt.vars.len() || !vt.vars[*v2 as usize].is_param {
8349 bad = true;
8350 }
8351 }
8352 });
8353 if bad {
8354 return None;
8355 }
8356 }
8357 }
8358 Some(Mode::Position(k))
8359 })();
8360 match positioned {
8361 Some(m) => m,
8362 None if full_gate => Mode::Full,
8363 None => return,
8364 }
8365 };
8366 let l = stmts.iter().rposition(is_bare_ctor_call);
8367 if l == Some(0) {
8368 return;
8369 }
8370 let (mut extracted, mut tail): (Vec<Stmt>, Vec<Stmt>) = match l {
8371 Some(l) => (stmts[..l].to_vec(), stmts[l + 1..].to_vec()),
8372 None => (stmts.clone(), Vec::new()),
8373 };
8374 if !extraction_shape_ok(&extracted) {
8375 return;
8376 }
8377 let mut site_tails: Vec<Vec<Stmt>> = Vec::new();
8384 {
8385 let mut probe = extracted.clone();
8386 site_tails_walk(&mut probe, &mut site_tails, false);
8387 }
8388 let hoist = !site_tails.is_empty() && {
8389 let n0 = normalize_tail(&site_tails[0]);
8390 site_tails.iter().all(|t| normalize_tail(t) == n0)
8391 && (l.is_none() || normalize_tail(&tail) == n0)
8392 };
8393 if hoist {
8394 site_tails_walk(&mut extracted, &mut Vec::new(), true);
8395 if l.is_none() {
8396 tail = normalize_tail(&site_tails[0]);
8397 }
8398 }
8399 let primary_del: Expr = match l {
8402 Some(l) => match &stmts[l] {
8403 Stmt::ExprStmt(e) => e.clone(),
8404 _ => return,
8405 },
8406 None => dels[0].clone(),
8407 };
8408 let ret = match mode {
8409 Mode::Single(cv) => vt.var(cv).ty.erased(),
8410 Mode::Position(k) => match &primary_del {
8411 Expr::Method { args, .. } => match args.get(k) {
8412 Some(Expr::Local { var, .. }) => vt.var(*var).ty.erased(),
8413 _ => return,
8414 },
8415 _ => return,
8416 },
8417 Mode::Full => JavaType::Array(Box::new(JavaType::Object(std::sync::Arc::from(
8418 "java/lang/Object",
8419 )))),
8420 };
8421 match mode {
8422 Mode::Single(cv) => {
8423 strip_delegations_deep(&mut extracted);
8424 let cv_local = Expr::Local {
8425 var: cv,
8426 ty: vt.var(cv).ty.clone(),
8427 };
8428 rewrite_bare_returns(&mut extracted, &cv_local);
8429 if !always_returns(&extracted) {
8430 extracted.push(Stmt::Return(Some(cv_local)));
8431 }
8432 }
8433 Mode::Position(k) => {
8434 fill_returns_by_position(&mut extracted, &|args: &[Expr]| args.get(k).cloned());
8438 let fallback = match &primary_del {
8439 Expr::Method { args, .. } => args[k].clone(),
8440 _ => return,
8441 };
8442 rewrite_bare_returns(&mut extracted, &fallback);
8443 if !always_returns(&extracted) {
8444 extracted.push(Stmt::Return(Some(fallback)));
8445 }
8446 }
8447 Mode::Full => {
8448 fill_returns_by_position(&mut extracted, &|args: &[Expr]| {
8455 Some(array_init(args.to_vec()))
8456 });
8457 if l.is_none() {
8458 if !always_returns(&extracted) {
8465 let cut = (1..=extracted.len())
8466 .find(|&i| always_returns(&extracted[..i]));
8467 match cut {
8468 Some(i) => extracted.truncate(i),
8469 None => return,
8470 }
8471 }
8472 }
8473 let fallback_args = match &primary_del {
8474 Expr::Method { args, .. } => args.clone(),
8475 _ => return,
8476 };
8477 let fallback = array_init(fallback_args);
8478 rewrite_bare_returns(&mut extracted, &fallback);
8479 if !always_returns(&extracted) {
8480 extracted.push(Stmt::Return(Some(fallback)));
8481 }
8482 }
8483 }
8484 let this_id = vt
8485 .vars
8486 .iter()
8487 .find(|v| v.is_param && v.name == "this")
8488 .map(|v| v.id);
8489 if !static_safe(&extracted, this_id) {
8490 return;
8491 }
8492 let mut defs: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
8493 collect_def_vars(&extracted, &mut defs);
8494 if defs.iter().any(|v| var_occurs_in(&tail, *v)) {
8495 return;
8496 }
8497 let params: Vec<(u32, JavaType, String)> = vt
8498 .vars
8499 .iter()
8500 .filter(|v| v.is_param && v.name != "this")
8501 .map(|v| (v.id, v.ty.erased(), v.name.clone()))
8502 .collect();
8503 let name = helper_name(&class.name, desc_str);
8504 let call = Expr::Method {
8505 owner: None,
8506 cls: std::sync::Arc::from(class.name.as_str()),
8507 name: std::sync::Arc::from(name.as_str()),
8508 desc: std::sync::Arc::new(jdc_core::types::MethodDescriptor {
8509 ret: ret.clone(),
8510 args: params.iter().map(|p| p.1.clone()).collect(),
8511 }),
8512 args: params
8513 .iter()
8514 .map(|(id, ty, _)| Expr::Local {
8515 var: *id,
8516 ty: TypeRef::J(ty.clone()),
8517 })
8518 .collect(),
8519 is_static: true,
8520 is_interface: false,
8521 is_special: false,
8522 is_super: false,
8523 is_dynamic: false,
8524 type_args: Vec::new(),
8525 };
8526 let mut new_del = primary_del.clone();
8527 match mode {
8528 Mode::Single(cv) => {
8529 deep_rewrite(&mut new_del, &mut |x| {
8530 if let Expr::Local { var, .. } = x {
8531 if *var == cv {
8532 *x = call.clone();
8533 }
8534 }
8535 });
8536 }
8537 Mode::Position(k) => {
8538 let Expr::Method { args, .. } = &mut new_del else {
8539 return;
8540 };
8541 if k >= args.len() {
8542 return;
8543 }
8544 args[k] = call.clone();
8545 }
8546 Mode::Full => {
8547 new_del = Expr::Method {
8551 owner: None,
8552 cls: std::sync::Arc::from(class.name.as_str()),
8553 name: "<init>".into(),
8554 desc: std::sync::Arc::new(jdc_core::types::MethodDescriptor {
8555 args: vec![JavaType::Array(Box::new(JavaType::Object(
8556 std::sync::Arc::from("java/lang/Object"),
8557 )))],
8558 ret: JavaType::Void,
8559 }),
8560 args: vec![call.clone()],
8561 is_static: false,
8562 is_interface: false,
8563 is_special: true,
8564 is_super: false,
8565 is_dynamic: false,
8566 type_args: Vec::new(),
8567 };
8568 }
8569 }
8570 {
8571 let mut m = ctor_helpers().lock().unwrap_or_else(|e| e.into_inner());
8572 let v = m.entry(class.name.clone()).or_default();
8573 if !v.iter().any(|h| h.name == name) {
8574 let mut hbody = Stmt::Block(extracted);
8578 invert_empty_thens(&mut hbody);
8579 v.push(CtorHelper {
8580 name,
8581 ret: ret.clone(),
8582 params: params.into_iter().map(|(_, ty, nm)| (ty, nm)).collect(),
8583 body: hbody,
8584 vt: vt.clone(),
8585 });
8586 if matches!(mode, Mode::Full) {
8587 let Expr::Method {
8588 desc: d0,
8589 is_super: s0,
8590 ..
8591 } = &primary_del
8592 else {
8593 return;
8594 };
8595 let mut rm = ctor_relays().lock().unwrap_or_else(|e| e.into_inner());
8596 let rv = rm.entry(class.name.clone()).or_default();
8597 if !rv.iter().any(|(d, _)| d == desc_str) {
8598 rv.push((
8599 desc_str.to_string(),
8600 CtorRelay {
8601 formals: d0.args.clone(),
8602 is_super: *s0,
8603 },
8604 ));
8605 }
8606 }
8607 } else {
8608 return; }
8610 }
8611 *stmts = std::iter::once(Stmt::ExprStmt(new_del)).chain(tail).collect();
8612}
8613
8614fn fill_returns_by_position(
8621 stmts: &mut Vec<Stmt>,
8622 pick: &dyn Fn(&[Expr]) -> Option<Expr>,
8623) {
8624 let mut i = 0;
8625 while i < stmts.len() {
8626 if is_bare_ctor_call(&stmts[i]) {
8627 let karg = match &stmts[i] {
8628 Stmt::ExprStmt(Expr::Method { args, .. }) => pick(args),
8629 _ => None,
8630 };
8631 if let Some(v) = karg {
8632 let mut j = i + 1;
8633 let mut found = None;
8634 while j < stmts.len() {
8635 if matches!(stmts[j], Stmt::Return(None)) {
8636 found = Some(j);
8637 break;
8638 }
8639 if is_bare_ctor_call(&stmts[j]) {
8640 break; }
8642 j += 1;
8643 }
8644 match found {
8645 Some(j) => {
8646 stmts[j] = Stmt::Return(Some(v));
8647 stmts.remove(i);
8648 continue;
8649 }
8650 None => {
8651 stmts[i] = Stmt::Return(Some(v));
8652 stmts.truncate(i + 1);
8653 break;
8654 }
8655 }
8656 }
8657 }
8658 fill_returns_one(&mut stmts[i], pick);
8659 i += 1;
8660 }
8661}
8662
8663fn fill_returns_one(st: &mut Stmt, pick: &dyn Fn(&[Expr]) -> Option<Expr>) {
8664 match st {
8665 Stmt::Block(v) => fill_returns_by_position(v, pick),
8666 Stmt::If { then_stmt, else_stmt, .. } => {
8667 fill_returns_stmt(then_stmt, pick);
8668 if let Some(e) = else_stmt {
8669 fill_returns_stmt(e, pick);
8670 }
8671 }
8672 Stmt::While { body, .. }
8673 | Stmt::DoWhile { body, .. }
8674 | Stmt::ForEach { body, .. }
8675 | Stmt::Synchronized { body, .. }
8676 | Stmt::Labeled { body, .. } => fill_returns_stmt(body, pick),
8677 Stmt::For { init, body, .. } => {
8678 fill_returns_by_position(init, pick);
8679 fill_returns_stmt(body, pick);
8680 }
8681 Stmt::Switch { cases, default, .. } => {
8682 for c in cases.iter_mut() {
8683 fill_returns_by_position(&mut c.body, pick);
8684 }
8685 if let Some(d) = default {
8686 fill_returns_stmt(d, pick);
8687 }
8688 }
8689 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
8690 fill_returns_stmt(body, pick);
8691 for c in catches.iter_mut() {
8692 fill_returns_stmt(&mut c.body, pick);
8693 }
8694 if let Some(f) = finally {
8695 fill_returns_stmt(f, pick);
8696 }
8697 }
8698 _ => {}
8699 }
8700}
8701
8702fn fill_returns_stmt(st: &mut Stmt, pick: &dyn Fn(&[Expr]) -> Option<Expr>) {
8703 if is_bare_ctor_call(st) {
8704 if let Stmt::ExprStmt(Expr::Method { args, .. }) = st {
8707 if let Some(v) = pick(args) {
8708 *st = Stmt::Return(Some(v));
8709 return;
8710 }
8711 }
8712 }
8713 fill_returns_one(st, pick);
8714}
8715
8716fn normalize_tail(t: &[Stmt]) -> Vec<Stmt> {
8717 let mut v = t.to_vec();
8718 while matches!(v.last(), Some(Stmt::Return(None))) {
8719 v.pop();
8720 }
8721 v
8722}
8723
8724fn site_tails_walk(stmts: &mut Vec<Stmt>, out: &mut Vec<Vec<Stmt>>, carve: bool) {
8729 let mut i = 0;
8730 while i < stmts.len() {
8731 if is_bare_ctor_call(&stmts[i]) {
8732 let mut j = i + 1;
8733 let mut end = stmts.len();
8734 while j < stmts.len() {
8735 if matches!(stmts[j], Stmt::Return(None)) {
8736 end = j;
8737 break;
8738 }
8739 if is_bare_ctor_call(&stmts[j]) {
8740 end = j;
8741 break;
8742 }
8743 j += 1;
8744 }
8745 out.push(stmts[i + 1..end].to_vec());
8746 if carve {
8747 stmts.splice(i + 1..end, []);
8748 }
8749 i += 1;
8750 continue;
8751 }
8752 site_tails_one(&mut stmts[i], out, carve);
8753 i += 1;
8754 }
8755}
8756
8757fn site_tails_one(st: &mut Stmt, out: &mut Vec<Vec<Stmt>>, carve: bool) {
8758 if is_bare_ctor_call(st) {
8759 out.push(Vec::new());
8760 return;
8761 }
8762 match st {
8763 Stmt::Block(v) => site_tails_walk(v, out, carve),
8764 Stmt::If { then_stmt, else_stmt, .. } => {
8765 site_tails_one(then_stmt, out, carve);
8766 if let Some(e) = else_stmt {
8767 site_tails_one(e, out, carve);
8768 }
8769 }
8770 Stmt::While { body, .. }
8771 | Stmt::DoWhile { body, .. }
8772 | Stmt::ForEach { body, .. }
8773 | Stmt::Synchronized { body, .. }
8774 | Stmt::Labeled { body, .. } => site_tails_one(body, out, carve),
8775 Stmt::For { init, body, .. } => {
8776 site_tails_walk(init, out, carve);
8777 site_tails_one(body, out, carve);
8778 }
8779 Stmt::Switch { cases, default, .. } => {
8780 for c in cases.iter_mut() {
8781 site_tails_walk(&mut c.body, out, carve);
8782 }
8783 if let Some(d) = default {
8784 site_tails_one(d, out, carve);
8785 }
8786 }
8787 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
8788 site_tails_one(body, out, carve);
8789 for c in catches.iter_mut() {
8790 site_tails_one(&mut c.body, out, carve);
8791 }
8792 if let Some(f) = finally {
8793 site_tails_one(f, out, carve);
8794 }
8795 }
8796 _ => {}
8797 }
8798}
8799
8800fn always_returns(stmts: &[Stmt]) -> bool {
8806 let Some(last) = stmts
8807 .iter()
8808 .rev()
8809 .find(|s| !matches!(s, Stmt::Block(v) if v.is_empty()))
8810 else {
8811 return false;
8812 };
8813 match last {
8814 Stmt::Return(Some(_)) | Stmt::Throw(_) => true,
8815 Stmt::Block(v) => always_returns(v),
8816 Stmt::If { then_stmt, else_stmt, .. } => match else_stmt {
8817 Some(e) => always_returns_one(then_stmt) && always_returns_one(e),
8818 None => false,
8819 },
8820 Stmt::Labeled { body, .. } | Stmt::Synchronized { body, .. } => {
8821 always_returns_one(body)
8822 }
8823 Stmt::Switch { cases, default, .. } => {
8824 default.as_ref().is_some_and(|d| always_returns_one(d))
8825 && cases.iter().all(|c| always_returns(&c.body))
8826 }
8827 Stmt::Try { body, catches, finally }
8828 | Stmt::TryWithResources { body, catches, finally, .. } => {
8829 finally.as_ref().is_some_and(|f| always_returns_one(f))
8830 || (always_returns_one(body)
8831 && catches.iter().all(|c| always_returns_one(&c.body)))
8832 }
8833 _ => false,
8834 }
8835}
8836
8837fn always_returns_one(st: &Stmt) -> bool {
8838 always_returns(std::slice::from_ref(st))
8839}
8840
8841fn inline_ctor_arg_defs(
8842 call: &mut Stmt,
8843 prefix: &[&Stmt],
8844 vt: &VarTable,
8845 consumed: &mut Vec<(u32, usize)>,
8846) -> bool {
8847 let Stmt::ExprStmt(cexpr) = call else {
8848 return false;
8849 };
8850 fn subst(e: &mut Expr, v: u32, val: &Option<Expr>) -> bool {
8853 let mut ok = true;
8854 deep_rewrite(e, &mut |x| {
8855 if let Expr::Local { var, .. } = x {
8856 if *var == v {
8857 match val {
8858 Some(e2) => *x = e2.clone(),
8859 None => ok = false,
8860 }
8861 }
8862 }
8863 });
8864 ok
8865 }
8866 let mut seen: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
8867 for _ in 0..32 {
8868 let mut need: Option<u32> = None;
8869 visit_exprs(cexpr, &mut |x| {
8870 if need.is_none() {
8871 if let Expr::Local { var, .. } = x {
8872 if (*var as usize) < vt.vars.len() && !vt.vars[*var as usize].is_param {
8873 need = Some(*var);
8874 }
8875 }
8876 }
8877 });
8878 let Some(v) = need else { break };
8879 if !seen.insert(v) {
8880 return false; }
8882 let mut val: Option<Expr> = None;
8894 let mut ok = true;
8895 for (si, st) in prefix.iter().enumerate() {
8896 match st {
8897 Stmt::LocalDef { var, init: None, .. } if *var == v => {}
8901 Stmt::LocalDef { var, init, .. } if *var == v => {
8902 let Some(e) = init else { ok = false; break };
8903 let mut e2 = (*e).clone();
8904 if !subst(&mut e2, v, &val) {
8905 ok = false;
8906 break;
8907 }
8908 val = Some(e2);
8909 consumed.push((v, si));
8910 }
8911 Stmt::ExprStmt(Expr::Assign { target, op, .. })
8912 if matches!(&**target, Expr::Local { var: tv, .. } if *tv == v) =>
8913 {
8914 if !matches!(op, AssignOp::Plain) {
8915 ok = false; break;
8917 }
8918 let Stmt::ExprStmt(Expr::Assign { value, .. }) = st else {
8919 unreachable!()
8920 };
8921 let mut e2 = value.as_ref().clone();
8922 if !subst(&mut e2, v, &val) {
8923 ok = false;
8924 break;
8925 }
8926 val = Some(e2);
8927 consumed.push((v, si));
8928 }
8929 _ => {
8930 let mut touched = false;
8931 visit_all_exprs(st, &mut |x| {
8932 if let Expr::Local { var, .. } = x {
8933 if *var == v {
8934 touched = true;
8935 }
8936 }
8937 });
8938 if touched {
8939 ok = false;
8940 break;
8941 }
8942 }
8943 }
8944 }
8945 if !ok {
8946 return false;
8947 }
8948 let Some(de) = val else { return false };
8949 if contains_this_access(&de) {
8950 return false;
8951 }
8952 let mut de_nodes = 0usize;
8954 visit_exprs(&de, &mut |_| de_nodes += 1);
8955 if de_nodes > 256 {
8956 return false;
8957 }
8958 deep_rewrite(cexpr, &mut |x| {
8959 if let Expr::Local { var, .. } = x {
8960 if *var == v {
8961 *x = de.clone();
8962 }
8963 }
8964 });
8965 let mut nodes = 0usize;
8970 visit_exprs(cexpr, &mut |_| nodes += 1);
8971 if nodes > 2_048 {
8972 return false;
8973 }
8974 }
8975 true
8976}
8977
8978fn fold_sb_chain(stmts: &mut Vec<Stmt>, pos: usize) -> usize {
8991 let mut sbv: Option<u32> = None;
8993 let mut def_i: Option<usize> = None;
8994 let mut def_init: Option<Expr> = None;
8995 for (i, st) in stmts[..pos].iter().enumerate() {
8996 let cand: Option<(u32, &Expr)> = match st {
8997 Stmt::LocalDef {
8998 var,
8999 init: Some(e),
9000 ..
9001 } => Some((*var, e)),
9002 Stmt::ExprStmt(Expr::Assign {
9003 target,
9004 value,
9005 op: AssignOp::Plain,
9006 ..
9007 }) => match &**target {
9008 Expr::Local { var, .. } => Some((*var, value.as_ref())),
9009 _ => None,
9010 },
9011 _ => None,
9012 };
9013 if let Some((v, init)) = cand {
9014 if let Expr::New { cls, .. } = init {
9015 if cls.as_ref() == "java/lang/StringBuilder" {
9016 sbv = Some(v);
9017 def_i = Some(i);
9018 def_init = Some(init.clone());
9019 break;
9020 }
9021 }
9022 }
9023 }
9024 let (Some(sbv), Some(def_i), Some(init)) = (sbv, def_i, def_init) else {
9025 return pos;
9026 };
9027 if contains_this_access(&init) {
9028 return pos;
9029 }
9030 let mut appends: Vec<usize> = Vec::new();
9032 for (i, st) in stmts[..pos].iter().enumerate() {
9033 if let Stmt::ExprStmt(Expr::Method {
9034 owner: Some(o),
9035 cls,
9036 name,
9037 ..
9038 }) = st
9039 {
9040 if matches!(&**o, Expr::Local { var, .. } if *var == sbv)
9041 && cls.as_ref() == "java/lang/StringBuilder"
9042 && &**name == "append"
9043 {
9044 appends.push(i);
9045 }
9046 }
9047 }
9048 let mut post_read = false;
9050 for st in stmts.iter().skip(pos + 1) {
9051 visit_all_exprs(st, &mut |e| {
9052 if let Expr::Local { var, .. } = e {
9053 if *var == sbv {
9054 post_read = true;
9055 }
9056 }
9057 });
9058 }
9059 if post_read {
9060 return pos;
9061 }
9062 for (i, st) in stmts[..pos].iter().enumerate() {
9065 if i == def_i || appends.contains(&i) {
9066 if let Stmt::ExprStmt(Expr::Method { args, .. }) = st {
9068 if args.iter().any(contains_this_access) {
9069 return pos;
9070 }
9071 }
9072 continue;
9073 }
9074 let mut leak = false;
9075 visit_all_exprs(st, &mut |e| {
9076 if let Expr::Local { var, .. } = e {
9077 if *var == sbv {
9078 leak = true;
9079 }
9080 }
9081 });
9082 if leak {
9083 return pos;
9084 }
9085 }
9086 let mut local_reads = 0usize;
9088 let mut tostring_reads = 0usize;
9089 visit_all_exprs(&stmts[pos], &mut |e| match e {
9090 Expr::Local { var, .. } if *var == sbv => local_reads += 1,
9091 Expr::Method {
9092 owner: Some(o),
9093 name,
9094 args,
9095 ..
9096 } if args.is_empty()
9097 && &**name == "toString"
9098 && matches!(&**o, Expr::Local { var, .. } if *var == sbv) =>
9099 {
9100 tostring_reads += 1;
9101 }
9102 _ => {}
9103 });
9104 if local_reads != 1 || tostring_reads != 1 {
9105 return pos;
9106 }
9107 let mut chain = init;
9109 for ai in &appends {
9110 if let Stmt::ExprStmt(Expr::Method {
9111 owner: _,
9112 cls,
9113 name,
9114 desc,
9115 args,
9116 is_static,
9117 is_interface,
9118 is_special,
9119 is_super,
9120 is_dynamic,
9121 type_args,
9122 ..
9123 }) = &stmts[*ai]
9124 {
9125 chain = Expr::Method {
9126 owner: Some(Box::new(chain)),
9127 cls: cls.clone(),
9128 name: name.clone(),
9129 desc: desc.clone(),
9130 args: args.clone(),
9131 is_static: *is_static,
9132 is_interface: *is_interface,
9133 is_special: *is_special,
9134 is_super: *is_super,
9135 is_dynamic: *is_dynamic,
9136 type_args: type_args.clone(),
9137 };
9138 }
9139 }
9140 let mut replaced = false;
9141 walk_stmt_exprs(&mut stmts[pos], &mut |e| {
9142 deep_rewrite(e, &mut |x| {
9143 if let Expr::Method {
9144 owner: Some(o),
9145 name,
9146 args,
9147 ..
9148 } = x
9149 {
9150 if args.is_empty()
9151 && &**name == "toString"
9152 && matches!(&**o, Expr::Local { var, .. } if *var == sbv)
9153 {
9154 **o = chain.clone();
9155 replaced = true;
9156 }
9157 }
9158 });
9159 });
9160 if !replaced {
9161 return pos; }
9163 let mut removed: Vec<usize> = appends.clone();
9165 removed.push(def_i);
9166 removed.sort_unstable();
9167 removed.dedup();
9168 for i in removed.iter().rev() {
9169 stmts.remove(*i);
9170 }
9171 pos - removed.len()
9172}
9173
9174pub fn fix_ctor_super_first(body: &mut Stmt, vt: &VarTable) {
9175 let Stmt::Block(stmts) = body else { return };
9176 if stmts.is_empty() || is_bare_ctor_call(stmts.first().unwrap()) {
9177 return;
9178 }
9179 if let Some(pos0) = stmts.iter().position(is_bare_ctor_call) {
9181 let pos = fold_sb_chain(stmts, pos0);
9182 let mut call = stmts[pos].clone();
9183 let prefix: Vec<&Stmt> = stmts[..pos].iter().collect();
9184 let mut consumed: Vec<(u32, usize)> = Vec::new();
9185 if !inline_ctor_arg_defs(&mut call, &prefix, vt, &mut consumed) {
9186 return;
9187 }
9188 stmts.remove(pos);
9189 let mut drop_idx: Vec<usize> = consumed
9196 .iter()
9197 .filter(|(v, _)| !var_occurs_in(&stmts[pos..], *v))
9198 .map(|(_, k)| *k)
9199 .collect();
9200 drop_idx.sort_unstable();
9201 drop_idx.dedup();
9202 for k in drop_idx.iter().rev() {
9203 stmts.remove(*k);
9204 }
9205 stmts.insert(0, call);
9206 return;
9207 }
9208 let mut found: Option<(usize, usize)> = None;
9218 for (i, st) in stmts.iter().enumerate() {
9219 if let Stmt::Block(inner) = st {
9220 if let Some(p) = inner.iter().position(is_bare_ctor_call) {
9221 if inner[..p]
9222 .iter()
9223 .all(|s| matches!(s, Stmt::LocalDef { .. } | Stmt::ExprStmt(_)))
9224 {
9225 found = Some((i, p));
9226 break;
9227 }
9228 }
9229 }
9230 }
9231 if let Some((i, p)) = found {
9232 let mut call = match &stmts[i] {
9233 Stmt::Block(inner) => inner[p].clone(),
9234 _ => unreachable!(),
9235 };
9236 let mut prefix: Vec<&Stmt> = stmts[..i].iter().collect();
9237 if let Stmt::Block(inner) = &stmts[i] {
9238 prefix.extend(inner[..p].iter());
9239 }
9240 let mut consumed: Vec<(u32, usize)> = Vec::new();
9241 if !inline_ctor_arg_defs(&mut call, &prefix, vt, &mut consumed) {
9242 return;
9243 }
9244 let mut ks: Vec<usize> = Vec::new();
9251 {
9252 let inner_tail: &[Stmt] = match &stmts[i] {
9253 Stmt::Block(inner) if p < inner.len() => &inner[p + 1..],
9254 _ => &[],
9255 };
9256 for (v, k) in &consumed {
9257 if !var_occurs_in(inner_tail, *v) && !var_occurs_in(&stmts[i + 1..], *v) {
9258 ks.push(*k);
9259 }
9260 }
9261 }
9262 ks.sort_unstable();
9263 ks.dedup();
9264 if let Stmt::Block(inner) = &mut stmts[i] {
9265 inner.remove(p);
9266 for k in ks.iter().rev().filter(|k| **k >= i) {
9267 inner.remove(k - i);
9268 }
9269 }
9270 for k in ks.iter().rev().filter(|k| **k < i) {
9271 stmts.remove(*k);
9272 }
9273 stmts.insert(0, call);
9274 }
9275}
9276
9277fn is_delegation_expr(e: &Expr) -> bool {
9286 matches!(
9287 e,
9288 Expr::Method { name, is_special: true, owner, .. }
9289 if &**name == "<init>"
9290 && (owner.is_none() || matches!(owner.as_deref(), Some(Expr::This)))
9291 )
9292}
9293
9294pub(crate) fn is_bare_ctor_call(s: &Stmt) -> bool {
9295 matches!(s, Stmt::ExprStmt(e) if is_delegation_expr(e))
9296}
9297
9298pub(crate) fn local_is_written(body: &Stmt, var: u32) -> bool {
9302 let mut hit = false;
9303 visit_stmt_exprs_ro(body, &mut |e| {
9304 if hit {
9305 return;
9306 }
9307 visit_exprs(e, &mut |x| {
9308 if hit {
9309 return;
9310 }
9311 match x {
9312 Expr::Assign { target, .. }
9313 if matches!(&**target, Expr::Local { var: v, .. } if *v == var) =>
9314 {
9315 hit = true;
9316 }
9317 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. }
9318 if matches!(&**e, Expr::Local { var: v, .. } if *v == var) =>
9319 {
9320 hit = true;
9321 }
9322 _ => {}
9323 }
9324 });
9325 });
9326 hit
9327}
9328
9329pub(crate) fn rewrite_inner_ctor_outer_param(
9339 body: &mut Stmt,
9340 param0: u32,
9341 outer_display: &str,
9342 outer_ty: &TypeRef,
9343 eligible: &[String],
9344) {
9345 walk_stmt_exprs(body, &mut |e| {
9349 if let Expr::Method { name, cls, args, .. } = e {
9350 if &**name == "<init>"
9351 && eligible.iter().any(|c| c.as_str() == cls.as_ref())
9352 && args.first().is_some_and(|a| matches!(a, Expr::Local { var: v, .. } if *v == param0))
9353 {
9354 args.remove(0);
9355 }
9356 }
9357 });
9358 let text = format!("{outer_display}.this");
9360 let ty = outer_ty.clone();
9361 walk_stmt_exprs(body, &mut |e| {
9362 deep_rewrite_reads(e, &mut |x| {
9363 if let Expr::Local { var: v, .. } = x {
9364 if *v == param0 {
9365 *x = Expr::RawT(text.clone(), ty.clone());
9366 }
9367 }
9368 });
9369 });
9370}
9371
9372fn stmts_count_writes(stmts: &[Stmt], var: u32) -> usize {
9420 let mut n = 0usize;
9421 for s in stmts {
9422 visit_stmt_exprs_ro(s, &mut |e| {
9423 visit_exprs(e, &mut |x| match x {
9424 Expr::Assign { target, .. }
9425 if matches!(&**target, Expr::Local { var: v, .. } if *v == var) =>
9426 {
9427 n += 1;
9428 }
9429 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. }
9430 if matches!(&**e, Expr::Local { var: v, .. } if *v == var) =>
9431 {
9432 n += 1;
9433 }
9434 _ => {}
9435 });
9436 });
9437 }
9438 n
9439}
9440
9441fn prefix_written_vars(stmts: &[Stmt]) -> jdc_core::FxHashSet<u32> {
9445 let mut set = jdc_core::FxHashSet::default();
9446 for s in stmts {
9447 let mut c = s.clone();
9448 walk_stmt_exprs(&mut c, &mut |e| {
9449 deep_rewrite(e, &mut |x| match x {
9450 Expr::Assign { target, .. } => {
9451 if let Expr::Local { var, .. } = &**target {
9452 set.insert(*var);
9453 }
9454 }
9455 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => {
9456 if let Expr::Local { var, .. } = &**e {
9457 set.insert(*var);
9458 }
9459 }
9460 _ => {}
9461 });
9462 });
9463 if let Stmt::LocalDef { var, .. } = s {
9464 set.insert(*var);
9465 }
9466 }
9467 set
9468}
9469
9470fn pre_this_safe(e: &Expr, prefix_vars: &jdc_core::FxHashSet<u32>) -> bool {
9474 match e {
9475 Expr::Local { var, .. } => !prefix_vars.contains(var),
9476 Expr::Const(_) => true,
9477 Expr::Cast { e, .. } => pre_this_safe(e, prefix_vars),
9478 Expr::Bin { l, r, .. } => {
9479 pre_this_safe(l, prefix_vars) && pre_this_safe(r, prefix_vars)
9480 }
9481 Expr::Cond { c, t, f } => {
9482 pre_this_safe(c, prefix_vars)
9483 && pre_this_safe(t, prefix_vars)
9484 && pre_this_safe(f, prefix_vars)
9485 }
9486 Expr::Field { owner, is_static, .. } => {
9487 *is_static
9488 && owner
9489 .as_ref()
9490 .is_none_or(|o| pre_this_safe(o, prefix_vars))
9491 }
9492 Expr::Method {
9493 owner,
9494 args,
9495 is_static,
9496 ..
9497 } => {
9498 args.iter().all(|a| pre_this_safe(a, prefix_vars))
9499 && match owner {
9500 Some(o) => pre_this_safe(o, prefix_vars),
9501 None => *is_static,
9502 }
9503 }
9504 Expr::New { args, .. } => args.iter().all(|a| pre_this_safe(a, prefix_vars)),
9512 Expr::NewArray { dims, init, .. } => {
9513 dims.iter().all(|d| pre_this_safe(d, prefix_vars))
9514 && init
9515 .as_ref()
9516 .map(|l| l.iter().all(|x| pre_this_safe(x, prefix_vars)))
9517 .unwrap_or(true)
9518 }
9519 Expr::Un { e, .. } | Expr::InstanceOf { e, .. } => pre_this_safe(e, prefix_vars),
9521 Expr::ArrayIndex { array, index } => {
9522 pre_this_safe(array, prefix_vars) && pre_this_safe(index, prefix_vars)
9523 }
9524 _ => false,
9525 }
9526}
9527
9528fn single_assign_to_v(s: &Stmt, v: u32) -> Option<Expr> {
9532 match s {
9533 Stmt::ExprStmt(Expr::Assign {
9534 target,
9535 op: AssignOp::Plain,
9536 value,
9537 }) if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v) => {
9538 Some((**value).clone())
9539 }
9540 Stmt::Block(xs) if xs.len() == 1 => single_assign_to_v(&xs[0], v),
9541 _ => None,
9542 }
9543}
9544
9545pub(crate) fn flatten_top_blocks(stmts: &mut Vec<Stmt>) {
9567 fn rec(s: Stmt, out: &mut Vec<Stmt>) {
9568 match s {
9569 Stmt::Block(inner) => {
9570 for x in inner {
9571 rec(x, out);
9572 }
9573 }
9574 other => out.push(other),
9575 }
9576 }
9577 if !stmts.iter().any(|s| matches!(s, Stmt::Block(_))) {
9578 return;
9579 }
9580 let mut out: Vec<Stmt> = Vec::with_capacity(stmts.len());
9581 for s in std::mem::take(stmts) {
9582 rec(s, &mut out);
9583 }
9584 *stmts = out;
9585}
9586
9587fn fold_default_arg_bridge(stmts: &mut Vec<Stmt>) -> bool {
9588 flatten_top_blocks(stmts);
9589 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
9590 return false;
9591 };
9592 if pos == 0 {
9593 return false;
9594 }
9595 let Stmt::ExprStmt(Expr::Method { args, .. }) = &stmts[pos] else {
9596 return false;
9597 };
9598 let mut arg_vars: Vec<u32> = Vec::new();
9599 let mut reads = 0usize;
9600 for a in args {
9601 let mut c = a.clone();
9602 deep_rewrite_reads(&mut c, &mut |x| {
9603 if let Expr::Local { var, .. } = x {
9604 reads += 1;
9605 if !arg_vars.contains(var) {
9606 arg_vars.push(*var);
9607 }
9608 }
9609 });
9610 }
9611 if arg_vars.is_empty() || reads != arg_vars.len() {
9612 return false; }
9614 for &v in &arg_vars {
9617 if stmts_read_var(&stmts[pos + 1..], v) != 0
9618 || stmts_count_writes(&stmts[pos + 1..], v) != 0
9619 {
9620 return false;
9621 }
9622 }
9623 let prefix_vars = prefix_written_vars(&stmts[..pos]);
9624 let mut repl: Vec<(u32, Expr)> = Vec::new();
9625 let mut drop: Vec<usize> = Vec::new();
9626 for &v in &arg_vars {
9627 let mut base: Option<(usize, Expr)> = None;
9629 let mut decl: Option<usize> = None;
9630 let mut condf: Option<(usize, Expr, Option<Expr>, Option<Expr>)> = None;
9631 let mut ok = true;
9632 for (i, s) in stmts[..pos].iter().enumerate() {
9633 match s {
9634 Stmt::LocalDef { var, init, .. } if *var == v => match init {
9635 None => {
9636 if decl.is_some() {
9637 ok = false;
9638 break;
9639 }
9640 decl = Some(i);
9641 }
9642 Some(e) => {
9643 if base.is_some() {
9644 ok = false;
9645 break;
9646 }
9647 base = Some((i, e.clone()));
9648 }
9649 },
9650 Stmt::ExprStmt(Expr::Assign {
9651 target,
9652 op: AssignOp::Plain,
9653 value,
9654 }) if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v) => {
9655 if base.is_some() {
9656 ok = false;
9657 break;
9658 }
9659 base = Some((i, (**value).clone()));
9660 }
9661 Stmt::If {
9662 cond,
9663 then_stmt,
9664 else_stmt,
9665 } => {
9666 let t = single_assign_to_v(then_stmt, v);
9667 let f = else_stmt.as_ref().and_then(|b| single_assign_to_v(b, v));
9668 let then_touches = t.is_some()
9669 || stmts_read_var(std::slice::from_ref(&**then_stmt), v) != 0
9670 || stmts_count_writes(std::slice::from_ref(&**then_stmt), v) != 0;
9671 let else_touches = else_stmt.as_ref().is_some_and(|b| {
9672 f.is_some()
9673 || stmts_read_var(std::slice::from_ref(b), v) != 0
9674 || stmts_count_writes(std::slice::from_ref(b), v) != 0
9675 });
9676 if !then_touches && !else_touches {
9677 continue; }
9679 if condf.is_some()
9684 || base.is_none()
9685 || (then_touches && t.is_none())
9686 || (else_touches && f.is_none())
9687 {
9688 ok = false;
9689 break;
9690 }
9691 let cond_stmt = Stmt::ExprStmt(cond.clone());
9692 if stmts_read_var(std::slice::from_ref(&cond_stmt), v) != 0 {
9693 ok = false;
9694 break;
9695 }
9696 condf = Some((i, cond.clone(), t, f));
9697 }
9698 _ => {}
9699 }
9700 }
9701 if !ok {
9702 continue;
9703 }
9704 let Some((bi, be)) = base else { continue };
9705 let want_writes = 1
9708 + condf
9709 .as_ref()
9710 .map(|(_, _, t, f)| t.is_some() as usize + f.is_some() as usize)
9711 .unwrap_or(0);
9712 if stmts_count_writes(&stmts[..pos], v) != want_writes
9713 || stmts_read_var(&stmts[..pos], v) != 0
9714 {
9715 continue;
9716 }
9717 let rep = match condf {
9718 Some((ci, c, t, f)) => {
9719 drop.push(ci);
9720 match (t, f) {
9721 (Some(t), Some(f)) => Expr::Cond {
9722 c: Box::new(c),
9723 t: Box::new(t),
9724 f: Box::new(f),
9725 },
9726 (Some(t), None) => Expr::Cond {
9727 c: Box::new(c),
9728 t: Box::new(t),
9729 f: Box::new(be.clone()),
9730 },
9731 (None, Some(f)) => Expr::Cond {
9732 c: Box::new(c),
9733 t: Box::new(be.clone()),
9734 f: Box::new(f),
9735 },
9736 (None, None) => unreachable!("condf requires a branch assign"),
9737 }
9738 }
9739 None => be.clone(),
9740 };
9741 if !pre_this_safe(&rep, &prefix_vars) {
9742 continue;
9743 }
9744 drop.push(bi);
9745 if let Some(d) = decl {
9746 drop.push(d);
9747 }
9748 repl.push((v, rep));
9749 }
9750 if repl.is_empty() {
9751 return false;
9752 }
9753 if let Stmt::ExprStmt(Expr::Method { args, .. }) = &mut stmts[pos] {
9754 for a in args.iter_mut() {
9755 deep_rewrite_reads(a, &mut |x| {
9756 if let Expr::Local { var, .. } = x {
9757 if let Some((_, e)) = repl.iter().find(|(vv, _)| vv == var) {
9758 *x = e.clone();
9759 }
9760 }
9761 });
9762 }
9763 }
9764 drop.sort_unstable();
9765 drop.dedup();
9766 for i in drop.into_iter().rev() {
9767 stmts.remove(i);
9768 }
9769 true
9770}
9771
9772pub fn rewrite_kotlin_facades(body: &mut Stmt, pool: &crate::DexPool) {
9780 let map = pool.kotlin_facade_map();
9781 if map.is_empty() {
9782 return;
9783 }
9784 walk_stmt_exprs(body, &mut |e| {
9785 match e {
9786 Expr::Method {
9787 cls,
9788 name,
9789 desc,
9790 is_static,
9791 ..
9792 } if *is_static => {
9793 if let Some((base, cover)) = map.get(cls.as_ref()) {
9794 let hit = match cover {
9795 None => true,
9796 Some(c) => c.0.contains(&(name.to_string(), desc.to_string())),
9797 };
9798 if hit {
9799 *cls = std::sync::Arc::from(base.as_str());
9800 }
9801 }
9802 }
9803 Expr::Field {
9804 cls,
9805 name,
9806 is_static,
9807 ..
9808 } if *is_static => {
9809 if let Some((base, cover)) = map.get(cls.as_ref()) {
9810 let hit = match cover {
9811 None => true,
9812 Some(c) => c.1.contains(name.as_ref()),
9813 };
9814 if hit {
9815 *cls = std::sync::Arc::from(base.as_str());
9816 }
9817 }
9818 }
9819 _ => {}
9820 }
9821 });
9822}
9823
9824pub fn fold_enum_default_arg_bridge(body: &mut Stmt) {
9832 let Stmt::Block(stmts) = body else { return };
9833 fold_default_arg_bridge(stmts);
9834}
9835
9836pub fn fix_ctor_delegation_arg_defs(body: &mut Stmt) {
9837 let Stmt::Block(stmts) = body else { return };
9838 fold_default_arg_bridge(stmts);
9841 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
9845 return;
9846 };
9847 if pos == 0 {
9848 return;
9849 }
9850 let mut arg_vars: Vec<u32> = Vec::new();
9853 let mut total_reads = 0usize;
9854 {
9855 let Stmt::ExprStmt(Expr::Method { args, .. }) = &stmts[pos] else {
9856 return;
9857 };
9858 for a in args {
9859 let mut c = a.clone();
9860 deep_rewrite_reads(&mut c, &mut |x| {
9861 if let Expr::Local { var: v, .. } = x {
9862 total_reads += 1;
9863 if !arg_vars.contains(v) {
9864 arg_vars.push(*v);
9865 }
9866 }
9867 });
9868 }
9869 }
9870 if arg_vars.is_empty() || total_reads != arg_vars.len() {
9871 return;
9872 }
9873 let prefix_defs: Vec<u32> = stmts[..pos]
9876 .iter()
9877 .filter_map(|s| match s {
9878 Stmt::LocalDef { var, init: Some(_), .. } => Some(*var),
9879 _ => None,
9880 })
9881 .collect();
9882 let mut plan: Vec<(u32, usize, Option<usize>)> = Vec::new();
9887 for v in arg_vars {
9888 let defs: Vec<usize> = stmts[..pos]
9889 .iter()
9890 .enumerate()
9891 .filter(|(i, _)| {
9892 matches!(&stmts[*i], Stmt::LocalDef { var, init: Some(_), .. } if *var == v)
9893 })
9894 .map(|(i, _)| i)
9895 .collect();
9896 if defs.len() > 1 {
9897 return; }
9899 let Some(&q) = defs.first() else {
9900 continue; };
9902 let init = match &stmts[q] {
9903 Stmt::LocalDef { init: Some(e), .. } => e.clone(),
9904 _ => return,
9905 };
9906 if stmts_read_var(&stmts[q + 1..pos], v) != 0 {
9907 return; }
9909 let mut init_vars: Vec<u32> = Vec::new();
9912 deep_rewrite_reads(&mut { init.clone() }, &mut |x| {
9913 if let Expr::Local { var: vv, .. } = x {
9914 if !init_vars.contains(vv) {
9915 init_vars.push(*vv);
9916 }
9917 }
9918 });
9919 if init_vars.iter().any(|iv| prefix_defs.contains(iv)) {
9920 return;
9921 }
9922 match stmts[pos + 1..]
9923 .iter()
9924 .position(|s| {
9925 matches!(s, Stmt::ExprStmt(Expr::Assign { target, .. })
9926 if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v))
9927 }) {
9928 Some(w) => {
9929 if stmts_read_var(&stmts[pos + 1..pos + 1 + w], v) != 0 {
9931 return;
9932 }
9933 plan.push((v, q, Some(pos + 1 + w)));
9934 }
9935 None => {
9936 if stmts_read_var(&stmts[pos + 1..], v) != 0 {
9938 return;
9939 }
9940 plan.push((v, q, None));
9941 }
9942 }
9943 }
9944 if plan.is_empty() {
9945 return;
9946 }
9947 let values: Vec<(u32, Expr)> = plan
9949 .iter()
9950 .map(|(_v, q, _)| match &stmts[*q] {
9951 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e.clone()),
9952 _ => unreachable!("plan entries carry a LocalDef with init"),
9953 })
9954 .collect();
9955 if let Stmt::ExprStmt(Expr::Method { args, .. }) = &mut stmts[pos] {
9956 for a in args {
9957 deep_rewrite_reads(a, &mut |x| {
9958 if let Expr::Local { var: v, .. } = x {
9959 if let Some((_, e)) = values.iter().find(|(vv, _)| vv == v) {
9960 *x = e.clone();
9961 }
9962 }
9963 });
9964 }
9965 }
9966 for (v, _, w) in &plan {
9970 if let Some(w) = w {
9971 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = &mut stmts[*w] {
9972 if matches!(&**target, Expr::Local { var: vv, .. } if vv == v) {
9973 let taken = std::mem::replace(&mut **value, Expr::Const(ConstVal::Null));
9974 stmts[*w] = Stmt::LocalDef {
9975 var: *v,
9976 init: Some(taken),
9977 is_final: false,
9978 force_type: false,
9979 };
9980 }
9981 }
9982 }
9983 }
9984 let mut order: Vec<usize> = plan.iter().map(|(_, q, _)| *q).collect();
9985 order.sort_unstable();
9986 order.reverse();
9987 for q in order {
9988 stmts.remove(q);
9989 }
9990}
9991
9992pub fn fix_bool_returns(vt: &VarTable, body: &mut Stmt) {
10003 walk_mut_deep(body, &mut |st| {
10004 if let Stmt::Return(Some(e)) = st {
10005 let numeric = match &*e {
10006 Expr::Local { var, ty } => {
10007 let t = if (*var as usize) < vt.vars.len() {
10008 vt.var(*var).ty.erased()
10009 } else {
10010 ty.erased()
10011 };
10012 matches!(
10013 t,
10014 JavaType::Int
10015 | JavaType::Long
10016 | JavaType::Short
10017 | JavaType::Byte
10018 | JavaType::Char
10019 )
10020 }
10021 other => matches!(
10022 other.type_ref().erased(),
10023 JavaType::Int | JavaType::Long | JavaType::Short | JavaType::Byte
10024 | JavaType::Char
10025 ),
10026 };
10027 if numeric {
10028 let taken = e.clone();
10029 *e = Expr::Bin {
10030 op: BinOp::Ne,
10031 l: Box::new(taken),
10032 r: Box::new(Expr::Const(ConstVal::Int(0))),
10033 ty: Some(TypeRef::J(JavaType::Boolean)),
10034 };
10035 }
10036 }
10037 });
10038}
10039
10040pub fn fix_int_returns(vt: &VarTable, body: &mut Stmt) {
10041 walk_mut_deep(body, &mut |st| {
10042 if let Stmt::Return(Some(e)) = st {
10043 let is_bool = match e {
10044 Expr::Local { var, .. } => {
10045 (*var as usize) < vt.vars.len()
10046 && vt.vars[*var as usize].ty.erased() == JavaType::Boolean
10047 }
10048 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
10049 _ => false,
10050 };
10051 if is_bool {
10052 *e = Expr::Cond {
10053 c: Box::new(e.clone()),
10054 t: Box::new(Expr::Const(ConstVal::Int(1))),
10055 f: Box::new(Expr::Const(ConstVal::Int(0))),
10056 };
10057 }
10058 }
10059 });
10060}
10061
10062pub fn split_generations(vt: &mut VarTable, body: &mut Stmt, pool: &DexPool) {
10078 let mut counter: u32 = 0;
10079 let mut gen: std::collections::HashMap<u32, u32> = std::collections::HashMap::default();
10080 split_walk_stmt(body, vt, &mut gen, &mut counter, pool);
10081}
10082
10083fn split_needed(vt: &VarTable, target: u32, value: &Expr, pool: &DexPool) -> bool {
10085 let n = vt.vars.len();
10086 if target as usize >= n {
10087 return false;
10088 }
10089 let declared = vt.vars[target as usize].ty.erased();
10090 let vt_is_ref = |t: &JavaType| matches!(t, JavaType::Object(_) | JavaType::Array(_));
10091 fn side_bool(e: &Expr, vt: &VarTable) -> bool {
10100 match e {
10101 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
10102 matches!(vt.var(*var).ty.erased(), JavaType::Boolean)
10103 }
10104 Expr::Const(ConstVal::Int(0 | 1)) => true,
10105 Expr::Bin { op: BinOp::Or | BinOp::And | BinOp::Xor, l, r, .. } => {
10106 side_bool(l, vt) && side_bool(r, vt)
10107 }
10108 other => matches!(other.type_ref().erased(), JavaType::Boolean),
10109 }
10110 }
10111 let value_ty = match value {
10112 Expr::Local { var, ty } if (*var as usize) < vt.vars.len() => vt.var(*var).ty.erased(),
10113 Expr::Bin {
10114 op: BinOp::Or | BinOp::And | BinOp::Xor,
10115 l,
10116 r,
10117 ..
10118 } if side_bool(l, vt) && side_bool(r, vt) => JavaType::Boolean,
10119 other => other.type_ref().erased(),
10120 };
10121 if vt_is_ref(&declared) && vt_is_ref(&value_ty) {
10122 let obj = JavaType::Object("java/lang/Object".into());
10132 if declared == value_ty || declared == obj || value_ty == obj {
10133 return false;
10134 }
10135 if let (JavaType::Object(v), JavaType::Object(d)) = (&value_ty, &declared) {
10148 if matches!(value, Expr::New { .. })
10149 && (pool.get(v).is_none() || pool.get(d).is_none())
10150 {
10151 return false;
10152 }
10153 }
10154 true
10155 } else {
10156 if vt_is_ref(&declared) != vt_is_ref(&value_ty) {
10160 return true;
10161 }
10162 let d_bool = matches!(declared, JavaType::Boolean);
10170 let v_bool = matches!(value_ty, JavaType::Boolean);
10171 let zero_one = matches!(value, Expr::Const(ConstVal::Int(0 | 1)));
10172 d_bool != v_bool && !zero_one
10173 }
10174}
10175
10176fn rewrite_gen_reads(e: &mut Expr, gen: &std::collections::HashMap<u32, u32>, vt: &VarTable) {
10177 deep_rewrite(e, &mut |x| {
10178 if let Expr::Local { var, ty } = x {
10179 if let Some(&to) = gen.get(var) {
10180 if to != *var {
10181 *var = to;
10182 *ty = vt.var(to).ty.clone();
10183 }
10184 }
10185 }
10186 });
10187}
10188
10189fn do_split(
10190 vt: &mut VarTable,
10191 target: &mut Expr,
10192 old: u32,
10193 value_ty: &JavaType,
10194 gen: &mut std::collections::HashMap<u32, u32>,
10195 counter: &mut u32,
10196) {
10197 *counter += 1;
10198 let info = &vt.vars[old as usize];
10199 let id = vt.vars.len() as u32;
10200 let name = format!("{}_g{}", info.name, counter);
10201 let slot = info.slot;
10202 let is_param = false;
10210 vt.vars.push(jdc_core::var::VarInfo {
10211 id,
10212 slot,
10213 name,
10214 ty: TypeRef::J(value_ty.clone()),
10215 is_param,
10216 range_start: 0,
10217 range_end: u16::MAX,
10218 synthetic_name: true,
10219 });
10220 while vt.by_slot.len() <= slot as usize {
10221 vt.by_slot.push(Vec::new());
10222 }
10223 vt.by_slot[slot as usize].push((0, u16::MAX, id));
10224 gen.insert(old, id);
10225 if let Expr::Local { var, ty, .. } = target {
10226 *var = id;
10227 *ty = TypeRef::J(value_ty.clone());
10228 }
10229}
10230
10231fn rename_gen_back(s: &mut Stmt, from: u32, to: u32, vt: &VarTable) {
10243 if from == to || (to as usize) >= vt.vars.len() {
10244 return;
10245 }
10246 let to_ty = vt.vars[to as usize].ty.clone();
10247 walk_mut_deep(s, &mut |st| {
10248 if let Stmt::LocalDef { var, .. } = st {
10249 if *var == from {
10250 *var = to;
10251 }
10252 }
10253 });
10254 rewrite_exprs(s, &mut |e| {
10255 deep_rewrite(e, &mut |x| {
10256 if let Expr::Local { var, ty } = x {
10257 if *var == from {
10258 *var = to;
10259 *ty = to_ty.clone();
10260 }
10261 }
10262 });
10263 });
10264}
10265
10266fn drop_gen_slot(vt: &mut VarTable, dead: u32) {
10270 if (dead as usize) >= vt.vars.len() {
10271 return;
10272 }
10273 let slot = vt.vars[dead as usize].slot as usize;
10274 if let Some(seg) = vt.by_slot.get_mut(slot) {
10275 seg.retain(|&(_, _, id)| id != dead);
10276 }
10277}
10278
10279fn undo_type_safe(vt: &VarTable, k: u32, v: u32) -> bool {
10288 if (k as usize) >= vt.vars.len() || (v as usize) >= vt.vars.len() {
10289 return false;
10290 }
10291 let kt = vt.vars[k as usize].ty.erased();
10292 let vt_ty = vt.vars[v as usize].ty.erased();
10293 if kt == vt_ty {
10294 return true;
10295 }
10296 let is_prim = |t: &JavaType| {
10297 matches!(
10298 t,
10299 JavaType::Int
10300 | JavaType::Long
10301 | JavaType::Short
10302 | JavaType::Byte
10303 | JavaType::Char
10304 | JavaType::Float
10305 | JavaType::Double
10306 | JavaType::Boolean
10307 )
10308 };
10309 is_prim(&kt) && is_prim(&vt_ty)
10310}
10311
10312fn split_walk_stmt(
10313 st: &mut Stmt,
10314 vt: &mut VarTable,
10315 gen: &mut std::collections::HashMap<u32, u32>,
10316 counter: &mut u32,
10317 pool: &DexPool,
10318) {
10319 match st {
10320 Stmt::Block(v) => {
10321 for x in v.iter_mut() {
10322 split_walk_stmt(x, vt, gen, counter, pool);
10323 }
10324 }
10325 Stmt::ExprStmt(e) => {
10326 rewrite_gen_reads(e, gen, vt);
10327 if let Expr::Assign {
10328 target,
10329 value,
10330 op: AssignOp::Plain,
10331 ..
10332 } = e
10333 {
10334 if let Expr::Local { var, .. } = &**target {
10335 let old = *var;
10336 if split_needed(vt, old, value, pool) {
10337 let value_ty = value.type_ref().erased();
10338 do_split(vt, target, old, &value_ty, gen, counter);
10339 }
10340 }
10341 }
10342 }
10343 Stmt::LocalDef { var, init, .. } => {
10344 if let Some(e) = init {
10345 rewrite_gen_reads(e, gen, vt);
10346 if split_needed(vt, *var, e, pool) {
10347 let value_ty = e.type_ref().erased();
10351 let old = *var;
10352 let mut fake = Expr::Local {
10353 var: old,
10354 ty: vt.vars[old as usize].ty.clone(),
10355 };
10356 do_split(vt, &mut fake, old, &value_ty, gen, counter);
10357 if let Expr::Local { var: nv, .. } = fake {
10358 *var = nv;
10359 }
10360 }
10361 }
10362 }
10363 Stmt::If {
10364 cond,
10365 then_stmt,
10366 else_stmt,
10367 ..
10368 } => {
10369 rewrite_gen_reads(cond, gen, vt);
10370 let then_floor = vt.vars.len() as u32;
10371 let mut g_then = gen.clone();
10372 split_walk_stmt(then_stmt, vt, &mut g_then, counter, pool);
10373 let else_floor = vt.vars.len() as u32;
10374 let mut g_else = gen.clone();
10375 if let Some(e) = else_stmt {
10376 split_walk_stmt(e, vt, &mut g_else, counter, pool);
10377 }
10378 let then_reads = (else_floor > then_floor)
10395 .then(|| read_counts_all(std::slice::from_ref(&**then_stmt)));
10396 let mut undo_then: Vec<(u32, u32)> = g_then
10397 .iter()
10398 .filter(|(&k, &v)| {
10399 v >= then_floor
10400 && v < else_floor
10401 && g_else.get(&k).copied() == gen.get(&k).copied()
10402 && undo_type_safe(vt, k, v)
10403 && then_reads
10404 .as_ref()
10405 .map(|m| m.get(&v).copied().unwrap_or(0) == 0)
10406 .unwrap_or(true)
10407 })
10408 .map(|(&k, &v)| (v, k))
10409 .collect();
10410 undo_then.sort_unstable_by_key(|&(v, _)| std::cmp::Reverse(v));
10411 for &(v, k) in &undo_then {
10412 rename_gen_back(then_stmt, v, k, vt);
10413 drop_gen_slot(vt, v);
10414 }
10415 if let Some(e) = else_stmt.as_deref_mut() {
10416 let else_reads = ((vt.vars.len() as u32) > else_floor)
10417 .then(|| read_counts_all(std::slice::from_ref(&*e)));
10418 let mut undo_else: Vec<(u32, u32)> = g_else
10419 .iter()
10420 .filter(|(&k, &v)| {
10421 v >= else_floor
10422 && g_then.get(&k).copied() == gen.get(&k).copied()
10423 && undo_type_safe(vt, k, v)
10424 && else_reads
10425 .as_ref()
10426 .map(|m| m.get(&v).copied().unwrap_or(0) == 0)
10427 .unwrap_or(true)
10428 })
10429 .map(|(&k, &v)| (v, k))
10430 .collect();
10431 undo_else.sort_unstable_by_key(|&(v, _)| std::cmp::Reverse(v));
10432 for &(v, k) in &undo_else {
10433 rename_gen_back(e, v, k, vt);
10434 drop_gen_slot(vt, v);
10435 }
10436 }
10437 gen.retain(|k, v| g_then.get(k) == Some(v) && g_else.get(k) == Some(v));
10440 }
10441 Stmt::While { cond, body, .. } | Stmt::DoWhile { cond, body, .. } => {
10442 let entry = gen.clone();
10446 for _ in 0..3 {
10447 rewrite_gen_reads(cond, gen, vt);
10448 split_walk_stmt(body, vt, gen, counter, pool);
10449 }
10450 *gen = entry;
10453 }
10454 Stmt::For { init, body, .. } => {
10455 for x in init.iter_mut() {
10456 split_walk_stmt(x, vt, gen, counter, pool);
10457 }
10458 let entry = gen.clone();
10459 for _ in 0..3 {
10460 split_walk_stmt(body, vt, gen, counter, pool);
10461 }
10462 *gen = entry;
10463 }
10464 Stmt::ForEach { body, .. } => {
10465 let entry = gen.clone();
10466 for _ in 0..3 {
10467 split_walk_stmt(body, vt, gen, counter, pool);
10468 }
10469 *gen = entry;
10470 }
10471 Stmt::Switch { cases, default, .. } => {
10472 let entry = gen.clone();
10473 for c in cases.iter_mut() {
10474 let mut g_case = entry.clone();
10475 for x in c.body.iter_mut() {
10476 split_walk_stmt(x, vt, &mut g_case, counter, pool);
10477 }
10478 gen.retain(|k, v| g_case.get(k) == Some(v));
10479 }
10480 if let Some(d) = default {
10481 let mut g_def = entry.clone();
10482 split_walk_stmt(d, vt, &mut g_def, counter, pool);
10483 gen.retain(|k, v| g_def.get(k) == Some(v));
10484 }
10485 }
10486 Stmt::Try {
10487 body,
10488 catches,
10489 finally,
10490 } => {
10491 let entry = gen.clone();
10492 let mut g_try = entry.clone();
10493 split_walk_stmt(body, vt, &mut g_try, counter, pool);
10494 let mut g_all = g_try.clone();
10495 for c in catches.iter_mut() {
10496 let mut g_c = entry.clone();
10499 split_walk_stmt(&mut c.body, vt, &mut g_c, counter, pool);
10500 g_all.retain(|k, v| g_c.get(k) == Some(v));
10501 }
10502 *gen = g_all;
10503 if let Some(f) = finally {
10504 split_walk_stmt(f, vt, gen, counter, pool);
10505 }
10506 }
10507 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => {
10508 split_walk_stmt(body, vt, gen, counter, pool);
10509 }
10510 Stmt::Return(Some(e)) | Stmt::Throw(e) => {
10511 rewrite_gen_reads(e, gen, vt);
10512 }
10513 _ => {}
10514 }
10515}
10516
10517fn fw_super(c: &str) -> Option<&'static str> {
10524 crate::fwdb::super_of(c)
10525}
10526
10527fn exc_subtype(sub: &str, sup: &str, pool: &DexPool) -> bool {
10533 if sub == sup {
10534 return true;
10535 }
10536 if pool.is_subtype(sub, sup) {
10537 return true;
10538 }
10539 let mut cur = sub;
10540 for _ in 0..24 {
10541 let Some(next) = fw_super(cur) else { break };
10542 if next == sup {
10543 return true;
10544 }
10545 cur = next;
10546 }
10547 false
10548}
10549
10550pub fn dedupe_multicatch(s: &mut Stmt, pool: &DexPool) {
10556 dedupe_multicatch_walk(s, pool);
10557}
10558
10559fn dedupe_multicatch_walk(s: &mut Stmt, pool: &DexPool) {
10560 match s {
10561 Stmt::Try {
10562 body,
10563 catches,
10564 finally,
10565 }
10566 | Stmt::TryWithResources {
10567 body,
10568 catches,
10569 finally,
10570 ..
10571 } => {
10572 dedupe_multicatch_walk(body, pool);
10573 for c in catches.iter_mut() {
10574 if c.exc.len() > 1 {
10575 let mut keep: Vec<std::sync::Arc<str>> = Vec::with_capacity(c.exc.len());
10576 for e in c.exc.iter() {
10577 let shadowed = c.exc.iter().any(|o| {
10578 o.as_ref() != e.as_ref() && exc_subtype(e, o, pool)
10579 });
10580 if !shadowed && !keep.iter().any(|k| k.as_ref() == e.as_ref()) {
10581 keep.push(e.clone());
10582 }
10583 }
10584 c.exc = keep;
10585 }
10586 dedupe_multicatch_walk(&mut c.body, pool);
10587 }
10588 if let Some(f) = finally {
10589 dedupe_multicatch_walk(f, pool);
10590 }
10591 }
10592 Stmt::Block(v) => {
10593 for x in v.iter_mut() {
10594 dedupe_multicatch_walk(x, pool);
10595 }
10596 }
10597 Stmt::If {
10598 then_stmt,
10599 else_stmt,
10600 ..
10601 } => {
10602 dedupe_multicatch_walk(then_stmt, pool);
10603 if let Some(e) = else_stmt {
10604 dedupe_multicatch_walk(e, pool);
10605 }
10606 }
10607 Stmt::While { body, .. }
10608 | Stmt::DoWhile { body, .. }
10609 | Stmt::For { body, .. }
10610 | Stmt::ForEach { body, .. }
10611 | Stmt::Labeled { body, .. }
10612 | Stmt::Synchronized { body, .. } => dedupe_multicatch_walk(body, pool),
10613 Stmt::Switch { cases, default, .. } => {
10614 for c in cases.iter_mut() {
10615 for x in c.body.iter_mut() {
10616 dedupe_multicatch_walk(x, pool);
10617 }
10618 }
10619 if let Some(d) = default {
10620 dedupe_multicatch_walk(d, pool);
10621 }
10622 }
10623 _ => {}
10624 }
10625}
10626
10627pub fn resolve_dangling_gotos(s: &mut Stmt) {
10639 let mut labels: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
10640 walk_all(s, &mut |st| {
10643 if let Stmt::Label(id) = st {
10644 labels.insert(*id);
10645 }
10646 });
10647 resolve_gotos_walk(s, &labels, 0);
10648}
10649
10650fn resolve_gotos_walk(s: &mut Stmt, labels: &jdc_core::FxHashSet<u32>, loop_depth: u32) {
10651 let depth = match s {
10652 Stmt::While { .. } | Stmt::DoWhile { .. } | Stmt::For { .. } | Stmt::ForEach { .. } => {
10653 loop_depth + 1
10654 }
10655 _ => loop_depth,
10656 };
10657 match s {
10658 Stmt::Goto(id) => {
10659 if labels.contains(id) {
10660 return; }
10662 if depth > 0 {
10663 *s = Stmt::Break(None);
10664 } else {
10665 *s = Stmt::Comment(format!(
10666 "$DDC: unresolved jump to block {} (irreducible control flow)",
10667 id
10668 ));
10669 }
10670 }
10671 Stmt::Block(v) => {
10672 let mut i = 0usize;
10673 while i < v.len() {
10674 let dangling_here =
10679 depth == 0 && matches!(&v[i], Stmt::Goto(id) if !labels.contains(id));
10680 resolve_gotos_walk(&mut v[i], labels, depth);
10681 if dangling_here {
10682 v.truncate(i + 1);
10683 break;
10684 }
10685 i += 1;
10686 }
10687 }
10688 Stmt::If { then_stmt, else_stmt, .. } => {
10689 resolve_gotos_walk(then_stmt, labels, depth);
10690 if let Some(e) = else_stmt {
10691 resolve_gotos_walk(e, labels, depth);
10692 }
10693 }
10694 Stmt::While { body, .. }
10695 | Stmt::DoWhile { body, .. }
10696 | Stmt::For { body, .. }
10697 | Stmt::ForEach { body, .. }
10698 | Stmt::Labeled { body, .. }
10699 | Stmt::Synchronized { body, .. } => resolve_gotos_walk(body, labels, depth),
10700 Stmt::Try {
10701 body,
10702 catches,
10703 finally,
10704 } => {
10705 resolve_gotos_walk(body, labels, depth);
10706 for c in catches.iter_mut() {
10707 resolve_gotos_walk(&mut c.body, labels, depth);
10708 }
10709 if let Some(f) = finally {
10710 resolve_gotos_walk(f, labels, depth);
10711 }
10712 }
10713 Stmt::Switch { cases, default, .. } => {
10714 for c in cases.iter_mut() {
10715 for x in c.body.iter_mut() {
10716 resolve_gotos_walk(x, labels, depth);
10717 }
10718 }
10719 if let Some(d) = default {
10720 resolve_gotos_walk(d, labels, depth);
10721 }
10722 }
10723 _ => {}
10724 }
10725}
10726
10727fn read_counts_all(stmts: &[Stmt]) -> std::collections::HashMap<u32, usize> {
10732 let mut m = std::collections::HashMap::new();
10733 for s in stmts {
10734 visit_stmt_exprs_ro(s, &mut |e| {
10735 visit_exprs_reads(e, &mut |x| {
10736 if let Expr::Local { var, .. } = x {
10737 *m.entry(*var).or_insert(0) += 1;
10738 }
10739 });
10740 });
10741 }
10742 m
10743}
10744
10745fn stmts_read_var(stmts: &[Stmt], v: u32) -> usize {
10746 let mut n = 0usize;
10747 for s in stmts {
10748 visit_stmt_exprs_ro(s, &mut |e| {
10749 visit_exprs_reads(e, &mut |x| {
10750 if let Expr::Local { var: vv, .. } = x {
10751 if *vv == v {
10752 n += 1;
10753 }
10754 }
10755 });
10756 });
10757 }
10758 n
10759}
10760
10761pub fn fix_ctor_conditional_super(body: &mut Stmt) {
10762 if !matches!(body, Stmt::Block(_)) {
10765 let inner = std::mem::replace(body, Stmt::Block(Vec::new()));
10766 *body = Stmt::Block(vec![inner]);
10767 }
10768 let Stmt::Block(stmts) = body else { return };
10769 while stmts.len() == 1 {
10773 let Stmt::Block(inner) = &stmts[0] else { break };
10774 *stmts = inner.clone();
10775 }
10776 if stmts.is_empty() {
10777 return;
10778 }
10779 if is_bare_ctor_call(stmts.first().unwrap()) {
10780 dedupe_ctor_delegations(body);
10781 return;
10782 }
10783 if try_linear_super_inline(stmts) {
10784 return;
10785 }
10786 try_merge_branched_super(stmts);
10787}
10788
10789pub fn strip_secondary_delegations(body: &mut Stmt) {
10813 let Stmt::Block(stmts) = body else { return };
10814 if !stmts.first().is_some_and(is_bare_ctor_call) {
10815 return;
10816 }
10817 fn strip_in(s: &mut Stmt) {
10818 if is_bare_ctor_call(s) {
10819 *s = Stmt::Block(Vec::new());
10820 return;
10821 }
10822 match s {
10823 Stmt::Block(v) => v.iter_mut().for_each(strip_in),
10824 Stmt::If {
10825 then_stmt,
10826 else_stmt,
10827 ..
10828 } => {
10829 strip_in(then_stmt);
10830 if let Some(e) = else_stmt {
10831 strip_in(e);
10832 }
10833 }
10834 Stmt::While { body, .. }
10835 | Stmt::DoWhile { body, .. }
10836 | Stmt::ForEach { body, .. }
10837 | Stmt::Synchronized { body, .. }
10838 | Stmt::Labeled { body, .. } => strip_in(body),
10839 Stmt::For { init, body, .. } => {
10840 init.iter_mut().for_each(strip_in);
10841 strip_in(body);
10842 }
10843 Stmt::Switch { cases, default, .. } => {
10844 for c in cases.iter_mut() {
10845 c.body.iter_mut().for_each(strip_in);
10846 }
10847 if let Some(d) = default {
10848 strip_in(d);
10849 }
10850 }
10851 Stmt::Try {
10852 body,
10853 catches,
10854 finally,
10855 }
10856 | Stmt::TryWithResources {
10857 body,
10858 catches,
10859 finally,
10860 ..
10861 } => {
10862 strip_in(body);
10863 for c in catches.iter_mut() {
10864 strip_in(&mut c.body);
10865 }
10866 if let Some(f) = finally {
10867 strip_in(f);
10868 }
10869 }
10870 _ => {}
10871 }
10872 }
10873 for st in stmts.iter_mut().skip(1) {
10874 strip_in(st);
10875 }
10876}
10877
10878pub fn dedupe_ctor_delegations(body: &mut Stmt) {
10879 let Stmt::Block(stmts) = body else { return };
10880 let Some(d0) = stmts.first().and_then(|s| match s {
10881 Stmt::ExprStmt(e) if is_delegation_expr(e) => Some(e.clone()),
10882 _ => None,
10883 }) else {
10884 return;
10885 };
10886 fn rec(s: &mut Stmt, d0: &Expr) {
10887 match s {
10888 Stmt::Block(v) => {
10889 v.retain(|x| {
10890 !matches!(x, Stmt::ExprStmt(e) if is_delegation_expr(e) && same_delegation(e, d0))
10891 });
10892 for x in v.iter_mut() {
10893 rec(x, d0);
10894 }
10895 }
10896 Stmt::If { then_stmt, else_stmt, .. } => {
10897 rec(then_stmt, d0);
10898 if let Some(e) = else_stmt {
10899 rec(e, d0);
10900 }
10901 }
10902 _ => {}
10903 }
10904 }
10905 {
10909 let d0r = &d0;
10910 let mut idx = 0usize;
10911 stmts.retain(|s| {
10912 idx += 1;
10913 idx == 1
10914 || !(matches!(s, Stmt::ExprStmt(e)
10915 if is_delegation_expr(e) && same_delegation(e, d0r)))
10916 });
10917 }
10918 for s in stmts.iter_mut().skip(1) {
10919 rec(s, &d0);
10920 }
10921}
10922
10923fn same_delegation(a: &Expr, b: &Expr) -> bool {
10927 match (a, b) {
10928 (
10929 Expr::Method { name: n1, cls: c1, is_super: s1, args: a1, .. },
10930 Expr::Method { name: n2, cls: c2, is_super: s2, args: a2, .. },
10931 ) => {
10932 &**n1 == "<init>"
10933 && &**n2 == "<init>"
10934 && c1 == c2
10935 && s1 == s2
10936 && (a1 == a2 || (a1.is_empty() && a2.is_empty()))
10937 }
10938 _ => false,
10939 }
10940}
10941
10942fn ctor_call_expr(s: &Stmt) -> Option<&Expr> {
10944 match s {
10945 Stmt::ExprStmt(e) if is_delegation_expr(e) => Some(e),
10946 _ => None,
10947 }
10948}
10949
10950fn same_ctor_kind(a: &Expr, b: &Expr) -> bool {
10952 match (a, b) {
10953 (
10954 Expr::Method { cls: c1, is_super: s1, owner: o1, .. },
10955 Expr::Method { cls: c2, is_super: s2, owner: o2, .. },
10956 ) => c1 == c2 && s1 == s2 && o1 == o2,
10957 _ => false,
10958 }
10959}
10960
10961fn flat_list(s: &Stmt) -> Vec<&Stmt> {
10966 fn rec<'a>(s: &'a Stmt, out: &mut Vec<&'a Stmt>) {
10967 match s {
10968 Stmt::Block(v) => {
10969 for x in v {
10970 rec(x, out);
10971 }
10972 }
10973 other => out.push(other),
10974 }
10975 }
10976 let mut out = Vec::new();
10977 rec(s, &mut out);
10978 out
10979}
10980
10981fn strip_lost_alloc_owners(e: &mut Expr) {
10987 deep_rewrite(e, &mut |x| {
10988 if let Expr::Method { name, is_special: true, owner: Some(o), .. } = x {
10989 if &**name == "<init>" && !matches!(**o, Expr::This) {
10990 **o = Expr::Const(jdc_core::ir::expr::ConstVal::Int(0));
10991 }
10992 }
10993 });
10994}
10995
10996fn count_locals_expr(e: &Expr) -> std::collections::HashMap<u32, usize> {
10999 let mut m = std::collections::HashMap::new();
11000 count_expr_ro(e, &mut m);
11001 m
11002}
11003
11004pub(crate) fn count_locals_stmts(ss: &[Stmt]) -> std::collections::HashMap<u32, usize> {
11005 let mut m = std::collections::HashMap::new();
11006 for s in ss {
11007 visit_stmt_exprs_ro(s, &mut |e| count_expr_ro(e, &mut m));
11008 }
11009 m
11010}
11011
11012fn cnt(m: &std::collections::HashMap<u32, usize>, v: u32) -> usize {
11013 m.get(&v).copied().unwrap_or(0)
11014}
11015
11016fn inline_locals(
11020 e: &Expr,
11021 map: &std::collections::HashMap<u32, Expr>,
11022 depth: u32,
11023) -> Option<Expr> {
11024 if depth > 4 {
11025 return None;
11026 }
11027 let mut out = e.clone();
11028 let mut fail = false;
11029 deep_rewrite(&mut out, &mut |x| {
11030 if let Expr::Local { var, .. } = x {
11031 if let Some(def) = map.get(var) {
11032 match inline_locals(def, map, depth + 1) {
11033 Some(r) => *x = r,
11034 None => fail = true,
11035 }
11036 }
11037 }
11038 });
11039 if fail {
11040 None
11041 } else {
11042 Some(out)
11043 }
11044}
11045
11046fn def_of(s: &Stmt) -> Option<(u32, &Expr)> {
11048 match s {
11049 Stmt::LocalDef { var, init: Some(e), .. } => Some((*var, e)),
11050 Stmt::ExprStmt(Expr::Assign {
11051 target,
11052 value,
11053 op: jdc_core::ir::expr::AssignOp::Plain,
11054 ..
11055 }) => match &**target {
11056 Expr::Local { var, .. } => Some((*var, value)),
11057 _ => None,
11058 },
11059 _ => None,
11060 }
11061}
11062
11063fn try_linear_super_inline(stmts: &mut Vec<Stmt>) -> bool {
11065 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
11066 return false;
11067 };
11068 if pos == 0 {
11069 return false; }
11071 if !stmts[..pos]
11073 .iter()
11074 .all(|s| matches!(s, Stmt::LocalDef { .. } | Stmt::ExprStmt(_)))
11075 {
11076 return false;
11077 }
11078 let Some(call) = ctor_call_expr(&stmts[pos]) else {
11079 return false;
11080 };
11081 let Expr::Method { args, .. } = call else {
11082 return false;
11083 };
11084 let mut map: std::collections::HashMap<u32, Expr> =
11086 std::collections::HashMap::new();
11087 let mut def_idx: std::collections::HashMap<u32, usize> =
11088 std::collections::HashMap::new();
11089 for (i, s) in stmts[..pos].iter().enumerate() {
11090 if let Some((v, e)) = def_of(s) {
11091 map.insert(v, e.clone());
11092 def_idx.insert(v, i);
11093 }
11094 }
11095 let mut needed: Vec<u32> = Vec::new();
11097 for a in args {
11098 let mut probe = a.clone();
11099 strip_lost_alloc_owners(&mut probe);
11100 deep_rewrite(&mut probe, &mut |x| {
11101 if let Expr::Local { var, .. } = x {
11102 if map.contains_key(var) && !needed.contains(var) {
11103 needed.push(*var);
11104 }
11105 }
11106 });
11107 }
11108 if needed.is_empty() {
11109 return false; }
11111 let mut consumed: Vec<usize> = Vec::new();
11114 let use_counts = count_locals_stmts(stmts);
11115 for &v in &needed {
11116 let def = &map[&v];
11117 let uses = cnt(&use_counts, v);
11118 if jdc_core::ir::build::has_side_effects(def) && uses != 1 {
11119 return false;
11120 }
11121 if uses == 1 {
11122 consumed.push(def_idx[&v]);
11123 }
11124 }
11125 let mut new_args: Vec<Expr> = Vec::with_capacity(args.len());
11127 for a in args {
11128 match inline_locals(a, &map, 0) {
11129 Some(r) => new_args.push(r),
11130 None => return false,
11131 }
11132 }
11133 let mut call_stmt = stmts.remove(pos);
11136 if let Stmt::ExprStmt(Expr::Method { args: slot, .. }) = &mut call_stmt {
11137 *slot = new_args;
11138 }
11139 let mut rest: Vec<Stmt> = Vec::with_capacity(stmts.len() + 1);
11140 rest.push(call_stmt);
11141 for (i, s) in stmts.drain(..).enumerate() {
11142 let orig = if i >= pos { i + 1 } else { i };
11145 if !consumed.contains(&orig) {
11146 rest.push(s);
11147 }
11148 }
11149 *stmts = rest;
11150 true
11151}
11152
11153fn try_merge_branched_super(stmts: &mut Vec<Stmt>) -> bool {
11155 let if_positions: Vec<usize> = stmts
11159 .iter()
11160 .enumerate()
11161 .filter(|(_, s)| matches!(s, Stmt::If { .. }))
11162 .map(|(i, _)| i)
11163 .collect();
11164 for if_pos in if_positions {
11165 let gate = match &stmts[if_pos] {
11171 Stmt::If { then_stmt, else_stmt, .. } => {
11172 contains_delegation(then_stmt)
11173 || else_stmt.as_ref().is_some_and(|e| contains_delegation(e))
11174 }
11175 _ => false,
11176 };
11177 if gate && merge_at(stmts, if_pos) {
11178 return true;
11179 }
11180 }
11181 false
11182}
11183
11184fn contains_delegation(s: &Stmt) -> bool {
11187 match s {
11188 Stmt::ExprStmt(e) => is_delegation_expr(e),
11189 Stmt::Block(v) => v.iter().any(contains_delegation),
11190 Stmt::If { then_stmt, else_stmt, .. } => {
11191 contains_delegation(then_stmt)
11192 || else_stmt.as_ref().is_some_and(|e| contains_delegation(e))
11193 }
11194 _ => false,
11195 }
11196}
11197
11198
11199
11200fn merge_at(stmts: &mut Vec<Stmt>, if_pos: usize) -> bool {
11213 enum Br {
11218 Call(usize, Vec<(u32, Expr)>, Vec<usize>),
11220 Clean,
11221 Dirty,
11222 }
11223 fn split_branch(list: &[&Stmt]) -> Br {
11224 let call_pos = list.iter().position(|s| ctor_call_expr(s).is_some());
11225 let Some(c) = call_pos else {
11226 return if list.iter().any(|s| contains_delegation(s)) {
11229 Br::Dirty
11230 } else {
11231 Br::Clean
11232 };
11233 };
11234 if list.iter().skip(c + 1).any(|s| ctor_call_expr(s).is_some()) {
11235 return Br::Dirty; }
11237 let mut defs: Vec<(u32, Expr)> = Vec::new();
11238 let mut def_idxs: Vec<usize> = Vec::new();
11239 for (i, s) in list.iter().enumerate().take(c) {
11240 match def_of(s) {
11241 Some((v, e)) => {
11242 defs.push((v, e.clone()));
11243 def_idxs.push(i);
11244 }
11245 None => match s {
11246 Stmt::ExprStmt(_) | Stmt::LocalDef { .. } => {}
11249 _ => return Br::Dirty, },
11251 }
11252 }
11253 Br::Call(c, defs, def_idxs)
11254 }
11255 fn cond_def_of(s: &Stmt, scope: &[Stmt]) -> Option<(u32, Expr)> {
11257 let Stmt::If { cond, then_stmt, else_stmt: Some(e), .. } = s else {
11258 return None;
11259 };
11260 fn one<'x>(b: &'x Stmt, scope: &[Stmt]) -> Option<(u32, &'x Expr)> {
11261 let l = flat_list(b);
11262 if l.len() == 1 {
11263 return def_of(l[0]);
11264 }
11265 if l.len() == 2 {
11274 let (w, ex) = def_of(l[0])?;
11275 let Stmt::ExprStmt(Expr::Assign {
11276 target,
11277 op: AssignOp::Plain,
11278 value,
11279 }) = l[1]
11280 else {
11281 return None;
11282 };
11283 let Expr::Local { var: tv, .. } = &**target else {
11284 return None;
11285 };
11286 let Expr::Local { var: rv, .. } = &**value else {
11287 return None;
11288 };
11289 if *rv != w || *tv == w {
11290 return None;
11291 }
11292 let mut ldef_writes = 0usize;
11296 for st in scope {
11297 walk_all(st, &mut |x| {
11298 if let Stmt::LocalDef {
11299 var,
11300 init: Some(_),
11301 ..
11302 } = x
11303 {
11304 if *var == w {
11305 ldef_writes += 1;
11306 }
11307 }
11308 });
11309 }
11310 if stmts_read_var(scope, w) != 1
11311 || stmts_count_writes(scope, w) + ldef_writes != 1
11312 {
11313 return None;
11314 }
11315 return Some((*tv, ex));
11316 }
11317 None
11318 }
11319 let (v1, e1) = one(then_stmt, scope)?;
11320 let (v2, e2) = one(e, scope)?;
11321 if v1 != v2 {
11322 return None;
11323 }
11324 Some((
11325 v1,
11326 Expr::Cond {
11327 c: Box::new(cond.clone()),
11328 t: Box::new(e1.clone()),
11329 f: Box::new(e2.clone()),
11330 },
11331 ))
11332 }
11333
11334 let mut pmap: std::collections::HashMap<u32, Expr> =
11336 std::collections::HashMap::new();
11337 let mut pidx: std::collections::HashMap<u32, usize> =
11338 std::collections::HashMap::new();
11339 for (i, s) in stmts[..if_pos].iter().enumerate() {
11340 match s {
11341 Stmt::LocalDef { .. } | Stmt::ExprStmt(_) => {
11342 if let Some((v, e)) = def_of(s) {
11343 pmap.insert(v, e.clone());
11344 pidx.insert(v, i);
11345 }
11346 }
11347 Stmt::If { .. } => match cond_def_of(s, stmts) {
11348 Some((v, e)) => {
11349 pmap.insert(v, e);
11350 pidx.insert(v, i);
11351 }
11352 None => return false,
11353 },
11354 _ => return false,
11355 }
11356 }
11357 let (cond, then_v, else_v, had_else) = match &stmts[if_pos] {
11358 Stmt::If { cond, then_stmt, else_stmt, .. } => (
11359 cond.clone(),
11360 then_stmt.as_ref().clone(),
11361 else_stmt.as_ref().map_or_else(|| Stmt::Block(Vec::new()), |e| e.as_ref().clone()),
11362 else_stmt.is_some(),
11363 ),
11364 _ => return false,
11365 };
11366 fn normalize_branch(b: &mut Stmt) {
11372 let slot: &mut Stmt = match b {
11377 Stmt::Block(v) if v.len() == 1 => &mut v[0],
11378 other => other,
11379 };
11380 if !matches!(slot, Stmt::If { .. }) || !contains_delegation(slot) {
11381 return;
11382 }
11383 let taken = std::mem::replace(slot, Stmt::Block(Vec::new()));
11384 let mut tmp = vec![taken];
11385 if merge_at(&mut tmp, 0) {
11386 *slot = Stmt::Block(tmp);
11387 } else {
11388 *slot = tmp.pop().unwrap();
11389 }
11390 }
11391 let mut then_v = then_v;
11392 let mut else_v = else_v;
11393 normalize_branch(&mut then_v);
11394 normalize_branch(&mut else_v);
11395 let then_list = flat_list(&then_v);
11396 let else_list = flat_list(&else_v);
11397
11398 enum Side {
11401 Call {
11402 at: usize,
11403 defs: Vec<(u32, Expr)>,
11404 def_idxs: Vec<usize>,
11405 },
11406 Clean,
11407 }
11408 let (t_side, e_side, two_sided) = match (split_branch(&then_list), split_branch(&else_list)) {
11409 (Br::Call(tc, td, ti), Br::Call(ec, ed, ei)) => (
11410 Side::Call { at: tc, defs: td, def_idxs: ti },
11411 Side::Call { at: ec, defs: ed, def_idxs: ei },
11412 true,
11413 ),
11414 (Br::Call(tc, td, ti), Br::Clean) => (
11415 Side::Call { at: tc, defs: td, def_idxs: ti },
11416 Side::Clean,
11417 false,
11418 ),
11419 (Br::Clean, Br::Call(ec, ed, ei)) => (
11420 Side::Clean,
11421 Side::Call { at: ec, defs: ed, def_idxs: ei },
11422 false,
11423 ),
11424 _ => return false,
11425 };
11426 let (t_call, e_call) = match (&t_side, &e_side) {
11428 (Side::Call { at: ta, .. }, Side::Call { at: ea, .. }) => (
11429 ctor_call_expr(then_list[*ta]),
11430 ctor_call_expr(else_list[*ea]),
11431 ),
11432 (Side::Call { at: ta, .. }, Side::Clean) => {
11433 (ctor_call_expr(then_list[*ta]), None)
11434 }
11435 (Side::Clean, Side::Call { at: ea, .. }) => {
11436 (None, ctor_call_expr(else_list[*ea]))
11437 }
11438 _ => return false,
11439 };
11440 let (call_list, call_at) = match &t_side {
11442 Side::Call { at, .. } => (&then_list, *at),
11443 Side::Clean => match &e_side {
11444 Side::Call { at, .. } => (&else_list, *at),
11445 Side::Clean => return false,
11446 },
11447 };
11448 let (base_args, other_args) = if two_sided {
11449 let (Some(tc), Some(ec)) = (t_call, e_call) else {
11450 return false;
11451 };
11452 if !same_ctor_kind(tc, ec) {
11453 return false;
11454 }
11455 let (Expr::Method { args: a1, .. }, Expr::Method { args: a2, .. }) = (tc, ec) else {
11456 return false;
11457 };
11458 if a1.len() != a2.len() {
11459 return false;
11460 }
11461 (a1, Some(a2))
11462 } else {
11463 let c = t_call.or(e_call);
11464 let Some(c) = c else { return false };
11465 let Expr::Method { args, .. } = c else {
11466 return false;
11467 };
11468 (args, None)
11469 };
11470
11471 let mut tmap: std::collections::HashMap<u32, Expr> = pmap.clone();
11473 let mut emap: std::collections::HashMap<u32, Expr> = pmap.clone();
11474 if let Side::Call { defs, .. } = &t_side {
11475 for (v, e) in defs {
11476 tmap.insert(*v, e.clone());
11477 }
11478 }
11479 if let Side::Call { defs, .. } = &e_side {
11480 for (v, e) in defs {
11481 emap.insert(*v, e.clone());
11482 }
11483 }
11484 let Some(cond_i) = inline_locals(&cond, &pmap, 0) else {
11485 return false;
11486 };
11487 let mut then_args: Vec<Expr> = Vec::with_capacity(base_args.len());
11489 let mut else_args: Vec<Expr> = Vec::new();
11490 if two_sided {
11491 for a in base_args {
11492 match inline_locals(a, &tmap, 0) {
11493 Some(r) => then_args.push(r),
11494 None => return false,
11495 }
11496 }
11497 for a in other_args.unwrap() {
11498 match inline_locals(a, &emap, 0) {
11499 Some(r) => else_args.push(r),
11500 None => return false,
11501 }
11502 }
11503 } else {
11504 let from_then = matches!(&t_side, Side::Call { .. });
11507 for a in base_args {
11508 let r = if from_then {
11509 inline_locals(a, &tmap, 0)
11510 } else {
11511 inline_locals(a, &emap, 0)
11512 };
11513 match r {
11514 Some(r) => then_args.push(r),
11515 None => return false,
11516 }
11517 }
11518 }
11519 let primary_args = base_args;
11520 let mut merged: Vec<Expr> = Vec::with_capacity(then_args.len());
11522 let mut differ = 0usize;
11523 for (j, t) in then_args.iter().enumerate() {
11524 if two_sided {
11525 let e = &else_args[j];
11526 if t == e {
11527 merged.push(t.clone());
11528 } else {
11529 differ += 1;
11530 merged.push(Expr::Cond {
11531 c: Box::new(cond_i.clone()),
11532 t: Box::new(t.clone()),
11533 f: Box::new(e.clone()),
11534 });
11535 }
11536 } else {
11537 merged.push(t.clone());
11538 }
11539 }
11540
11541 let vars_in = |e: &Expr| -> Vec<u32> {
11543 let mut vs = Vec::new();
11544 let mut probe = e.clone();
11545 strip_lost_alloc_owners(&mut probe);
11546 deep_rewrite(&mut probe, &mut |x| {
11547 if let Expr::Local { var, .. } = x {
11548 if !vs.contains(var) {
11549 vs.push(*var);
11550 }
11551 }
11552 });
11553 vs
11554 };
11555 let branch_remainder = |list: &[&Stmt], side: &Side| -> Vec<Stmt> {
11563 match side {
11564 Side::Clean => list.iter().map(|s| (*s).clone()).collect(),
11565 Side::Call { at, def_idxs, .. } => {
11566 list.iter()
11567 .enumerate()
11568 .filter(|(i, _)| *i != *at && !def_idxs.contains(i))
11569 .map(|(_, s)| (*s).clone())
11570 .collect()
11571 }
11572 }
11573 };
11574 let tail_then_v = branch_remainder(&then_list, &t_side);
11575 let tail_else_v = branch_remainder(&else_list, &e_side);
11576 let post = &stmts[if_pos + 1..];
11577 let mut inlined: Vec<u32> = vars_in(&cond);
11578 for a in primary_args.iter().chain(else_args.iter()) {
11579 for v in vars_in(a) {
11580 if !inlined.contains(&v) {
11581 inlined.push(v);
11582 }
11583 }
11584 }
11585 inlined.retain(|v| pmap.contains_key(v) || tmap.contains_key(v) || emap.contains_key(v));
11586 for &v in &inlined {
11588 let in_branch = match (&t_side, &e_side) {
11589 (Side::Call { defs: td, .. }, Side::Call { defs: ed, .. }) => {
11590 td.iter().any(|(d, _)| *d == v) || ed.iter().any(|(d, _)| *d == v)
11591 }
11592 (Side::Call { defs, .. }, Side::Clean)
11593 | (Side::Clean, Side::Call { defs, .. }) => {
11594 defs.iter().any(|(d, _)| *d == v)
11595 }
11596 _ => false,
11597 };
11598 if pmap.contains_key(&v) && in_branch {
11599 return false;
11600 }
11601 }
11602 type Counts = std::collections::HashMap<u32, usize>;
11603 type GuardCounts = (
11604 Counts,
11605 Vec<Counts>,
11606 Vec<Counts>,
11607 Counts,
11608 Counts,
11609 Counts,
11610 Vec<(u32, Counts)>,
11611 );
11612 let (cond_counts, a1_counts, a2_counts, tail1_counts, tail2_counts, post_counts, def_counts): GuardCounts =
11613 if inlined.is_empty() {
11614 Default::default()
11615 } else {
11616 let all_defs: Vec<(u32, &Expr)> = pmap
11617 .iter()
11618 .map(|(k, v)| (*k, v))
11619 .chain(match &t_side {
11620 Side::Call { defs, .. } => defs.iter().map(|(k, v)| (*k, v)).collect(),
11621 Side::Clean => Vec::new(),
11622 })
11623 .chain(match &e_side {
11624 Side::Call { defs, .. } => defs.iter().map(|(k, v)| (*k, v)).collect(),
11625 Side::Clean => Vec::new(),
11626 })
11627 .collect();
11628 (
11629 count_locals_expr(&cond),
11630 primary_args.iter().map(count_locals_expr).collect(),
11631 else_args.iter().map(count_locals_expr).collect(),
11632 count_locals_stmts(&tail_then_v),
11633 count_locals_stmts(&tail_else_v),
11634 count_locals_stmts(post),
11635 all_defs.into_iter().map(|(v, e)| (v, count_locals_expr(e))).collect(),
11636 )
11637 };
11638 for &v in &inlined {
11639 let in_t = matches!(&t_side, Side::Call { defs, .. } if defs.iter().any(|(d, _)| *d == v));
11640 let in_e = matches!(&e_side, Side::Call { defs, .. } if defs.iter().any(|(d, _)| *d == v));
11641 let effectful = [
11642 in_t.then(|| tmap.get(&v)).flatten(),
11643 in_e.then(|| emap.get(&v)).flatten(),
11644 pmap.get(&v),
11645 ]
11646 .into_iter()
11647 .flatten()
11648 .any(jdc_core::ir::build::has_side_effects);
11649 if !effectful {
11650 continue;
11651 }
11652 let u_cond = cnt(&cond_counts, v);
11653 let u_post = cnt(&post_counts, v);
11654 let u_t1 = cnt(&tail1_counts, v);
11655 let u_t2 = cnt(&tail2_counts, v);
11656 let mut u_odef = 0usize;
11657 for (dv, dc) in &def_counts {
11658 if *dv != v && inlined.contains(dv) {
11659 u_odef += cnt(dc, v);
11660 }
11661 }
11662 let mut emit = u_cond * (1 + differ);
11663 let mut uses_args = 0usize;
11664 for j in 0..primary_args.len() {
11665 let c1 = cnt(&a1_counts[j], v);
11666 let c2 = if two_sided && j < a2_counts.len() {
11667 cnt(&a2_counts[j], v)
11668 } else {
11669 0
11670 };
11671 uses_args += c1 + c2;
11672 emit += if two_sided && then_args[j] != else_args[j] {
11673 c1 + c2
11674 } else {
11675 c1.max(c2)
11676 };
11677 }
11678 let ok = if in_t || in_e {
11679 u_cond == 0
11680 && u_post == 0
11681 && u_t1 == 0
11682 && u_t2 == 0
11683 && u_odef == 0
11684 && a1_counts.iter().map(|c| cnt(c, v)).sum::<usize>() <= 1
11685 && a2_counts.iter().map(|c| cnt(c, v)).sum::<usize>() <= 1
11686 && emit <= 2
11687 && (in_t || a1_counts.iter().all(|c| cnt(c, v) == 0))
11688 && (in_e || a2_counts.iter().all(|c| cnt(c, v) == 0))
11689 } else {
11690 u_post == 0
11691 && u_t1 == 0
11692 && u_t2 == 0
11693 && u_odef == 0
11694 && ((u_cond == 1 && uses_args == 0 && emit <= 2)
11695 || (u_cond == 0 && emit <= 1))
11696 };
11697 if !ok {
11698 return false;
11699 }
11700 }
11701
11702 let mut call_stmt = call_list[call_at].clone();
11704 if let Stmt::ExprStmt(Expr::Method { args: slot, .. }) = &mut call_stmt {
11705 *slot = merged;
11706 }
11707 let residual = |v: u32| -> usize {
11708 cnt(&tail1_counts, v) + cnt(&tail2_counts, v) + cnt(&post_counts, v)
11709 };
11710 let mut kept_prelude: Vec<Stmt> = Vec::new();
11711 for (i, s) in stmts[..if_pos].iter().enumerate() {
11712 let consumed = match s {
11713 Stmt::LocalDef { var, .. } => Some(*var),
11714 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
11715 Expr::Local { var, .. } => Some(*var),
11716 _ => None,
11717 },
11718 Stmt::If { .. } => cond_def_of(s, stmts).map(|(v, _)| v),
11719 _ => None,
11720 };
11721 if let Some(v) = consumed {
11722 if inlined.contains(&v) && pidx.get(&v) == Some(&i) && residual(v) == 0 {
11723 continue;
11724 }
11725 }
11726 kept_prelude.push(s.clone());
11727 }
11728 let build_keep = |list: &[&Stmt], side: &Side| -> Vec<Stmt> {
11729 match side {
11730 Side::Clean => list.iter().map(|s| (*s).clone()).collect(),
11731 Side::Call { at, .. } => {
11732 let mut out = Vec::new();
11733 for (i, s) in list.iter().enumerate() {
11734 if i == *at {
11735 continue;
11736 }
11737 if i < *at {
11738 if let Some((v, _)) = def_of(s) {
11739 if inlined.contains(&v) && residual(v) == 0 {
11740 continue;
11741 }
11742 }
11743 }
11744 out.push((*s).clone());
11745 }
11746 out
11747 }
11748 }
11749 };
11750 let then_keep = build_keep(&then_list, &t_side);
11751 let else_keep = build_keep(&else_list, &e_side);
11752 let else_part = if else_keep.is_empty() && !had_else {
11753 None
11754 } else {
11755 Some(Box::new(Stmt::Block(else_keep)))
11756 };
11757 let new_if = Stmt::If {
11758 cond: cond_i,
11759 then_stmt: Box::new(Stmt::Block(then_keep)),
11760 else_stmt: else_part,
11761 };
11762 let if_empty = match &new_if {
11763 Stmt::If { then_stmt, else_stmt, .. } => {
11764 let be = |s: &Stmt| matches!(s, Stmt::Block(v) if v.is_empty());
11765 be(then_stmt) && else_stmt.as_ref().is_none_or(|e| be(e))
11766 }
11767 _ => false,
11768 };
11769 let mut out: Vec<Stmt> = Vec::with_capacity(stmts.len());
11770 out.push(call_stmt);
11771 out.extend(kept_prelude);
11772 if !if_empty {
11773 out.push(new_if);
11774 }
11775 out.extend(stmts[if_pos + 1..].to_vec());
11776 *stmts = out;
11777 true
11778}
11779
11780
11781pub fn strip_enum_ctor_super(body: &mut Stmt) {
11782 fn is_enum_super(s: &Stmt) -> bool {
11783 matches!(
11784 s,
11785 Stmt::ExprStmt(Expr::Method { name, is_super: true, .. }) if &**name == "<init>"
11786 )
11787 }
11788 fn strip_deep(stmts: &mut Vec<Stmt>) {
11798 stmts.retain(|s| !is_enum_super(s));
11799 for st in stmts.iter_mut() {
11800 strip_one(st);
11801 }
11802 }
11803 fn strip_one(st: &mut Stmt) {
11804 match st {
11805 Stmt::Block(v) => strip_deep(v),
11806 Stmt::If { then_stmt, else_stmt, .. } => {
11807 strip_one(then_stmt);
11808 if let Some(e) = else_stmt {
11809 strip_one(e);
11810 }
11811 }
11812 Stmt::While { body, .. }
11813 | Stmt::DoWhile { body, .. }
11814 | Stmt::ForEach { body, .. }
11815 | Stmt::Synchronized { body, .. }
11816 | Stmt::Labeled { body, .. } => strip_one(body),
11817 Stmt::For { init, body, .. } => {
11818 strip_deep(init);
11819 strip_one(body);
11820 }
11821 Stmt::Switch { cases, default, .. } => {
11822 for c in cases.iter_mut() {
11823 strip_deep(&mut c.body);
11824 }
11825 if let Some(d) = default {
11826 strip_one(d);
11827 }
11828 }
11829 Stmt::Try { body, catches, finally }
11830 | Stmt::TryWithResources { body, catches, finally, .. } => {
11831 strip_one(body);
11832 for c in catches.iter_mut() {
11833 strip_one(&mut c.body);
11834 }
11835 if let Some(f) = finally {
11836 strip_one(f);
11837 }
11838 }
11839 _ => {}
11840 }
11841 }
11842 let Stmt::Block(stmts) = body else {
11843 return;
11844 };
11845 strip_deep(stmts);
11846}
11847
11848pub fn ensure_declared(body: &mut Stmt, vt: &VarTable) {
11849 let n = vt.vars.len().max(1);
11853
11854 let mut seen: HashSet<u32> = HashSet::default();
11857 demote_dup_decls(body, &mut seen, vt);
11858
11859 let mut def_block: Vec<u32> = vec![u32::MAX; n];
11867 let mut sizes: Vec<u32> = vec![0];
11868 let mut next_id: u32 = 1;
11869 scope_pass_a(body, 0, &mut next_id, &mut def_block, &mut sizes);
11870 sizes[0] = next_id;
11871 let mut within: Vec<u32> = vec![0; n];
11872 let mut total: Vec<u32> = vec![0; n];
11873 let mut next_b: u32 = 1;
11874 scope_pass_b(body, 0, &mut next_b, &def_block, &sizes, &mut within, &mut total);
11875 let mut hoist: HashSet<u32> = HashSet::default();
11876 for v in 0..n {
11877 if def_block[v] != u32::MAX && def_block[v] != 0 && within[v] < total[v] {
11878 hoist.insert(v as u32);
11879 }
11880 }
11881 if !hoist.is_empty() {
11882 let mut seed: HashSet<u32> = hoist.iter().copied().collect();
11883 demote_dup_decls(body, &mut seed, vt);
11884 }
11885
11886 let mut declared: Vec<bool> = vec![false; n];
11887 let mut is_param: Vec<bool> = vec![false; n];
11888 for v in &vt.vars {
11889 if v.is_param && (v.id as usize) < n {
11890 is_param[v.id as usize] = true;
11891 }
11892 }
11893 let mut is_catch: Vec<bool> = vec![false; n];
11899 walk_all(body, &mut |st| {
11900 if let Stmt::Try { catches, .. } = st {
11901 for c in catches {
11902 if (c.var as usize) < n {
11903 is_catch[c.var as usize] = true;
11904 }
11905 }
11906 }
11907 });
11908 walk_all(body, &mut |st| {
11909 if let Stmt::LocalDef { var, .. } = st {
11910 if (*var as usize) < n {
11911 declared[*var as usize] = true;
11912 }
11913 }
11914 });
11915 let mut used: HashSet<u32> = HashSet::default();
11916 stmt_collect_vars(body, &mut used, true);
11920
11921 let mut needs_decl: Vec<u32> = used
11924 .into_iter()
11925 .filter(|v| {
11926 let i = *v as usize;
11927 i >= n
11928 || ((!declared[i] && !is_param[i] && !is_catch[i]) || hoist.contains(v))
11929 })
11930 .collect();
11931 needs_decl.sort_unstable();
11932 needs_decl.dedup();
11933
11934 if !needs_decl.is_empty() {
11935 let mut decls: Vec<Stmt> = needs_decl
11936 .into_iter()
11937 .map(|v| Stmt::LocalDef {
11938 var: v,
11939 init: None,
11940 is_final: false,
11941 force_type: true,
11942 })
11943 .collect();
11944 decls.reverse();
11945 match body {
11946 Stmt::Block(v) => {
11947 for d in decls {
11948 v.insert(0, d);
11949 }
11950 }
11951 other => {
11952 let inner = std::mem::replace(other, Stmt::Block(vec![]));
11953 *other = Stmt::Block({
11954 let mut vs = decls;
11955 vs.push(inner);
11956 vs
11957 });
11958 }
11959 }
11960 }
11961}
11962
11963fn demote_dup_decls(s: &mut Stmt, seen: &mut HashSet<u32>, vt: &VarTable) {
11964 match s {
11965 Stmt::Block(v) => {
11968 for x in v.iter_mut() {
11969 demote_dup_decls(x, seen, vt);
11970 }
11971 }
11972 Stmt::LocalDef { var, init, .. } => {
11973 if seen.contains(var) {
11974 let ty = vt.var(*var).ty.clone();
11975 if let Some(e) = init.take() {
11976 *s = Stmt::ExprStmt(Expr::Assign {
11977 target: Box::new(Expr::Local { var: *var, ty }),
11978 op: AssignOp::Plain,
11979 value: Box::new(e),
11980 });
11981 } else {
11982 *s = Stmt::Block(vec![]);
11983 }
11984 } else {
11985 seen.insert(*var);
11986 }
11987 }
11988 other => {
11989 walk_mut(other, &mut |x| demote_dup_decls(x, seen, vt));
11990 }
11991 }
11992}
11993
11994#[allow(dead_code)]
11995fn unused(_: &Vec<CaseGroup>, _: &Catch) {}
11996
11997pub fn apply_local_names(vt: &mut VarTable, body: &Stmt) {
12011 use std::collections::{HashMap, HashSet};
12012
12013 let mut by_call: HashMap<u32, Vec<String>> = HashMap::new();
12016 let mut by_intrinsics: HashMap<u32, String> = HashMap::new();
12017
12018 visit_all_exprs(body, &mut |e| {
12019 if let Expr::Method { cls, name, args, .. } = e {
12020 if (cls.as_ref() == "kotlin/jvm/internal/Intrinsics"
12021 || cls.as_ref() == "kotlin/jvm/internal/IntrinsicsKt")
12022 && matches!(name.as_ref(), "checkNotNullParameter" | "checkParameterIsNotNull")
12023 && args.len() == 2
12024 {
12025 if let (Expr::Local { var, .. }, Expr::Const(ConstVal::Str(s))) = (&args[0], &args[1])
12026 {
12027 by_intrinsics
12028 .entry(*var)
12029 .or_insert_with(|| sanitize_name(s).unwrap_or_default());
12030 }
12031 }
12032 }
12033 });
12034
12035 walk_all(body, &mut |st| {
12036 let (var, value) = match st {
12037 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e),
12038 Stmt::ExprStmt(Expr::Assign { target, value, .. })
12039 if matches!(&**target, Expr::Local { .. }) =>
12040 {
12041 let Expr::Local { var, .. } = &**target else { return };
12042 (*var, value.as_ref())
12043 }
12044 _ => return,
12045 };
12046 if let Some(n) = defining_call_name(value) {
12047 by_call.entry(var).or_default().push(n);
12048 }
12049 });
12050
12051 let sanit = |n: &str| crate::classdec::java_ident(n).into_owned();
12062 let mut taken: HashSet<String> = HashSet::default();
12063 for v in vt.vars.iter() {
12068 if v.synthetic_name {
12073 taken.insert(sanit(&v.name));
12074 }
12075 }
12076 for v in vt.vars.iter_mut() {
12077 if v.synthetic_name {
12078 continue;
12079 }
12080 if !taken.insert(sanit(&v.name)) {
12081 let base = sanit(&v.name);
12082 for i in 2.. {
12083 let cand = format!("{base}{i}");
12084 if taken.insert(cand.clone()) {
12085 v.name = cand;
12086 break;
12087 }
12088 }
12089 }
12090 }
12091 let claim = |taken: &mut HashSet<String>, want: &str| -> String {
12092 let w = crate::classdec::java_ident(want).into_owned();
12093 if taken.insert(w.clone()) {
12094 return w;
12095 }
12096 for i in 2.. {
12097 let cand = format!("{w}{i}");
12098 if taken.insert(cand.clone()) {
12099 return cand;
12100 }
12101 }
12102 w
12103 };
12104
12105 for info in vt.vars.iter_mut() {
12106 if !info.synthetic_name {
12107 continue;
12108 }
12109 if let Some(n) = by_intrinsics.get(&info.id) {
12112 if !n.is_empty() {
12113 info.name = claim(&mut taken, n);
12114 continue;
12115 }
12116 }
12117 if let Some(names) = by_call.get(&info.id) {
12119 let unique: HashSet<&String> = names.iter().collect();
12120 if unique.len() == 1 {
12121 let n = unique.into_iter().next().unwrap();
12122 if !is_java_keyword(n) {
12123 info.name = claim(&mut taken, n);
12124 continue;
12125 }
12126 }
12127 }
12128 let want = type_alias(&info.ty).map(str::to_string).or_else(|| simple_type_name(&info.ty));
12131 if let Some(n) = want {
12132 info.name = claim(&mut taken, &n);
12133 }
12134 }
12135
12136 let mut seen: HashSet<String> = HashSet::default();
12141 for v in vt.vars.iter_mut() {
12142 if !seen.insert(sanit(&v.name)) {
12143 let base = sanit(&v.name);
12144 for i in 2.. {
12145 let cand = format!("{base}{i}");
12146 if seen.insert(cand.clone()) {
12147 v.name = cand;
12148 break;
12149 }
12150 }
12151 }
12152 }
12153}
12154
12155fn simple_type_name(ty: &TypeRef) -> Option<String> {
12157 let TypeRef::J(JavaType::Object(n)) = ty else {
12158 return None;
12159 };
12160 let simple = n.rsplit('/').next().unwrap_or(n);
12161 let simple = simple.rsplit('$').next().unwrap_or(simple);
12162 let mut chars = simple.chars();
12165 let mut out = String::with_capacity(simple.len());
12166 let mut lowered = false;
12167 for c in chars.by_ref() {
12168 if c.is_ascii_alphabetic() && !lowered {
12169 out.push(c.to_ascii_lowercase());
12170 lowered = true;
12171 } else {
12172 out.push(c);
12173 }
12174 if lowered {
12175 break;
12176 }
12177 }
12178 out.extend(chars);
12179 sanitize_name(&out)
12180}
12181
12182fn defining_call_name(e: &Expr) -> Option<String> {
12186 match e {
12187 Expr::Method { name, .. } => {
12188 let base = name
12189 .strip_prefix("get")
12190 .or_else(|| name.strip_prefix("is"))
12191 .filter(|rest| rest.chars().next().is_some_and(|c| c.is_ascii_uppercase()))
12192 .unwrap_or(name);
12193 let first = base.chars().next()?;
12194 let mut out = String::with_capacity(base.len());
12195 out.push(first.to_ascii_lowercase());
12196 out.extend(base.chars().skip(1));
12197 sanitize_name(&out)
12198 }
12199 Expr::New { cls, .. } => {
12200 let simple = cls.rsplit('/').next().unwrap_or(cls);
12201 let simple = simple.rsplit('$').next().unwrap_or(simple);
12202 let first = simple.chars().next()?;
12203 let mut out = String::with_capacity(simple.len());
12204 out.push(first.to_ascii_lowercase());
12205 out.extend(simple.chars().skip(1));
12206 sanitize_name(&out)
12207 }
12208 _ => None,
12209 }
12210}
12211
12212fn sanitize_name(s: &str) -> Option<String> {
12214 let mut out = String::with_capacity(s.len());
12215 for c in s.chars() {
12216 if c.is_ascii_alphanumeric() || c == '_' || c == '$' {
12217 out.push(c);
12218 } else {
12219 out.push('_');
12220 }
12221 }
12222 if out.len() < 2
12223 || out.chars().next().is_some_and(|c| c.is_ascii_digit())
12224 || is_java_keyword(&out)
12225 {
12226 return None;
12227 }
12228 Some(out)
12229}
12230
12231fn type_alias(ty: &TypeRef) -> Option<&'static str> {
12234 let TypeRef::J(JavaType::Object(n)) = ty else {
12235 return None;
12236 };
12237 Some(match n.as_ref() {
12238 "java/lang/String" | "kotlin/String" => "str",
12239 "java/lang/Class" => "cls",
12240 "java/lang/Throwable" => "th",
12241 "java/lang/Object" | "kotlin/Any" => "obj",
12242 "java/util/Iterator" | "kotlin/collections/Iterator" => "it",
12243 "java/lang/Boolean" => "bool",
12244 "java/lang/Integer" => "num",
12245 "java/lang/Character" => "ch",
12246 "java/lang/Byte" => "b",
12247 "java/lang/Short" => "sh",
12248 "java/lang/Float" => "f",
12249 "java/lang/Double" => "d",
12250 "java/lang/Long" => "i",
12251 "java/lang/StringBuilder" => "sb",
12252 "java/util/ArrayList" => "list",
12253 "java/util/HashMap" => "map",
12254 "android/content/Context" => "context",
12255 "android/content/Intent" => "intent",
12256 "android/os/Bundle" => "bundle",
12257 "android/view/View" => "view",
12258 "android/graphics/Bitmap" => "bitmap",
12259 "android/view/ViewGroup" => "viewGroup",
12260 _ => return None,
12261 })
12262}
12263
12264pub(crate) fn visit_all_exprs<F: FnMut(&Expr)>(s: &Stmt, f: &mut F) {
12266 walk_all(s, &mut |st| {
12267 let exprs: Vec<&Expr> = match st {
12268 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
12269 let mut v: Vec<&Expr> = vec![value];
12270 if !matches!(&**target, Expr::Local { .. }) {
12271 v.push(target);
12272 }
12273 v
12274 }
12275 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
12276 vec![e]
12277 }
12278 Stmt::Return(Some(e)) => vec![e],
12279 Stmt::LocalDef { init: Some(e), .. } => vec![e],
12280 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
12281 vec![cond]
12282 }
12283 _ => Vec::new(),
12284 };
12285 for e in exprs {
12286 visit_exprs(e, f);
12287 }
12288 });
12289}
12290
12291pub fn platform_constants(body: &mut Stmt) {
12296 if !crate::platform::installed() {
12297 return;
12298 }
12299 rewrite_exprs(body, &mut |e| {
12300 if let Expr::Method { cls, name, desc, args, .. } = e {
12301 for a in args.iter_mut() {
12302 let (value, is_long) = match a {
12303 Expr::Const(ConstVal::Int(v)) => (*v as i64, false),
12304 Expr::Const(ConstVal::Long(v)) => (*v, true),
12305 _ => continue,
12306 };
12307 if let Some(named) =
12308 crate::platform::named_constant(cls, name, &desc_to_string(desc), 0, value)
12309 {
12310 let _ = is_long;
12311 *a = Expr::Raw(named);
12312 }
12313 }
12314 }
12315 });
12316}
12317
12318fn desc_to_string(d: &jdc_core::types::MethodDescriptor) -> String {
12320 let mut out = String::from("(");
12321 for a in &d.args {
12322 type_to_desc(a, &mut out);
12323 }
12324 out.push(')');
12325 type_to_desc(&d.ret, &mut out);
12326 out
12327}
12328
12329fn type_to_desc(t: &jdc_core::types::JavaType, out: &mut String) {
12330 match t {
12331 JavaType::Void => out.push('V'),
12332 JavaType::Boolean => out.push('Z'),
12333 JavaType::Byte => out.push('B'),
12334 JavaType::Char => out.push('C'),
12335 JavaType::Short => out.push('S'),
12336 JavaType::Int => out.push('I'),
12337 JavaType::Float => out.push('F'),
12338 JavaType::Long => out.push('J'),
12339 JavaType::Double => out.push('D'),
12340 JavaType::Object(n) => {
12341 out.push('L');
12342 out.push_str(n);
12343 out.push(';');
12344 }
12345 JavaType::Array(inner) => {
12346 out.push('[');
12347 type_to_desc(inner, out);
12348 }
12349 }
12350}
12351
12352pub fn remove_kotlin_checks(s: &mut Stmt) {
12358 walk_mut_deep(s, &mut |st| {
12359 let Stmt::Block(v) = st else { return };
12360 v.retain(|x| !is_kotlin_check(x));
12361 });
12362}
12363
12364fn is_kotlin_check(s: &Stmt) -> bool {
12365 let Stmt::ExprStmt(e) = s else { return false };
12366 let Expr::Method { cls, name, .. } = e else { return false };
12367 (cls.as_ref() == "kotlin/jvm/internal/Intrinsics" || cls.as_ref() == "kotlin/jvm/internal/IntrinsicsKt__Jdk7Kt")
12368 && matches!(
12369 name.as_ref(),
12370 "checkNotNullParameter"
12371 | "checkParameterIsNotNull"
12372 | "checkExpressionValueIsNotNull"
12373 | "checkNotNullExpressionValue"
12374 | "checkReturnedValueIsNotNull"
12375 | "checkFieldIsNotNull"
12376 | "checkNotNull"
12377 )
12378}
12379
12380fn inline_ref_ok(pool: &DexPool, host: &str, target: &str, member: Option<(&str, bool)>) -> bool {
12400 let hpkg = match host.rfind('/') {
12401 Some(i) => &host[..i],
12402 None => "",
12403 };
12404 let mut cur = target;
12405 loop {
12406 let tpkg = match cur.rfind('/') {
12407 Some(i) => &cur[..i],
12408 None => "",
12409 };
12410 if tpkg != hpkg {
12411 if let Some(pc) = pool.get(cur) {
12412 if pc.access & crate::access::ACC_PUBLIC == 0 {
12413 return false;
12414 }
12415 if cur == target {
12416 if let Some((mname, is_method)) = member {
12417 let ok = if is_method {
12418 pc.all_methods().any(|m| {
12419 &*m.name == mname
12420 && m.access & crate::access::ACC_PUBLIC != 0
12421 })
12422 } else {
12423 pc.static_fields
12424 .iter()
12425 .chain(pc.instance_fields.iter())
12426 .any(|f| {
12427 f.name == mname
12428 && f.access & crate::access::ACC_PUBLIC != 0
12429 })
12430 };
12431 if !ok
12436 && (if is_method {
12437 pc.all_methods().any(|m| &*m.name == mname)
12438 } else {
12439 pc.static_fields
12440 .iter()
12441 .chain(pc.instance_fields.iter())
12442 .any(|f| f.name == mname)
12443 })
12444 {
12445 return false;
12446 }
12447 }
12448 }
12449 }
12450 }
12451 match cur.rfind('$') {
12452 Some(i) if i > 0 => cur = &cur[..i],
12453 _ => break,
12454 }
12455 }
12456 true
12457}
12458
12459pub fn inline_accessors(s: &mut Stmt, pool: &DexPool, host: &str) {
12460 rewrite_exprs(s, &mut |e| {
12461 let Expr::Method { cls, name, desc, args, is_static, .. } = e else { return };
12462 if !*is_static || args.is_empty() {
12463 return;
12464 }
12465 let Some(target) = pool.get(cls) else { return };
12466 let desc_s = desc_to_string(desc);
12467 let Some(m) = target.find_method(name, &desc_s) else { return };
12468 if !(m.is_static() && m.access & access::ACC_SYNTHETIC != 0) {
12469 return;
12470 }
12471 let Some(dex) = pool.dex(m.dex_idx) else { return };
12472 let code = match pool.accessor_code(m.dex_idx, m.code_off) {
12477 Some(bytes) => ddc_dex::CodeItem::parse(&bytes, 0),
12478 None => dex.code_at(m.code_off),
12479 };
12480 let Some(code) = code else { return };
12481 let insns: Vec<&Insn> =
12482 code.insns.iter().filter(|i| !matches!(i.kind, InsnKind::Nop)).collect();
12483 let core: Vec<&Insn> = insns
12485 .iter()
12486 .copied()
12487 .filter(|i| !is_const_only_trace(&i.kind, &insns))
12488 .collect();
12489 match accessor_shape(&dex, &core, &code) {
12490 Some(Shape::Identity(arg_i)) => {
12491 if let Some(a) = args.get(arg_i) {
12492 *e = a.clone();
12493 }
12494 }
12495 Some(Shape::FieldRead { arg, cls: field_cls, field, field_ty }) => {
12496 if !inline_ref_ok(pool, host, &field_cls, Some((&field, false))) {
12497 return;
12498 }
12499 if let Some(owner) = args.get(arg).cloned().map(Box::new) {
12500 let ty = TypeRef::J(desc_type(&field_ty));
12501 let name = jdc_core::rename::field_display(
12507 &field_cls,
12508 &field,
12509 &field_ty,
12510 )
12511 .map(std::sync::Arc::from)
12512 .unwrap_or_else(|| field.into());
12513 *e = Expr::Field {
12514 owner: Some(owner),
12515 cls: field_cls.into(),
12516 name,
12517 ty,
12518 is_static: false,
12519 };
12520 }
12521 }
12522 Some(Shape::Forward { cls: tcls, name: tname, desc: tdesc, instance }) => {
12523 if !inline_ref_ok(pool, host, &tcls, Some((&tname, true))) {
12524 return;
12525 }
12526 let disp = jdc_core::rename::field_display(
12530 &tcls,
12531 &tname,
12532 &tdesc.to_string(),
12533 )
12534 .map(std::sync::Arc::from)
12535 .unwrap_or_else(|| tname.into());
12536 *cls = tcls.into();
12537 *name = disp;
12538 *desc = std::sync::Arc::new(tdesc);
12539 if instance && !args.is_empty() {
12540 let recv = args.remove(0);
12542 if let Expr::Method { owner, .. } = e {
12543 *owner = Some(Box::new(recv));
12544 }
12545 }
12546 }
12547 None => {}
12548 }
12549 });
12550}
12551
12552enum Shape {
12554 Identity(usize),
12556 FieldRead { arg: usize, cls: String, field: String, field_ty: String },
12558 Forward { cls: String, name: String, desc: MethodDescriptor, instance: bool },
12561}
12562
12563fn field_of(dex: &ddc_dex::DexFile, field_idx: u32) -> Option<(String, String, String)> {
12565 let f = dex.field(field_idx);
12566 Some((
12567 dex.class_name(f.class_idx),
12570 dex.string(f.name_idx).to_string(),
12571 dex.type_name(f.type_idx).to_string(),
12573 ))
12574}
12575
12576fn param_index(code: &ddc_dex::CodeItem, reg: u16) -> Option<usize> {
12579 let rs = code.registers_size as usize;
12580 let ins = code.ins_size as usize;
12581 let r = reg as usize;
12582 let first = rs.checked_sub(ins)?;
12583 (r >= first && r < rs).then(|| r - first)
12584}
12585
12586fn accessor_shape(
12587 dex: &ddc_dex::DexFile,
12588 core: &[&Insn],
12589 code: &ddc_dex::CodeItem,
12590) -> Option<Shape> {
12591 let reads = |rest: &[&Insn], r: u16| {
12601 rest.iter()
12602 .any(|i| crate::lift::src_regs(&i.kind).contains(&r))
12603 };
12604 let core: &[&Insn] = match core.split_first() {
12605 Some((first, rest))
12606 if matches!(first.kind, InsnKind::Const { dst, .. } if !reads(rest, dst)) =>
12607 {
12608 rest
12609 }
12610 _ => core,
12611 };
12612 let core: &[&Insn] = match core.split_last() {
12613 Some((last, rest))
12614 if matches!(last.kind, InsnKind::Const { dst, .. } if !reads(rest, dst)) =>
12615 {
12616 rest
12617 }
12618 _ => core,
12619 };
12620 match core {
12621 [Insn { kind: InsnKind::Return { src }, .. }] => {
12623 let idx = param_index(code, *src)?;
12624 Some(Shape::Identity(idx))
12625 }
12626 [
12628 Insn { kind: InsnKind::IGet { dst, obj, field_idx }, .. },
12629 Insn { kind: InsnKind::Return { src }, .. },
12630 ] if dst == src => {
12631 let arg = param_index(code, *obj)?;
12632 let (cls, field, field_ty) = field_of(dex, *field_idx)?;
12633 Some(Shape::FieldRead { arg, cls, field, field_ty })
12634 }
12635 [Insn { kind: InsnKind::Invoke { method_idx, regs, kind, .. }, .. }, rest @ ..] if rest.len() <= 2 => {
12637 if !regs.iter().all(|r| param_index(code, *r).is_some()) || regs.is_empty() {
12639 return None;
12640 }
12641 if rest.len() == 2 {
12644 let ok = matches!(rest[0].kind, InsnKind::MoveResult { .. })
12645 && matches!(rest[1].kind, InsnKind::Return { .. } | InsnKind::ReturnVoid);
12646 if !ok {
12647 return None;
12648 }
12649 }
12650 let mid = dex.method(*method_idx);
12651 let cls = dex.class_name(mid.class_idx);
12654 let name = dex.string(mid.name_idx).to_string();
12655 let mut s = String::from("(");
12656 for t in dex.proto_params(mid.proto_idx) {
12657 s.push_str(dex.type_name(*t));
12658 }
12659 s.push(')');
12660 s.push_str(dex.type_name(dex.proto(mid.proto_idx).return_type_idx));
12661 let desc = parse_method_descriptor(&s)?;
12662 Some(Shape::Forward { cls, name, desc, instance: !matches!(kind, InvokeKind::Static) })
12663 }
12664 _ => None,
12665 }
12666}
12667
12668
12669fn is_const_only_trace(kind: &InsnKind, insns: &[&Insn]) -> bool {
12675 let InsnKind::Invoke { kind, regs, .. } = kind else { return false };
12676 if !matches!(kind, InvokeKind::Static) || regs.is_empty() {
12677 return false;
12678 }
12679 regs.iter().all(|r| const_loaded(*r, insns, 0))
12680}
12681
12682fn const_loaded(r: u16, insns: &[&Insn], depth: u8) -> bool {
12685 if depth > 3 {
12686 return false;
12687 }
12688 insns.iter().any(|i| match &i.kind {
12689 InsnKind::Const { dst, .. } => *dst == r,
12690 InsnKind::Move { dst, src } => *dst == r && const_loaded(*src, insns, depth + 1),
12691 _ => false,
12692 })
12693}