1#![allow(clippy::collapsible_match)]
14
15use jdc_core::FxHashSet as HashSet;
16
17use jdc_core::ir::build::has_side_effects;
21use jdc_core::emit::is_java_keyword;
22use jdc_core::ir::expr::{AssignOp, BinOp, ConcatPart, ConstVal, Expr, TypeRef, UnOp};
23use jdc_core::ir::stmt::{CaseGroup, Catch, Stmt};
24use jdc_core::types::{JavaType, MethodDescriptor};
25use jdc_core::var::VarTable;
26
27use crate::lift::MethodEnv;
28use crate::{access, desc_type, DexPool};
29use ddc_dex::insn::{Insn, InsnKind, InvokeKind};
30use jdc_core::types::parse_method_descriptor;
31
32pub fn rewrite_exprs<F: FnMut(&mut Expr)>(s: &mut Stmt, f: &mut F) {
38 walk_stmt_exprs(s, f);
39}
40
41pub(crate) fn walk_stmt_exprs<F: FnMut(&mut Expr)>(s: &mut Stmt, f: &mut F) {
42 match s {
43 Stmt::Block(v) => {
44 for x in v.iter_mut() {
45 walk_stmt_exprs(x, f);
46 }
47 }
48 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
49 f(e);
50 }
51 Stmt::Return(Some(e)) => f(e),
52 Stmt::LocalDef { init, .. } => {
53 if let Some(e) = init {
54 f(e);
55 }
56 }
57 Stmt::If {
58 cond,
59 then_stmt,
60 else_stmt,
61 } => {
62 f(cond);
63 walk_stmt_exprs(then_stmt, f);
64 if let Some(e) = else_stmt {
65 walk_stmt_exprs(e, f);
66 }
67 }
68 Stmt::While { cond, body } => {
69 f(cond);
70 walk_stmt_exprs(body, f);
71 }
72 Stmt::DoWhile { body, cond } => {
73 walk_stmt_exprs(body, f);
74 f(cond);
75 }
76 Stmt::For {
77 init,
78 cond,
79 update,
80 body,
81 } => {
82 for x in init.iter_mut() {
83 walk_stmt_exprs(x, f);
84 }
85 if let Some(c) = cond {
86 f(c);
87 }
88 for u in update.iter_mut() {
89 f(u);
90 }
91 walk_stmt_exprs(body, f);
92 }
93 Stmt::ForEach { iterable, body, .. } => {
94 f(iterable);
95 walk_stmt_exprs(body, f);
96 }
97 Stmt::Switch {
98 selector,
99 cases,
100 default,
101 ..
102 } => {
103 f(selector);
104 for c in cases {
105 for x in c.body.iter_mut() {
106 walk_stmt_exprs(x, f);
107 }
108 if let Some(g) = &mut c.guard {
109 f(g);
110 }
111 }
112 if let Some(d) = default {
113 walk_stmt_exprs(d, f);
114 }
115 }
116 Stmt::Try {
117 body,
118 catches,
119 finally,
120 } => {
121 walk_stmt_exprs(body, f);
122 for c in catches {
123 walk_stmt_exprs(&mut c.body, f);
124 }
125 if let Some(fl) = finally {
126 walk_stmt_exprs(fl, f);
127 }
128 }
129 Stmt::TryWithResources {
130 resources,
131 body,
132 catches,
133 finally,
134 } => {
135 for x in resources.iter_mut() {
136 walk_stmt_exprs(x, f);
137 }
138 walk_stmt_exprs(body, f);
139 for c in catches {
140 walk_stmt_exprs(&mut c.body, f);
141 }
142 if let Some(fl) = finally {
143 walk_stmt_exprs(fl, f);
144 }
145 }
146 Stmt::Assert { cond, msg } => {
147 f(cond);
148 if let Some(m) = msg {
149 f(m);
150 }
151 }
152 Stmt::Synchronized { lock, body } => {
153 f(lock);
154 walk_stmt_exprs(body, f);
155 }
156 Stmt::TernaryValue { e } => f(e),
157 Stmt::Labeled { body, .. } => walk_stmt_exprs(body, f),
158 Stmt::Goto(_) | Stmt::Label(_) | Stmt::Break(_) | Stmt::Continue(_) => {}
159 _ => {}
160 }
161}
162
163pub(crate) fn visit_stmt_exprs_ro<F: FnMut(&Expr)>(s: &Stmt, f: &mut F) {
168 match s {
169 Stmt::Block(v) => {
170 for x in v {
171 visit_stmt_exprs_ro(x, f);
172 }
173 }
174 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
175 f(e);
176 }
177 Stmt::Return(Some(e)) => f(e),
178 Stmt::LocalDef { init: Some(e), .. } => f(e),
179 Stmt::If {
180 cond,
181 then_stmt,
182 else_stmt,
183 } => {
184 f(cond);
185 visit_stmt_exprs_ro(then_stmt, f);
186 if let Some(e) = else_stmt {
187 visit_stmt_exprs_ro(e, f);
188 }
189 }
190 Stmt::While { cond, body } => {
191 f(cond);
192 visit_stmt_exprs_ro(body, f);
193 }
194 Stmt::DoWhile { body, cond } => {
195 visit_stmt_exprs_ro(body, f);
196 f(cond);
197 }
198 Stmt::For {
199 init,
200 cond,
201 update,
202 body,
203 } => {
204 for x in init {
205 visit_stmt_exprs_ro(x, f);
206 }
207 if let Some(c) = cond {
208 f(c);
209 }
210 for u in update {
211 f(u);
212 }
213 visit_stmt_exprs_ro(body, f);
214 }
215 Stmt::ForEach { iterable, body, .. } => {
216 f(iterable);
217 visit_stmt_exprs_ro(body, f);
218 }
219 Stmt::Switch {
220 selector,
221 cases,
222 default,
223 ..
224 } => {
225 f(selector);
226 for c in cases {
227 for x in &c.body {
228 visit_stmt_exprs_ro(x, f);
229 }
230 if let Some(g) = &c.guard {
231 f(g);
232 }
233 }
234 if let Some(d) = default {
235 visit_stmt_exprs_ro(d, f);
236 }
237 }
238 Stmt::Try {
239 body,
240 catches,
241 finally,
242 } => {
243 visit_stmt_exprs_ro(body, f);
244 for c in catches {
245 visit_stmt_exprs_ro(&c.body, f);
246 }
247 if let Some(fl) = finally {
248 visit_stmt_exprs_ro(fl, f);
249 }
250 }
251 Stmt::TryWithResources {
252 resources,
253 body,
254 catches,
255 finally,
256 ..
257 } => {
258 for x in resources {
259 visit_stmt_exprs_ro(x, f);
260 }
261 visit_stmt_exprs_ro(body, f);
262 for c in catches {
263 visit_stmt_exprs_ro(&c.body, f);
264 }
265 if let Some(fl) = finally {
266 visit_stmt_exprs_ro(fl, f);
267 }
268 }
269 Stmt::Assert { cond, msg } => {
270 f(cond);
271 if let Some(m) = msg {
272 f(m);
273 }
274 }
275 Stmt::Synchronized { lock, body } => {
276 f(lock);
277 visit_stmt_exprs_ro(body, f);
278 }
279 Stmt::TernaryValue { e } => f(e),
280 Stmt::Labeled { body, .. } => visit_stmt_exprs_ro(body, f),
281 _ => {}
282 }
283}
284
285fn count_expr_ro(e: &Expr, m: &mut jdc_core::FxHashMap<u32, usize>) {
291 if let Expr::Local { var, .. } = e {
292 *m.entry(*var).or_insert(0) += 1;
293 return;
294 }
295 if let Expr::Method {
296 name,
297 is_special: true,
298 owner: Some(o),
299 args,
300 ..
301 } = e
302 {
303 if &**name == "<init>" && !matches!(**o, Expr::This) {
304 for a in args {
305 count_expr_ro(a, m);
306 }
307 return;
308 }
309 }
310 for_each_child(e, &mut |c| count_expr_ro(c, m));
311}
312
313fn visit_exprs<F: FnMut(&Expr)>(e: &Expr, f: &mut F) {
315 f(e);
316 for_each_child(e, &mut |c| visit_exprs(c, f));
317}
318
319fn for_each_child<F: FnMut(&Expr)>(e: &Expr, f: &mut F) {
320 match e {
321 Expr::Un { e, .. } | Expr::Cast { e, .. } | Expr::InstanceOf { e, .. } => f(e),
322 Expr::Bin { l, r, .. } => {
323 f(l);
324 f(r);
325 }
326 Expr::Cond { c, t, f: fe } => {
327 f(c);
328 f(t);
329 f(fe);
330 }
331 Expr::Assign { target, value, .. } => {
332 f(target);
333 f(value);
334 }
335 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => f(e),
336 Expr::Field { owner, .. } => {
337 if let Some(o) = owner {
338 f(o);
339 }
340 }
341 Expr::Method { owner, args, .. } => {
342 if let Some(o) = owner {
343 f(o);
344 }
345 for a in args {
346 f(a);
347 }
348 }
349 Expr::ArrayIndex { array, index } => {
350 f(array);
351 f(index);
352 }
353 Expr::New { args, .. } => {
354 for a in args {
355 f(a);
356 }
357 }
358 Expr::NewArray { dims, init, .. } => {
359 for d in dims {
360 f(d);
361 }
362 if let Some(v) = init {
363 for x in v {
364 f(x);
365 }
366 }
367 }
368 Expr::NewMultiArray { dims, .. } => {
369 for d in dims {
370 f(d);
371 }
372 }
373 Expr::StringConcat(parts) => {
374 for p in parts {
375 if let ConcatPart::Str(x) = p {
376 f(x);
377 }
378 }
379 }
380 Expr::Invokedynamic { args, .. } => {
381 for a in args {
382 f(a);
383 }
384 }
385 Expr::AnonNew { args, .. } => {
386 for a in args {
387 f(a);
388 }
389 }
390 _ => {}
391 }
392}
393
394fn for_each_child_mut<F: FnMut(&mut Expr)>(e: &mut Expr, f: &mut F) {
396 match e {
397 Expr::Un { e, .. } | Expr::Cast { e, .. } | Expr::InstanceOf { e, .. } => f(e),
398 Expr::Bin { l, r, .. } => {
399 f(l);
400 f(r);
401 }
402 Expr::Cond { c, t, f: fe } => {
403 f(c);
404 f(t);
405 f(fe);
406 }
407 Expr::Assign { target, value, .. } => {
408 f(target);
409 f(value);
410 }
411 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => f(e),
412 Expr::Field { owner, .. } => {
413 if let Some(o) = owner {
414 f(o);
415 }
416 }
417 Expr::Method { owner, args, .. } => {
418 if let Some(o) = owner {
419 f(o);
420 }
421 for a in args.iter_mut() {
422 f(a);
423 }
424 }
425 Expr::ArrayIndex { array, index } => {
426 f(array);
427 f(index);
428 }
429 Expr::New { args, .. } => {
430 for a in args.iter_mut() {
431 f(a);
432 }
433 }
434 Expr::NewArray { dims, init, .. } => {
435 for d in dims.iter_mut() {
436 f(d);
437 }
438 if let Some(v) = init {
439 for x in v.iter_mut() {
440 f(x);
441 }
442 }
443 }
444 Expr::NewMultiArray { dims, .. } => {
445 for d in dims.iter_mut() {
446 f(d);
447 }
448 }
449 Expr::StringConcat(parts) => {
450 for p in parts {
451 if let ConcatPart::Str(x) = p {
452 f(x);
453 }
454 }
455 }
456 Expr::Invokedynamic { args, .. } => {
457 for a in args.iter_mut() {
458 f(a);
459 }
460 }
461 Expr::AnonNew { args, .. } => {
462 for a in args.iter_mut() {
463 f(a);
464 }
465 }
466 _ => {}
467 }
468}
469
470pub(crate) fn deep_rewrite<F: FnMut(&mut Expr)>(e: &mut Expr, f: &mut F) {
472 f(e);
473 for_each_child_mut(e, &mut |c| deep_rewrite(c, f));
474}
475
476fn visit_exprs_reads<F: FnMut(&Expr)>(e: &Expr, f: &mut F) {
488 f(e);
489 match e {
490 Expr::Assign { target, value, .. } => {
491 if !matches!(**target, Expr::Local { .. }) {
492 visit_exprs_reads(target, f);
493 }
494 visit_exprs_reads(value, f);
495 }
496 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
497 if !matches!(**inner, Expr::Local { .. }) {
498 visit_exprs_reads(inner, f);
499 }
500 }
501 _ => for_each_child(e, &mut |c| visit_exprs_reads(c, f)),
502 }
503}
504
505fn deep_rewrite_reads<F: FnMut(&mut Expr)>(e: &mut Expr, f: &mut F) {
506 f(e);
507 match e {
508 Expr::Assign { target, value, .. } => {
509 if !matches!(**target, Expr::Local { .. }) {
510 deep_rewrite_reads(target, f);
511 }
512 deep_rewrite_reads(value, f);
513 }
514 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
515 if !matches!(**inner, Expr::Local { .. }) {
516 deep_rewrite_reads(inner, f);
517 }
518 }
519 _ => for_each_child_mut(e, &mut |c| deep_rewrite_reads(c, f)),
520 }
521}
522
523fn collect_vars(e: &Expr, out: &mut HashSet<u32>) {
524 visit_exprs(e, &mut |x| {
525 if let Expr::Local { var, .. } = x {
526 out.insert(*var);
527 }
528 });
529}
530
531fn stmt_collect_vars(s: &Stmt, out: &mut HashSet<u32>, assignments: bool) {
536 match s {
537 Stmt::Block(v) => {
538 for x in v {
539 stmt_collect_vars(x, out, assignments);
540 }
541 }
542 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
543 if let Expr::Local { var, .. } = &**target {
544 if assignments {
545 out.insert(*var);
546 }
547 } else {
548 collect_vars(target, out);
549 }
550 collect_vars(value, out);
551 }
552 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
553 collect_vars(e, out)
554 }
555 Stmt::Return(Some(e)) => collect_vars(e, out),
556 Stmt::LocalDef { var, init, .. } => {
557 if assignments {
558 out.insert(*var);
559 }
560 if let Some(e) = init {
561 collect_vars(e, out);
562 }
563 }
564 Stmt::If {
565 cond,
566 then_stmt,
567 else_stmt,
568 } => {
569 collect_vars(cond, out);
570 stmt_collect_vars(then_stmt, out, assignments);
571 if let Some(e) = else_stmt {
572 stmt_collect_vars(e, out, assignments);
573 }
574 }
575 Stmt::While { cond, body } => {
576 collect_vars(cond, out);
577 stmt_collect_vars(body, out, assignments);
578 }
579 Stmt::DoWhile { body, cond } => {
580 stmt_collect_vars(body, out, assignments);
581 collect_vars(cond, out);
582 }
583 Stmt::For {
584 init,
585 cond,
586 update,
587 body,
588 } => {
589 for x in init {
590 stmt_collect_vars(x, out, assignments);
591 }
592 if let Some(c) = cond {
593 collect_vars(c, out);
594 }
595 for u in update {
596 collect_vars(u, out);
597 }
598 stmt_collect_vars(body, out, assignments);
599 }
600 Stmt::ForEach {
601 var,
602 iterable,
603 body,
604 ..
605 } => {
606 if assignments {
607 out.insert(*var);
608 }
609 collect_vars(iterable, out);
610 stmt_collect_vars(body, out, assignments);
611 }
612 Stmt::Switch {
613 selector,
614 cases,
615 default,
616 ..
617 } => {
618 collect_vars(selector, out);
619 for c in cases {
620 for x in &c.body {
621 stmt_collect_vars(x, out, assignments);
622 }
623 }
624 if let Some(d) = default {
625 stmt_collect_vars(d, out, assignments);
626 }
627 }
628 Stmt::Try {
629 body,
630 catches,
631 finally,
632 } => {
633 stmt_collect_vars(body, out, assignments);
634 for c in catches {
635 stmt_collect_vars(&c.body, out, assignments);
636 }
637 if let Some(f) = finally {
638 stmt_collect_vars(f, out, assignments);
639 }
640 }
641 Stmt::TryWithResources {
642 resources,
643 body,
644 catches,
645 finally,
646 } => {
647 for x in resources {
648 stmt_collect_vars(x, out, assignments);
649 }
650 stmt_collect_vars(body, out, assignments);
651 for c in catches {
652 stmt_collect_vars(&c.body, out, assignments);
653 }
654 if let Some(f) = finally {
655 stmt_collect_vars(f, out, assignments);
656 }
657 }
658 Stmt::Synchronized { lock, body } => {
659 collect_vars(lock, out);
660 stmt_collect_vars(body, out, assignments);
661 }
662 Stmt::Assert { cond, msg } => {
663 collect_vars(cond, out);
664 if let Some(m) = msg {
665 collect_vars(m, out);
666 }
667 }
668 Stmt::Labeled { body, .. } => stmt_collect_vars(body, out, assignments),
669 _ => {}
670 }
671}
672
673pub(crate) fn walk_all<F: FnMut(&Stmt)>(s: &Stmt, f: &mut F) {
674 f(s);
675 match s {
676 Stmt::Block(v) => {
677 for x in v {
678 walk_all(x, f);
679 }
680 }
681 Stmt::If {
682 then_stmt,
683 else_stmt,
684 ..
685 } => {
686 walk_all(then_stmt, f);
687 if let Some(e) = else_stmt {
688 walk_all(e, f);
689 }
690 }
691 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => walk_all(body, f),
692 Stmt::For { init, body, .. } => {
693 for x in init {
694 walk_all(x, f);
695 }
696 walk_all(body, f);
697 }
698 Stmt::ForEach { body, .. } => walk_all(body, f),
699 Stmt::Switch { cases, default, .. } => {
700 for c in cases {
701 for x in &c.body {
702 walk_all(x, f);
703 }
704 }
705 if let Some(d) = default {
706 walk_all(d, f);
707 }
708 }
709 Stmt::Try {
710 body,
711 catches,
712 finally,
713 } => {
714 walk_all(body, f);
715 for c in catches {
716 walk_all(&c.body, f);
717 }
718 if let Some(fl) = finally {
719 walk_all(fl, f);
720 }
721 }
722 Stmt::TryWithResources {
723 resources,
724 body,
725 catches,
726 finally,
727 } => {
728 for x in resources {
729 walk_all(x, f);
730 }
731 walk_all(body, f);
732 for c in catches {
733 walk_all(&c.body, f);
734 }
735 if let Some(fl) = finally {
736 walk_all(fl, f);
737 }
738 }
739 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => walk_all(body, f),
740 _ => {}
741 }
742}
743
744pub fn bind_catches(s: &mut Stmt, vt: &mut VarTable) {
751 let mut defined: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
756 for v in &vt.vars {
757 if v.is_param {
758 defined.insert(v.id);
759 }
760 }
761 collect_defined_locals(s, &mut defined);
762 let reads_all = count_locals_stmts(std::slice::from_ref(s));
763 let mut assigned: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
767 collect_assigned_locals(s, &mut assigned);
768 let mut fallback_bound: Vec<u32> = Vec::new();
769 bind_catches_walk(s, vt, &defined, &reads_all, &assigned, &mut fallback_bound);
770 if !fallback_bound.is_empty() {
773 remove_bare_decls(s, &fallback_bound);
774 }
775}
776
777fn collect_defined_locals(s: &Stmt, out: &mut jdc_core::FxHashSet<u32>) {
778 crate::passes::walk_all(s, &mut |st| {
779 match st {
780 Stmt::LocalDef { var, .. } => {
781 out.insert(*var);
782 }
783 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
784 if let Expr::Local { var, .. } = &**target {
785 out.insert(*var);
786 }
787 }
788 _ => {}
789 }
790 });
791}
792
793fn collect_bare_decls(s: &Stmt, out: &mut Vec<u32>) {
796 walk_all(s, &mut |st| {
797 if let Stmt::LocalDef { var, init: None, .. } = st {
798 if !out.contains(var) {
799 out.push(*var);
800 }
801 }
802 });
803}
804
805
806fn collect_assigned_locals(s: &Stmt, out: &mut jdc_core::FxHashSet<u32>) {
811 crate::passes::walk_all(s, &mut |st| {
812 match st {
813 Stmt::LocalDef { var, init: Some(_), .. } => {
814 out.insert(*var);
815 }
816 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
817 if let Expr::Local { var, .. } = &**target {
818 out.insert(*var);
819 }
820 }
821 _ => {}
822 }
823 });
824}
825
826fn first_thrown_unassigned_local(
829 body: &Stmt,
830 vt: &VarTable,
831 assigned: &jdc_core::FxHashSet<u32>,
832) -> Option<u32> {
833 let mut found: Option<u32> = None;
834 let c = body.clone();
835 walk_all(&c, &mut |st| {
836 if let Stmt::Throw(th) = st {
837 if let Expr::Local { var, .. } = th {
838 let v = *var;
839 if !assigned.contains(&v) {
840 if let JavaType::Object(o) = vt.var(v).ty.erased() {
841 if o.as_ref() == "java/lang/Throwable" {
842 found = Some(v);
843 }
844 }
845 }
846 }
847 }
848 });
849 found
850}
851
852fn remove_first_leaf(s: &mut Stmt) {
856 if let Stmt::Block(v) = s {
857 if let Some(f) = v.first_mut() {
858 remove_first_leaf(f);
859 }
860 while matches!(v.first(), Some(Stmt::Block(b)) if b.is_empty()) {
861 v.remove(0);
862 }
863 } else {
864 *s = Stmt::Block(vec![]);
865 }
866}
867
868fn remove_bare_decls(s: &mut Stmt, vars: &[u32]) {
871 let vars: jdc_core::FxHashSet<u32> = vars.iter().copied().collect();
872 walk_mut_deep(s, &mut |st| {
873 if let Stmt::Block(v) = st {
874 v.retain(|x| {
875 !matches!(x, Stmt::LocalDef { var, init: None, .. } if vars.contains(var))
876 });
877 }
878 });
879}
880
881fn first_undefined_local_read(
882 body: &Stmt,
883 defined: &jdc_core::FxHashSet<u32>,
884) -> Option<u32> {
885 let mut found: Option<u32> = None;
886 visit_stmt_exprs_ro(body, &mut |e| {
887 if found.is_none() {
888 visit_exprs(e, &mut |x| {
889 if found.is_none() {
890 if let Expr::Local { var, .. } = x {
891 if !defined.contains(var) {
892 found = Some(*var);
893 }
894 }
895 }
896 });
897 }
898 });
899 found
900}
901
902#[allow(clippy::too_many_arguments)]
903fn bind_catches_walk(
904 s: &mut Stmt,
905 vt: &mut VarTable,
906 defined: &jdc_core::FxHashSet<u32>,
907 reads_all: &jdc_core::FxHashMap<u32, usize>,
908 assigned: &jdc_core::FxHashSet<u32>,
909 fallback_bound: &mut Vec<u32>,
910) {
911 match s {
912 Stmt::Try {
913 body,
914 catches,
915 finally,
916 } => {
917 bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound);
918 for c in catches.iter_mut() {
919 if c.var == u32::MAX {
920 let stored = match first_leaf_stmt(c.body.as_ref()) {
927 Stmt::LocalDef { var, init: None, .. } => Some(*var),
939 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
943 match (&**target, &**value) {
944 (Expr::Local { var, .. }, Expr::Local { var: src, .. })
945 if !defined.contains(src) =>
946 {
947 Some(*var)
948 }
949 _ => None,
950 }
951 }
952 _ => None,
953 };
954 if let Some(v) = stored {
955 let exc = c
956 .exc
957 .first()
958 .cloned()
959 .unwrap_or_else(|| "java/lang/Throwable".into());
960 let slot = vt.var(v).slot;
961 let name = "e".to_string();
962 let new_var =
963 vt.add_catch_var(slot, name, TypeRef::J(JavaType::Object(exc)));
964 remove_first_leaf(c.body.as_mut());
965 rewrite_local_refs(c.body.as_mut(), v, new_var);
966 c.var = new_var;
967 } else if let Some(v) = first_undefined_local_read(c.body.as_ref(), defined)
968 .filter(|v| {
969 let in_catch = count_locals_stmts(std::slice::from_ref(c.body.as_ref()));
975 reads_all.get(v).copied().unwrap_or(0)
976 == in_catch.get(v).copied().unwrap_or(0)
977 })
978 .filter(|v| {
979 let ty = vt.var(*v).ty.erased();
987 let exc = c
988 .exc
989 .first()
990 .cloned()
991 .unwrap_or_else(|| "java/lang/Throwable".into());
992 matches!(&ty, JavaType::Object(o)
993 if o.as_ref() == exc.as_ref()
994 || o.as_ref() == "java/lang/Throwable")
995 })
996 {
997 c.var = v;
1007 fallback_bound.push(v);
1008 } else if let Some(v) =
1009 first_thrown_unassigned_local(c.body.as_ref(), vt, assigned)
1010 .filter(|v| {
1011 let in_catch =
1012 count_locals_stmts(std::slice::from_ref(c.body.as_ref()));
1013 reads_all.get(v).copied().unwrap_or(0)
1014 == in_catch.get(v).copied().unwrap_or(0)
1015 })
1016 .filter(|v| {
1017 let ty = vt.var(*v).ty.erased();
1021 let exc = c
1022 .exc
1023 .first()
1024 .cloned()
1025 .unwrap_or_else(|| "java/lang/Throwable".into());
1026 matches!(&ty, JavaType::Object(o)
1027 if o.as_ref() == exc.as_ref()
1028 || o.as_ref() == "java/lang/Throwable")
1029 })
1030 {
1031 c.var = v;
1037 fallback_bound.push(v);
1038 }
1039 }
1040 if c.var != u32::MAX {
1041 let in_catch =
1056 count_locals_stmts(std::slice::from_ref(c.body.as_ref()));
1057 let mut twins: Vec<u32> = Vec::new();
1058 collect_bare_decls(c.body.as_ref(), &mut twins);
1059 let mut swept: Vec<u32> = Vec::new();
1060 for x in twins {
1061 if x == c.var || assigned.contains(&x) {
1062 continue;
1063 }
1064 if reads_all.get(&x).copied().unwrap_or(0)
1065 != in_catch.get(&x).copied().unwrap_or(0)
1066 {
1067 continue;
1068 }
1069 if !matches!(vt.var(x).ty.erased(),
1070 JavaType::Object(o) if o.as_ref() == "java/lang/Throwable")
1071 {
1072 continue;
1073 }
1074 rewrite_local_refs(c.body.as_mut(), x, c.var);
1075 swept.push(x);
1076 }
1077 if !swept.is_empty() {
1078 remove_bare_decls(c.body.as_mut(), &swept);
1079 }
1080 }
1081 bind_catches_walk(&mut c.body, vt, defined, reads_all, assigned, fallback_bound);
1082 }
1083 if let Some(f) = finally {
1084 bind_catches_walk(f.as_mut(), vt, defined, reads_all, assigned, fallback_bound);
1085 }
1086 }
1087 Stmt::Block(v) => {
1088 for x in v.iter_mut() {
1089 bind_catches_walk(x, vt, defined, reads_all, assigned, fallback_bound);
1090 }
1091 }
1092 Stmt::If {
1093 then_stmt,
1094 else_stmt,
1095 ..
1096 } => {
1097 bind_catches_walk(then_stmt, vt, defined, reads_all, assigned, fallback_bound);
1098 if let Some(e) = else_stmt {
1099 bind_catches_walk(e, vt, defined, reads_all, assigned, fallback_bound);
1100 }
1101 }
1102 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound),
1103 Stmt::For { init, body, .. } => {
1104 for x in init.iter_mut() {
1105 bind_catches_walk(x, vt, defined, reads_all, assigned, fallback_bound);
1106 }
1107 bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound);
1108 }
1109 Stmt::ForEach { body, .. }
1110 | Stmt::Labeled { body, .. }
1111 | Stmt::Synchronized { body, .. } => bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound),
1112 Stmt::Switch { cases, default, .. } => {
1113 for c in cases.iter_mut() {
1114 for x in c.body.iter_mut() {
1115 bind_catches_walk(x, vt, defined, reads_all, assigned, fallback_bound);
1116 }
1117 }
1118 if let Some(d) = default {
1119 bind_catches_walk(d, vt, defined, reads_all, assigned, fallback_bound);
1120 }
1121 }
1122 _ => {}
1123 }
1124}
1125
1126fn rewrite_local_refs(s: &mut Stmt, from: u32, to: u32) {
1127 rewrite_exprs(s, &mut |e| {
1128 deep_rewrite(e, &mut |x| {
1129 if let Expr::Local { var, .. } = x {
1130 if *var == from {
1131 *var = to;
1132 }
1133 }
1134 });
1135 });
1136}
1137
1138pub fn cleanup(s: &mut Stmt) {
1140 jdc_core::ir::stmt::flatten(s);
1141 prune_dead_tails(s);
1142 strip_empty(s);
1143 merge_singleton_stmts(s);
1144}
1145
1146fn seq_terminates(s: &Stmt) -> bool {
1157 match s {
1158 Stmt::Return(_) | Stmt::Throw(_) | Stmt::Break(_) | Stmt::Continue(_) => {
1159 true
1160 }
1161 Stmt::Block(v) => v.iter().any(seq_terminates),
1162 Stmt::If {
1163 then_stmt,
1164 else_stmt,
1165 ..
1166 } => {
1167 seq_terminates(then_stmt)
1168 && else_stmt
1169 .as_ref()
1170 .map(|e| seq_terminates(e))
1171 .unwrap_or(false)
1172 }
1173 Stmt::Try {
1174 body,
1175 catches,
1176 finally,
1177 }
1178 | Stmt::TryWithResources {
1179 body,
1180 catches,
1181 finally,
1182 ..
1183 } => {
1184 if let Some(f) = finally {
1185 if seq_terminates(f) {
1186 return true;
1187 }
1188 }
1189 seq_terminates(body) && catches.iter().all(|c| seq_terminates(&c.body))
1190 }
1191 Stmt::Labeled { body, .. } | Stmt::Synchronized { body, .. } => {
1192 seq_terminates(body)
1193 }
1194 Stmt::While { .. } | Stmt::DoWhile { .. } => {
1199 jdc_core::analysis::dead_end_infinite_while(s)
1200 }
1201 _ => false,
1202 }
1203}
1204
1205fn prune_seq(v: &mut Vec<Stmt>) {
1206 if v.iter()
1207 .any(|x| matches!(x, Stmt::Label(_) | Stmt::Goto(_)))
1208 {
1209 return;
1210 }
1211 if let Some(i) = v.iter().position(seq_terminates) {
1212 v.truncate(i + 1);
1213 }
1214}
1215
1216pub fn prune_dead_tails(s: &mut Stmt) {
1217 match s {
1218 Stmt::Block(v) => {
1219 for x in v.iter_mut() {
1220 prune_dead_tails(x);
1221 }
1222 prune_seq(v);
1223 }
1224 Stmt::If {
1225 then_stmt,
1226 else_stmt,
1227 ..
1228 } => {
1229 prune_dead_tails(then_stmt);
1230 if let Some(e) = else_stmt {
1231 prune_dead_tails(e);
1232 }
1233 }
1234 Stmt::While { body, .. }
1235 | Stmt::DoWhile { body, .. }
1236 | Stmt::ForEach { body, .. }
1237 | Stmt::Labeled { body, .. }
1238 | Stmt::Synchronized { body, .. } => prune_dead_tails(body),
1239 Stmt::For { init, body, .. } => {
1240 for x in init.iter_mut() {
1241 prune_dead_tails(x);
1242 }
1243 prune_seq(init);
1244 prune_dead_tails(body);
1245 }
1246 Stmt::Switch {
1247 cases, default, ..
1248 } => {
1249 for c in cases {
1250 for x in c.body.iter_mut() {
1251 prune_dead_tails(x);
1252 }
1253 prune_seq(&mut c.body);
1254 }
1255 if let Some(d) = default {
1256 prune_dead_tails(d);
1257 }
1258 }
1259 Stmt::Try {
1260 body,
1261 catches,
1262 finally,
1263 }
1264 | Stmt::TryWithResources {
1265 body,
1266 catches,
1267 finally,
1268 ..
1269 } => {
1270 prune_dead_tails(body);
1271 for c in catches {
1272 prune_dead_tails(&mut c.body);
1273 }
1274 if let Some(f) = finally {
1275 prune_dead_tails(f);
1276 }
1277 }
1278 _ => {}
1279 }
1280}
1281
1282pub fn strip_phantom_field_writes(body: &mut Stmt, pool: &DexPool) {
1298 fn phantom(pool: &DexPool, cls: &str, name: &str) -> bool {
1299 let Some(pc) = pool.get(cls) else {
1303 return false;
1304 };
1305 fn declares(pc: &crate::PoolClass, cls: &str, name: &str) -> bool {
1313 pc.static_fields
1314 .iter()
1315 .chain(pc.instance_fields.iter())
1316 .any(|f| {
1317 f.name.as_str() == name
1318 || jdc_core::rename::field_display(cls, &f.name, &f.desc)
1319 .is_some_and(|d| d == name)
1320 })
1321 }
1322 if declares(pc, cls, name) {
1323 return false;
1324 }
1325 let mut cur: Option<String> = pc.super_name.clone();
1335 let mut hops = 0u32;
1336 while let Some(s) = cur {
1337 hops += 1;
1338 if hops > 64 {
1339 return false;
1340 }
1341 if let Some(sup) = pool.get(&s) {
1342 if declares(sup, &s, name) {
1343 return false;
1344 }
1345 cur = sup.super_name.clone();
1346 } else {
1347 if !crate::fwdb::exists(&s) {
1348 return false;
1349 }
1350 let mut found = false;
1351 crate::fwdb::for_each_field(&s, |f, _| {
1352 if f == name {
1353 found = true;
1354 }
1355 });
1356 if found {
1357 return false;
1358 }
1359 cur = crate::fwdb::super_of(&s).map(|x| x.to_string());
1361 }
1362 }
1363 true
1364 }
1365 fn droppable_value(pool: &DexPool, e: &Expr) -> bool {
1366 match e {
1367 Expr::Const(_) | Expr::Local { .. } => true,
1368 Expr::Field { cls, name, .. } => phantom(pool, cls, name),
1369 Expr::Cast { e, .. } => droppable_value(pool, e),
1370 _ => false,
1371 }
1372 }
1373 fn is_phantom_write(pool: &DexPool, st: &Stmt) -> bool {
1374 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = st {
1375 if let Expr::Field { cls, name, .. } = &**target {
1376 return phantom(pool, cls, name) && droppable_value(pool, value);
1377 }
1378 }
1379 false
1380 }
1381 walk_mut_deep(body, &mut |st| match st {
1385 Stmt::Block(v) => v.retain(|x| !is_phantom_write(pool, x)),
1386 Stmt::Switch { cases, .. } => {
1387 for c in cases.iter_mut() {
1388 c.body.retain(|x| !is_phantom_write(pool, x));
1389 }
1390 }
1391 Stmt::For { init, .. } => init.retain(|x| !is_phantom_write(pool, x)),
1392 Stmt::TryWithResources { resources, .. } => {
1393 resources.retain(|x| !is_phantom_write(pool, x))
1394 }
1395 _ => {}
1396 });
1397 if is_phantom_write(pool, body) {
1398 *body = Stmt::Block(vec![]);
1399 }
1400}
1401
1402pub fn init_bare_decls(body: &mut Stmt, vt: &VarTable) {
1414 walk_mut_deep(body, &mut |st| {
1415 if let Stmt::LocalDef { var, init, .. } = st {
1416 if init.is_some() {
1417 return;
1418 }
1419 let lit = match vt.var(*var).ty.erased() {
1420 JavaType::Boolean => "false",
1421 JavaType::Int
1422 | JavaType::Byte
1423 | JavaType::Short
1424 | JavaType::Char => "0",
1425 JavaType::Long => "0L",
1426 JavaType::Float => "0.0f",
1427 JavaType::Double => "0.0d",
1428 JavaType::Void => return,
1429 JavaType::Object(_) | JavaType::Array(_) => "null",
1430 };
1431 *init = Some(Expr::Raw(lit.to_string()));
1432 }
1433 });
1434}
1435
1436fn strip_empty(s: &mut Stmt) {
1437 match s {
1438 Stmt::Block(v) => {
1439 v.retain(|x| !x.is_empty_block());
1440 for x in v.iter_mut() {
1441 strip_empty(x);
1442 }
1443 }
1444 Stmt::If {
1445 then_stmt,
1446 else_stmt,
1447 ..
1448 } => {
1449 strip_empty(then_stmt);
1450 if let Some(e) = else_stmt {
1451 strip_empty(e);
1452 if e.is_empty_block() {
1453 *else_stmt = None;
1454 }
1455 }
1456 }
1457 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => strip_empty(body),
1458 Stmt::For { init, body, .. } => {
1459 init.retain(|x| !x.is_empty_block());
1460 strip_empty(body);
1461 }
1462 Stmt::ForEach { body, .. } => strip_empty(body),
1463 Stmt::Switch { cases, default, .. } => {
1464 for c in cases {
1465 c.body.retain(|x| !x.is_empty_block());
1466 }
1467 if let Some(d) = default {
1468 strip_empty(d);
1469 }
1470 }
1471 Stmt::Try {
1472 body,
1473 catches,
1474 finally,
1475 } => {
1476 strip_empty(body);
1477 for c in catches {
1478 strip_empty(&mut c.body);
1479 }
1480 if let Some(f) = finally {
1481 strip_empty(f);
1482 }
1483 }
1484 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => strip_empty(body),
1485 _ => {}
1486 }
1487}
1488
1489fn merge_singleton_stmts(s: &mut Stmt) {
1490 if let Stmt::Block(v) = s {
1491 for x in v.iter_mut() {
1492 merge_singleton_stmts(x);
1493 }
1494 }
1495}
1496
1497pub fn prune_unreachable(s: &mut Stmt) {
1499 if let Stmt::Block(v) = s {
1500 let mut cut: Option<usize> = None;
1501 for (i, x) in v.iter().enumerate() {
1502 if matches!(x, Stmt::Return(_) | Stmt::Throw(_)) {
1503 cut = Some(i + 1);
1504 break;
1505 }
1506 }
1507 if let Some(c) = cut {
1508 if c < v.len() {
1509 v.truncate(c);
1510 }
1511 }
1512 for x in v.iter_mut() {
1513 prune_unreachable(x);
1514 }
1515 } else {
1516 walk_mut(s, &mut prune_unreachable);
1517 }
1518}
1519
1520fn walk_mut<F: FnMut(&mut Stmt)>(s: &mut Stmt, f: &mut F) {
1521 match s {
1522 Stmt::Block(v) => {
1523 for x in v.iter_mut() {
1524 f(x);
1525 }
1526 }
1527 Stmt::If {
1528 then_stmt,
1529 else_stmt,
1530 ..
1531 } => {
1532 f(then_stmt);
1533 if let Some(e) = else_stmt {
1534 f(e);
1535 }
1536 }
1537 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => f(body),
1538 Stmt::For { init, body, .. } => {
1539 for x in init.iter_mut() {
1540 f(x);
1541 }
1542 f(body);
1543 }
1544 Stmt::ForEach { body, .. } => f(body),
1545 Stmt::Switch { cases, default, .. } => {
1546 for c in cases {
1547 for x in c.body.iter_mut() {
1548 f(x);
1549 }
1550 }
1551 if let Some(d) = default {
1552 f(d);
1553 }
1554 }
1555 Stmt::Try {
1556 body,
1557 catches,
1558 finally,
1559 } => {
1560 f(body);
1561 for c in catches {
1562 f(&mut c.body);
1563 }
1564 if let Some(fl) = finally {
1565 f(fl);
1566 }
1567 }
1568 Stmt::TryWithResources {
1569 resources,
1570 body,
1571 catches,
1572 finally,
1573 } => {
1574 for r in resources.iter_mut() {
1575 f(r);
1576 }
1577 f(body);
1578 for c in catches {
1579 f(&mut c.body);
1580 }
1581 if let Some(fl) = finally {
1582 f(fl);
1583 }
1584 }
1585 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => f(body),
1586 _ => {}
1587 }
1588}
1589
1590pub fn count_localdefs(s: &Stmt, out: &mut usize) {
1592 if matches!(s, Stmt::LocalDef { .. }) {
1593 *out += 1;
1594 }
1595 walk_all(s, &mut |x| {
1596 if matches!(x, Stmt::LocalDef { .. }) {
1597 *out += 1;
1598 }
1599 });
1600}
1601
1602pub fn strip_trailing_void_return(s: &mut Stmt) {
1604 if let Stmt::Block(v) = s {
1605 loop {
1606 match v.last_mut() {
1607 Some(Stmt::Return(None)) => {
1608 v.pop();
1609 }
1610 Some(Stmt::Block(_)) => {
1615 let last = v.last_mut().unwrap();
1616 let before = match last {
1617 Stmt::Block(b) => b.len(),
1618 _ => 0,
1619 };
1620 strip_trailing_void_return(last);
1621 let after = match last {
1622 Stmt::Block(b) => b.len(),
1623 _ => 0,
1624 };
1625 if after == before {
1626 break; }
1628 }
1629 _ => break,
1630 }
1631 }
1632 }
1633}
1634
1635pub fn invert_empty_thens(s: &mut Stmt) {
1640 walk_mut_deep(s, &mut |st| {
1641 if let Stmt::If {
1642 cond,
1643 then_stmt,
1644 else_stmt,
1645 } = st
1646 {
1647 let then_empty = matches!(&**then_stmt, Stmt::Block(v) if v.is_empty());
1648 if then_empty {
1649 if let Some(els) = else_stmt.take() {
1650 let c = std::mem::replace(cond, Expr::Const(ConstVal::Int(0)));
1651 *cond = jdc_core::convert::negate(c);
1652 *then_stmt = els;
1653 }
1654 }
1655 }
1656 });
1657}
1658
1659pub fn fold_short_circuits(s: &mut Stmt) {
1670 for _ in 0..8 {
1671 let mut changed = false;
1672 walk_mut_deep(s, &mut |st| {
1673 if try_fold_diamond(st) {
1674 changed = true;
1675 }
1676 });
1677 if !changed {
1678 break;
1679 }
1680 }
1681}
1682
1683fn try_fold_diamond(st: &mut Stmt) -> bool {
1684 enum Shape {
1685 ThenAnd,
1686 ThenAndNot,
1687 ElseOr,
1688 ElseOrNot,
1689 BareAnd,
1690 BareAndNot,
1691 }
1692 let shape = match &*st {
1696 Stmt::If {
1699 then_stmt,
1700 else_stmt: None,
1701 ..
1702 } => match &**then_stmt {
1703 Stmt::If {
1704 then_stmt: t2,
1705 else_stmt: None,
1706 ..
1707 } if !matches!(&**t2, Stmt::Block(v) if v.is_empty()) => Some(Shape::BareAnd),
1708 Stmt::If {
1709 then_stmt: t2,
1710 else_stmt: Some(_),
1711 ..
1712 } if matches!(&**t2, Stmt::Block(v) if v.is_empty()) => Some(Shape::BareAndNot),
1713 _ => None,
1714 },
1715 Stmt::If {
1716 then_stmt,
1717 else_stmt: Some(els),
1718 ..
1719 } => match (&**then_stmt, &**els) {
1720 (Stmt::If { else_stmt: Some(f2), .. }, _) if f2.as_ref() == els.as_ref() => {
1721 Some(Shape::ThenAnd)
1722 }
1723 (Stmt::If { then_stmt: t2, else_stmt: Some(_), .. }, _)
1724 if t2.as_ref() == els.as_ref() =>
1725 {
1726 Some(Shape::ThenAndNot)
1727 }
1728 (_, Stmt::If { then_stmt: t2, else_stmt: Some(_), .. })
1729 if t2.as_ref() == then_stmt.as_ref() =>
1730 {
1731 Some(Shape::ElseOr)
1732 }
1733 (_, Stmt::If { else_stmt: Some(f2), .. })
1734 if f2.as_ref() == then_stmt.as_ref() =>
1735 {
1736 Some(Shape::ElseOrNot)
1737 }
1738 _ => None,
1739 },
1740 _ => None,
1741 };
1742 let Some(shape) = shape else {
1743 return false;
1744 };
1745
1746 let taken = std::mem::replace(st, Stmt::Block(vec![]));
1747 if matches!(shape, Shape::BareAnd | Shape::BareAndNot) {
1749 let Stmt::If {
1750 cond: c1,
1751 then_stmt,
1752 else_stmt: None,
1753 } = taken
1754 else {
1755 unreachable!()
1756 };
1757 let Stmt::If {
1758 cond: c2,
1759 then_stmt: t2,
1760 else_stmt: f2,
1761 } = *then_stmt
1762 else {
1763 unreachable!()
1764 };
1765 let sc = |op: BinOp, l: Expr, r: Expr| Expr::Bin {
1766 op,
1767 l: Box::new(l),
1768 r: Box::new(r),
1769 ty: None,
1770 };
1771 match shape {
1772 Shape::BareAnd => {
1774 *st = Stmt::If {
1775 cond: sc(BinOp::LogAnd, c1, c2),
1776 then_stmt: t2,
1777 else_stmt: None,
1778 };
1779 }
1780 _ => {
1782 let body = f2.unwrap();
1783 *st = Stmt::If {
1784 cond: sc(
1785 BinOp::LogAnd,
1786 c1,
1787 Expr::Un {
1788 op: UnOp::Not,
1789 e: Box::new(c2),
1790 },
1791 ),
1792 then_stmt: body,
1793 else_stmt: None,
1794 };
1795 }
1796 }
1797 return true;
1798 }
1799 let Stmt::If {
1800 cond: c1,
1801 then_stmt,
1802 else_stmt: Some(els),
1803 } = taken
1804 else {
1805 unreachable!("shape probe guaranteed an If with else")
1806 };
1807 let sc = |op: BinOp, l: Expr, r: Expr| Expr::Bin {
1808 op,
1809 l: Box::new(l),
1810 r: Box::new(r),
1811 ty: None,
1812 };
1813 let not = |e: Expr| Expr::Un {
1814 op: UnOp::Not,
1815 e: Box::new(e),
1816 };
1817 match shape {
1818 Shape::BareAnd | Shape::BareAndNot => unreachable!("handled above"),
1819 Shape::ThenAnd | Shape::ThenAndNot => {
1820 let Stmt::If {
1821 cond: c2,
1822 then_stmt: t2,
1823 else_stmt: Some(f2),
1824 } = *then_stmt
1825 else {
1826 unreachable!()
1827 };
1828 let (nc, body, alt) = match shape {
1829 Shape::ThenAnd => (sc(BinOp::LogAnd, c1, c2), t2, f2),
1831 _ => (sc(BinOp::LogAnd, c1, not(c2)), f2, t2),
1833 };
1834 *st = Stmt::If {
1835 cond: nc,
1836 then_stmt: body,
1837 else_stmt: Some(alt),
1838 };
1839 }
1840 Shape::ElseOr | Shape::ElseOrNot => {
1841 let Stmt::If {
1842 cond: c2,
1843 then_stmt: t2,
1844 else_stmt: Some(f2),
1845 } = *els
1846 else {
1847 unreachable!()
1848 };
1849 let body = then_stmt;
1852 let (nc, alt) = match shape {
1853 Shape::ElseOr => (sc(BinOp::LogOr, c1, c2), f2),
1855 _ => (sc(BinOp::LogOr, c1, not(c2)), t2),
1857 };
1858 *st = Stmt::If {
1859 cond: nc,
1860 then_stmt: body,
1861 else_stmt: Some(alt),
1862 };
1863 }
1864 }
1865 true
1866}
1867
1868pub fn strip_clinit_returns(s: &mut Stmt) {
1874 walk_mut_deep(s, &mut |st| {
1875 if let Stmt::Block(v) = st {
1876 v.retain(|x| !matches!(x, Stmt::Return(None)));
1877 }
1878 });
1879}
1880
1881pub fn prepend_comment(s: &mut Stmt, text: String) {
1882 let stmt = Stmt::Comment(text);
1883 match s {
1884 Stmt::Block(v) => v.insert(0, stmt),
1885 other => {
1886 let inner = std::mem::replace(other, Stmt::Block(vec![]));
1887 *other = Stmt::Block(vec![stmt, inner]);
1888 }
1889 }
1890}
1891
1892fn is_obj_expr(e: &Expr, obj_var: &dyn Fn(u32) -> bool) -> bool {
1900 match e {
1901 Expr::Local { var, .. } => obj_var(*var),
1902 Expr::Const(_) => false,
1903 _ => e.type_ref().erased().is_reference(),
1904 }
1905}
1906
1907fn null_side_rewrite(x: &mut Expr, obj_var: &dyn Fn(u32) -> bool) {
1912 if let Expr::Bin { op, l, r, .. } = x {
1913 if !matches!(op, BinOp::Eq | BinOp::Ne) {
1914 return;
1915 }
1916 let l_zero = matches!(&**l, Expr::Const(ConstVal::Int(0)));
1917 let r_zero = matches!(&**r, Expr::Const(ConstVal::Int(0)));
1918 if l_zero && !r_zero && is_obj_expr(r, obj_var) {
1919 **l = Expr::Const(ConstVal::Null);
1920 } else if r_zero && !l_zero && is_obj_expr(l, obj_var) {
1921 **r = Expr::Const(ConstVal::Null);
1922 }
1923 }
1924}
1925
1926pub fn fused_expr_rewrites(s: &mut Stmt, vt: &VarTable) {
1927 let mut obj_vars: Vec<bool> = Vec::with_capacity(vt.vars.len());
1929 for v in &vt.vars {
1930 obj_vars.push(v.ty.erased().is_reference());
1931 }
1932 rewrite_exprs(s, &mut |e| {
1933 deep_rewrite(e, &mut |x| {
1934 if let Expr::Invokedynamic { name, args, .. } = x {
1936 if name.starts_with('\0') && args.len() == 2 {
1937 let (cls, ty) = match name.as_str() {
1938 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
1939 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
1940 _ => ("java/lang/Double", JavaType::Double),
1941 };
1942 let desc = MethodDescriptor {
1943 args: vec![ty.clone(), ty],
1944 ret: JavaType::Int,
1945 };
1946 *x = Expr::Method {
1947 owner: None,
1948 cls: cls.into(),
1949 name: "compare".into(),
1950 desc: std::sync::Arc::new(desc),
1951 args: args.clone(),
1952 is_static: true,
1953 is_interface: false,
1954 is_special: false,
1955 is_super: false,
1956 is_dynamic: false,
1957 type_args: vec![],
1958 };
1959 return;
1960 }
1961 }
1962 null_side_rewrite(x, &|v| obj_vars.get(v as usize).copied().unwrap_or(false));
1965 if let Expr::Bin { op, l, r, .. } = x {
1967 if matches!(
1968 op,
1969 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
1970 ) {
1971 let l_const = matches!(&**l, Expr::Const(_));
1972 let r_var = matches!(&**r, Expr::Local { .. });
1973 if l_const && r_var {
1974 std::mem::swap(l, r);
1975 }
1976 }
1977 }
1978 });
1979 });
1980 walk_mut_deep(s, &mut |st| {
1988 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = st {
1989 let tgt_is_obj = match &**target {
1990 Expr::Local { var, .. } => {
1991 obj_vars.get(*var as usize).copied().unwrap_or(false)
1992 }
1993 other => other.type_ref().erased().is_reference(),
1994 };
1995 if tgt_is_obj {
1996 if let Expr::Const(ConstVal::Int(0)) = &**value {
1997 **value = Expr::Const(ConstVal::Null);
1998 }
1999 }
2000 } else if let Stmt::LocalDef { var, init, .. } = st {
2001 if obj_vars.get(*var as usize).copied().unwrap_or(false) {
2002 if let Some(iv) = init {
2003 if let Expr::Const(ConstVal::Int(0)) = iv {
2004 *iv = Expr::Const(ConstVal::Null);
2005 }
2006 }
2007 }
2008 }
2009 });
2010}
2011
2012#[allow(dead_code)]
2015pub fn desugar_cmp_residuals(s: &mut Stmt) {
2016 rewrite_exprs(s, &mut |e| {
2017 deep_rewrite(e, &mut |x| {
2018 if let Expr::Invokedynamic { name, args, .. } = x {
2019 if name.starts_with('\0') && args.len() == 2 {
2020 let (cls, ty) = match name.as_str() {
2021 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
2022 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
2023 _ => ("java/lang/Double", JavaType::Double),
2024 };
2025 let desc = jdc_core::types::MethodDescriptor {
2026 args: vec![ty.clone(), ty],
2027 ret: JavaType::Int,
2028 };
2029 *x = Expr::Method {
2030 owner: None,
2031 cls: cls.into(),
2032 name: "compare".into(),
2033 desc: std::sync::Arc::new(desc),
2034 args: args.clone(),
2035 is_static: true,
2036 is_interface: false,
2037 is_special: false,
2038 is_super: false,
2039 is_dynamic: false,
2040 type_args: vec![],
2041 };
2042 }
2043 }
2044 });
2045 });
2046}
2047
2048pub fn ternary_fold(s: &mut Stmt) {
2050 let mut changed = true;
2051 let mut guard = 0;
2052 while changed && guard < 8 {
2053 changed = false;
2054 guard += 1;
2055 ternary_fold_walk(s, &mut changed);
2056 strip_empty(s);
2057 }
2058}
2059
2060fn ternary_fold_walk(s: &mut Stmt, changed: &mut bool) {
2061 if let Stmt::Block(v) = s {
2062 for x in v.iter_mut() {
2063 ternary_fold_walk(x, changed);
2064 }
2065 return;
2066 }
2067 let Stmt::If {
2068 cond,
2069 then_stmt,
2070 else_stmt,
2071 } = s
2072 else {
2073 return;
2074 };
2075 ternary_fold_walk(then_stmt, changed);
2076 if let Some(e) = else_stmt {
2077 ternary_fold_walk(e, changed);
2078 }
2079 let Some(else_b) = else_stmt else { return };
2080 let target_of = |st: &Stmt| -> Option<u32> {
2081 let Stmt::ExprStmt(Expr::Assign { target, .. }) = st else {
2082 return None;
2083 };
2084 if let Expr::Local { var, .. } = &**target {
2085 Some(*var)
2086 } else {
2087 None
2088 }
2089 };
2090 let (Stmt::Block(tv), Stmt::Block(ev)) = (then_stmt.as_mut(), else_b.as_mut()) else {
2091 return;
2092 };
2093 if tv.len() != 1 || ev.len() != 1 {
2094 return;
2095 }
2096 let (Some(vt_), Some(ve)) = (target_of(&tv[0]), target_of(&ev[0])) else {
2097 return;
2098 };
2099 if vt_ != ve {
2100 return;
2101 }
2102 let mut touched = HashSet::default();
2106 let (Stmt::ExprStmt(a_then), Stmt::ExprStmt(a_else)) = (&tv[0], &ev[0]) else {
2107 return;
2108 };
2109 if let Expr::Assign { value, .. } = a_then {
2110 collect_vars(value, &mut touched);
2111 }
2112 if let Expr::Assign { value, .. } = a_else {
2113 collect_vars(value, &mut touched);
2114 }
2115 if touched.contains(&vt_) {
2116 return;
2117 }
2118 let value_then = match a_then {
2119 Expr::Assign { value, .. } => value.clone(),
2120 _ => return,
2121 };
2122 let value_else = match a_else {
2123 Expr::Assign { value, .. } => value.clone(),
2124 _ => return,
2125 };
2126 let cond_e = cond.clone();
2127 let folded = Stmt::ExprStmt(Expr::Assign {
2128 target: Box::new(Expr::Local {
2129 var: vt_,
2130 ty: vt_join_ty(&value_then, &value_else),
2131 }),
2132 op: AssignOp::Plain,
2133 value: Box::new(Expr::Cond {
2134 c: Box::new(cond_e),
2135 t: value_then,
2136 f: value_else,
2137 }),
2138 });
2139 *s = Stmt::Block(vec![folded]);
2140 *changed = true;
2141}
2142
2143fn vt_join_ty(a: &Expr, b: &Expr) -> TypeRef {
2144 let ta = a.type_ref();
2145 let tb = b.type_ref();
2146 if ta.erased() == tb.erased() {
2147 return ta;
2148 }
2149 TypeRef::J(JavaType::Object("java/lang/Object".into()))
2150}
2151
2152pub fn fold_string_builders(s: &mut Stmt, vt: &VarTable) {
2159 let n_vars = vt.vars.len().max(1);
2161 let mut counts = vec![0usize; n_vars];
2162 count_assignments(s, &mut counts);
2163 let mut values: Vec<Option<Expr>> = vec![None; n_vars];
2164 record_assignments(s, &counts, &mut values);
2165
2166 let mut appended_stmts: HashSet<u32> = HashSet::default();
2168 walk_all(s, &mut |st| {
2169 if let Stmt::ExprStmt(Expr::Method {
2170 cls,
2171 name,
2172 owner,
2173 args,
2174 ..
2175 }) = st
2176 {
2177 if name.as_ref() == "append" && is_string_builder(cls) && args.len() == 1 {
2178 if let Some(Expr::Local { var, .. }) = owner.as_deref() {
2179 appended_stmts.insert(*var);
2180 }
2181 }
2182 }
2183 });
2184
2185 rewrite_exprs(s, &mut |e| {
2186 fold_concat_in_expr(e, &values, &appended_stmts);
2187 });
2188
2189 for _ in 0..8 {
2193 let mut reads: HashSet<u32> = HashSet::default();
2194 stmt_collect_vars(s, &mut reads, false);
2195 if drop_unused_assigns(s, &reads) == 0 {
2196 break;
2197 }
2198 }
2199}
2200
2201pub fn count_stmts_deep(s: &Stmt, out: &mut usize) {
2203 *out += 1;
2204 walk_all(s, &mut |_| {
2205 *out += 1;
2206 });
2207}
2208
2209struct VarAnalysis {
2211 assigns: Vec<usize>,
2212 reads: Vec<usize>,
2213 values: Vec<Option<Expr>>,
2215}
2216
2217fn analyze_vars(s: &Stmt, a: &mut VarAnalysis) {
2218 walk_all(s, &mut |st| {
2219 let exprs: Vec<&Expr> = match st {
2220 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
2221 if let Expr::Local { var, .. } = &**target {
2222 grow_to(&mut a.assigns, *var);
2223 let idx = *var as usize;
2224 a.assigns[idx] += 1;
2225 if a.assigns[idx] == 1 {
2226 if idx >= a.values.len() {
2227 a.values.resize(idx + 1, None);
2228 }
2229 a.values[idx] = Some((**value).clone());
2230 } else if idx < a.values.len() {
2231 a.values[idx] = None;
2232 }
2233 }
2234 let mut v: Vec<&Expr> = vec![value];
2235 if !matches!(&**target, Expr::Local { .. }) {
2236 v.push(target);
2237 }
2238 v
2239 }
2240 Stmt::ExprStmt(e @ (Expr::PreIncDec { .. } | Expr::PostIncDec { .. })) => {
2241 let inner = match e {
2246 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => e,
2247 _ => unreachable!(),
2248 };
2249 if let Expr::Local { var, .. } = &**inner {
2250 grow_to(&mut a.assigns, *var);
2251 let idx = *var as usize;
2252 a.assigns[idx] += 1;
2253 if idx < a.values.len() {
2254 a.values[idx] = None;
2255 }
2256 return;
2257 }
2258 vec![e]
2259 }
2260 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
2261 vec![e]
2262 }
2263 Stmt::Return(Some(e)) => vec![e],
2264 Stmt::LocalDef {
2265 var, init: Some(e), ..
2266 } => {
2267 grow_to(&mut a.assigns, *var);
2268 let idx = *var as usize;
2269 a.assigns[idx] += 1;
2270 if a.assigns[idx] == 1 {
2271 if idx >= a.values.len() {
2272 a.values.resize(idx + 1, None);
2273 }
2274 a.values[idx] = Some(e.clone());
2275 } else if idx < a.values.len() {
2276 a.values[idx] = None;
2277 }
2278 vec![e]
2279 }
2280 Stmt::If { cond, .. } => vec![cond],
2281 Stmt::While { cond, .. } => vec![cond],
2282 Stmt::DoWhile { cond, .. } => vec![cond],
2283 _ => Vec::new(),
2284 };
2285 for e in exprs {
2286 visit_exprs(e, &mut |x| {
2287 if let Expr::Local { var, .. } = x {
2288 grow_to(&mut a.reads, *var);
2289 a.reads[*var as usize] += 1;
2290 }
2291 });
2292 }
2293 });
2294 let n = a.assigns.len().max(a.reads.len()).max(a.values.len());
2296 a.assigns.resize(n, 0);
2297 a.reads.resize(n, 0);
2298 a.values.resize(n, None);
2299}
2300
2301fn grow_to(v: &mut Vec<usize>, var: u32) {
2302 if var as usize >= v.len() {
2303 v.resize(var as usize + 1, 0);
2304 }
2305}
2306
2307fn count_assignments(s: &Stmt, counts: &mut Vec<usize>) {
2308 walk_all(s, &mut |st| match st {
2309 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
2310 if let Expr::Local { var, .. } = &**target {
2311 grow_to(counts, *var);
2312 counts[*var as usize] += 1;
2313 }
2314 }
2315 Stmt::LocalDef {
2316 var, init: Some(_), ..
2317 } => {
2318 grow_to(counts, *var);
2319 counts[*var as usize] += 1;
2320 }
2321 _ => {}
2322 });
2323}
2324
2325fn record_assignments(s: &Stmt, counts: &[usize], out: &mut Vec<Option<Expr>>) {
2326 walk_all(s, &mut |st| match st {
2327 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
2328 if let Expr::Local { var, .. } = &**target {
2329 if counts.get(*var as usize).copied().unwrap_or(0) == 1 {
2330 if *var as usize >= out.len() {
2331 out.resize(*var as usize + 1, None);
2332 }
2333 out[*var as usize] = Some((**value).clone());
2334 }
2335 }
2336 }
2337 Stmt::LocalDef {
2338 var, init: Some(e), ..
2339 } if counts.get(*var as usize).copied().unwrap_or(0) == 1 => {
2340 if *var as usize >= out.len() {
2341 out.resize(*var as usize + 1, None);
2342 }
2343 out[*var as usize] = Some(e.clone());
2344 }
2345 _ => {}
2346 });
2347}
2348
2349fn fold_concat_in_expr(e: &mut Expr, values: &[Option<Expr>], appended: &HashSet<u32>) {
2350 deep_rewrite(e, &mut |x| {
2351 if let Expr::Method {
2352 cls,
2353 name,
2354 args,
2355 owner,
2356 ..
2357 } = x
2358 {
2359 if name.as_ref() == "toString" && args.is_empty() && is_string_builder(cls) {
2360 if let Some(o) = owner {
2361 let mut has_stmt_appends = false;
2364 visit_exprs(o, &mut |y| {
2365 if let Expr::Local { var, .. } = y {
2366 if appended.contains(var) {
2367 has_stmt_appends = true;
2368 }
2369 }
2370 });
2371 if has_stmt_appends {
2372 return;
2373 }
2374 let resolved = resolve_local(o, values);
2375 if let Some(parts) = collect_sb_parts(&resolved, values, 0) {
2376 if !parts.is_empty() {
2385 *x = Expr::StringConcat(parts);
2386 }
2387 }
2388 }
2389 }
2390 }
2391 });
2392}
2393
2394fn is_string_builder(cls: &str) -> bool {
2395 cls == "java/lang/StringBuilder" || cls == "java/lang/StringBuffer"
2396}
2397
2398fn resolve_local(e: &Expr, values: &[Option<Expr>]) -> Expr {
2399 match e {
2400 Expr::Local { var, ty } => values
2401 .get(*var as usize)
2402 .and_then(|o| o.clone())
2403 .unwrap_or_else(|| Expr::Local {
2404 var: *var,
2405 ty: ty.clone(),
2406 }),
2407 other => other.clone(),
2408 }
2409}
2410
2411fn collect_sb_parts(e: &Expr, values: &[Option<Expr>], depth: u32) -> Option<Vec<ConcatPart>> {
2413 if depth > 16 {
2414 return None;
2415 }
2416 match e {
2417 Expr::Method {
2418 cls,
2419 name,
2420 args,
2421 owner,
2422 ..
2423 } if name.as_ref() == "append" && is_string_builder(cls) && args.len() == 1 => {
2424 let mut parts =
2425 collect_sb_parts(&resolve_local(owner.as_deref()?, values), values, depth + 1)?;
2426 parts.push(ConcatPart::Str(args[0].clone()));
2427 Some(parts)
2428 }
2429 Expr::New {
2430 cls,
2431 args,
2432 raw: false,
2433 ..
2434 } if is_string_builder(cls) => {
2435 let mut parts = Vec::new();
2436 for a in args {
2437 if let Expr::Const(ConstVal::Str(sv)) = a {
2438 parts.push(ConcatPart::Const(sv.to_string()));
2439 } else {
2440 parts.push(ConcatPart::Str(a.clone()));
2441 }
2442 }
2443 Some(parts)
2444 }
2445 _ => None,
2446 }
2447}
2448
2449fn drop_unused_assigns(s: &mut Stmt, reads: &HashSet<u32>) -> usize {
2450 let mut dropped = 0usize;
2451 walk_mut_deep(s, &mut |st| {
2452 if let Stmt::Block(v) = st {
2453 let before = v.len();
2454 v.retain(|x| match x {
2455 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
2458 Expr::Local { var, .. } => reads.contains(var) || !is_sbish(value),
2459 _ => true,
2460 },
2461 Stmt::LocalDef { var, init, .. } => {
2462 reads.contains(var) || !init.as_ref().map(is_sbish).unwrap_or(false)
2463 }
2464 _ => true,
2465 });
2466 dropped += before - v.len();
2467 }
2468 });
2469 dropped
2470}
2471
2472fn is_sbish(e: &Expr) -> bool {
2473 match e {
2474 Expr::New {
2475 cls, raw: false, ..
2476 } => is_string_builder(cls),
2477 Expr::Method { name, cls, .. } => name.as_ref() == "append" && is_string_builder(cls),
2478 _ => false,
2479 }
2480}
2481
2482fn walk_mut_deep<F: FnMut(&mut Stmt)>(s: &mut Stmt, f: &mut F) {
2483 f(s);
2484 walk_mut(s, &mut |x| walk_mut_deep(x, f));
2485}
2486
2487pub fn drop_empty_finallies(body: &mut Stmt) {
2503 fn is_empty_block(s: &Stmt) -> bool {
2504 match s {
2505 Stmt::Block(v) => v.iter().all(is_empty_block),
2506 _ => false,
2507 }
2508 }
2509 walk_mut_deep(body, &mut |st| {
2510 let Stmt::Try {
2511 body: b,
2512 catches,
2513 finally,
2514 } = st
2515 else {
2516 return;
2517 };
2518 if !catches.is_empty() {
2519 return;
2520 }
2521 match finally {
2522 None => {}
2526 Some(f) if is_empty_block(f) => {}
2527 Some(_) => return,
2528 }
2529 let inner = std::mem::replace(&mut **b, Stmt::Block(vec![]));
2530 *st = inner;
2531 });
2532}
2533
2534pub fn drop_pure_value_stmts(body: &mut Stmt) {
2539 fn is_pure(s: &Stmt) -> bool {
2542 let Stmt::ExprStmt(e) = s else {
2543 return false;
2544 };
2545 !has_side_effects(e)
2546 }
2547 walk_mut_deep(body, &mut |st| {
2548 if is_pure(st) {
2551 *st = Stmt::Block(vec![]);
2552 return;
2553 }
2554 match st {
2555 Stmt::Switch { cases, .. } => {
2556 for c in cases.iter_mut() {
2557 c.body.retain(|x| !is_pure(x));
2558 }
2559 }
2560 Stmt::For { init, .. } => init.retain(|x| !is_pure(x)),
2561 _ => {}
2562 }
2563 });
2564}
2565
2566pub fn drop_dead_raw_news(body: &mut Stmt) {
2567 fn is_orphan(s: &Stmt) -> bool {
2568 matches!(s, Stmt::ExprStmt(Expr::New { raw: true, .. }))
2569 }
2570 walk_mut_deep(body, &mut |st| match st {
2571 Stmt::Block(v) => v.retain(|x| !is_orphan(x)),
2572 Stmt::Switch { cases, .. } => {
2573 for c in cases.iter_mut() {
2574 c.body.retain(|x| !is_orphan(x));
2575 }
2576 }
2577 Stmt::For { init, .. } => init.retain(|x| !is_orphan(x)),
2578 Stmt::TryWithResources { resources, .. } => resources.retain(|x| !is_orphan(x)),
2579 _ => {}
2580 });
2581}
2582
2583pub fn fold_synchronized(s: &mut Stmt) {
2587 fold_sync_walk(s);
2588}
2589
2590
2591fn fold_sync_walk(s: &mut Stmt) {
2592 walk_mut_deep(s, &mut |st| {
2593 let Stmt::Block(v) = st else { return };
2594 let mut i = 0;
2595 while i < v.len() {
2596 #[allow(unused_assignments)]
2600 let mut advance = true;
2601 if i + 1 < v.len() {
2602 if let Some((repl, consumed)) = try_sync_at(v, i) {
2603 advance = !matches!(repl.first(), Some(Stmt::Synchronized { .. }));
2604 let _ = v.drain(i..i + consumed);
2605 let n = repl.len();
2606 v.splice(i..i, repl);
2607 if advance {
2608 i += n;
2609 }
2610 continue;
2611 }
2612 }
2613 if let Some((repl, consumed)) = try_sync_in_try(v, i) {
2614 let _ = v.drain(i..i + consumed);
2615 let n = repl.len();
2616 v.splice(i..i, repl);
2617 i += n;
2618 continue;
2619 }
2620 if let Some((repl, consumed)) = try_sync_deep(v, i) {
2621 let _ = v.drain(i..i + consumed);
2622 let n = repl.len();
2623 v.splice(i..i, repl);
2624 i += n;
2625 continue;
2626 }
2627 if let Some((repl, consumed)) = try_sync_absorb_after(v, i) {
2628 let _ = v.drain(i..i + consumed);
2629 let n = repl.len();
2630 v.splice(i..i, repl);
2631 i += n;
2632 continue;
2633 }
2634 i += 1;
2635 }
2636 });
2637}
2638
2639fn is_pure_read_expr(e: &Expr) -> bool {
2643 let mut pure = true;
2644 visit_exprs(e, &mut |x| match x {
2645 Expr::Const(_)
2646 | Expr::Local { .. }
2647 | Expr::This
2648 | Expr::Field { .. } => {}
2649 _ => pure = false,
2650 });
2651 pure
2652}
2653
2654fn resolve_lock(e: &Expr, defs: &jdc_core::FxHashMap<u32, Expr>, depth: u32) -> Expr {
2659 if depth >= 4 {
2660 return e.clone();
2661 }
2662 if let Expr::Local { var, .. } = e {
2663 if let Some(d) = defs.get(var) {
2664 return resolve_lock(d, defs, depth + 1);
2665 }
2666 }
2667 e.clone()
2668}
2669
2670fn lock_same(a: &Expr, b: &Expr) -> bool {
2671 match (a, b) {
2672 (Expr::This, Expr::This) => true,
2673 (Expr::Local { var: x, .. }, Expr::Local { var: y, .. }) => x == y,
2674 (
2675 Expr::Field {
2676 owner: oa,
2677 cls: ca,
2678 name: na,
2679 ..
2680 },
2681 Expr::Field {
2682 owner: ob,
2683 cls: cb,
2684 name: nb,
2685 ..
2686 },
2687 ) => {
2688 ca == cb
2689 && na == nb
2690 && match (oa, ob) {
2691 (None, None) => true,
2692 (Some(ra), Some(rb)) => lock_same(ra, rb),
2693 _ => false,
2694 }
2695 }
2696 (Expr::Const(x), Expr::Const(y)) => x == y,
2697 _ => false,
2698 }
2699}
2700
2701fn lock_equiv(a: &Expr, b: &Expr, defs: &jdc_core::FxHashMap<u32, Expr>) -> bool {
2702 lock_same(&resolve_lock(a, defs, 0), &resolve_lock(b, defs, 0))
2703}
2704
2705fn flatten_leaves<'a>(s: &'a Stmt, out: &mut Vec<&'a Stmt>) {
2706 match s {
2707 Stmt::Block(v) => {
2708 for x in v {
2709 flatten_leaves(x, out);
2710 }
2711 }
2712 _ => out.push(s),
2713 }
2714}
2715
2716fn strip_lock_exits(s: &mut Stmt, lock: &Expr, defs: &jdc_core::FxHashMap<u32, Expr>) {
2720 walk_mut_deep(s, &mut |st| {
2721 let Stmt::Block(v) = st else { return };
2722 v.retain(|x| !matches!(x, Stmt::MonitorExit(e) if lock_equiv(e, lock, defs)));
2723 });
2724}
2725
2726fn try_sync_at(v: &[Stmt], i: usize) -> Option<(Vec<Stmt>, usize)> {
2727 let mut defs: jdc_core::FxHashMap<u32, Expr> = jdc_core::FxHashMap::default();
2729 for s in &v[..i] {
2730 if let Stmt::LocalDef { var, init: Some(e), .. } = s {
2731 defs.insert(*var, e.clone());
2732 }
2733 }
2734 let (pre, enter_e, mut j, mut absorbed): (Vec<Stmt>, &Expr, usize, Vec<Stmt>) = match &v[i] {
2738 Stmt::MonitorEnter(e) => (Vec::new(), e, i + 1, Vec::new()),
2739 Stmt::Block(b) => {
2740 let split = b
2741 .iter()
2742 .position(|x| matches!(x, Stmt::MonitorEnter(_)))?;
2743 let e = match &b[split] {
2744 Stmt::MonitorEnter(e) => e,
2745 _ => unreachable!(),
2746 };
2747 let mut absorbed: Vec<Stmt> = Vec::new();
2748 let mut k = split + 1;
2749 while let Some(Stmt::LocalDef { var, init: Some(ex), .. }) = b.get(k) {
2750 if !is_pure_read_expr(ex) {
2751 break;
2752 }
2753 defs.insert(*var, ex.clone());
2754 absorbed.push(b[k].clone());
2755 k += 1;
2756 }
2757 if k != b.len() {
2760 return None;
2761 }
2762 let pre: Vec<Stmt> = b[..split].to_vec();
2763 for s in &pre {
2764 if let Stmt::LocalDef { var, init: Some(e2), .. } = s {
2765 defs.insert(*var, e2.clone());
2766 }
2767 }
2768 (pre, e, i + 1, absorbed)
2769 }
2770 _ => return None,
2771 };
2772 let mut absorbed_vars: Vec<u32> = Vec::new();
2776 while let Some(Stmt::LocalDef { var, init: Some(e), .. }) = v.get(j) {
2777 if !is_pure_read_expr(e) {
2778 break;
2779 }
2780 defs.insert(*var, e.clone());
2781 absorbed_vars.push(*var);
2782 absorbed.push(v[j].clone());
2783 j += 1;
2784 }
2785 let Stmt::Try {
2786 body,
2787 catches,
2788 finally,
2789 } = v.get(j)?
2790 else {
2791 return None;
2792 };
2793 if finally.is_some() || catches.len() != 1 {
2794 return None;
2795 }
2796 let c = &catches[0];
2797 if !c.exc.is_empty() {
2798 return None;
2799 }
2800 let mut flat: Vec<&Stmt> = Vec::new();
2803 flatten_leaves(c.body.as_ref(), &mut flat);
2804 if flat.len() < 2 {
2805 return None;
2806 }
2807 let Stmt::MonitorExit(exit_e) = flat[0] else {
2808 return None;
2809 };
2810 if !lock_equiv(enter_e, exit_e, &defs) {
2811 return None;
2812 }
2813 if !matches!(flat[flat.len() - 1], Stmt::Throw(_)) {
2814 return None;
2815 }
2816 for mid in &flat[1..flat.len() - 1] {
2817 if !matches!(mid, Stmt::MonitorEnter(_) | Stmt::MonitorExit(_)) {
2818 return None;
2819 }
2820 }
2821 if !absorbed_vars.is_empty() {
2824 let outside = count_locals_stmts(&v[j + 1..]);
2825 if absorbed_vars
2826 .iter()
2827 .any(|av| outside.get(av).copied().unwrap_or(0) > 0)
2828 {
2829 return None;
2830 }
2831 }
2832 let mut body_stmt = (**body).clone();
2833 strip_lock_exits(&mut body_stmt, enter_e, &defs);
2834 absorbed.push(body_stmt);
2835 let mut consumed = j - i + 1;
2836 if let Some(Stmt::MonitorExit(e)) = v.get(j + 1) {
2839 if lock_equiv(e, enter_e, &defs) {
2840 consumed += 1;
2841 }
2842 }
2843 let mut repl = pre;
2844 repl.push(Stmt::Synchronized {
2845 lock: enter_e.clone(),
2846 body: Box::new(Stmt::Block(absorbed)),
2847 });
2848 Some((repl, consumed))
2849}
2850
2851fn try_sync_in_try(v: &[Stmt], i: usize) -> Option<(Vec<Stmt>, usize)> {
2857 let Stmt::Try {
2858 body,
2859 catches,
2860 finally,
2861 } = &v[i]
2862 else {
2863 return None;
2864 };
2865 if finally.is_some() || catches.len() != 1 {
2866 return None;
2867 }
2868 let c = &catches[0];
2869 if !c.exc.is_empty() {
2870 return None;
2871 }
2872 let mut flat: Vec<&Stmt> = Vec::new();
2873 flatten_leaves(c.body.as_ref(), &mut flat);
2874 if flat.len() < 2 {
2875 return None;
2876 }
2877 let Stmt::MonitorExit(_) = flat[0] else {
2878 return None;
2879 };
2880 if !matches!(flat[flat.len() - 1], Stmt::Throw(_)) {
2881 return None;
2882 }
2883 for mid in &flat[1..flat.len() - 1] {
2884 if !matches!(mid, Stmt::MonitorEnter(_) | Stmt::MonitorExit(_)) {
2885 return None;
2886 }
2887 }
2888 let Stmt::Block(bv) = body.as_ref() else {
2892 return None;
2893 };
2894 let mut defs: jdc_core::FxHashMap<u32, Expr> = jdc_core::FxHashMap::default();
2895 for s in &v[..i] {
2896 if let Stmt::LocalDef { var, init: Some(e), .. } = s {
2897 defs.insert(*var, e.clone());
2898 }
2899 }
2900 let mut pre: Vec<Stmt> = Vec::new();
2901 let mut enter_e: Option<Expr> = None;
2902 let mut absorbed: Vec<Stmt> = Vec::new();
2903 let mut absorbed_vars: Vec<u32> = Vec::new();
2904 let mut rest_start: Option<usize> = None;
2905 for (p, el) in bv.iter().enumerate() {
2906 match el {
2907 Stmt::MonitorEnter(e) => {
2908 enter_e = Some(e.clone());
2909 rest_start = Some(p + 1);
2910 break;
2911 }
2912 Stmt::Block(inner) => {
2913 let split = inner
2914 .iter()
2915 .position(|x| matches!(x, Stmt::MonitorEnter(_)));
2916 if let Some(sp) = split {
2917 let mut k = sp + 1;
2918 let mut ok = true;
2919 let mut tail: Vec<Stmt> = Vec::new();
2920 while let Some(Stmt::LocalDef { var: _, init: Some(ex), .. }) = inner.get(k) {
2921 if !is_pure_read_expr(ex) {
2922 ok = false;
2923 break;
2924 }
2925 tail.push(inner[k].clone());
2926 k += 1;
2927 }
2928 if ok && k == inner.len() {
2929 for s in &inner[..sp] {
2930 if let Stmt::LocalDef { var, init: Some(ex), .. } = s {
2931 defs.insert(*var, ex.clone());
2932 }
2933 }
2934 pre.extend(inner[..sp].iter().cloned());
2935 enter_e = Some(match &inner[sp] {
2936 Stmt::MonitorEnter(e) => e.clone(),
2937 _ => unreachable!(),
2938 });
2939 absorbed = tail;
2940 rest_start = Some(p + 1);
2941 break;
2942 }
2943 }
2944 for s in inner.iter() {
2946 if let Stmt::LocalDef { var, init: Some(ex), .. } = s {
2947 defs.insert(*var, ex.clone());
2948 }
2949 }
2950 pre.push(el.clone());
2951 }
2952 other => {
2953 if let Stmt::LocalDef { var, init: Some(ex), .. } = other {
2954 defs.insert(*var, ex.clone());
2955 }
2956 pre.push(other.clone());
2957 }
2958 }
2959 }
2960 let enter_e = enter_e?;
2961 let rs = rest_start?;
2962 let mut q = rs;
2964 while let Some(Stmt::LocalDef { var, init: Some(ex), .. }) = bv.get(q) {
2965 if !is_pure_read_expr(ex) {
2966 break;
2967 }
2968 defs.insert(*var, ex.clone());
2969 absorbed_vars.push(*var);
2970 absorbed.push(bv[q].clone());
2971 q += 1;
2972 }
2973 let rest: Vec<Stmt> = bv[q..].to_vec();
2974 if rest.is_empty() {
2975 return None;
2976 }
2977 if pre
2980 .iter()
2981 .any(|s| !matches!(s, Stmt::LocalDef { init: Some(e), .. } if is_pure_read_expr(e)))
2982 {
2983 return None;
2984 }
2985 let Stmt::MonitorExit(exit_e) = flat[0] else {
2987 return None;
2988 };
2989 if !lock_equiv(&enter_e, exit_e, &defs) {
2990 return None;
2991 }
2992 let outside_vars: Vec<u32> = pre
2994 .iter()
2995 .chain(absorbed.iter())
2996 .filter_map(|s| match s {
2997 Stmt::LocalDef { var, .. } => Some(*var),
2998 _ => None,
2999 })
3000 .collect();
3001 if !outside_vars.is_empty() {
3002 let outside = count_locals_stmts(&v[i + 1..]);
3003 if outside_vars
3004 .iter()
3005 .any(|av| outside.get(av).copied().unwrap_or(0) > 0)
3006 {
3007 return None;
3008 }
3009 }
3010 let mut body_stmts = pre;
3011 body_stmts.extend(absorbed);
3012 body_stmts.extend(rest);
3013 let mut sync_body = Stmt::Block(body_stmts);
3014 strip_lock_exits(&mut sync_body, &enter_e, &defs);
3015 let mut consumed = 1;
3016 if let Some(Stmt::MonitorExit(e)) = v.get(i + 1) {
3017 if lock_equiv(e, &enter_e, &defs) {
3018 consumed += 1;
3019 }
3020 }
3021 Some((
3022 vec![Stmt::Synchronized {
3023 lock: enter_e,
3024 body: Box::new(sync_body),
3025 }],
3026 consumed,
3027 ))
3028}
3029
3030fn try_sync_absorb_after(v: &[Stmt], i: usize) -> Option<(Vec<Stmt>, usize)> {
3040 let Stmt::Synchronized { lock, body } = &v[i] else {
3041 return None;
3042 };
3043 let Stmt::Try {
3044 body: tbody,
3045 catches,
3046 finally,
3047 } = v.get(i + 1)?
3048 else {
3049 return None;
3050 };
3051 if finally.is_some() {
3052 return None;
3053 }
3054 let defs = defs_probe(v, i, body);
3060 let mut absorbed_mid: Vec<Stmt> = Vec::new();
3061 let mut rest_after: Option<Stmt> = None;
3062 let mut found = false;
3063 let mut scan = i + 2;
3064 'scan: while scan < v.len() && scan < i + 14 {
3065 match &v[scan] {
3066 Stmt::MonitorExit(e) => {
3067 if !lock_equiv(e, lock, &defs) {
3068 return None;
3069 }
3070 found = true;
3071 scan += 1;
3072 break;
3073 }
3074 Stmt::Block(bv) => {
3075 for (k, s) in bv.iter().enumerate() {
3076 match s {
3077 Stmt::MonitorExit(e) => {
3078 if !lock_equiv(e, lock, &defs) {
3079 return None;
3080 }
3081 absorbed_mid.push(Stmt::Block(bv[..k].to_vec()));
3082 let rest = &bv[k + 1..];
3083 rest_after = (!rest.is_empty())
3084 .then(|| Stmt::Block(rest.to_vec()));
3085 found = true;
3086 scan += 1;
3087 break 'scan;
3088 }
3089 Stmt::ExprStmt(e) if mid_safe(e) || is_local_commit(e) => {}
3090 _ => break 'scan,
3091 }
3092 }
3093 absorbed_mid.push(v[scan].clone());
3094 scan += 1;
3095 }
3096 Stmt::ExprStmt(e) if mid_safe(e) || is_local_commit(e) => {
3097 absorbed_mid.push(v[scan].clone());
3098 scan += 1;
3099 }
3100 _ => break,
3101 }
3102 }
3103 if !found {
3104 return None;
3105 }
3106 let consumed = scan - i;
3107 let mut t = (**tbody).clone();
3110 let defs = defs_probe(v, i, body);
3111 strip_lock_exits(&mut t, lock, &defs);
3112 let mut cs = Vec::with_capacity(catches.len());
3113 for c in catches.iter() {
3114 let mut cb = (*c.body).clone();
3115 strip_lock_exits(&mut cb, lock, &defs);
3116 let bare_rethrow = c.exc.is_empty() && {
3120 let mut leaves: Vec<&Stmt> = Vec::new();
3121 flatten_leaves(&cb, &mut leaves);
3122 matches!(leaves.as_slice(), [Stmt::Throw(Expr::Local { var, .. })] if *var == c.var)
3123 };
3124 if bare_rethrow {
3125 continue;
3126 }
3127 let mut c2 = c.clone();
3128 *c2.body = cb;
3129 cs.push(c2);
3130 }
3131 let mut new_body = (**body).clone();
3132 let adopted = Stmt::Try {
3133 body: Box::new(t),
3134 catches: cs,
3135 finally: None,
3136 };
3137 match &mut new_body {
3138 Stmt::Block(bv) => bv.push(adopted),
3139 other => {
3140 let inner = std::mem::replace(other, Stmt::Block(vec![]));
3141 *other = Stmt::Block(vec![inner, adopted]);
3142 }
3143 }
3144 if let Stmt::Block(bv) = &mut new_body {
3145 bv.extend(absorbed_mid);
3146 }
3147 let sync = Stmt::Synchronized {
3148 lock: lock.clone(),
3149 body: Box::new(new_body),
3150 };
3151 let mut repl = vec![sync];
3152 if let Some(r) = rest_after {
3153 repl.push(r);
3154 }
3155 Some((repl, consumed))
3156}
3157
3158fn mid_safe(e: &Expr) -> bool {
3162 !has_side_effects(e)
3163}
3164
3165fn is_local_commit(e: &Expr) -> bool {
3167 match e {
3168 Expr::Assign { target, op: AssignOp::Plain, .. } => {
3169 matches!(&**target, Expr::Local { .. })
3170 }
3171 _ => false,
3172 }
3173}
3174
3175fn defs_probe<'a>(
3178 v: &'a [Stmt],
3179 i: usize,
3180 body: &'a Stmt,
3181) -> jdc_core::FxHashMap<u32, Expr> {
3182 let mut defs: jdc_core::FxHashMap<u32, Expr> = jdc_core::FxHashMap::default();
3183 for s in &v[..i] {
3184 if let Stmt::LocalDef { var, init: Some(e), .. } = s {
3185 defs.insert(*var, e.clone());
3186 }
3187 }
3188 if let Stmt::Block(bv) = body {
3189 for s in bv {
3190 if let Stmt::LocalDef { var, init: Some(e), .. } = s {
3191 defs.insert(*var, e.clone());
3192 }
3193 }
3194 }
3195 defs
3196}
3197
3198
3199fn try_sync_deep(v: &[Stmt], i: usize) -> Option<(Vec<Stmt>, usize)> {
3208 let Stmt::Try {
3209 body,
3210 catches,
3211 finally,
3212 } = &v[i]
3213 else {
3214 return None;
3215 };
3216 if finally.is_some() || catches.len() != 1 {
3217 return None;
3218 }
3219 let c = &catches[0];
3220 if !c.exc.is_empty() {
3221 return None;
3222 }
3223 let mut flat: Vec<&Stmt> = Vec::new();
3224 flatten_leaves(c.body.as_ref(), &mut flat);
3225 if flat.len() < 2 {
3226 return None;
3227 }
3228 let catch_exit = match flat[0] {
3229 Stmt::MonitorExit(e) => e.clone(),
3230 _ => return None,
3231 };
3232 if !matches!(flat[flat.len() - 1], Stmt::Throw(_)) {
3233 return None;
3234 }
3235 for mid in &flat[1..flat.len() - 1] {
3236 if !matches!(mid, Stmt::MonitorEnter(_) | Stmt::MonitorExit(_)) {
3237 return None;
3238 }
3239 }
3240 let mut defs0: jdc_core::FxHashMap<u32, Expr> = jdc_core::FxHashMap::default();
3241 for s in &v[..i] {
3242 if let Stmt::LocalDef { var, init: Some(e), .. } = s {
3243 defs0.insert(*var, e.clone());
3244 }
3245 }
3246 let mut body_mut = (**body).clone();
3247 let folded = match &mut body_mut {
3248 Stmt::Block(b) => try_fold_spine(b, &catch_exit, &defs0),
3249 Stmt::If {
3250 then_stmt,
3251 else_stmt,
3252 ..
3253 } => {
3254 let mut hit = false;
3255 if let Stmt::Block(b) = &mut **then_stmt {
3256 hit |= try_fold_spine(b, &catch_exit, &defs0);
3257 }
3258 if !hit {
3259 if let Some(els) = else_stmt {
3260 if let Stmt::Block(b) = &mut **els {
3261 hit |= try_fold_spine(b, &catch_exit, &defs0);
3262 }
3263 }
3264 }
3265 hit
3266 }
3267 _ => false,
3268 };
3269 if !folded {
3270 return None;
3271 }
3272 let throw = flat[flat.len() - 1].clone();
3274 let mut catches_out = catches.clone();
3275 *catches_out[0].body = Stmt::Block(vec![throw]);
3276 let mut consumed = 1;
3277 if let Some(Stmt::MonitorExit(e)) = v.get(i + 1) {
3278 if lock_equiv(e, &catch_exit, &defs0) {
3279 consumed += 1;
3280 }
3281 }
3282 Some((
3283 vec![Stmt::Try {
3284 body: Box::new(body_mut),
3285 catches: catches_out,
3286 finally: None,
3287 }],
3288 consumed,
3289 ))
3290}
3291
3292fn try_fold_spine(
3298 seq: &mut Vec<Stmt>,
3299 catch_exit: &Expr,
3300 defs_in: &jdc_core::FxHashMap<u32, Expr>,
3301) -> bool {
3302 for p in (0..seq.len()).rev() {
3303 if let Stmt::MonitorEnter(e0) = &seq[p] {
3305 let mut defs = defs_in.clone();
3306 for s in &seq[..p] {
3307 if let Stmt::LocalDef { var, init: Some(ex), .. } = s {
3308 defs.insert(*var, ex.clone());
3309 }
3310 }
3311 for s in &seq[p + 1..] {
3314 match s {
3315 Stmt::LocalDef { var, init: Some(ex), .. } if is_pure_read_expr(ex) => {
3316 defs.insert(*var, ex.clone());
3317 }
3318 _ => break,
3319 }
3320 }
3321 if !lock_equiv(e0, catch_exit, &defs) {
3322 return false;
3323 }
3324 let enter_e = e0.clone();
3325 let rest: Vec<Stmt> = seq.split_off(p + 1);
3326 let mut body = Stmt::Block(rest);
3327 strip_lock_exits(&mut body, &enter_e, &defs);
3328 seq[p] = Stmt::Synchronized {
3329 lock: enter_e,
3330 body: Box::new(body),
3331 };
3332 return true;
3333 }
3334 let is_last = p + 1 == seq.len();
3337 let mut trial = seq[p].clone();
3338 let hit = match &mut trial {
3339 Stmt::Block(b) => try_fold_spine(b, catch_exit, defs_in),
3340 Stmt::If {
3341 then_stmt,
3342 else_stmt,
3343 ..
3344 } => {
3345 let mut hit = false;
3346 if let Stmt::Block(b) = &mut **then_stmt {
3347 hit |= try_fold_spine(b, catch_exit, defs_in);
3348 }
3349 if !hit {
3350 if let Some(els) = else_stmt {
3351 if let Stmt::Block(b) = &mut **els {
3352 hit |= try_fold_spine(b, catch_exit, defs_in);
3353 }
3354 }
3355 }
3356 hit
3357 }
3358 _ => false,
3359 };
3360 if hit {
3361 if is_last {
3362 seq[p] = trial;
3363 return true;
3364 }
3365 return false;
3366 }
3367 }
3368 false
3369}
3370
3371pub fn forward_single_use(s: &mut Stmt, _vt: &VarTable) {
3376 let mut analysis = VarAnalysis {
3380 assigns: Vec::new(),
3381 reads: Vec::new(),
3382 values: Vec::new(),
3383 };
3384 analyze_vars(s, &mut analysis);
3385 let mut assigns = analysis.assigns;
3386 let mut reads = analysis.reads;
3387 let n_vars = assigns.len().max(_vt.vars.len()).max(1);
3390 assigns.resize(n_vars, 0);
3391 reads.resize(n_vars, 0);
3392 let mut values = analysis.values;
3393 values.resize(n_vars, None);
3394
3395 let mut cand = vec![false; n_vars];
3400 let mut edges: Vec<Vec<u32>> = vec![Vec::new(); n_vars];
3401 for (v, cand_v) in cand.iter_mut().enumerate() {
3402 if assigns.get(v).copied().unwrap_or(0) != 1 || reads.get(v).copied().unwrap_or(0) != 1 {
3403 continue;
3404 }
3405 if let Some(val) = values.get(v).and_then(|o| o.as_ref()) {
3406 let mut refs = HashSet::default();
3407 collect_vars(val, &mut refs);
3408 if refs.iter().any(|r| assigns[*r as usize] > 1) {
3409 continue;
3410 }
3411 edges[v] = refs.into_iter().collect();
3415 }
3416 *cand_v = true;
3417 }
3418
3419 let mut color = vec![0u8; n_vars]; let mut bad = vec![false; n_vars];
3437 for root in 0..n_vars {
3438 if color[root] != 0 || edges[root].is_empty() {
3439 continue;
3440 }
3441 color[root] = 1;
3442 let mut stack: Vec<(usize, usize)> = vec![(root, 0)];
3443 while let Some(top) = stack.last_mut() {
3444 let v = top.0;
3445 let i = top.1;
3446 top.1 += 1;
3447 if i < edges[v].len() {
3448 let r = edges[v][i] as usize;
3449 if color[r] == 1 {
3450 for &(pn, _) in &stack {
3451 bad[pn] = true;
3452 }
3453 } else if color[r] == 0 {
3454 color[r] = 1;
3455 stack.push((r, 0));
3456 } else if bad[r] {
3457 for &(pn, _) in &stack {
3458 bad[pn] = true;
3459 }
3460 }
3461 } else {
3462 color[v] = 2;
3463 stack.pop();
3464 }
3465 }
3466 }
3467 let single: Vec<bool> = (0..n_vars).map(|v| cand[v] && !bad[v]).collect();
3468 if !single.iter().any(|&b| b) {
3469 return;
3470 }
3471 let pure: Vec<bool> = (0..n_vars)
3472 .map(|v| {
3473 single[v]
3474 && values
3475 .get(v)
3476 .and_then(|o| o.as_ref())
3477 .map(|e| !has_side_effects(e))
3478 .unwrap_or(false)
3479 })
3480 .collect();
3481 let impure: Vec<bool> = (0..n_vars).map(|v| single[v] && !pure[v]).collect();
3482
3483 if pure.iter().any(|&b| b) {
3485 let vals: Vec<Option<Expr>> = (0..n_vars)
3486 .map(|v| {
3487 if pure[v] {
3488 values.get(v).cloned().flatten()
3489 } else {
3490 None
3491 }
3492 })
3493 .collect();
3494 rewrite_exprs(s, &mut |e| {
3495 deep_rewrite_reads(e, &mut |x| {
3496 if let Expr::Local { var, .. } = x {
3497 if let Some(Some(v)) = vals.get(*var as usize) {
3498 *x = v.clone();
3499 }
3500 }
3501 });
3502 });
3503 drop_defs(s, &pure);
3504 }
3505
3506 if impure.iter().any(|&b| b) {
3509 forward_adjacent_impure(s, &values, &impure);
3510 }
3511}
3512
3513fn count_reads(s: &Stmt, out: &mut Vec<usize>) {
3515 walk_all(s, &mut |st| {
3516 let exprs: Vec<&Expr> = match st {
3517 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
3520 let mut v: Vec<&Expr> = vec![value];
3521 if !matches!(&**target, Expr::Local { .. }) {
3522 v.push(target);
3523 }
3524 v
3525 }
3526 Stmt::ExprStmt(e @ (Expr::PreIncDec { .. } | Expr::PostIncDec { .. })) => {
3529 let inner = match e {
3530 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => e,
3531 _ => unreachable!(),
3532 };
3533 if matches!(**inner, Expr::Local { .. }) {
3534 Vec::new()
3535 } else {
3536 vec![e]
3537 }
3538 }
3539 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
3540 vec![e]
3541 }
3542 Stmt::Return(Some(e)) => vec![e],
3543 Stmt::LocalDef { init: Some(e), .. } => vec![e],
3544 Stmt::If { cond, .. } => vec![cond],
3545 Stmt::While { cond, .. } => vec![cond],
3546 Stmt::DoWhile { cond, .. } => vec![cond],
3547 Stmt::Switch { selector, .. } => vec![selector],
3553 Stmt::ForEach { iterable, .. } => vec![iterable],
3554 Stmt::Synchronized { lock, .. } => vec![lock],
3555 Stmt::For { cond, update, .. } => {
3556 let mut v: Vec<&Expr> = Vec::with_capacity(update.len() + 1);
3557 if let Some(c) = cond {
3558 v.push(c);
3559 }
3560 v.extend(update.iter());
3561 v
3562 }
3563 Stmt::Assert { cond, msg } => {
3564 let mut v: Vec<&Expr> = vec![cond];
3565 if let Some(m) = msg {
3566 v.push(m);
3567 }
3568 v
3569 }
3570 _ => Vec::new(),
3571 };
3572 for e in exprs {
3573 visit_exprs(e, &mut |x| {
3574 if let Expr::Local { var, .. } = x {
3575 grow_to(out, *var);
3576 out[*var as usize] += 1;
3577 }
3578 });
3579 }
3580 });
3581}
3582
3583fn drop_defs(s: &mut Stmt, vars: &[bool]) {
3584 walk_mut_deep(s, &mut |st| {
3585 if let Stmt::Block(v) = st {
3586 v.retain(|x| match x {
3587 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
3588 Expr::Local { var, .. } => !vars.get(*var as usize).copied().unwrap_or(false),
3589 _ => true,
3590 },
3591 Stmt::LocalDef { var, .. } => !vars.get(*var as usize).copied().unwrap_or(false),
3592 _ => true,
3593 });
3594 }
3595 });
3596}
3597
3598fn forward_adjacent_impure(s: &mut Stmt, values: &[Option<Expr>], impure: &[bool]) {
3600 if let Stmt::Block(v) = s {
3601 let mut i = 0;
3602 while i + 1 < v.len() {
3603 let def_var = match &v[i] {
3604 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
3605 Expr::Local { var, .. } => Some(*var),
3606 _ => None,
3607 },
3608 Stmt::LocalDef { var, .. } => Some(*var),
3609 _ => None,
3610 };
3611 if let Some(var) = def_var {
3612 if impure.get(var as usize).copied().unwrap_or(false) {
3613 let mut reads_here = vec![0usize; values.len().max(1)];
3615 count_reads(&v[i + 1], &mut reads_here);
3616 if reads_here.get(var as usize).copied().unwrap_or(0) == 1 {
3617 let val = match &v[i] {
3621 Stmt::ExprStmt(Expr::Assign { value, .. }) => (**value).clone(),
3622 Stmt::LocalDef { init: Some(e), .. } => (*e).clone(),
3623 _ => {
3624 i += 1;
3625 continue;
3626 }
3627 };
3628 rewrite_exprs(&mut v[i + 1], &mut |e| {
3629 deep_rewrite_reads(e, &mut |x| {
3630 if let Expr::Local { var: v2, .. } = x {
3631 if *v2 == var {
3632 *x = val.clone();
3633 }
3634 }
3635 });
3636 });
3637 v.remove(i);
3638 i = i.saturating_sub(1);
3641 continue;
3642 }
3643 }
3644 }
3645 i += 1;
3646 }
3647 }
3648 walk_mut(s, &mut |x| forward_adjacent_impure(x, values, impure));
3649}
3650
3651pub fn infer_types(vt: &mut VarTable, body: &mut Stmt, ret: &JavaType, env: &MethodEnv) {
3657 let n = vt.vars.len();
3658 let mut evidence: Vec<Vec<(JavaType, bool)>> = vec![Vec::new(); n];
3666 let ev = |evidence: &mut Vec<Vec<(JavaType, bool)>>, var: u32, t: JavaType, strong: bool| {
3667 if (var as usize) < evidence.len() {
3668 evidence[var as usize].push((t, strong));
3669 }
3670 };
3671
3672 walk_all(body, &mut |st| match st {
3673 Stmt::LocalDef { var, init, .. } => {
3674 if let Some(e) = init {
3675 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
3676 let strong = match e {
3684 Expr::Const(_) => false,
3685 Expr::Local { var: src, .. } => {
3686 let src_ty = vt.vars.get(*src as usize).map(|v| v.ty.erased());
3687 let tgt_ty = vt.vars.get(*var as usize).map(|v| v.ty.erased());
3688 match (src_ty, tgt_ty) {
3689 (Some(s), Some(g)) => {
3690 (s.is_numeric() && g.is_numeric())
3691 || (s.is_reference() && g.is_reference())
3692 }
3693 _ => false,
3694 }
3695 }
3696 _ => true,
3697 };
3698 ev(&mut evidence, *var, e.type_ref().erased(), strong);
3699 }
3700 }
3701 Stmt::ExprStmt(e) => {
3702 if let Expr::Assign { target, value, .. } = e {
3709 if let Expr::Field { owner: Some(o), cls, is_static: false, .. } = &**target {
3716 if cls.is_empty() {
3717 } else if let Expr::Local { var, .. } = &**o {
3719 ev(
3720 &mut evidence,
3721 *var,
3722 JavaType::Object(cls.clone()),
3723 true,
3724 );
3725 }
3726 }
3727 if let Expr::Local { var, .. } = &**target {
3728 let strong = match &**value {
3729 Expr::Const(_) => false,
3730 Expr::Local { var: src, .. } => {
3731 let src_ty = vt.vars.get(*src as usize).map(|v| v.ty.erased());
3738 let tgt_ty = vt.vars.get(*var as usize).map(|v| v.ty.erased());
3739 match (src_ty, tgt_ty) {
3740 (Some(s), Some(g)) => {
3741 (s.is_numeric() && g.is_numeric())
3742 || (s.is_reference() && g.is_reference())
3743 }
3744 _ => false,
3745 }
3746 }
3747 _ => true,
3748 };
3749 ev(&mut evidence, *var, value.type_ref().erased(), strong);
3750 }
3751 }
3752 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
3753 }
3754 Stmt::Throw(e) => {
3755 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
3756 if let Expr::Local { var, .. } = e {
3757 ev(
3758 &mut evidence,
3759 *var,
3760 JavaType::Object("java/lang/Throwable".into()),
3761 true,
3762 );
3763 }
3764 }
3765 Stmt::Return(Some(e)) => {
3766 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
3767 if let Expr::Local { var, .. } = e {
3768 ev(&mut evidence, *var, ret.clone(), true);
3769 }
3770 }
3771 Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
3772 if let Expr::Local { var, .. } = e {
3773 ev(
3774 &mut evidence,
3775 *var,
3776 JavaType::Object("java/lang/Object".into()),
3777 true,
3778 );
3779 }
3780 }
3781 Stmt::ForEach {
3782 var,
3783 iterable,
3784 is_array,
3785 ..
3786 } => {
3787 if let Expr::Local { var: v0, .. } = iterable {
3788 if *is_array {
3789 ev(&mut evidence, *v0, JavaType::Array(Box::new(JavaType::Int)), true);
3790 } else {
3791 ev(
3792 &mut evidence,
3793 *v0,
3794 JavaType::Object("java/lang/Object".into()),
3795 true,
3796 );
3797 }
3798 }
3799 let _ = var;
3800 }
3801 _ => {}
3802 });
3803 let _ = env;
3804
3805 let mut derefed: Vec<bool> = vec![false; vt.vars.len()];
3815 mark_derefs(body, &mut derefed);
3816 let mut arr_derefed: Vec<bool> = vec![false; vt.vars.len()];
3821 visit_all_exprs(body, &mut |x| {
3822 if let Expr::ArrayIndex { array, .. } = x {
3823 if let Expr::Local { var, .. } = &**array {
3824 if (*var as usize) < arr_derefed.len() {
3825 arr_derefed[*var as usize] = true;
3826 }
3827 }
3828 }
3829 if let Expr::Bin { op, l, r, .. } = x {
3837 if matches!(op, BinOp::Eq | BinOp::Ne)
3838 && (matches!(&**r, Expr::Const(ConstVal::Null))
3839 || matches!(&**l, Expr::Const(ConstVal::Null)))
3840 {
3841 for side in [l, r] {
3842 if let Expr::Local { var, .. } = &**side {
3843 ev(
3844 &mut evidence,
3845 *var,
3846 JavaType::Object("java/lang/Object".into()),
3847 true,
3848 );
3849 }
3850 }
3851 }
3852 }
3853 });
3854 const FINAL_JDK: &[&str] = &[
3855 "java/lang/Boolean",
3856 "java/lang/String",
3857 "java/lang/Integer",
3858 "java/lang/Long",
3859 "java/lang/Short",
3860 "java/lang/Byte",
3861 "java/lang/Character",
3862 "java/lang/Float",
3863 "java/lang/Double",
3864 ];
3865 for (i, evs) in evidence.iter().enumerate() {
3866 let info = &vt.vars[i];
3867 if info.is_param {
3868 continue;
3869 }
3870 let strong: Vec<JavaType> = evs
3871 .iter()
3872 .filter(|(_, s)| *s)
3873 .map(|(t, _)| t.clone())
3874 .collect();
3875 let all: Vec<JavaType> = evs.iter().map(|(t, _)| t.clone()).collect();
3876 if info.ty.erased().is_reference() {
3877 if strong.is_empty() {
3883 let refs: std::collections::HashSet<&str> = all
3884 .iter()
3885 .filter_map(|t| match t {
3886 JavaType::Object(n) if n.as_ref() != "java/lang/Object" => {
3887 Some(n.as_ref())
3888 }
3889 _ => None,
3890 })
3891 .collect();
3892 let mixed = all.iter().any(|t| t.is_numeric());
3893 if refs.len() >= 2 || (mixed && !refs.is_empty()) || (mixed && all.iter().any(|t| matches!(t, JavaType::Array(_)))) {
3894 vt.vars[i].ty =
3895 TypeRef::J(JavaType::Object("java/lang/Object".into()));
3896 continue;
3897 }
3898 }
3899 if arr_derefed[i] {
3902 let arr = strong
3903 .iter()
3904 .chain(all.iter())
3905 .find(|t| matches!(t, JavaType::Array(_)))
3906 .cloned();
3907 if let Some(t) = arr {
3908 vt.vars[i].ty = TypeRef::J(t);
3909 continue;
3910 }
3911 }
3912 let deref_pick = || {
3916 if !derefed[i] {
3917 return None;
3918 }
3919 strong
3920 .iter()
3921 .find(|t| {
3922 matches!(t, JavaType::Object(n)
3923 if !n.is_empty()
3924 && n.as_ref() != "java/lang/Object"
3925 && !FINAL_JDK.contains(&n.as_ref()))
3926 })
3927 .cloned()
3928 };
3929 if let Some(t) = deref_pick()
3930 .or_else(|| pick_object(&strong))
3931 .or_else(|| pick_object(&all))
3932 {
3933 vt.vars[i].ty = TypeRef::J(t);
3934 }
3935 } else if let Some(t) = pick_object(&strong) {
3936 vt.vars[i].ty = TypeRef::J(t);
3940 } else if strong.contains(&JavaType::Boolean) {
3941 vt.vars[i].ty = TypeRef::J(JavaType::Boolean);
3945 } else if !all.is_empty() && all.iter().all(|t| t.is_numeric()) {
3946 if let Some(t) = pick_numeric(&all) {
3947 vt.vars[i].ty = TypeRef::J(t);
3948 }
3949 } else {
3950 let refs = all.iter().filter(|t| t.is_reference()).count();
3954 let nums = all.iter().filter(|t| t.is_numeric()).count();
3955 if refs > 0 && nums > 0 {
3956 vt.vars[i].ty =
3957 TypeRef::J(JavaType::Object("java/lang/Object".into()));
3958 } else if let Some(t) = pick_object(&strong).or_else(|| pick_object(&all)) {
3959 vt.vars[i].ty = TypeRef::J(t);
3960 }
3961 }
3962 }
3963
3964 let types: Vec<&TypeRef> = vt.vars.iter().map(|v| &v.ty).collect();
3969 rewrite_exprs(body, &mut |e| {
3970 deep_rewrite(e, &mut |x| {
3971 if let Expr::Local { var, ty } = x {
3972 if let Some(want) = types.get(*var as usize) {
3973 if ty != *want {
3974 *ty = (*want).clone();
3975 }
3976 }
3977 }
3978 });
3979 });
3980
3981 coerce_num_consts(body, &types);
3984}
3985
3986fn coerce_num_consts(body: &mut Stmt, types: &[&TypeRef]) {
3987 walk_mut_deep(body, &mut |st| {
3988 let (var, val): (u32, &mut Expr) = match st {
3989 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &mut **target {
3990 Expr::Local { var, .. } => (*var, value),
3991 _ => return,
3992 },
3993 Stmt::LocalDef {
3994 var, init: Some(e), ..
3995 } => (*var, e),
3996 _ => return,
3997 };
3998 let want = match types.get(var as usize) {
3999 Some(TypeRef::J(t)) => t.clone(),
4000 _ => return,
4001 };
4002 let rewritten = match &*val {
4003 Expr::Const(ConstVal::Long(l)) => match &want {
4004 JavaType::Double => Some(Expr::Const(ConstVal::Double(*l as f64))),
4005 JavaType::Float => Some(Expr::Const(ConstVal::Float(*l as f32))),
4006 _ => None,
4007 },
4008 Expr::Const(ConstVal::Int(i)) => match &want {
4009 JavaType::Double => Some(Expr::Const(ConstVal::Double(*i as f64))),
4010 JavaType::Float => Some(Expr::Const(ConstVal::Float(*i as f32))),
4011 _ => None,
4012 },
4013 _ => None,
4014 };
4015 if let Some(e) = rewritten {
4016 *val = e;
4017 }
4018 });
4019}
4020
4021fn pick_object(evs: &[JavaType]) -> Option<JavaType> {
4022 evs.iter()
4023 .find(|t| {
4024 t.is_reference()
4025 && !matches!(t, JavaType::Object(n) if n.is_empty() || n.as_ref() == "java/lang/Object")
4026 })
4027 .cloned()
4028}
4029
4030fn pick_numeric(evs: &[JavaType]) -> Option<JavaType> {
4031 if evs.is_empty() {
4032 return None;
4033 }
4034 let mut t = evs[0].clone();
4035 for x in &evs[1..] {
4036 t = join_numeric(&t, x);
4037 }
4038 Some(t)
4039}
4040
4041fn join_numeric(a: &JavaType, b: &JavaType) -> JavaType {
4042 if a == b {
4043 return a.clone();
4044 }
4045 match (a, b) {
4046 (JavaType::Double, _) | (_, JavaType::Double) => JavaType::Double,
4047 (JavaType::Float, _) | (_, JavaType::Float) => JavaType::Float,
4048 (JavaType::Long, _) | (_, JavaType::Long) => JavaType::Long,
4049 (JavaType::Int, x) if x.is_integral() => JavaType::Int,
4050 (x, JavaType::Int) if x.is_integral() => JavaType::Int,
4051 (x, y) if x.is_integral() && y.is_integral() => JavaType::Int,
4052 _ => JavaType::Int,
4053 }
4054}
4055
4056fn expr_evidence<F: FnMut(u32, JavaType)>(e: &Expr, f: &mut F) {
4058 visit_exprs(e, &mut |x| match x {
4059 Expr::Method {
4060 owner,
4061 args,
4062 desc,
4063 is_static,
4064 cls,
4065 ..
4066 } => {
4067 if !*is_static {
4068 if let Some(o) = owner {
4069 if let Expr::Local { var, .. } = &**o {
4070 f(*var, JavaType::Object(cls.clone()));
4071 }
4072 }
4073 }
4074 for (i, a) in args.iter().enumerate() {
4075 if let Expr::Local { var, .. } = a {
4076 if let Some(t) = desc.args.get(i) {
4077 f(*var, t.clone());
4078 }
4079 }
4080 }
4081 }
4082 Expr::Field {
4083 owner,
4084 cls,
4085 ty,
4086 is_static,
4087 ..
4088 } => {
4089 if !*is_static && !cls.is_empty() {
4090 if let Some(o) = owner {
4091 if let Expr::Local { var, .. } = &**o {
4092 f(*var, JavaType::Object(cls.clone()));
4102 }
4103 }
4104 }
4105 let _ = ty;
4106 }
4107 Expr::Bin { op, l, r, .. }
4112 if matches!(op, BinOp::Eq | BinOp::Ne)
4113 && (matches!(&**r, Expr::Const(ConstVal::Null))
4114 || matches!(&**l, Expr::Const(ConstVal::Null))) =>
4115 {
4116 for side in [l, r] {
4117 if let Expr::Local { var, .. } = &**side {
4118 f(*var, JavaType::Object("java/lang/Object".into()));
4119 }
4120 }
4121 }
4122 Expr::ArrayIndex { array, .. } => {
4123 if let Expr::Local { var, ty, .. } = &**array {
4124 let t = ty.erased();
4129 if matches!(t, JavaType::Array(_)) {
4130 f(*var, t);
4131 } else {
4132 f(*var, JavaType::Array(Box::new(JavaType::Int)));
4133 }
4134 }
4135 }
4136 Expr::Assign { .. } => {
4137 }
4140 Expr::Cast { e: inner, .. } => {
4141 let _ = inner;
4147 }
4148 Expr::InstanceOf { e: inner, .. } => {
4149 let _ = inner;
4150 }
4151 _ => {}
4152 });
4153}
4154
4155pub fn mark_field_owner_concrete(vt: &mut VarTable, body: &mut Stmt) {
4167 let mut owners: HashSet<u32> = HashSet::default();
4168 rewrite_exprs(body, &mut |e| {
4169 visit_exprs(e, &mut |x| {
4170 if let Expr::Field {
4171 owner: Some(o),
4172 is_static: false,
4173 ..
4174 } = x
4175 {
4176 if let Expr::Local { var, .. } = &**o {
4177 owners.insert(*var);
4178 }
4179 }
4180 });
4181 });
4182 for var in owners {
4183 let i = var as usize;
4184 if i >= vt.vars.len() {
4185 continue;
4186 }
4187 if let JavaType::Object(cls) = vt.vars[i].ty.erased() {
4188 if let Some(last) = cls.rsplit('$').next() {
4189 if !last.is_empty() && last.chars().all(|c| c.is_ascii_digit()) {
4190 vt.force_concrete_vars.insert(var);
4191 }
4192 }
4193 }
4194 }
4195 let concrete: Vec<u32> = vt
4204 .vars
4205 .iter()
4206 .filter(|v| {
4207 if let JavaType::Object(cls) = v.ty.erased() {
4208 cls.rsplit('$')
4209 .next()
4210 .is_some_and(|l| !l.is_empty() && l.chars().all(|c| c.is_ascii_digit()))
4211 } else {
4212 false
4213 }
4214 })
4215 .map(|v| v.id)
4216 .collect();
4217 for var in concrete {
4218 vt.force_concrete_vars.insert(var);
4219 }
4220}
4221
4222pub fn insert_object_narrowing_casts(
4234 vt: &VarTable,
4235 body: &mut Stmt,
4236 pool: &DexPool,
4237 ret: &JavaType,
4238) {
4239 let value_ty = |e: &Expr| -> JavaType {
4242 match e {
4243 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4244 other => other.type_ref().erased(),
4245 }
4246 };
4247 let is_top_object = |e: &Expr| -> bool {
4248 value_ty(e) == JavaType::Object("java/lang/Object".into())
4249 };
4250 let specific_ref = |ty: &TypeRef| -> Option<TypeRef> {
4254 match ty.erased() {
4255 JavaType::Object(c) if c.as_ref() != "java/lang/Object" => Some(ty.clone()),
4256 JavaType::Array(_) => Some(ty.clone()),
4257 _ => None,
4258 }
4259 };
4260 let castable = |e: &Expr| -> bool {
4261 is_top_object(e)
4262 && !matches!(e, Expr::Const(_) | Expr::Cast { .. } | Expr::InstanceOf { .. })
4263 };
4264 let needs_downcast = |e: &Expr, tt: &JavaType| -> bool {
4274 if matches!(e, Expr::Const(_) | Expr::Cast { .. } | Expr::InstanceOf { .. }) {
4275 return false;
4276 }
4277 let v = value_ty(e);
4278 let (JavaType::Object(vn), JavaType::Object(tn)) = (&v, tt) else {
4279 return false;
4280 };
4281 if vn.as_ref() == "java/lang/Object" || tn.as_ref() == "java/lang/Object" {
4282 return false;
4283 }
4284 vn != tn && !pool.is_subtype(vn, tn)
4285 };
4286 walk_mut_deep(body, &mut |st| match st {
4287 Stmt::ExprStmt(Expr::Assign {
4288 target,
4289 value,
4290 op: AssignOp::Plain,
4291 ..
4292 }) => {
4293 let tgt = match &**target {
4294 Expr::Local { var, .. } => vt.var(*var).ty.clone(),
4295 Expr::Field { ty, .. } => ty.clone(),
4296 Expr::ArrayIndex { array, .. } => {
4303 let aty = match &**array {
4304 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4305 other => other.type_ref().erased(),
4306 };
4307 match aty {
4308 JavaType::Array(inner) => TypeRef::J(*inner),
4309 _ => return,
4310 }
4311 }
4312 _ => return,
4313 };
4314 if let Some(t) = specific_ref(&tgt) {
4315 if castable(value) || needs_downcast(value, &tgt.erased()) {
4316 let v = std::mem::replace(value, Box::new(Expr::This));
4317 **value = Expr::Cast { ty: t, e: v };
4318 }
4319 }
4320 }
4321 Stmt::LocalDef { var, init: Some(value), .. } => {
4322 if let Some(t) = specific_ref(&vt.var(*var).ty) {
4323 let tt = vt.var(*var).ty.erased();
4324 if castable(value) || needs_downcast(value, &tt) {
4325 let v = std::mem::replace(value, Expr::This);
4326 *value = Expr::Cast {
4327 ty: t,
4328 e: Box::new(v),
4329 };
4330 }
4331 }
4332 }
4333 Stmt::Return(Some(value)) => {
4334 if let Some(t) = specific_ref(&TypeRef::J(ret.clone())) {
4335 if castable(value) || needs_downcast(value, ret) {
4336 let v = std::mem::replace(value, Expr::This);
4337 *value = Expr::Cast { ty: t, e: Box::new(v) };
4338 }
4339 }
4340 }
4341 _ => {}
4342 });
4343 walk_stmt_exprs(body, &mut |e| {
4350 deep_rewrite(e, &mut |x| {
4351 let (args, arg_tys): (&mut Vec<Expr>, &[JavaType]) = match x {
4352 Expr::Method { desc, args, is_dynamic: false, .. } => {
4353 (args, desc.args.as_slice())
4354 }
4355 Expr::New { arg_tys, args, .. } => (args, arg_tys.as_slice()),
4359 _ => return,
4360 };
4361 for (i, a) in args.iter_mut().enumerate() {
4362 let Some(formal) = arg_tys.get(i) else {
4363 continue;
4364 };
4365 let Some(t) = specific_ref(&TypeRef::J(formal.clone())) else {
4366 continue;
4367 };
4368 if !(castable(a) || needs_downcast(a, formal)) {
4369 continue;
4370 }
4371 let v = std::mem::replace(a, Expr::Const(ConstVal::Null));
4372 *a = Expr::Cast { ty: t, e: Box::new(v) };
4373 }
4374 });
4375 });
4376}
4377
4378pub fn fix_primitive_assign_casts(vt: &VarTable, body: &mut Stmt, ret_ty: &JavaType) {
4387 fn numlike(t: &JavaType) -> bool {
4388 matches!(
4389 t,
4390 JavaType::Byte
4391 | JavaType::Short
4392 | JavaType::Int
4393 | JavaType::Char
4394 | JavaType::Long
4395 | JavaType::Float
4396 | JavaType::Double
4397 )
4398 }
4399 fn val_ty(e: &Expr, vt: &VarTable) -> JavaType {
4400 match e {
4401 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
4402 vt.var(*var).ty.erased()
4403 }
4404 other => other.type_ref().erased(),
4405 }
4406 }
4407 fn wrap(ty: &JavaType, value: &mut Expr) {
4408 if matches!(value, Expr::Cast { .. }) {
4409 return;
4410 }
4411 if val_ty_static(value) == *ty {
4412 return;
4413 }
4414 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
4415 *value = Expr::Cast {
4416 ty: TypeRef::J(ty.clone()),
4417 e: Box::new(taken),
4418 };
4419 }
4420 fn val_ty_static(e: &Expr) -> JavaType {
4421 e.type_ref().erased()
4422 }
4423 fn coerce(tt: &JavaType, value: &mut Expr, vt: &VarTable) {
4431 match (&*value, tt) {
4436 (Expr::Const(ConstVal::Int(b)), JavaType::Float) => {
4437 *value = Expr::Const(ConstVal::Float(f32::from_bits(*b as u32)));
4438 return;
4439 }
4440 (Expr::Const(ConstVal::Long(b)), JavaType::Double) => {
4441 *value = Expr::Const(ConstVal::Double(f64::from_bits(*b as u64)));
4442 return;
4443 }
4444 (Expr::Const(ConstVal::Float(f)), JavaType::Int) => {
4445 *value = Expr::Const(ConstVal::Int(f.to_bits() as i32));
4446 return;
4447 }
4448 (Expr::Const(ConstVal::Double(d)), JavaType::Long) => {
4449 *value = Expr::Const(ConstVal::Long(d.to_bits() as i64));
4450 return;
4451 }
4452 _ => {}
4453 }
4454 let vt_val = val_ty(value, vt);
4455 let tt_bool = matches!(tt, JavaType::Boolean);
4456 let val_bool = matches!(vt_val, JavaType::Boolean);
4457 if tt_bool && !val_bool && numlike(&vt_val) {
4458 if matches!(
4459 value,
4460 Expr::Const(_)
4461 | Expr::Bin { op: BinOp::Eq | BinOp::Ne, .. }
4462 | Expr::Un { op: UnOp::Not, .. }
4463 ) {
4464 return;
4469 }
4470 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
4471 *value = Expr::Bin {
4472 op: BinOp::Ne,
4473 l: Box::new(taken),
4474 r: Box::new(Expr::Const(ConstVal::Int(0))),
4475 ty: Some(TypeRef::J(JavaType::Boolean)),
4476 };
4477 } else if !tt_bool && val_bool && numlike(tt) {
4478 if matches!(value, Expr::Cast { .. }) {
4479 return;
4480 }
4481 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
4482 *value = Expr::Cast {
4483 ty: TypeRef::J(tt.clone()),
4484 e: Box::new(Expr::Cond {
4485 c: Box::new(taken),
4486 t: Box::new(Expr::Const(ConstVal::Int(1))),
4487 f: Box::new(Expr::Const(ConstVal::Int(0))),
4488 }),
4489 };
4490 } else if numlike(tt) && numlike(&vt_val) {
4491 wrap(tt, value);
4492 }
4493 }
4494 walk_mut_deep(body, &mut |st| match st {
4495 Stmt::ExprStmt(Expr::Assign {
4496 target,
4497 op: AssignOp::Plain,
4498 value,
4499 }) => {
4500 let tt = match &**target {
4501 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
4502 vt.var(*var).ty.erased()
4503 }
4504 Expr::Field { ty, .. } => ty.erased(),
4505 Expr::ArrayIndex { array, .. } => match array.type_ref().erased() {
4509 JavaType::Array(inner) => *inner,
4510 _ => return,
4511 },
4512 _ => return,
4513 };
4514 coerce(&tt, value, vt);
4515 }
4516 Stmt::LocalDef {
4517 var,
4518 init: Some(value),
4519 ..
4520 } => {
4521 if (*var as usize) >= vt.vars.len() {
4522 return;
4523 }
4524 let tt = vt.var(*var).ty.erased();
4525 coerce(&tt, value, vt);
4526 }
4527 Stmt::Return(Some(e)) => {
4528 if !numlike(ret_ty) || !numlike(&val_ty(e, vt)) {
4529 return;
4530 }
4531 wrap(ret_ty, e);
4532 }
4533 _ => {}
4534 });
4535}
4536
4537pub fn fix_shadowed_super_fields(
4561 body: &mut Stmt,
4562 vt: &VarTable,
4563 pool: &DexPool,
4564 class_name: &str,
4565) {
4566 fn display(pool: &DexPool, cls: &str, name: &str) -> Option<String> {
4570 let pc = pool.get(cls)?;
4571 let f = pc
4572 .static_fields
4573 .iter()
4574 .chain(pc.instance_fields.iter())
4575 .find(|f| f.name.as_str() == name)?;
4576 Some(
4577 jdc_core::rename::field_display(cls, name, &f.desc)
4578 .map(|d| d.to_string())
4579 .unwrap_or_else(|| name.to_string()),
4580 )
4581 }
4582 fn owner_ct(
4583 o: &Option<Box<Expr>>,
4584 vt: &VarTable,
4585 class_name: &str,
4586 ) -> Option<std::sync::Arc<str>> {
4587 match o.as_deref() {
4588 None | Some(Expr::This) => Some(std::sync::Arc::from(class_name)),
4589 Some(Expr::Local { var, .. }) => match vt.var(*var).ty.erased() {
4590 JavaType::Object(n) => Some(n),
4591 _ => None,
4592 },
4593 Some(other) => match other.type_ref().erased() {
4594 JavaType::Object(n) => Some(n),
4595 _ => None,
4596 },
4597 }
4598 }
4599 walk_stmt_exprs(body, &mut |e| {
4600 deep_rewrite(e, &mut |x| {
4601 let Expr::Field {
4602 owner,
4603 cls,
4604 name,
4605 is_static: false,
4606 ..
4607 } = x
4608 else {
4609 return;
4610 };
4611 let Some(ot) = owner_ct(owner, vt, class_name) else {
4612 return;
4613 };
4614 if cls.as_ref() == ot.as_ref() {
4615 return;
4616 }
4617 let Some(ref_display) = display(pool, cls, name) else {
4618 return;
4619 };
4620 let mut cur = ot.to_string();
4624 let mut shadowed = false;
4625 let mut found = false;
4626 for _ in 0..64 {
4627 if display(pool, &cur, name).as_deref() == Some(ref_display.as_str()) {
4628 shadowed = true;
4629 }
4630 let Some(pc) = pool.get(&cur) else {
4631 return;
4632 };
4633 let Some(sup) = pc.super_name.clone() else {
4634 return;
4635 };
4636 if sup.as_str() == cls.as_ref() {
4637 found = true;
4638 break;
4639 }
4640 cur = sup;
4641 }
4642 if !found || !shadowed {
4643 return;
4644 }
4645 let ty = TypeRef::J(JavaType::Object(cls.clone()));
4646 match owner {
4647 Some(o) => {
4648 if let Expr::Cast { ty: ct, .. } = &mut **o {
4649 *ct = ty;
4650 } else {
4651 let taken = std::mem::replace(&mut **o, Expr::This);
4652 **o = Expr::Cast {
4653 ty,
4654 e: Box::new(taken),
4655 };
4656 }
4657 }
4658 None => {
4659 *owner = Some(Box::new(Expr::Cast {
4660 ty,
4661 e: Box::new(Expr::This),
4662 }))
4663 }
4664 }
4665 });
4666 });
4667}
4668
4669pub fn fix_field_owner_downcasts(body: &mut Stmt, vt: &VarTable, pool: &DexPool) {
4688 fn owner_internal(o: &Expr, vt: &VarTable) -> Option<std::sync::Arc<str>> {
4689 let t = match o {
4690 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4691 other => other.type_ref().erased(),
4692 };
4693 match t {
4694 JavaType::Object(n) => Some(n),
4695 _ => None,
4696 }
4697 }
4698 fn declares_up(pool: &DexPool, class: &str, name: &str) -> bool {
4699 let mut cur = Some(class.to_string());
4700 let mut hops = 0;
4701 while let Some(c) = cur {
4702 hops += 1;
4703 if hops > 64 {
4704 return false;
4705 }
4706 let Some(pc) = pool.get(&c) else { return false };
4707 if pc
4708 .instance_fields
4709 .iter()
4710 .chain(pc.static_fields.iter())
4711 .any(|f| f.name.as_str() == name)
4712 {
4713 return true;
4714 }
4715 cur = pc.super_name.clone();
4716 }
4717 false
4718 }
4719 walk_stmt_exprs(body, &mut |e| {
4720 deep_rewrite(e, &mut |x| {
4721 if let Expr::Field {
4722 owner: Some(o),
4723 cls,
4724 name,
4725 is_static: false,
4726 ..
4727 } = x
4728 {
4729 let Some(ot) = owner_internal(o, vt) else {
4730 return;
4731 };
4732 if ot.as_ref() == cls.as_ref() || !pool.is_subtype(cls, &ot) {
4733 return;
4734 }
4735 if declares_up(pool, &ot, name) || !declares_up(pool, cls, name) {
4736 return;
4737 }
4738 let ty = TypeRef::J(JavaType::Object(cls.clone()));
4739 if let Expr::Cast { ty: ct, .. } = &mut **o {
4740 *ct = ty;
4741 } else {
4742 let taken = std::mem::replace(&mut **o, Expr::This);
4743 **o = Expr::Cast { ty, e: Box::new(taken) };
4744 }
4745 }
4746 let (tgt_ty, value) = match x {
4748 Expr::Assign { target, op: AssignOp::Plain, value } => {
4749 let t = match &**target {
4750 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4751 Expr::Field { ty, .. } => ty.erased(),
4752 _ => return,
4753 };
4754 (t, value)
4755 }
4756 _ => return,
4757 };
4758 let JavaType::Object(sn) = tgt_ty else { return };
4759 if let Expr::Cast { ty, .. } = &mut **value {
4760 if let TypeRef::J(JavaType::Object(tn)) = ty {
4761 if tn.as_ref() != sn.as_ref() && pool.is_subtype(&sn, tn) {
4762 *ty = TypeRef::J(JavaType::Object(sn.clone()));
4763 }
4764 }
4765 }
4766 });
4767 });
4768 walk_mut_deep(body, &mut |st| {
4770 if let Stmt::LocalDef { var, init: Some(value), .. } = st {
4771 let tt = if (*var as usize) < vt.vars.len() {
4772 vt.vars[*var as usize].ty.erased()
4773 } else {
4774 return;
4775 };
4776 let JavaType::Object(sn) = tt else { return };
4777 if let Expr::Cast { ty, .. } = value {
4778 if let TypeRef::J(JavaType::Object(tn)) = ty {
4779 if tn.as_ref() != sn.as_ref() && pool.is_subtype(&sn, tn) {
4780 *ty = TypeRef::J(JavaType::Object(sn.clone()));
4781 }
4782 }
4783 }
4784 }
4785 });
4786}
4787
4788pub fn fix_incomparable_equality(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
4795 fn side_ty(e: &Expr, vt: &VarTable) -> JavaType {
4796 match e {
4797 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
4798 vt.var(*var).ty.erased()
4799 }
4800 other => other.type_ref().erased(),
4801 }
4802 }
4803 let incomparable = |a: &JavaType, b: &JavaType| -> bool {
4804 use JavaType::{Array, Object};
4805 match (a, b) {
4806 (Object(x), Object(y)) => {
4807 if x == y || x.as_ref() == "java/lang/Object" || y.as_ref() == "java/lang/Object"
4808 {
4809 return false;
4810 }
4811 let xi = pool.get(x).map(|c| c.is_interface()).unwrap_or(true);
4812 let yi = pool.get(y).map(|c| c.is_interface()).unwrap_or(true);
4813 if xi || yi {
4817 return false;
4818 }
4819 !pool.is_subtype(x, y) && !pool.is_subtype(y, x)
4820 }
4821 (Array(_), Object(y)) | (Object(y), Array(_)) => {
4822 y.as_ref() != "java/lang/Object"
4823 && y.as_ref() != "java/lang/Cloneable"
4824 && y.as_ref() != "java/io/Serializable"
4825 && !pool.get(y).map(|c| c.is_interface()).unwrap_or(false)
4826 }
4827 _ => false,
4828 }
4829 };
4830 let is_ref = |t: &JavaType| matches!(t, JavaType::Object(_) | JavaType::Array(_));
4831 walk_stmt_exprs(body, &mut |e| {
4832 deep_rewrite(e, &mut |x| {
4833 if let Expr::Bin {
4834 op: BinOp::Eq | BinOp::Ne,
4835 l,
4836 r,
4837 ..
4838 } = x
4839 {
4840 let lt = side_ty(l, vt);
4841 let rt = side_ty(r, vt);
4842 if is_ref(<) && matches!(&**r, Expr::Const(ConstVal::Int(0))) {
4848 **r = Expr::Const(ConstVal::Null);
4849 return;
4850 }
4851 if is_ref(&rt) && matches!(&**l, Expr::Const(ConstVal::Int(0))) {
4852 **l = Expr::Const(ConstVal::Null);
4853 return;
4854 }
4855 if incomparable(<, &rt) {
4856 let taken = std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
4857 **l = Expr::Cast {
4858 ty: TypeRef::J(JavaType::Object("java/lang/Object".into())),
4859 e: taken,
4860 };
4861 }
4862 }
4863 });
4864 });
4865}
4866
4867pub fn fix_primitive_arg_bridges(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
4881 fn val_ty(e: &Expr, vt: &VarTable) -> JavaType {
4882 match e {
4883 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4884 other => other.type_ref().erased(),
4885 }
4886 }
4887 #[allow(clippy::type_complexity)]
4888 let is_bool = |t: &JavaType| matches!(t, JavaType::Boolean);
4889 let is_num = |t: &JavaType| {
4890 matches!(
4891 t,
4892 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
4893 | JavaType::Long | JavaType::Float | JavaType::Double
4894 )
4895 };
4896 fn bridge(actual: &JavaType, formal: &JavaType, arg: &Expr) -> Option<Expr> {
4901 match (arg, formal) {
4911 (Expr::Const(ConstVal::Int(b)), JavaType::Float) => {
4912 return Some(Expr::Const(ConstVal::Float(f32::from_bits(*b as u32))));
4913 }
4914 (Expr::Const(ConstVal::Long(b)), JavaType::Double) => {
4915 return Some(Expr::Const(ConstVal::Double(f64::from_bits(*b as u64))));
4916 }
4917 (Expr::Const(ConstVal::Float(f)), JavaType::Int) => {
4918 return Some(Expr::Const(ConstVal::Int(f.to_bits() as i32)));
4919 }
4920 (Expr::Const(ConstVal::Double(d)), JavaType::Long) => {
4921 return Some(Expr::Const(ConstVal::Long(d.to_bits() as i64)));
4922 }
4923 _ => {}
4924 }
4925 if matches!(actual, JavaType::Boolean)
4926 && matches!(
4927 formal,
4928 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
4929 | JavaType::Long | JavaType::Float | JavaType::Double
4930 )
4931 {
4932 Some(Expr::Cast {
4933 ty: TypeRef::J(formal.clone()),
4934 e: Box::new(Expr::Cond {
4935 c: Box::new(arg.clone()),
4936 t: Box::new(Expr::Const(ConstVal::Int(1))),
4937 f: Box::new(Expr::Const(ConstVal::Int(0))),
4938 }),
4939 })
4940 } else if matches!(formal, JavaType::Boolean)
4941 && matches!(
4942 actual,
4943 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
4944 | JavaType::Long | JavaType::Float | JavaType::Double
4945 )
4946 {
4947 Some(Expr::Bin {
4948 op: BinOp::Ne,
4949 l: Box::new(arg.clone()),
4950 r: Box::new(Expr::Const(ConstVal::Int(0))),
4951 ty: Some(TypeRef::J(JavaType::Boolean)),
4952 })
4953 } else if matches!(
4954 actual,
4955 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
4956 | JavaType::Long | JavaType::Float | JavaType::Double
4957 ) && matches!(
4958 formal,
4959 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
4960 | JavaType::Long | JavaType::Float | JavaType::Double
4961 ) && !implicit_widening(actual, formal)
4962 {
4963 Some(Expr::Cast {
4972 ty: TypeRef::J(formal.clone()),
4973 e: Box::new(arg.clone()),
4974 })
4975 } else {
4976 None
4977 }
4978 }
4979 fn fw_resolvable(n: &str) -> bool {
4995 crate::fwdb::exists(n)
4996 }
4997 fn null_arg_cast(a: &mut Expr, f: &JavaType, pool: &DexPool) -> bool {
4998 if !matches!(&*a, Expr::Const(ConstVal::Null)) {
4999 return false;
5000 }
5001 if let JavaType::Object(n) = f {
5002 let resolvable = pool.get(n).is_some() || fw_resolvable(n);
5008 if n.as_ref() != "java/lang/Object" && resolvable {
5009 *a = Expr::Cast {
5010 ty: TypeRef::J(f.clone()),
5011 e: Box::new(Expr::Const(ConstVal::Null)),
5012 };
5013 return true;
5014 }
5015 } else if let JavaType::Array(_) = f {
5016 let mut e: &JavaType = f;
5023 while let JavaType::Array(inner) = e {
5024 e = &**inner;
5025 }
5026 let resolvable = match e {
5027 JavaType::Object(n) => pool.get(n).is_some() || fw_resolvable(n),
5028 _ => true, };
5030 if resolvable {
5031 *a = Expr::Cast {
5032 ty: TypeRef::J(f.clone()),
5033 e: Box::new(Expr::Const(ConstVal::Null)),
5034 };
5035 return true;
5036 }
5037 }
5038 false
5041 }
5042 fn implicit_widening(a: &JavaType, f: &JavaType) -> bool {
5045 use JavaType::*;
5046 match (a, f) {
5047 (Byte, Short) | (Byte, Int) | (Byte, Long) | (Byte, Float) | (Byte, Double) => true,
5048 (Short, Int) | (Short, Long) | (Short, Float) | (Short, Double) => true,
5049 (Char, Int) | (Char, Long) | (Char, Float) | (Char, Double) => true,
5050 (Int, Long) | (Int, Float) | (Int, Double) => true,
5051 (Long, Float) | (Long, Double) => true,
5052 (Float, Double) => true,
5053 (x, y) => x == y,
5054 }
5055 }
5056 let overload_competes = |cls: &str, name: &str, args: &[JavaType]| -> bool {
5065 if let Some(c) = pool.get(cls) {
5066 return c.all_methods().any(|m| {
5067 &*m.name == name
5068 && m.access & (crate::access::ACC_BRIDGE | crate::access::ACC_SYNTHETIC) == 0
5074 && m.parsed_desc()
5075 .is_some_and(|d| d.args.len() == args.len() && d.args != args)
5076 });
5077 }
5078 let _ = args;
5087 false
5088 };
5089 let ctor_formals = |cls: &str, n: usize| -> Option<Vec<JavaType>> {
5092 let c = pool.get(cls)?;
5093 let mut out: Option<Vec<JavaType>> = None;
5094 for m in c.all_methods() {
5095 if &*m.name != "<init>" {
5096 continue;
5097 }
5098 let Some(d) = m.parsed_desc() else { continue };
5099 if d.args.len() != n {
5100 continue;
5101 }
5102 match &out {
5103 None => out = Some(d.args.clone()),
5104 Some(prev) if *prev != d.args => return None,
5105 Some(_) => {}
5106 }
5107 }
5108 out
5109 };
5110 walk_stmt_exprs(body, &mut |e| {
5111 deep_rewrite(e, &mut |x| match x {
5112 Expr::Method {
5113 cls,
5114 name,
5115 desc,
5116 args,
5117 is_static,
5118 ..
5119 } => {
5120 if args.len() != desc.args.len() {
5121 return;
5122 }
5123 let _ = is_static;
5124 let mut competes: Option<bool> = None;
5125 for (a, f) in args.iter_mut().zip(desc.args.iter()) {
5126 if null_arg_cast(a, f, pool) {
5127 continue;
5128 }
5129 if matches!(a, Expr::Cast { .. }) {
5130 continue;
5131 }
5132 let subtype_arg = match (&val_ty(a, vt), f) {
5143 (JavaType::Object(an), JavaType::Object(fn_)) => {
5144 (an != fn_ && pool.is_subtype(an, fn_))
5149 || (fn_.as_ref() == "java/lang/Object"
5150 && an.as_ref() != "java/lang/Object")
5151 }
5152 (JavaType::Array(_), JavaType::Object(fn_))
5153 if fn_.as_ref() == "java/lang/Object" =>
5154 {
5155 true
5156 }
5157 _ => false,
5158 };
5159 let null_obj =
5173 matches!(a, Expr::Const(ConstVal::Null)) && matches!(f, JavaType::Object(_));
5174 let framework_owner = pool.get(cls.as_ref()).is_none();
5175 let fw_null_ambiguous = framework_owner
5176 && matches!(
5177 (cls.as_ref(), name.as_ref()),
5178 ("java/lang/StringBuilder", "append")
5179 | ("java/lang/StringBuffer", "append")
5180 | ("java/io/PrintStream", "println")
5181 | ("java/io/PrintStream", "print")
5182 | ("java/io/PrintWriter", "println")
5183 | ("java/io/PrintWriter", "print")
5184 | ("java/lang/String", "valueOf")
5185 );
5186 let fw_subtype_pinnable = framework_owner
5197 && subtype_arg
5198 && !matches!(f, JavaType::Object(fn_) if fn_.as_ref() == "java/lang/Object")
5199 && matches!(
5200 (cls.as_ref(), name.as_ref()),
5201 ("android/content/Intent", "putExtra")
5202 | ("java/util/concurrent/ScheduledExecutorService", "schedule")
5203 | ("java/util/concurrent/ExecutorService", "submit")
5204 | ("java/util/concurrent/ExecutorService", "invokeAll")
5205 );
5206 if fw_subtype_pinnable
5207 || (subtype_arg
5208 && *competes.get_or_insert_with(|| {
5209 overload_competes(cls, name, &desc.args)
5210 }))
5211 || (null_obj
5212 && (fw_null_ambiguous
5213 || *competes.get_or_insert_with(|| {
5214 overload_competes(cls, name, &desc.args)
5215 })))
5216 {
5217 let old =
5218 std::mem::replace(a, Expr::Const(ConstVal::Null));
5219 *a = Expr::Cast {
5220 ty: TypeRef::J(f.clone()),
5221 e: Box::new(old),
5222 };
5223 continue;
5224 }
5225 if !is_bool(&val_ty(a, vt)) && !is_num(&val_ty(a, vt)) {
5226 continue;
5227 }
5228 if !is_bool(f) && !is_num(f) {
5229 continue;
5230 }
5231 if let Some(b) = bridge(&val_ty(a, vt), f, a) {
5232 *a = b;
5233 }
5234 }
5235 }
5236 Expr::New { cls, args, arg_tys, .. } => {
5237 let formals_owned = if arg_tys.len() == args.len() && !arg_tys.is_empty() {
5238 Some(arg_tys.clone())
5239 } else {
5240 ctor_formals(cls, args.len())
5241 };
5242 if let Some(formals) = formals_owned {
5243 for (a, f) in args.iter_mut().zip(formals.iter()) {
5244 if null_arg_cast(a, f, pool) {
5245 continue;
5246 }
5247 if matches!(a, Expr::Cast { .. }) {
5248 continue;
5249 }
5250 if !is_bool(f) && !is_num(f) {
5251 continue;
5252 }
5253 if let Some(b) = bridge(&val_ty(a, vt), f, a) {
5254 *a = b;
5255 }
5256 }
5257 }
5258 }
5259 _ => {}
5260 });
5261 });
5262}
5263
5264pub fn deshadow_locals(vt: &mut VarTable, pool: &crate::DexPool, class_name: &str, body: &Stmt) {
5276 let segs = pool.root_pkg_segs();
5277 let obscured = crate::classdec::obscured_simples_snapshot();
5281 let self_pkg = class_name.rfind('/').map(|i| &class_name[..i]).unwrap_or("");
5288 let mut same_pkg_simples: jdc_core::FxHashSet<String> = jdc_core::FxHashSet::default();
5289 visit_all_exprs(body, &mut |x| {
5290 let cls = match x {
5291 Expr::Field { cls, is_static: true, .. } => Some(cls.as_ref()),
5292 Expr::Method { cls, is_static: true, .. } => Some(cls.as_ref()),
5293 _ => None,
5294 };
5295 if let Some(c) = cls {
5296 let pkg = c.rfind('/').map(|i| &c[..i]).unwrap_or("");
5297 if pkg == self_pkg {
5298 let simple = &c[c.rfind('/').map(|i| i + 1).unwrap_or(0)..];
5299 let head = simple.split('$').next().unwrap_or(simple);
5300 if !head.is_empty() {
5301 same_pkg_simples.insert(head.to_string());
5302 }
5303 }
5304 }
5305 });
5306 let hit = |name: &str| {
5307 segs.contains(name) || obscured.contains(name) || same_pkg_simples.contains(name)
5308 };
5309 if (segs.is_empty() && obscured.is_empty() && same_pkg_simples.is_empty())
5310 || !vt.vars.iter().any(|v| hit(&v.name))
5311 {
5312 return;
5313 }
5314 let mut taken: jdc_core::FxHashSet<String> =
5315 vt.vars.iter().map(|v| v.name.clone()).collect();
5316 for v in vt.vars.iter_mut() {
5317 if !hit(&v.name) {
5318 continue;
5319 }
5320 let mut k = 0u32;
5321 let cand = loop {
5322 k += 1;
5323 let c = if k == 1 {
5324 format!("{}x", v.name)
5325 } else {
5326 format!("{}x{}", v.name, k)
5327 };
5328 if !taken.contains(&c) {
5329 break c;
5330 }
5331 };
5332 taken.insert(cand.clone());
5333 v.name = cand;
5334 }
5335}
5336
5337pub fn fix_ctor_this_aliases(body: &mut Stmt, vt: &VarTable) {
5363 let Some(this_var) = vt
5364 .vars
5365 .iter()
5366 .find(|v| v.is_param && v.name == "this")
5367 .map(|v| v.id)
5368 else {
5369 return;
5370 };
5371 let mut analysis = VarAnalysis {
5372 assigns: Vec::new(),
5373 reads: Vec::new(),
5374 values: Vec::new(),
5375 };
5376 analyze_vars(body, &mut analysis);
5377 enum Src {
5388 This,
5389 Loc(u32),
5390 Other,
5391 }
5392 let mut sources: jdc_core::FxHashMap<u32, Vec<Src>> = jdc_core::FxHashMap::default();
5393 walk_all(body, &mut |st| {
5394 let (tgt, val): (u32, &Expr) = match st {
5395 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
5396 Expr::Local { var, .. } => (*var, &**value),
5397 _ => return,
5398 },
5399 Stmt::LocalDef { var, init: Some(value), .. } => (*var, value),
5400 _ => return,
5401 };
5402 let s = match val {
5403 Expr::This => Src::This,
5404 Expr::Local { var: src, .. } if *src == this_var => Src::This,
5405 Expr::Local { var: src, .. } => Src::Loc(*src),
5406 _ => Src::Other,
5407 };
5408 sources.entry(tgt).or_default().push(s);
5409 });
5410 let n = vt
5411 .vars
5412 .len()
5413 .max(analysis.assigns.len())
5414 .max(sources.keys().map(|k| *k as usize + 1).max().unwrap_or(0));
5415 let mut alias = vec![false; n];
5416 loop {
5417 let mut changed = false;
5418 for (v, srcs) in &sources {
5419 let vi = *v as usize;
5420 if vi >= n || alias[vi] || srcs.is_empty() {
5421 continue;
5422 }
5423 let ok = srcs.iter().all(|s| match s {
5424 Src::This => true,
5425 Src::Loc(w) => (*w as usize) < n && alias[*w as usize],
5426 Src::Other => false,
5427 });
5428 if ok {
5429 alias[vi] = true;
5430 changed = true;
5431 }
5432 }
5433 if !changed {
5434 break;
5435 }
5436 }
5437 if !alias.iter().any(|&b| b) {
5438 return;
5439 }
5440 walk_stmt_exprs(body, &mut |e| {
5441 deep_rewrite_reads(e, &mut |x| {
5442 if let Expr::Local { var, .. } = x {
5443 if (*var as usize) < n && alias[*var as usize] {
5444 *x = Expr::This;
5445 }
5446 }
5447 });
5448 });
5449}
5450
5451pub fn fix_this0_owners(body: &mut Stmt, class_name: &str) {
5452 walk_stmt_exprs(body, &mut |e| {
5453 deep_rewrite(e, &mut |x| {
5454 if let Expr::Field { owner: Some(_), cls, name, .. } = x {
5455 if name.as_ref() == "this$0" && cls.as_ref() == class_name {
5456 if let Expr::Field { owner, .. } = x {
5457 *owner = None;
5458 }
5459 }
5460 }
5461 });
5462 });
5463}
5464
5465pub fn rescue_primitive_receivers(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
5466 fn prim(t: &JavaType) -> bool {
5467 match t {
5468 JavaType::Int
5469 | JavaType::Long
5470 | JavaType::Short
5471 | JavaType::Byte
5472 | JavaType::Char
5473 | JavaType::Float
5474 | JavaType::Double
5475 | JavaType::Boolean => true,
5476 JavaType::Object(n) => matches!(
5480 n.as_ref(),
5481 "java/lang/Boolean"
5482 | "java/lang/String"
5483 | "java/lang/Integer"
5484 | "java/lang/Long"
5485 | "java/lang/Short"
5486 | "java/lang/Byte"
5487 | "java/lang/Character"
5488 | "java/lang/Float"
5489 | "java/lang/Double"
5490 ),
5491 _ => false,
5492 }
5493 }
5494 walk_stmt_exprs(body, &mut |e| {
5495 deep_rewrite(e, &mut |x| {
5496 let (recv_var, recv_ty) = {
5498 let o: &Expr = match &*x {
5499 Expr::Method { owner: Some(o), .. } => o,
5500 Expr::Field { owner: Some(o), is_static: false, .. } => o,
5501 _ => return,
5502 };
5503 let Expr::Local { var, ty } = o else {
5504 return;
5505 };
5506 let vt_ty = if (*var as usize) < vt.vars.len() {
5507 vt.var(*var).ty.erased()
5508 } else {
5509 ty.erased()
5510 };
5511 if !prim(&vt_ty) {
5512 return;
5513 }
5514 (*var, vt_ty)
5515 };
5516 let cls: std::sync::Arc<str> = match &*x {
5517 Expr::Method { cls, .. } | Expr::Field { cls, .. } => cls.clone(),
5518 _ => return,
5519 };
5520 if let JavaType::Object(n) = &recv_ty {
5529 if n.as_ref() == cls.as_ref() || pool.is_subtype(n.as_ref(), cls.as_ref()) {
5530 return;
5531 }
5532 }
5533 let mut cands: Vec<u32> = Vec::new();
5535 for v in &vt.vars {
5536 if let JavaType::Object(n) = v.ty.erased() {
5537 if n.as_ref() == cls.as_ref()
5538 || pool.is_subtype(n.as_ref(), cls.as_ref())
5539 {
5540 cands.push(v.id);
5541 }
5542 }
5543 }
5544 let id = if cands.len() == 1 {
5545 cands[0]
5546 } else if cands.len() > 1 {
5547 let slot = if (recv_var as usize) < vt.vars.len() {
5552 Some(vt.var(recv_var).slot)
5553 } else {
5554 None
5555 };
5556 let mut hits: Vec<u32> = match slot {
5557 Some(sl) => cands
5558 .iter()
5559 .copied()
5560 .filter(|&c| {
5561 c != recv_var
5562 && (c as usize) < vt.vars.len()
5563 && vt.var(c).slot == sl
5564 })
5565 .collect(),
5566 None => Vec::new(),
5567 };
5568 if hits.is_empty() {
5569 hits = cands
5572 .iter()
5573 .copied()
5574 .filter(|&c| c != recv_var)
5575 .collect();
5576 }
5577 hits.sort_unstable();
5578 hits.dedup();
5579 if hits.is_empty() {
5580 return;
5581 }
5582 *hits.last().unwrap()
5583 } else {
5584 if cands.is_empty() {
5591 return;
5592 }
5593 let slot = if (recv_var as usize) < vt.vars.len() {
5594 vt.var(recv_var).slot
5595 } else {
5596 return;
5597 };
5598 let mut hits: Vec<u32> = cands
5599 .into_iter()
5600 .filter(|&c| {
5601 c != recv_var
5602 && (c as usize) < vt.vars.len()
5603 && vt.var(c).slot == slot
5604 })
5605 .collect();
5606 hits.sort_unstable();
5607 hits.dedup();
5608 if hits.len() != 1 {
5609 return;
5610 }
5611 hits[0]
5612 };
5613 let o: &mut Box<Expr> = match x {
5615 Expr::Method { owner: Some(o), .. } => o,
5616 Expr::Field { owner: Some(o), is_static: false, .. } => o,
5617 _ => return,
5618 };
5619 if let Expr::Local { var, ty } = &mut **o {
5620 if *var == recv_var {
5621 *var = id;
5622 *ty = vt.vars[id as usize].ty.clone();
5623 }
5624 }
5625 });
5626 });
5627
5628}
5629
5630pub fn rescue_arg_return_swaps(
5636 body: &mut Stmt,
5637 vt: &VarTable,
5638 pool: &crate::DexPool,
5639 ret_ty: &JavaType,
5640) {
5641 let mut top_ids: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
5649 for v in &vt.vars {
5650 if v.is_param {
5651 top_ids.insert(v.id);
5652 }
5653 }
5654 if let Stmt::Block(stmts) = body {
5655 for st in stmts.iter() {
5656 if let Stmt::LocalDef { var, .. } = st {
5657 top_ids.insert(*var);
5658 }
5659 }
5660 }
5661 let ret_wants_ref = match ret_ty {
5662 JavaType::Object(n) => n.as_ref() != "java/lang/Object",
5663 JavaType::Array(_) => true,
5664 _ => false,
5665 };
5666 let try_swap = |a: &mut Expr, want: &JavaType| {
5667 let Expr::Local { var, ty } = &mut *a else {
5668 return;
5669 };
5670 if (*var as usize) >= vt.vars.len() {
5671 return;
5672 }
5673 let avt = vt.var(*var).ty.erased();
5674 let avt_ref = matches!(avt, JavaType::Object(_) | JavaType::Array(_));
5675 let want_arr = matches!(want, JavaType::Array(_));
5676 if avt_ref && !(avt == JavaType::Object("java/lang/Object".into())) {
5679 return;
5680 }
5681 if avt_ref && !matches!(want, JavaType::Object(_)) {
5682 return;
5683 }
5684 let slot = vt.var(*var).slot;
5685 let mut hits: Vec<u32> = Vec::new();
5686 for v in &vt.vars {
5687 if v.slot != slot || !top_ids.contains(&v.id) || v.id == *var {
5688 continue;
5689 }
5690 let t = v.ty.erased();
5691 let ok = match (&t, want) {
5692 (JavaType::Object(n), JavaType::Object(w)) => {
5693 n.as_ref() == w.as_ref()
5694 || pool.is_subtype(n.as_ref(), w.as_ref())
5695 }
5696 (JavaType::Array(_), JavaType::Array(_)) => want_arr,
5697 _ => false,
5698 };
5699 if ok {
5700 hits.push(v.id);
5701 }
5702 }
5703 hits.sort_unstable();
5704 hits.dedup();
5705 if hits.is_empty() {
5706 return;
5707 }
5708 let id = *hits.last().unwrap();
5714 *var = id;
5715 *ty = vt.vars[id as usize].ty.clone();
5716 };
5717 walk_stmt_exprs(body, &mut |e| {
5718 deep_rewrite(e, &mut |x| {
5719 match x {
5720 Expr::Method { desc, args, is_dynamic: false, .. } => {
5721 for (i, a) in args.iter_mut().enumerate() {
5722 if let Some(f) = desc.args.get(i) {
5723 if matches!(f, JavaType::Object(n) if n.as_ref() != "java/lang/Object")
5724 || matches!(f, JavaType::Array(_))
5725 {
5726 try_swap(a, f);
5727 }
5728 }
5729 }
5730 }
5731 Expr::New { arg_tys, args, .. } => {
5732 for (i, a) in args.iter_mut().enumerate() {
5733 if let Some(f) = arg_tys.get(i) {
5734 if matches!(f, JavaType::Object(n) if n.as_ref() != "java/lang/Object")
5735 || matches!(f, JavaType::Array(_))
5736 {
5737 try_swap(a, f);
5738 }
5739 }
5740 }
5741 }
5742 _ => {}
5743 }
5744 });
5745 });
5746 if ret_wants_ref {
5747 walk_mut_deep(body, &mut |st| {
5748 if let Stmt::Return(Some(e)) = st {
5749 try_swap(e, ret_ty);
5750 }
5751 });
5752 }
5753}
5754
5755pub fn fix_ref_null_assigns(vt: &VarTable, body: &mut Stmt) {
5770 let is_ref_var = |var: u32| -> bool {
5771 (var as usize) < vt.vars.len()
5772 && matches!(
5773 vt.vars[var as usize].ty.erased(),
5774 JavaType::Object(_) | JavaType::Array(_)
5775 )
5776 };
5777 walk_mut_deep(body, &mut |st| match st {
5778 Stmt::ExprStmt(Expr::Assign {
5779 target,
5780 value,
5781 op: AssignOp::Plain,
5782 ..
5783 }) => {
5784 if let Expr::Local { var, .. } = &**target {
5785 if is_ref_var(*var) && matches!(&**value, Expr::Const(ConstVal::Int(0))) {
5786 **value = Expr::Const(ConstVal::Null);
5787 }
5788 }
5789 }
5790 Stmt::LocalDef {
5791 var,
5792 init: Some(e),
5793 ..
5794 } => {
5795 if is_ref_var(*var) && matches!(e, Expr::Const(ConstVal::Int(0))) {
5796 *e = Expr::Const(ConstVal::Null);
5797 }
5798 }
5799 _ => {}
5800 });
5801}
5802
5803pub fn fix_ref_array_null_consts(body: &mut Stmt) {
5804 walk_stmt_exprs(body, &mut |e| {
5805 deep_rewrite(e, &mut |x| {
5806 if let Expr::NewArray { elem, init: Some(list), .. } = x {
5807 if matches!(elem.erased(), JavaType::Object(_) | JavaType::Array(_)) {
5808 for slot in list.iter_mut() {
5809 if matches!(&*slot, Expr::Const(ConstVal::Int(0))) {
5810 *slot = Expr::Const(ConstVal::Null);
5811 }
5812 }
5813 }
5814 }
5815 });
5816 });
5817}
5818
5819pub fn fix_null_sentinels(body: &mut Stmt, vt: &VarTable, ret: &JavaType) {
5858 #[derive(Clone, Copy, PartialEq)]
5859 enum WK {
5860 Zero,
5861 Copy(u32),
5862 Other,
5863 }
5864 fn is_ref_jt(t: &JavaType) -> bool {
5865 matches!(t, JavaType::Object(_) | JavaType::Array(_))
5866 }
5867 fn cls_write(e: &Expr) -> WK {
5868 match e {
5869 Expr::Const(ConstVal::Int(0)) => WK::Zero,
5870 Expr::Local { var, .. } => WK::Copy(*var),
5871 _ => WK::Other,
5872 }
5873 }
5874 fn expr_writes(e: &Expr, writes: &mut jdc_core::FxHashMap<u32, Vec<WK>>) {
5875 visit_exprs(e, &mut |x| match x {
5876 Expr::Assign { target, op, value } => {
5877 if let Expr::Local { var, .. } = &**target {
5878 let k = if matches!(op, AssignOp::Plain) {
5879 cls_write(value)
5880 } else {
5881 WK::Other
5882 };
5883 writes.entry(*var).or_default().push(k);
5884 }
5885 }
5886 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
5887 if let Expr::Local { var, .. } = &**inner {
5888 writes.entry(*var).or_default().push(WK::Other);
5889 }
5890 }
5891 _ => {}
5892 });
5893 }
5894 fn collect_writes(
5895 s: &Stmt,
5896 writes: &mut jdc_core::FxHashMap<u32, Vec<WK>>,
5897 has_zero: &mut bool,
5898 ) {
5899 fn note(e: &Expr, has_zero: &mut bool, writes: &mut jdc_core::FxHashMap<u32, Vec<WK>>) {
5900 visit_exprs(e, &mut |x| {
5901 if let Expr::Const(ConstVal::Int(0)) = x {
5902 *has_zero = true;
5903 }
5904 });
5905 expr_writes(e, writes);
5906 }
5907 match s {
5908 Stmt::Block(v) => {
5909 for x in v {
5910 collect_writes(x, writes, has_zero);
5911 }
5912 }
5913 Stmt::LocalDef { var, init, .. } => {
5914 if let Some(e) = init {
5915 if matches!(e, Expr::Const(ConstVal::Int(0))) {
5916 *has_zero = true;
5917 }
5918 writes.entry(*var).or_default().push(cls_write(e));
5919 }
5920 }
5921 Stmt::ExprStmt(e) => note(e, has_zero, writes),
5922 Stmt::Return(e) => {
5923 if let Some(x) = e {
5924 note(x, has_zero, writes);
5925 }
5926 }
5927 Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
5928 note(e, has_zero, writes);
5929 }
5930 Stmt::TernaryValue { e } => note(e, has_zero, writes),
5931 Stmt::If { cond, then_stmt, else_stmt } => {
5932 note(cond, has_zero, writes);
5933 collect_writes(then_stmt, writes, has_zero);
5934 if let Some(b) = else_stmt {
5935 collect_writes(b, writes, has_zero);
5936 }
5937 }
5938 Stmt::While { cond, body } | Stmt::DoWhile { body, cond } => {
5939 note(cond, has_zero, writes);
5940 collect_writes(body, writes, has_zero);
5941 }
5942 Stmt::For { init, cond, update, body } => {
5943 for x in init {
5944 collect_writes(x, writes, has_zero);
5945 }
5946 if let Some(c) = cond {
5947 note(c, has_zero, writes);
5948 }
5949 for u in update {
5950 note(u, has_zero, writes);
5951 }
5952 collect_writes(body, writes, has_zero);
5953 }
5954 Stmt::ForEach { var, iterable, body, .. } => {
5955 writes.entry(*var).or_default().push(WK::Other);
5956 note(iterable, has_zero, writes);
5957 collect_writes(body, writes, has_zero);
5958 }
5959 Stmt::Switch { selector, cases, default, .. } => {
5960 note(selector, has_zero, writes);
5961 for c in cases {
5962 if let Some(g) = &c.guard {
5963 note(g, has_zero, writes);
5964 }
5965 for x in &c.body {
5966 collect_writes(x, writes, has_zero);
5967 }
5968 }
5969 if let Some(d) = default {
5970 collect_writes(d, writes, has_zero);
5971 }
5972 }
5973 Stmt::Try { body, catches, finally } => {
5974 collect_writes(body, writes, has_zero);
5975 for c in catches {
5976 writes.entry(c.var).or_default().push(WK::Other);
5977 collect_writes(&c.body, writes, has_zero);
5978 }
5979 if let Some(f) = finally {
5980 collect_writes(f, writes, has_zero);
5981 }
5982 }
5983 Stmt::TryWithResources { resources, body, catches, finally, .. } => {
5984 for r in resources {
5985 collect_writes(r, writes, has_zero);
5986 }
5987 collect_writes(body, writes, has_zero);
5988 for c in catches {
5989 writes.entry(c.var).or_default().push(WK::Other);
5990 collect_writes(&c.body, writes, has_zero);
5991 }
5992 if let Some(f) = finally {
5993 collect_writes(f, writes, has_zero);
5994 }
5995 }
5996 Stmt::Assert { cond, msg } => {
5997 note(cond, has_zero, writes);
5998 if let Some(m) = msg {
5999 note(m, has_zero, writes);
6000 }
6001 }
6002 Stmt::Synchronized { lock, body } => {
6003 note(lock, has_zero, writes);
6004 collect_writes(body, writes, has_zero);
6005 }
6006 Stmt::Labeled { body, .. } => collect_writes(body, writes, has_zero),
6007 _ => {}
6008 }
6009 }
6010
6011 let mut writes: jdc_core::FxHashMap<u32, Vec<WK>> =
6012 jdc_core::FxHashMap::default();
6013 let mut has_zero = false;
6014 collect_writes(body, &mut writes, &mut has_zero);
6015
6016 let mut eligible: HashSet<u32> = HashSet::default();
6018 let int_typed = |v: u32| {
6019 (v as usize) < vt.vars.len() && vt.vars[v as usize].ty.erased() == JavaType::Int
6020 };
6021 loop {
6022 let mut changed = false;
6023 for (&v, ws) in writes.iter() {
6024 if eligible.contains(&v) || ws.is_empty() || !int_typed(v) {
6025 continue;
6026 }
6027 if ws.iter().all(|k| match k {
6028 WK::Zero => true,
6029 WK::Copy(u) => *u != v && eligible.contains(u),
6030 WK::Other => false,
6031 }) {
6032 eligible.insert(v);
6033 changed = true;
6034 }
6035 }
6036 if !changed {
6037 break;
6038 }
6039 }
6040 if eligible.is_empty() && !has_zero {
6041 return; }
6043
6044 #[derive(Clone, Copy, PartialEq)]
6045 enum Mode {
6046 Scan,
6047 Rewrite,
6048 }
6049 struct St<'a> {
6050 mode: Mode,
6051 vt: &'a VarTable,
6052 eligible: &'a HashSet<u32>,
6053 ref_read: HashSet<u32>,
6054 dead: HashSet<u32>,
6055 sentinels: HashSet<u32>,
6056 n_lit: usize,
6057 n_var: usize,
6058 }
6059 fn arg_ctx(t: &JavaType) -> Option<JavaType> {
6063 match t {
6064 JavaType::Object(n) if n.as_ref() == "java/lang/Object" => None,
6065 t if is_ref_jt(t) => Some(t.clone()),
6066 _ => None,
6067 }
6068 }
6069 fn walk_expr(e: &mut Expr, ctx: Option<JavaType>, st: &mut St) {
6070 walk_expr_c(e, ctx, false, st);
6071 }
6072 fn walk_expr_c(e: &mut Expr, ctx: Option<JavaType>, cast_arg: bool, st: &mut St) {
6078 let slot_ref = ctx.as_ref().is_some_and(is_ref_jt);
6079 fn mk_null(ctx: &Option<JavaType>, cast_arg: bool) -> Expr {
6080 match (cast_arg, ctx) {
6081 (true, Some(t)) => Expr::Cast {
6082 ty: TypeRef::J(t.clone()),
6083 e: Box::new(Expr::Const(ConstVal::Null)),
6084 },
6085 _ => Expr::Const(ConstVal::Null),
6086 }
6087 }
6088 match e {
6089 Expr::Const(ConstVal::Int(0)) if slot_ref => {
6090 if st.mode == Mode::Rewrite {
6091 *e = mk_null(&ctx, cast_arg);
6092 st.n_lit += 1;
6093 }
6094 return;
6095 }
6096 Expr::Local { var, .. } => {
6097 if st.eligible.contains(var) {
6098 if slot_ref {
6099 st.ref_read.insert(*var);
6100 } else {
6101 st.dead.insert(*var);
6102 }
6103 }
6104 if st.mode == Mode::Rewrite && slot_ref && st.sentinels.contains(var) {
6105 *e = mk_null(&ctx, cast_arg);
6106 st.n_var += 1;
6107 }
6108 return;
6109 }
6110 _ => {}
6111 }
6112 match e {
6113 Expr::Method { owner, desc, args, .. } => {
6114 if let Some(o) = owner {
6115 walk_expr(o, None, st);
6116 }
6117 for (i, a) in args.iter_mut().enumerate() {
6118 let c = desc.args.get(i).and_then(arg_ctx);
6119 walk_expr_c(a, c, true, st);
6120 }
6121 }
6122 Expr::Invokedynamic { desc, args, .. } => {
6123 for (i, a) in args.iter_mut().enumerate() {
6124 let c = desc.args.get(i).and_then(arg_ctx);
6125 walk_expr_c(a, c, true, st);
6126 }
6127 }
6128 Expr::New { args, arg_tys, .. } => {
6129 for (i, a) in args.iter_mut().enumerate() {
6130 let c = arg_tys.get(i).and_then(arg_ctx);
6131 walk_expr_c(a, c, true, st);
6132 }
6133 }
6134 Expr::AnonNew { args, .. } => {
6135 for a in args.iter_mut() {
6136 walk_expr(a, None, st);
6137 }
6138 }
6139 Expr::Assign { target, value, .. } => {
6140 if let Expr::Local { var: u, .. } = &**target {
6144 if st.eligible.contains(u) {
6145 return;
6146 }
6147 }
6148 let vctx = match &**target {
6149 Expr::Field { ty, .. } => {
6150 let t = ty.erased();
6151 is_ref_jt(&t).then_some(t)
6152 }
6153 Expr::ArrayIndex { array, .. } => match array.type_ref().erased() {
6154 JavaType::Array(inner) if is_ref_jt(&inner) => Some(*inner),
6155 _ => None,
6156 },
6157 _ => None,
6158 };
6159 walk_expr(target, None, st);
6160 walk_expr(value, vctx, st);
6161 }
6162 Expr::Cast { ty, e: inner } => {
6163 let t = ty.erased();
6164 let c = is_ref_jt(&t).then_some(t);
6165 walk_expr(inner, c, st);
6166 }
6167 Expr::InstanceOf { e: inner, .. } => {
6168 walk_expr(inner, Some(JavaType::Object("java/lang/Object".into())), st);
6169 }
6170 Expr::Cond { c, t, f } => {
6171 walk_expr(c, None, st);
6172 let tc = ctx.clone();
6173 walk_expr(t, tc, st);
6174 walk_expr(f, ctx, st);
6175 }
6176 Expr::NewArray { elem, dims, init, .. } => {
6177 for d in dims.iter_mut() {
6178 walk_expr(d, None, st);
6179 }
6180 if let Some(list) = init {
6181 let et = elem.erased();
6182 let c = is_ref_jt(&et).then_some(et);
6183 for slot in list.iter_mut() {
6184 walk_expr(slot, c.clone(), st);
6185 }
6186 }
6187 }
6188 Expr::NewMultiArray { dims, .. } => {
6189 for d in dims.iter_mut() {
6190 walk_expr(d, None, st);
6191 }
6192 }
6193 Expr::ArrayIndex { array, index } => {
6194 walk_expr(array, None, st);
6195 walk_expr(index, None, st);
6196 }
6197 Expr::Field { owner, .. } => {
6198 if let Some(o) = owner {
6199 walk_expr(o, None, st);
6200 }
6201 }
6202 Expr::Bin { l, r, .. } => {
6203 walk_expr(l, None, st);
6204 walk_expr(r, None, st);
6205 }
6206 Expr::Un { e: inner, .. } => walk_expr(inner, None, st),
6207 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
6208 walk_expr(inner, None, st);
6209 }
6210 Expr::StringConcat(parts) => {
6211 for p in parts.iter_mut() {
6212 if let ConcatPart::Str(x) = p {
6213 walk_expr(x, None, st);
6214 }
6215 }
6216 }
6217 Expr::Lambda(l) => {
6218 for c in l.captures.iter_mut() {
6219 walk_expr(c, None, st);
6220 }
6221 }
6222 _ => {}
6223 }
6224 }
6225 fn walk_stmt(s: &mut Stmt, st: &mut St, ret: &JavaType) {
6226 match s {
6227 Stmt::Block(v) => {
6228 for x in v.iter_mut() {
6229 walk_stmt(x, st, ret);
6230 }
6231 }
6232 Stmt::ExprStmt(e) => walk_expr(e, None, st),
6233 Stmt::LocalDef { var, init, .. } => {
6234 if let Some(e) = init {
6235 let ctx = if (*var as usize) < st.vt.vars.len() {
6236 let t = st.vt.vars[*var as usize].ty.erased();
6237 is_ref_jt(&t).then_some(t)
6238 } else {
6239 None
6240 };
6241 walk_expr(e, ctx, st);
6242 }
6243 }
6244 Stmt::Return(e) => {
6245 if let Some(x) = e {
6246 let c = is_ref_jt(ret).then(|| ret.clone());
6247 walk_expr(x, c, st);
6248 }
6249 }
6250 Stmt::Throw(e) => {
6251 walk_expr(e, Some(JavaType::Object("java/lang/Throwable".into())), st);
6252 }
6253 Stmt::If { cond, then_stmt, else_stmt } => {
6254 walk_expr(cond, None, st);
6255 walk_stmt(then_stmt, st, ret);
6256 if let Some(b) = else_stmt {
6257 walk_stmt(b, st, ret);
6258 }
6259 }
6260 Stmt::While { cond, body } => {
6261 walk_expr(cond, None, st);
6262 walk_stmt(body, st, ret);
6263 }
6264 Stmt::DoWhile { body, cond } => {
6265 walk_stmt(body, st, ret);
6266 walk_expr(cond, None, st);
6267 }
6268 Stmt::For { init, cond, update, body } => {
6269 for x in init.iter_mut() {
6270 walk_stmt(x, st, ret);
6271 }
6272 if let Some(c) = cond {
6273 walk_expr(c, None, st);
6274 }
6275 for u in update.iter_mut() {
6276 walk_expr(u, None, st);
6277 }
6278 walk_stmt(body, st, ret);
6279 }
6280 Stmt::ForEach { iterable, body, .. } => {
6281 walk_expr(iterable, None, st);
6282 walk_stmt(body, st, ret);
6283 }
6284 Stmt::Switch { selector, cases, default, .. } => {
6285 walk_expr(selector, None, st);
6286 for c in cases.iter_mut() {
6287 if let Some(g) = &mut c.guard {
6288 walk_expr(g, None, st);
6289 }
6290 for x in c.body.iter_mut() {
6291 walk_stmt(x, st, ret);
6292 }
6293 }
6294 if let Some(d) = default {
6295 walk_stmt(d, st, ret);
6296 }
6297 }
6298 Stmt::Try { body, catches, finally } => {
6299 walk_stmt(body, st, ret);
6300 for c in catches.iter_mut() {
6301 walk_stmt(&mut c.body, st, ret);
6302 }
6303 if let Some(f) = finally {
6304 walk_stmt(f, st, ret);
6305 }
6306 }
6307 Stmt::TryWithResources { resources, body, catches, finally, .. } => {
6308 for r in resources.iter_mut() {
6309 walk_stmt(r, st, ret);
6310 }
6311 walk_stmt(body, st, ret);
6312 for c in catches.iter_mut() {
6313 walk_stmt(&mut c.body, st, ret);
6314 }
6315 if let Some(f) = finally {
6316 walk_stmt(f, st, ret);
6317 }
6318 }
6319 Stmt::Assert { cond, msg } => {
6320 walk_expr(cond, None, st);
6321 if let Some(m) = msg {
6322 walk_expr(m, None, st);
6323 }
6324 }
6325 Stmt::Synchronized { lock, body } => {
6326 walk_expr(lock, Some(JavaType::Object("java/lang/Object".into())), st);
6327 walk_stmt(body, st, ret);
6328 }
6329 Stmt::TernaryValue { e } => walk_expr(e, None, st),
6330 Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
6331 walk_expr(e, Some(JavaType::Object("java/lang/Object".into())), st);
6332 }
6333 Stmt::Labeled { body, .. } => walk_stmt(body, st, ret),
6334 _ => {}
6335 }
6336 }
6337
6338 let mut st = St {
6339 mode: Mode::Scan,
6340 vt,
6341 eligible: &eligible,
6342 ref_read: HashSet::default(),
6343 dead: HashSet::default(),
6344 sentinels: HashSet::default(),
6345 n_lit: 0,
6346 n_var: 0,
6347 };
6348 if !eligible.is_empty() {
6349 walk_stmt(body, &mut st, ret);
6350 st.sentinels = eligible
6351 .iter()
6352 .copied()
6353 .filter(|v| st.ref_read.contains(v) && !st.dead.contains(v))
6354 .collect();
6355 }
6356 if st.sentinels.is_empty() && !has_zero {
6357 return;
6358 }
6359 st.mode = Mode::Rewrite;
6360 walk_stmt(body, &mut st, ret);
6361 if std::env::var_os("DDC_SENT").is_some() {
6362 eprintln!(
6363 "[sent] elig={} confirmed={} lit={} var={}",
6364 eligible.len(),
6365 st.sentinels.len(),
6366 st.n_lit,
6367 st.n_var
6368 );
6369 }
6370}
6371
6372pub fn fix_int_operand_bridges(body: &mut Stmt, vt: &VarTable) {
6380 fn side_bool(e: &Expr, vt: &VarTable) -> bool {
6381 match e {
6382 Expr::Local { var, .. } => {
6383 matches!(vt.var(*var).ty.erased(), JavaType::Boolean)
6384 }
6385 Expr::Bin {
6395 op: BinOp::And | BinOp::Or | BinOp::Xor,
6396 l,
6397 r,
6398 ..
6399 } => side_bool(l, vt) && side_bool(r, vt),
6400 other => matches!(other.type_ref().erased(), JavaType::Boolean),
6401 }
6402 }
6403 fn side_int(e: &Expr, vt: &VarTable) -> bool {
6404 if side_bool(e, vt) {
6408 return false;
6409 }
6410 matches!(
6411 match e {
6412 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
6413 other => other.type_ref().erased(),
6414 },
6415 JavaType::Int | JavaType::Short | JavaType::Byte | JavaType::Char | JavaType::Long
6416 )
6417 }
6418 fn wrap(e: Box<Expr>) -> Box<Expr> {
6419 Box::new(Expr::Cond {
6420 c: e,
6421 t: Box::new(Expr::Const(ConstVal::Int(1))),
6422 f: Box::new(Expr::Const(ConstVal::Int(0))),
6423 })
6424 }
6425 walk_stmt_exprs(body, &mut |e| {
6426 deep_rewrite(e, &mut |x| {
6427 if let Expr::Assign { target, value, op: AssignOp::Plain } = x {
6434 let tgt_ty: Option<JavaType> = match &**target {
6435 Expr::Field { ty, .. } => Some(ty.erased()),
6436 Expr::ArrayIndex { array, .. } => {
6437 match array.type_ref().erased() {
6438 JavaType::Array(el) => Some(el.as_ref().clone()),
6439 _ => None,
6440 }
6441 }
6442 Expr::Local { var, .. } => {
6447 if (*var as usize) < vt.vars.len() {
6448 Some(vt.var(*var).ty.erased())
6449 } else {
6450 None
6451 }
6452 }
6453 _ => None,
6454 };
6455 if let Some(t) = tgt_ty {
6456 let tgt_int = matches!(
6457 t,
6458 JavaType::Int
6459 | JavaType::Short
6460 | JavaType::Byte
6461 | JavaType::Char
6462 | JavaType::Long
6463 );
6464 if tgt_int && side_bool(value, vt) && !side_int(value, vt) {
6465 let taken = std::mem::replace(
6466 value,
6467 Box::new(Expr::Const(ConstVal::Null)),
6468 );
6469 let mut wrapped = wrap(taken);
6470 if !matches!(t, JavaType::Int | JavaType::Long) {
6471 wrapped = Box::new(Expr::Cast {
6472 ty: TypeRef::J(t.clone()),
6473 e: wrapped,
6474 });
6475 }
6476 *value = wrapped;
6477 } else if matches!(t, JavaType::Boolean)
6478 && side_int(value, vt)
6479 && !side_bool(value, vt)
6480 {
6481 let taken = std::mem::replace(
6482 value,
6483 Box::new(Expr::Const(ConstVal::Null)),
6484 );
6485 **value = Expr::Bin {
6486 op: BinOp::Ne,
6487 l: taken,
6488 r: Box::new(Expr::Const(ConstVal::Int(0))),
6489 ty: Some(TypeRef::J(JavaType::Boolean)),
6490 };
6491 }
6492 }
6493 }
6494 if let Expr::Cast { ty, e } = x {
6500 let t = ty.erased();
6501 let t_num = matches!(
6502 t,
6503 JavaType::Int
6504 | JavaType::Short
6505 | JavaType::Byte
6506 | JavaType::Char
6507 | JavaType::Long
6508 | JavaType::Float
6509 | JavaType::Double
6510 );
6511 if t_num && side_bool(e, vt) && !side_int(e, vt) {
6512 let taken = std::mem::replace(e, Box::new(Expr::Const(ConstVal::Null)));
6513 **e = Expr::Cond {
6514 c: taken,
6515 t: Box::new(Expr::Const(ConstVal::Int(1))),
6516 f: Box::new(Expr::Const(ConstVal::Int(0))),
6517 };
6518 } else if matches!(t, JavaType::Boolean)
6519 && side_int(e, vt)
6520 && !side_bool(e, vt)
6521 {
6522 let taken = std::mem::replace(e, Box::new(Expr::Const(ConstVal::Null)));
6523 *x = Expr::Bin {
6524 op: BinOp::Ne,
6525 l: taken,
6526 r: Box::new(Expr::Const(ConstVal::Int(0))),
6527 ty: Some(TypeRef::J(JavaType::Boolean)),
6528 };
6529 }
6530 }
6531 if let Expr::Bin { op, l, r, .. } = x {
6540 if matches!(op, BinOp::Eq | BinOp::Ne) {
6541 if matches!(&**r, Expr::Const(ConstVal::Null))
6542 && side_int(l, vt)
6543 && !side_bool(l, vt)
6544 {
6545 **r = Expr::Const(ConstVal::Int(0));
6546 } else if matches!(&**l, Expr::Const(ConstVal::Null))
6547 && side_int(r, vt)
6548 && !side_bool(r, vt)
6549 {
6550 **l = Expr::Const(ConstVal::Int(0));
6551 } else if matches!(&**r, Expr::Const(ConstVal::Null))
6552 && side_bool(l, vt)
6553 {
6554 let taken =
6555 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
6556 *x = if matches!(op, BinOp::Eq) {
6557 Expr::Un { op: UnOp::Not, e: taken }
6558 } else {
6559 *taken
6560 };
6561 } else if matches!(&**l, Expr::Const(ConstVal::Null))
6562 && side_bool(r, vt)
6563 {
6564 let taken =
6565 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
6566 *x = if matches!(op, BinOp::Eq) {
6567 Expr::Un { op: UnOp::Not, e: taken }
6568 } else {
6569 *taken
6570 };
6571 }
6572 }
6573 }
6574 if let Expr::ArrayIndex { index, .. } = x {
6578 if side_bool(index, vt) && !side_int(index, vt) {
6579 let taken =
6580 std::mem::replace(index, Box::new(Expr::Const(ConstVal::Null)));
6581 *index = wrap(taken);
6582 }
6583 }
6584 if let Expr::NewArray { dims, .. } = x {
6590 for d in dims.iter_mut() {
6591 if side_bool(d, vt) && !side_int(d, vt) {
6592 let taken =
6593 std::mem::replace(d, Expr::Const(ConstVal::Null));
6594 *d = *wrap(Box::new(taken));
6595 }
6596 }
6597 }
6598 if let Expr::Bin { op, l, r, .. } = x {
6599 let int_kind = matches!(
6600 op,
6601 BinOp::Or
6602 | BinOp::And
6603 | BinOp::Xor
6604 | BinOp::Add
6605 | BinOp::Sub
6606 | BinOp::Mul
6607 | BinOp::Div
6608 | BinOp::Rem
6609 | BinOp::Shl
6610 | BinOp::Shr
6611 | BinOp::Ushr
6612 | BinOp::Eq
6613 | BinOp::Ne
6614 | BinOp::Lt
6615 | BinOp::Ge
6616 | BinOp::Gt
6617 | BinOp::Le
6618 );
6619 if !int_kind {
6620 return;
6621 }
6622 if matches!(
6635 op,
6636 BinOp::Or
6637 | BinOp::And
6638 | BinOp::Xor
6639 | BinOp::Add
6640 | BinOp::Sub
6641 | BinOp::Mul
6642 | BinOp::Div
6643 | BinOp::Rem
6644 | BinOp::Shl
6645 | BinOp::Shr
6646 | BinOp::Ushr
6647 ) {
6648 if matches!(&**r, Expr::Const(ConstVal::Null))
6649 && (side_bool(l, vt) || side_int(l, vt))
6650 {
6651 **r = Expr::Const(ConstVal::Int(0));
6652 } else if matches!(&**l, Expr::Const(ConstVal::Null))
6653 && (side_bool(r, vt) || side_int(r, vt))
6654 {
6655 **l = Expr::Const(ConstVal::Int(0));
6656 }
6657 }
6658 let arith = matches!(
6668 op,
6669 BinOp::Add
6670 | BinOp::Sub
6671 | BinOp::Mul
6672 | BinOp::Div
6673 | BinOp::Rem
6674 | BinOp::Shl
6675 | BinOp::Shr
6676 | BinOp::Ushr
6677 );
6678 let ordered = matches!(op, BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le);
6679 if arith || ordered {
6680 let side_str = |e: &Expr| {
6681 matches!(
6682 match e {
6683 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
6684 other => other.type_ref().erased(),
6685 },
6686 JavaType::Object(ref s) if s.as_ref() == "java/lang/String"
6687 )
6688 };
6689 let concat = arith && (side_str(l) || side_str(r));
6690 if !concat {
6691 if side_bool(l, vt) {
6692 let taken =
6693 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
6694 *l = wrap(taken);
6695 }
6696 if side_bool(r, vt) {
6697 let taken =
6698 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
6699 *r = wrap(taken);
6700 }
6701 }
6702 return;
6703 }
6704 if side_bool(l, vt) && side_int(r, vt) && !side_bool(r, vt) {
6707 let taken = std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
6708 *l = wrap(taken);
6709 } else if side_bool(r, vt) && side_int(l, vt) && !side_bool(l, vt) {
6710 let taken = std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
6711 *r = wrap(taken);
6712 }
6713 }
6714 });
6715 });
6716}
6717
6718pub fn fold_value_diamonds(body: &mut Stmt, vt: &VarTable) {
6756 let Stmt::Block(stmts) = body else { return };
6757 fold_diamonds_in(stmts, vt);
6758}
6759
6760fn diamond_types_ok(a: &Expr, b: &Expr) -> bool {
6761 fn num(t: &JavaType) -> bool {
6762 matches!(
6763 t,
6764 JavaType::Byte
6765 | JavaType::Short
6766 | JavaType::Int
6767 | JavaType::Char
6768 | JavaType::Long
6769 | JavaType::Float
6770 | JavaType::Double
6771 )
6772 }
6773 if let Some(other) = if matches!(a, Expr::Const(ConstVal::Null)) {
6781 Some(b)
6782 } else if matches!(b, Expr::Const(ConstVal::Null)) {
6783 Some(a)
6784 } else {
6785 None
6786 } {
6787 return !num(&other.type_ref().erased());
6788 }
6789 let ta = a.type_ref().erased();
6790 let tb = b.type_ref().erased();
6791 ta == tb || (num(&ta) && num(&tb))
6792}
6793
6794fn single_local_assign(st: &Stmt) -> Option<(u32, Expr, Expr)> {
6802 let mut inner = st;
6803 while let Stmt::Block(v) = inner {
6804 if v.len() != 1 {
6805 return None;
6806 }
6807 inner = &v[0];
6808 }
6809 match inner {
6810 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain }) => {
6811 match &**target {
6812 Expr::Local { var, .. } => Some((*var, *target.clone(), *value.clone())),
6813 _ => None,
6814 }
6815 }
6816 _ => None,
6817 }
6818}
6819
6820fn arm_assign_inlined(st: &Stmt) -> Option<(u32, Expr, Expr)> {
6829 if let Some(r) = single_local_assign(st) {
6830 return Some(r);
6831 }
6832 let mut inner = st;
6833 while let Stmt::Block(v) = inner {
6834 if v.len() == 1 {
6835 inner = &v[0];
6836 } else {
6837 break;
6838 }
6839 }
6840 let Stmt::Block(v) = inner else { return None };
6841 if v.len() != 2 {
6842 return None;
6843 }
6844 let Stmt::LocalDef {
6845 var: x,
6846 init: Some(init),
6847 ..
6848 } = &v[0]
6849 else {
6850 return None;
6851 };
6852 let (var, tgt, val) = single_local_assign(&v[1])?;
6853 let mut count = 0usize;
6854 let mut c = val.clone();
6855 deep_rewrite(&mut c, &mut |e| {
6856 if let Expr::Local { var: lv, .. } = e {
6857 if lv == x {
6858 count += 1;
6859 }
6860 }
6861 });
6862 if count != 1 {
6863 return None;
6864 }
6865 let mut out = val.clone();
6866 deep_rewrite(&mut out, &mut |e| {
6867 if let Expr::Local { var: lv, .. } = e {
6868 if lv == x {
6869 *e = init.clone();
6870 }
6871 }
6872 });
6873 Some((var, tgt, out))
6874}
6875
6876fn mk_ternary_assign(tgt: Expr, c: Expr, a: Expr, b: Expr) -> Stmt {
6877 Stmt::ExprStmt(Expr::Assign {
6878 target: Box::new(tgt),
6879 op: AssignOp::Plain,
6880 value: Box::new(Expr::Cond {
6881 c: Box::new(c),
6882 t: Box::new(a),
6883 f: Box::new(b),
6884 }),
6885 })
6886}
6887
6888fn same_render_var(v1: u32, v2: u32, vt: &VarTable) -> bool {
6893 let (a, b) = (vt.vars.get(v1 as usize), vt.vars.get(v2 as usize));
6894 match (a, b) {
6895 (Some(x), Some(y)) => x.name == y.name && x.ty.erased() == y.ty.erased(),
6896 _ => false,
6897 }
6898}
6899
6900fn canon_stmts_eq(a: &[Stmt], b: &[Stmt], vt: &VarTable) -> bool {
6907 if a.len() != b.len() {
6908 return false;
6909 }
6910 let mut map: std::collections::HashMap<String, u32> = std::collections::HashMap::default();
6911 let canon = |s: &Stmt, map: &mut std::collections::HashMap<String, u32>| -> Stmt {
6912 let mut c = s.clone();
6913 walk_stmt_exprs(&mut c, &mut |e| {
6914 deep_rewrite(e, &mut |x| {
6915 if let Expr::Local { var, .. } = x {
6916 let key = match vt.vars.get(*var as usize) {
6917 Some(vi) => format!("{}#{:?}", vi.name, vi.ty.erased()),
6918 None => format!("v{}", var),
6919 };
6920 let id = *map.entry(key).or_insert(*var);
6921 *x = Expr::Local {
6922 var: id,
6923 ty: vt.var(id).ty.clone(),
6924 };
6925 }
6926 });
6927 });
6928 c
6929 };
6930 a.iter()
6931 .zip(b.iter())
6932 .all(|(x, y)| canon(x, &mut map) == canon(y, &mut map))
6933}
6934
6935fn fold_diamonds_in(stmts: &mut Vec<Stmt>, vt: &VarTable) {
6936 let mut i = 0usize;
6937 while i < stmts.len() {
6938 match &mut stmts[i] {
6939 Stmt::Block(v) => fold_diamonds_in(v, vt),
6940 Stmt::If { then_stmt, else_stmt, .. } => {
6941 if let Stmt::Block(v) = &mut **then_stmt {
6942 fold_diamonds_in(v, vt);
6943 }
6944 if let Some(e) = else_stmt {
6945 if let Stmt::Block(v) = &mut **e {
6946 fold_diamonds_in(v, vt);
6947 }
6948 }
6949 }
6950 _ => {}
6951 }
6952 let shape1 = if let Stmt::If { cond, then_stmt, else_stmt: Some(el), .. } = &stmts[i] {
6954 match (arm_assign_inlined(then_stmt), arm_assign_inlined(el)) {
6955 (Some((v1, tgt, a)), Some((v2, _, b)))
6956 if v1 == v2 && diamond_types_ok(&a, &b) =>
6957 {
6958 Some(mk_ternary_assign(tgt, cond.clone(), a, b))
6959 }
6960 _ => None,
6961 }
6962 } else {
6963 None
6964 };
6965 if let Some(f) = shape1 {
6966 stmts[i] = f;
6967 i += 1;
6968 continue;
6969 }
6970 let shape3 = if let Stmt::If {
6983 cond,
6984 then_stmt,
6985 else_stmt: Some(el),
6986 ..
6987 } = &stmts[i]
6988 {
6989 let arm = |st: &Stmt| -> Option<((u32, Expr, Expr), Vec<Stmt>)> {
6990 let Stmt::Block(v) = st else { return None };
6991 if v.len() < 2 {
6992 return None;
6993 }
6994 let first = single_local_assign(&v[0])?;
6995 Some((first, v[1..].to_vec()))
6996 };
6997 match (arm(then_stmt), arm(el)) {
6998 (Some(((v1, tgt1, a), mut rest_t)), Some(((v2, _t2, b), rest_e)))
6999 if !rest_t.is_empty()
7000 && is_bare_ctor_call(first_leaf_stmt(&rest_t[0]))
7001 && stmts_count_writes(&rest_t, v1) == 0
7002 && stmts_count_writes(&rest_t, v2) == 0
7003 && diamond_types_ok(&a, &b) =>
7004 {
7005 if v1 == v2 || same_render_var(v1, v2, vt) {
7006 if canon_stmts_eq(&rest_t, &rest_e, vt) {
7008 Some((mk_ternary_assign(tgt1, cond.clone(), a, b), rest_t))
7009 } else {
7010 None
7011 }
7012 } else if stmts_read_var(&rest_t, v1) == 1
7013 && stmts_read_var(&rest_e, v2) == 1
7014 && stmts_read_var(&rest_t, v2) == 0
7015 && stmts_read_var(&rest_e, v1) == 0
7016 {
7017 let mut unified = rest_e.clone();
7025 for st in unified.iter_mut() {
7026 let mut c2 = st.clone();
7027 walk_stmt_exprs(&mut c2, &mut |e| {
7028 deep_rewrite(e, &mut |x| {
7029 if let Expr::Local { var, .. } = x {
7030 if *var == v2 {
7031 *x = Expr::Local {
7032 var: v1,
7033 ty: vt.var(v1).ty.clone(),
7034 };
7035 }
7036 }
7037 });
7038 });
7039 *st = c2;
7040 }
7041 if unified != rest_t {
7042 None
7043 } else {
7044 let tern = Expr::Cond {
7045 c: Box::new(cond.clone()),
7046 t: Box::new(a.clone()),
7047 f: Box::new(b.clone()),
7048 };
7049 for st in rest_t.iter_mut() {
7050 let mut c2 = st.clone();
7051 walk_stmt_exprs(&mut c2, &mut |e| {
7052 deep_rewrite(e, &mut |x| {
7053 if let Expr::Local { var, .. } = x {
7054 if *var == v1 {
7055 *x = tern.clone();
7056 }
7057 }
7058 });
7059 });
7060 *st = c2;
7061 }
7062 let first = rest_t.remove(0);
7066 Some((first, rest_t))
7067 }
7068 } else {
7069 None
7070 }
7071 }
7072 _ => None,
7073 }
7074 } else {
7075 None
7076 };
7077 if let Some((folded, rest)) = shape3 {
7078 stmts[i] = folded;
7079 stmts.splice(i + 1..i + 1, rest);
7080 i += 1;
7081 continue;
7082 }
7083 if i + 1 < stmts.len() {
7093 let shape2 = {
7113 let s_i = single_local_assign(&stmts[i]);
7114 if let Stmt::If {
7115 cond,
7116 then_stmt,
7117 else_stmt,
7118 ..
7119 } = &stmts[i + 1]
7120 {
7121 let then_a = single_local_assign(then_stmt);
7122 let else_a = else_stmt.as_deref().and_then(single_local_assign);
7123 let then_empty =
7124 matches!(&**then_stmt, Stmt::Block(v) if v.is_empty());
7125 let else_empty = else_stmt
7126 .as_deref()
7127 .map(|e| matches!(e, Stmt::Block(v) if v.is_empty()))
7128 .unwrap_or(true);
7129 let try_fold = |over: Option<(u32, Expr, Expr)>,
7130 keep_over: bool|
7131 -> Option<Stmt> {
7132 let (v2, tgt2, a) = over?;
7133 let (v1, _t1, b) = s_i.as_ref()?;
7134 if !(*v1 == v2 || same_render_var(*v1, v2, vt)) {
7135 return None;
7136 }
7137 let keep = if *v1 == v2 {
7138 b.clone()
7139 } else {
7140 Expr::Local {
7141 var: *v1,
7142 ty: vt.var(*v1).ty.clone(),
7143 }
7144 };
7145 if !diamond_types_ok(&a, &keep) {
7146 return None;
7147 }
7148 if keep_over {
7149 Some(mk_ternary_assign(tgt2, cond.clone(), keep, a))
7150 } else {
7151 Some(mk_ternary_assign(tgt2, cond.clone(), a, keep))
7152 }
7153 };
7154 if then_a.is_some() && else_empty {
7155 try_fold(then_a, false)
7156 } else if else_a.is_some() && then_empty {
7157 try_fold(else_a, true)
7158 } else {
7159 None
7160 }
7161 } else {
7162 None
7163 }
7164 };
7165 let mut lead_fold: Option<(Stmt, Stmt)> = None;
7174 if shape2.is_none() && i + 1 < stmts.len() {
7175 let s_i2 = single_local_assign(&stmts[i]);
7176 let leading = |st: &Stmt| -> Option<((u32, Expr, Expr), Vec<Stmt>)> {
7177 let Stmt::Block(v) = st else { return None };
7178 if v.len() < 2 {
7179 return None;
7180 }
7181 let first = single_local_assign(&v[0])?;
7182 Some((first, v[1..].to_vec()))
7183 };
7184 if let (Some((v1, _t1, b)), Stmt::If { cond, then_stmt, else_stmt, .. }) =
7185 (&s_i2, &stmts[i + 1])
7186 {
7187 let else_absent = else_stmt.is_none()
7188 || else_stmt
7189 .as_deref()
7190 .is_some_and(|e| matches!(e, Stmt::Block(v) if v.is_empty()));
7191 let then_absent = matches!(&**then_stmt, Stmt::Block(v) if v.is_empty());
7192 let try_lead = |over: Option<((u32, Expr, Expr), Vec<Stmt>)>,
7193 keep_over: bool|
7194 -> Option<(Stmt, Stmt)> {
7195 let ((v2, tgt2, a), rest) = over?;
7196 if !(*v1 == v2 || same_render_var(*v1, v2, vt)) {
7197 return None;
7198 }
7199 if stmts_count_writes(&rest, *v1) != 0
7200 || stmts_count_writes(&rest, v2) != 0
7201 {
7202 return None;
7203 }
7204 let keep = if *v1 == v2 {
7205 b.clone()
7206 } else {
7207 Expr::Local {
7208 var: *v1,
7209 ty: vt.var(*v1).ty.clone(),
7210 }
7211 };
7212 if !diamond_types_ok(&a, &keep) {
7213 return None;
7214 }
7215 let t = if keep_over {
7216 mk_ternary_assign(tgt2, cond.clone(), keep, a)
7217 } else {
7218 mk_ternary_assign(tgt2, cond.clone(), a, keep)
7219 };
7220 let mut new_if = stmts[i + 1].clone();
7221 if let Stmt::If { then_stmt, else_stmt, .. } = &mut new_if {
7222 if keep_over {
7223 *else_stmt = Some(Box::new(Stmt::Block(rest)));
7224 } else {
7225 **then_stmt = Stmt::Block(rest);
7226 }
7227 }
7228 Some((t, new_if))
7229 };
7230 if else_absent {
7231 lead_fold = try_lead(leading(then_stmt), false);
7232 } else if then_absent {
7233 if let Some(e) = else_stmt {
7234 lead_fold = try_lead(leading(e), true);
7235 }
7236 }
7237 }
7238 }
7239 if let Some((folded, new_if)) = lead_fold {
7240 stmts[i] = folded;
7241 stmts[i + 1] = new_if;
7242 continue;
7243 }
7244 if let Some(f) = shape2 {
7245 stmts[i] = f;
7246 stmts.remove(i + 1);
7247 continue; }
7249 }
7250 i += 1;
7251 }
7252}
7253
7254pub fn fold_bool_value_diamonds(body: &mut Stmt, vt: &VarTable) {
7255 fn is_bool_var(v: u32, vt: &VarTable) -> bool {
7256 (v as usize) < vt.vars.len()
7257 && matches!(vt.vars[v as usize].ty.erased(), JavaType::Boolean)
7258 }
7259 fn bool_const(e: &Expr) -> Option<bool> {
7260 fn cint(e: &Expr) -> Option<i32> {
7261 match e {
7262 Expr::Const(ConstVal::Int(n)) => Some(*n),
7263 _ => None,
7264 }
7265 }
7266 match e {
7267 Expr::Const(ConstVal::Int(0)) => Some(false),
7268 Expr::Const(ConstVal::Int(1)) => Some(true),
7269 Expr::Bin { op: op @ (BinOp::Eq | BinOp::Ne), l, r, .. } => {
7273 let (a, b) = (cint(l)?, cint(r)?);
7274 let eq = a == b;
7275 Some(if matches!(op, BinOp::Eq) { eq } else { !eq })
7276 }
7277 _ => None,
7278 }
7279 }
7280 fn reads_var(e: &Expr, v: u32) -> bool {
7281 let mut hit = false;
7282 visit_exprs(e, &mut |x| {
7283 if let Expr::Local { var, .. } = x {
7284 if *var == v {
7285 hit = true;
7286 }
7287 }
7288 });
7289 hit
7290 }
7291 fn bool_valued(e: &Expr, vt: &VarTable) -> bool {
7295 fn side(e: &Expr, vt: &VarTable) -> bool {
7296 bool_const(e).is_some() || bool_valued(e, vt)
7297 }
7298 match e {
7299 Expr::Local { var, .. } => is_bool_var(*var, vt),
7300 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
7301 Expr::InstanceOf { .. } => true,
7302 Expr::Un { op: UnOp::Not, e: x, .. } => bool_valued(x, vt) || bool_const(x).is_some(),
7303 Expr::Bin { op, l, r, .. } => match op {
7304 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le => true,
7305 BinOp::LogAnd | BinOp::LogOr => {
7308 side(l, vt) && side(r, vt)
7309 }
7310 _ => false,
7311 },
7312 Expr::Cond { t, f, .. } => side(t, vt) && side(f, vt),
7313 _ => false,
7314 }
7315 }
7316 fn lone_assign(
7325 b: &Stmt,
7326 v: u32,
7327 vt: &VarTable,
7328 an: &VarAnalysis,
7329 consumed: &mut HashSet<u32>,
7330 ) -> Option<Expr> {
7331 let l = flat_list(b);
7332 if l.len() == 2 {
7333 let (u, e): (u32, &Expr) = match l[0] {
7334 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e),
7335 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
7336 match &**target {
7337 Expr::Local { var, .. } => (*var, &**value),
7338 _ => return None,
7339 }
7340 }
7341 _ => return None,
7342 };
7343 if u == v || consumed.contains(&u) {
7344 return None;
7345 }
7346 if an.assigns.get(u as usize).copied().unwrap_or(0) != 1
7353 || an.reads.get(u as usize).copied().unwrap_or(0) != 1
7354 {
7355 return None;
7356 }
7357 let (tv, rv) = match l[1] {
7358 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
7359 match (&**target, &**value) {
7360 (Expr::Local { var: t, .. }, Expr::Local { var: r, .. }) => (*t, *r),
7361 _ => return None,
7362 }
7363 }
7364 Stmt::LocalDef { var, init: Some(Expr::Local { var: r, .. }), .. } => (*var, *r),
7365 _ => return None,
7366 };
7367 if tv != v || rv != u {
7368 return None;
7369 }
7370 if reads_var(e, v) || !(bool_valued(e, vt) || bool_const(e).is_some()) {
7371 return None;
7372 }
7373 consumed.insert(u);
7374 return Some(e.clone());
7375 }
7376 if l.len() != 1 {
7377 return None;
7378 }
7379 let (target, value) = match l[0] {
7380 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => (target, value),
7381 Stmt::LocalDef { var, init: Some(value), .. } if *var == v => {
7382 if reads_var(value, v) || !(bool_valued(value, vt) || bool_const(value).is_some()) {
7383 return None;
7384 }
7385 return Some(value.clone());
7386 }
7387 _ => return None,
7388 };
7389 let Expr::Local { var: tv, .. } = &**target else { return None };
7390 if *tv != v || reads_var(value, v) {
7391 return None;
7392 }
7393 if !(bool_valued(value, vt) || bool_const(value).is_some()) {
7394 return None;
7395 }
7396 Some((**value).clone())
7397 }
7398 fn not(e: Expr) -> Expr {
7399 Expr::Un { op: UnOp::Not, e: Box::new(e) }
7400 }
7401 fn simplify_bool(e: Expr) -> Expr {
7406 match e {
7407 Expr::Un { op: UnOp::Not, e: inner, .. } => match *inner {
7408 Expr::Bin { op: BinOp::Ne, l, r, ty } => {
7413 simplify_bool(Expr::Bin { op: BinOp::Eq, l, r, ty })
7414 }
7415 Expr::Bin { op: BinOp::Eq, l, r, ty } => {
7416 simplify_bool(Expr::Bin { op: BinOp::Ne, l, r, ty })
7417 }
7418 other => Expr::Un { op: UnOp::Not, e: Box::new(simplify_bool(other)) },
7419 },
7420 Expr::Bin { op: op @ (BinOp::Eq | BinOp::Ne), l, r, ty } => {
7421 fn cond01(e: &Expr) -> Option<Expr> {
7423 if let Expr::Cond { c, t, f } = e {
7424 if matches!(&**t, Expr::Const(ConstVal::Int(1)))
7425 && matches!(&**f, Expr::Const(ConstVal::Int(0)))
7426 {
7427 return Some((**c).clone());
7428 }
7429 }
7430 None
7431 }
7432 let (lc, rc) = (cond01(&l), cond01(&r));
7433 match (lc, rc) {
7434 (Some(c), None) => match (&*r, op) {
7435 (Expr::Const(ConstVal::Int(1)), BinOp::Eq) => c,
7436 (Expr::Const(ConstVal::Int(1)), BinOp::Ne) => not(c),
7437 (Expr::Const(ConstVal::Int(0)), BinOp::Eq) => not(c),
7438 (Expr::Const(ConstVal::Int(0)), BinOp::Ne) => c,
7439 _ => Expr::Bin { op, l: Box::new(c), r, ty },
7440 },
7441 (None, Some(c)) => match (&*l, op) {
7442 (Expr::Const(ConstVal::Int(1)), BinOp::Eq) => c,
7443 (Expr::Const(ConstVal::Int(1)), BinOp::Ne) => not(c),
7444 (Expr::Const(ConstVal::Int(0)), BinOp::Eq) => not(c),
7445 (Expr::Const(ConstVal::Int(0)), BinOp::Ne) => c,
7446 _ => Expr::Bin { op, l, r: Box::new(c), ty },
7447 },
7448 _ => Expr::Bin { op, l, r, ty },
7449 }
7450 }
7451 other => other,
7452 }
7453 }
7454 fn bin(op: BinOp, l: Expr, r: Expr) -> Expr {
7455 Expr::Bin { op, l: Box::new(l), r: Box::new(r), ty: None }
7456 }
7457
7458 for _round in 0..4 {
7459 let mut changed = false;
7460 let mut analysis = VarAnalysis {
7461 assigns: Vec::new(),
7462 reads: Vec::new(),
7463 values: Vec::new(),
7464 };
7465 analyze_vars(body, &mut analysis);
7466 let mut consumed: HashSet<u32> = HashSet::default();
7467 walk_mut_deep(body, &mut |st| {
7468 let Stmt::Block(list) = st else { return };
7469 let mut i = 0usize;
7470 while i + 1 < list.len() {
7471 let folded = try_at(list, i, vt, &is_bool_var, &bool_const, &reads_var, &lone_assign, &analysis, &mut consumed);
7472 match folded {
7473 Some(expr) => {
7474 let is_def = matches!(&list[i], Stmt::LocalDef { .. });
7475 let var = match &list[i] {
7476 Stmt::LocalDef { var, .. } => *var,
7477 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
7478 match &**target { Expr::Local { var, .. } => *var, _ => unreachable!() }
7479 }
7480 _ => unreachable!(),
7481 };
7482 let (is_final, force_type) = match &list[i] {
7483 Stmt::LocalDef { is_final, force_type, .. } => (*is_final, *force_type),
7484 _ => (false, false),
7485 };
7486 list[i] = if is_def {
7487 Stmt::LocalDef { var, init: Some(expr), is_final, force_type }
7488 } else {
7489 Stmt::ExprStmt(Expr::Assign {
7490 target: Box::new(Expr::Local {
7491 var,
7492 ty: TypeRef::J(JavaType::Boolean),
7493 }),
7494 op: AssignOp::Plain,
7495 value: Box::new(expr),
7496 })
7497 };
7498 list.remove(i + 1);
7499 changed = true;
7500 }
7503 None => i += 1,
7504 }
7505 }
7506 });
7507 if !changed {
7508 break;
7509 }
7510 }
7511
7512 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
7513 fn try_at(
7514 list: &[Stmt],
7515 i: usize,
7516 vt: &VarTable,
7517 is_bool_var: &dyn Fn(u32, &VarTable) -> bool,
7518 bool_const: &dyn Fn(&Expr) -> Option<bool>,
7519 reads_var: &dyn Fn(&Expr, u32) -> bool,
7520 lone_assign: &dyn Fn(&Stmt, u32, &VarTable, &VarAnalysis, &mut HashSet<u32>) -> Option<Expr>,
7521 an: &VarAnalysis,
7522 consumed: &mut HashSet<u32>,
7523 ) -> Option<Expr> {
7524 let (v, base) = match &list[i] {
7526 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
7527 match &**target {
7528 Expr::Local { var, .. } => {
7529 let b = bool_const(value)?;
7532 if !is_bool_var(*var, vt) { return None; }
7533 (*var, b)
7534 }
7535 _ => return None,
7536 }
7537 }
7538 Stmt::LocalDef { var, init: Some(e), .. } => {
7539 let b = bool_const(e)?;
7540 if !is_bool_var(*var, vt) { return None; }
7541 (*var, b)
7542 }
7543 _ => return None,
7544 };
7545 let Stmt::If { cond, then_stmt, else_stmt } = &list[i + 1] else {
7546 return None;
7547 };
7548 if reads_var(cond, v) {
7549 return None;
7550 }
7551 let mut touches_v = false;
7552 visit_exprs(cond, &mut |x| {
7553 if let Expr::Assign { target, .. } = x {
7554 if let Expr::Local { var, .. } = &**target {
7555 if *var == v { touches_v = true; }
7556 }
7557 }
7558 });
7559 if touches_v {
7560 return None;
7561 }
7562 let (cond_n, then_arm, else_arm): (Expr, &Stmt, Option<&Stmt>) =
7565 if flat_list(then_stmt).is_empty() && else_stmt.is_some() {
7566 (not(cond.clone()), else_stmt.as_ref().unwrap(), None)
7567 } else {
7568 (cond.clone(), then_stmt.as_ref(), else_stmt.as_deref())
7569 };
7570 let e1 = lone_assign(then_arm, v, vt, an, consumed)?;
7571 let e2 = match else_arm {
7572 None => None,
7573 Some(b) => Some(lone_assign(b, v, vt, an, consumed)?),
7574 };
7575 let e1c = bool_const(&e1);
7576 let c = simplify_bool(cond_n);
7577 Some(match e2 {
7578 None => {
7579 if !base {
7580 match e1c {
7581 Some(true) => c,
7582 Some(false) => return None, None => bin(BinOp::LogAnd, c, e1),
7584 }
7585 } else {
7586 match e1c {
7587 Some(false) => not(c),
7588 Some(true) => return None,
7589 None => bin(BinOp::LogOr, not(c), e1),
7590 }
7591 }
7592 }
7593 Some(e2) => {
7594 if e1 == e2 {
7596 return None; }
7598 Expr::Cond { c: Box::new(c), t: Box::new(e1), f: Box::new(e2) }
7599 }
7600 })
7601 }
7602}
7603
7604pub fn idiom_compounds(body: &mut Stmt, vt: &VarTable) {
7605 walk_mut_deep(body, &mut |st| {
7606 let Stmt::ExprStmt(e) = st else { return };
7607 let Expr::Assign { target, op: AssignOp::Plain, value } = &mut *e else {
7608 return;
7609 };
7610 enum Tgt {
7613 Loc(u32),
7614 Fld,
7615 }
7616 let tgt = match &**target {
7617 Expr::Local { var, .. } => Tgt::Loc(*var),
7618 Expr::Field { owner, .. }
7619 if owner.is_none()
7620 || matches!(&**owner.as_ref().unwrap(), Expr::This) =>
7621 {
7622 Tgt::Fld
7623 }
7624 _ => return,
7625 };
7626 let Expr::Bin { op, l, r, .. } = &mut **value else {
7627 return;
7628 };
7629 match (&tgt, &**l) {
7631 (Tgt::Loc(v), Expr::Local { var, .. }) if *var == *v => {}
7632 (Tgt::Fld, Expr::Field { cls: c2, name: n2, .. }) => {
7633 let Expr::Field { cls: c1, name: n1, .. } = &**target else {
7634 return;
7635 };
7636 if c1 != c2 || n1 != n2 {
7637 return;
7638 }
7639 }
7640 _ => return,
7641 }
7642 let one = match &**r {
7644 Expr::Const(ConstVal::Int(1)) => Some(1i64),
7645 Expr::Const(ConstVal::Int(-1)) => Some(-1),
7646 Expr::Const(ConstVal::Long(1)) => Some(1),
7647 Expr::Const(ConstVal::Long(-1)) => Some(-1),
7648 _ => None,
7649 };
7650 if *op == BinOp::Add || *op == BinOp::Sub {
7651 if let Some(mut d) = one {
7652 if *op == BinOp::Sub {
7653 d = -d;
7654 }
7655 let tgt_ty = match &tgt {
7656 Tgt::Loc(v) => vt.var(*v).ty.erased(),
7657 Tgt::Fld => {
7658 let Expr::Field { ty, .. } = &**target else {
7659 return;
7660 };
7661 ty.erased()
7662 }
7663 };
7664 let numeric = matches!(
7665 tgt_ty,
7666 JavaType::Int
7667 | JavaType::Long
7668 | JavaType::Short
7669 | JavaType::Byte
7670 | JavaType::Char
7671 | JavaType::Float
7672 | JavaType::Double
7673 );
7674 if numeric {
7675 let wide = matches!(tgt_ty, JavaType::Long);
7676 let inner = match &tgt {
7677 Tgt::Loc(v) => Expr::Local {
7678 var: *v,
7679 ty: vt.var(*v).ty.clone(),
7680 },
7681 Tgt::Fld => (**target).clone(),
7682 };
7683 *e = Expr::PostIncDec {
7684 e: Box::new(inner),
7685 delta: d,
7686 wide,
7687 };
7688 return;
7689 }
7690 }
7691 }
7692 let cop = match op {
7693 BinOp::Add => AssignOp::Add,
7694 BinOp::Sub => AssignOp::Sub,
7695 BinOp::Mul => AssignOp::Mul,
7696 BinOp::Div => AssignOp::Div,
7697 BinOp::Rem => AssignOp::Rem,
7698 BinOp::Shl => AssignOp::Shl,
7699 BinOp::Shr => AssignOp::Shr,
7700 BinOp::Ushr => AssignOp::Ushr,
7701 BinOp::And => AssignOp::And,
7702 BinOp::Or => AssignOp::Or,
7703 BinOp::Xor => AssignOp::Xor,
7704 _ => return,
7705 };
7706 let rhs = std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
7707 let new_target = match &tgt {
7708 Tgt::Loc(v) => Box::new(Expr::Local {
7709 var: *v,
7710 ty: vt.var(*v).ty.clone(),
7711 }),
7712 Tgt::Fld => target.clone(),
7713 };
7714 *e = Expr::Assign {
7715 target: new_target,
7716 op: cop,
7717 value: rhs,
7718 };
7719 });
7720}
7721
7722pub fn fix_bool_xor(body: &mut Stmt, vt: &VarTable, ret_bool: bool) {
7723 fn is_one(e: &Expr) -> bool {
7724 matches!(e, Expr::Const(ConstVal::Int(1)))
7725 }
7726 fn bty(e: &Expr, vt: &VarTable) -> bool {
7727 match e {
7728 Expr::Local { var, .. } => {
7729 matches!(vt.var(*var).ty.erased(), JavaType::Boolean)
7730 }
7731 Expr::Bin {
7740 op: BinOp::And | BinOp::Or | BinOp::Xor,
7741 l,
7742 r,
7743 ..
7744 } => bty(l, vt) && bty(r, vt),
7745 other => matches!(other.type_ref().erased(), JavaType::Boolean),
7746 }
7747 }
7748 fn inv_of_xor(x: &Expr, vt: &VarTable) -> Option<Expr> {
7753 if let Expr::Bin { op: BinOp::Xor, l, r, .. } = x {
7754 let (int_side, one_side) = if is_one(r) {
7755 (l, r)
7756 } else if is_one(l) {
7757 (r, l)
7758 } else {
7759 return None;
7760 };
7761 let _ = one_side;
7762 if !bty(int_side, vt) {
7763 return Some(Expr::Bin {
7764 op: BinOp::Eq,
7765 l: int_side.clone(),
7766 r: Box::new(Expr::Const(ConstVal::Int(0))),
7767 ty: Some(TypeRef::J(JavaType::Boolean)),
7768 });
7769 }
7770 }
7771 None
7772 }
7773 let tgt_bool = |target: &Expr, vt: &VarTable| -> bool {
7774 match target {
7775 Expr::Local { var, .. } => matches!(vt.var(*var).ty.erased(), JavaType::Boolean),
7776 Expr::Field { ty, .. } => matches!(ty.erased(), JavaType::Boolean),
7777 _ => false,
7778 }
7779 };
7780 walk_mut_deep(body, &mut |st| match st {
7781 Stmt::Return(Some(e)) if ret_bool => {
7782 if let Some(r) = inv_of_xor(e, vt) {
7783 *e = r;
7784 }
7785 }
7786 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain, .. }) => {
7787 if tgt_bool(target, vt) {
7788 if let Some(r) = inv_of_xor(value, vt) {
7789 **value = r;
7790 }
7791 }
7792 }
7793 Stmt::LocalDef { var, init: Some(value), .. }
7794 if matches!(vt.var(*var).ty.erased(), JavaType::Boolean) =>
7795 {
7796 if let Some(r) = inv_of_xor(value, vt) {
7797 *value = r;
7798 }
7799 }
7800 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
7801 if let Some(r) = inv_of_xor(cond, vt) {
7802 *cond = r;
7803 }
7804 }
7805 _ => {}
7806 });
7807 fn ity(e: &Expr, vt: &VarTable) -> bool {
7808 matches!(
7809 match e {
7810 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
7811 other => other.type_ref().erased(),
7812 },
7813 JavaType::Int | JavaType::Short | JavaType::Byte | JavaType::Char
7814 )
7815 }
7816 fn mixed_rewrite(e: &mut Expr, vt: &VarTable) {
7825 deep_rewrite(e, &mut |x| {
7826 if let Expr::Bin { op, l, r, ty } = x {
7827 match op {
7828 BinOp::Xor => {
7829 if is_one(r) && bty(l, vt) {
7830 let taken =
7831 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
7832 *x = Expr::Un { op: UnOp::Not, e: taken };
7833 } else if is_one(l) && bty(r, vt) {
7834 let taken =
7835 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
7836 *x = Expr::Un { op: UnOp::Not, e: taken };
7837 }
7838 }
7839 BinOp::Or | BinOp::And => {
7840 if bty(l, vt) && ity(r, vt) {
7841 let taken =
7842 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
7843 **r = Expr::Bin {
7844 op: BinOp::Ne,
7845 l: taken,
7846 r: Box::new(Expr::Const(ConstVal::Int(0))),
7847 ty: Some(TypeRef::J(JavaType::Boolean)),
7848 };
7849 *ty = Some(TypeRef::J(JavaType::Boolean));
7850 } else if ity(l, vt) && bty(r, vt) {
7851 let taken =
7852 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
7853 **l = Expr::Bin {
7854 op: BinOp::Ne,
7855 l: taken,
7856 r: Box::new(Expr::Const(ConstVal::Int(0))),
7857 ty: Some(TypeRef::J(JavaType::Boolean)),
7858 };
7859 *ty = Some(TypeRef::J(JavaType::Boolean));
7860 }
7861 }
7862 _ => {}
7863 }
7864 }
7865 });
7866 }
7867 walk_mut_deep(body, &mut |st| match st {
7870 Stmt::Return(Some(e)) if ret_bool => mixed_rewrite(e, vt),
7871 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain, .. }) => {
7872 if tgt_bool(target, vt) {
7873 mixed_rewrite(value, vt);
7874 }
7875 }
7876 Stmt::LocalDef { var, init: Some(e), .. }
7877 if matches!(vt.var(*var).ty.erased(), JavaType::Boolean) =>
7878 {
7879 mixed_rewrite(e, vt);
7880 }
7881 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
7882 mixed_rewrite(cond, vt);
7883 }
7884 _ => {}
7885 });
7886 walk_stmt_exprs(body, &mut |e| {
7889 deep_rewrite(e, &mut |x| {
7890 if let Expr::Cond { c, .. } = x {
7891 mixed_rewrite(c, vt);
7892 }
7893 });
7894 });
7895}
7896
7897pub fn booleanize(vt: &mut VarTable, body: &mut Stmt, ret_bool: bool) -> usize {
7903 let n0 = vt.vars.len();
7910 let mut derefed = vec![false; n0];
7911 mark_derefs(body, &mut derefed);
7912 let mut total = 0usize;
7913 for _ in 0..4 {
7914 let n = booleanize_round(vt, body, ret_bool, &derefed);
7915 if n == 0 {
7916 break;
7917 }
7918 total += n;
7919 }
7920 total
7921}
7922
7923fn mark_deref_expr(e: &Expr, derefed: &mut [bool]) {
7927 match e {
7928 Expr::Field { owner: Some(o), is_static: false, .. }
7929 | Expr::Method { owner: Some(o), .. } => {
7930 if let Expr::Local { var, .. } = &**o {
7931 if (*var as usize) < derefed.len() {
7932 derefed[*var as usize] = true;
7933 }
7934 }
7935 }
7936 Expr::ArrayIndex { array, .. } => {
7937 if let Expr::Local { var, .. } = &**array {
7938 if (*var as usize) < derefed.len() {
7939 derefed[*var as usize] = true;
7940 }
7941 }
7942 }
7943 _ => {}
7944 }
7945 for_each_child(e, &mut |c| mark_deref_expr(c, derefed));
7946}
7947
7948fn mark_derefs(body: &Stmt, derefed: &mut [bool]) {
7949 walk_all(body, &mut |st| {
7950 match st {
7951 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
7952 mark_deref_expr(e, derefed);
7953 }
7954 Stmt::Return(Some(e)) => mark_deref_expr(e, derefed),
7955 Stmt::LocalDef { init: Some(e), .. } => mark_deref_expr(e, derefed),
7956 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
7957 mark_deref_expr(cond, derefed);
7958 }
7959 Stmt::For { init, .. } => {
7960 for x in init {
7961 mark_derefs(x, derefed);
7962 }
7963 }
7964 Stmt::Switch { selector, .. } => mark_deref_expr(selector, derefed),
7965 Stmt::ForEach { iterable, .. } => mark_deref_expr(iterable, derefed),
7966 _ => {}
7967 }
7968 });
7969}
7970
7971fn booleanize_round(
7972 vt: &mut VarTable,
7973 body: &mut Stmt,
7974 ret_bool: bool,
7975 derefed: &[bool],
7976) -> usize {
7977 let n = vt.vars.len();
7978 let mut in_cond = vec![false; n];
7979
7980 walk_all(body, &mut |st| {
7983 let cond: Option<&Expr> = match st {
7984 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
7985 Some(cond)
7986 }
7987 _ => None,
7988 };
7989 if let Some(c) = cond {
7990 if let Expr::Bin {
7991 op: BinOp::Eq | BinOp::Ne,
7992 l,
7993 r,
7994 ..
7995 } = c
7996 {
7997 for (x, other) in [(l, r), (r, l)] {
7998 if let (Expr::Local { var, .. }, Expr::Const(ConstVal::Int(0))) =
7999 (&**x, &**other)
8000 {
8001 if (*var as usize) < n {
8002 in_cond[*var as usize] = true;
8003 }
8004 }
8005 }
8006 }
8007 }
8008 if ret_bool {
8013 if let Stmt::Return(Some(Expr::Local { var, .. })) = st {
8014 if (*var as usize) < n {
8015 in_cond[*var as usize] = true;
8016 }
8017 }
8018 }
8019 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = st {
8026 if let Expr::Field { ty, .. } = &**target {
8027 if ty.erased() == JavaType::Boolean {
8028 if let Expr::Local { var, .. } = &**value {
8029 if (*var as usize) < n {
8030 in_cond[*var as usize] = true;
8031 }
8032 }
8033 }
8034 }
8035 }
8036 let val: Option<&Expr> = match st {
8037 Stmt::ExprStmt(Expr::Assign { value, .. }) => Some(value),
8038 Stmt::ExprStmt(e) => Some(e),
8041 Stmt::LocalDef { init: Some(e), .. } => Some(e),
8042 Stmt::Return(Some(e)) => Some(e),
8043 _ => None,
8044 };
8045 if let Some(v) = val {
8046 visit_exprs(v, &mut |x| {
8047 if let Expr::Method { desc, args, .. } = x {
8053 for (i, a) in args.iter().enumerate() {
8054 if desc.args.get(i) == Some(&JavaType::Boolean) {
8055 if let Expr::Local { var, .. } = a {
8056 if (*var as usize) < n {
8057 in_cond[*var as usize] = true;
8058 }
8059 }
8060 }
8061 }
8062 }
8063 if let Expr::Bin { op, l, r, .. } = x {
8064 if matches!(
8065 op,
8066 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
8067 ) {
8068 for side in [l, r] {
8069 if let Expr::Local { var, .. } = &**side {
8070 if (*var as usize) < n {
8071 in_cond[*var as usize] = true;
8072 }
8073 }
8074 }
8075 }
8076 if matches!(op, BinOp::Or | BinOp::And) {
8082 let l_bool = l.type_ref().erased() == JavaType::Boolean;
8083 let r_bool = r.type_ref().erased() == JavaType::Boolean;
8084 if l_bool != r_bool {
8085 let side = if l_bool { r } else { l };
8086 if let Expr::Local { var, .. } = &**side {
8087 if (*var as usize) < n {
8088 in_cond[*var as usize] = true;
8089 }
8090 }
8091 }
8092 }
8093 }
8094 });
8095 }
8096 });
8097
8098 let mut assigned_any = vec![false; n];
8105 let mut all_bool = vec![true; n];
8106 let mut edges: Vec<(usize, usize)> = Vec::new();
8107 walk_all(body, &mut |st| {
8108 let (var, value) = match st {
8109 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
8110 Expr::Local { var, .. } => (var, &**value),
8111 _ => return,
8112 },
8113 Stmt::LocalDef {
8114 var, init: Some(e), ..
8115 } => (var, e),
8116 _ => return,
8117 };
8118 let i = *var as usize;
8119 if i < n {
8120 assigned_any[i] = true;
8121 if !is_boolean_valued(value, vt) {
8122 all_bool[i] = false;
8123 }
8124 if let Expr::Local { var: src, .. } = value {
8130 if (*src as usize) < n {
8131 edges.push((i, *src as usize));
8132 }
8133 }
8134 }
8135 });
8136 loop {
8141 let mut changed = false;
8142 for &(tgt, src) in &edges {
8143 if in_cond[tgt] && !in_cond[src] && all_bool[src] {
8144 in_cond[src] = true;
8145 changed = true;
8146 }
8147 if in_cond[src] && !in_cond[tgt] && all_bool[tgt] {
8153 in_cond[tgt] = true;
8154 changed = true;
8155 }
8156 }
8157 if !changed {
8158 break;
8159 }
8160 }
8161 let reads = count_locals_stmts(std::slice::from_ref(body));
8162 let mut boolean_vars: HashSet<u32> = HashSet::default();
8163 for i in 0..n {
8164 if vt.vars[i].is_param {
8165 continue;
8166 }
8167 if derefed[i] {
8168 continue;
8169 }
8170 if !in_cond[i] && !(reads.get(&(i as u32)).copied().unwrap_or(0) == 0) {
8171 continue;
8172 }
8173 if assigned_any[i] && all_bool[i] {
8174 boolean_vars.insert(i as u32);
8175 }
8176 }
8177 if boolean_vars.is_empty() {
8178 return 0;
8179 }
8180 for v in &boolean_vars {
8181 vt.vars[*v as usize].ty = TypeRef::J(JavaType::Boolean);
8182 }
8183 let types: Vec<&TypeRef> = vt.vars.iter().map(|v| &v.ty).collect();
8184 rewrite_exprs(body, &mut |e| {
8185 deep_rewrite(e, &mut |x| {
8186 if let Expr::Local { var, ty } = x {
8187 if let Some(want) = types.get(*var as usize) {
8188 if ty != *want {
8189 *ty = (*want).clone();
8190 }
8191 }
8192 }
8193 });
8194 });
8195
8196 fold_bool_conditions(body, &boolean_vars);
8198 boolean_vars.len()
8199}
8200
8201fn is_boolean_valued(e: &Expr, vt: &VarTable) -> bool {
8202 match e {
8203 Expr::Const(ConstVal::Int(0)) | Expr::Const(ConstVal::Int(1)) => true,
8204 Expr::Bin { op: BinOp::Or | BinOp::And, l, r, .. } => {
8211 is_boolean_valued(l, vt) || is_boolean_valued(r, vt)
8212 }
8213 Expr::Bin {
8221 op: BinOp::Xor,
8222 l,
8223 r,
8224 ..
8225 } => is_boolean_valued(l, vt) && is_boolean_valued(r, vt),
8226 Expr::Bin { op, .. } => matches!(
8227 op,
8228 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
8229 ),
8230 Expr::InstanceOf { .. } => true,
8231 Expr::Local { var, ty } => {
8237 let t = if (*var as usize) < vt.vars.len() {
8238 vt.var(*var).ty.erased()
8239 } else {
8240 ty.erased()
8241 };
8242 t == JavaType::Boolean
8243 }
8244 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
8245 Expr::Cond { t, f, .. } => is_boolean_valued(t, vt) && is_boolean_valued(f, vt),
8246 Expr::Un { op: UnOp::Not, .. } => true,
8247 Expr::Field { ty, .. } => ty.erased() == JavaType::Boolean,
8251 _ => false,
8252 }
8253}
8254
8255fn fold_bool_conditions(s: &mut Stmt, bv: &HashSet<u32>) {
8256 let fold = |e: &mut Expr| {
8257 deep_rewrite(e, &mut |x| {
8258 if let Expr::Bin { op, l, r, .. } = x {
8259 let (var_side, const_side) = match (&**l, &**r) {
8260 (Expr::Local { .. }, Expr::Const(ConstVal::Int(_))) => (l, 0),
8261 (Expr::Const(ConstVal::Int(_)), Expr::Local { .. }) => (r, 1),
8262 _ => return,
8263 };
8264 let var = match &**var_side {
8265 Expr::Local { var, .. } => *var,
8266 _ => return,
8267 };
8268 if !bv.contains(&var) {
8269 return;
8270 }
8271 let _ = const_side;
8272 match op {
8273 BinOp::Ne => {
8274 let inner = var_side.clone();
8275 *x = *inner;
8276 }
8277 BinOp::Eq => {
8278 let inner = var_side.clone();
8279 *x = Expr::Un {
8280 op: UnOp::Not,
8281 e: Box::new(*inner),
8282 };
8283 }
8284 _ => {}
8285 }
8286 }
8287 });
8288 };
8289 match s {
8290 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => fold(cond),
8291 _ => {}
8292 }
8293 walk_mut_deep(s, &mut |st| {
8294 let cond = match st {
8295 Stmt::If { cond, .. } => Some(cond),
8296 Stmt::While { cond, .. } => Some(cond),
8297 Stmt::DoWhile { cond, .. } => Some(cond),
8298 _ => None,
8299 };
8300 if let Some(c) = cond {
8301 fold(c);
8302 }
8303 });
8304}
8305
8306pub fn flip_const_compares(s: &mut Stmt) {
8308 rewrite_exprs(s, &mut |e| {
8309 deep_rewrite(e, &mut |x| {
8310 if let Expr::Bin { op, l, r, .. } = x {
8311 if matches!(
8312 op,
8313 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
8314 ) {
8315 let l_const = matches!(&**l, Expr::Const(_));
8316 let r_var = matches!(&**r, Expr::Local { .. });
8317 if l_const && r_var {
8318 std::mem::swap(l, r);
8319 }
8320 }
8321 }
8322 });
8323 });
8324}
8325
8326pub fn null_compares(vt: &VarTable, body: &mut Stmt) {
8328 let obj_vars: HashSet<u32> = vt
8329 .vars
8330 .iter()
8331 .filter(|v| v.ty.erased().is_reference())
8332 .map(|v| v.id)
8333 .collect();
8334 rewrite_exprs(body, &mut |e| {
8335 deep_rewrite(e, &mut |x| {
8336 null_side_rewrite(x, &|v| obj_vars.contains(&v));
8337 });
8338 });
8339}
8340
8341pub fn drop_dead_locals(body: &mut Stmt) {
8350 loop {
8351 let mut reads: Vec<usize> = Vec::new();
8352 count_reads(body, &mut reads);
8353 let mut dropped = 0usize;
8354 walk_mut_deep(body, &mut |st| match st {
8355 Stmt::Block(items) => prune_dead_items(items, &reads, &mut dropped),
8356 Stmt::Switch { cases, .. } => {
8366 for c in cases {
8367 prune_dead_items(&mut c.body, &reads, &mut dropped);
8368 }
8369 }
8370 Stmt::For { init, .. } => prune_dead_items(init, &reads, &mut dropped),
8371 _ => {}
8372 });
8373 if dropped == 0 {
8374 break;
8375 }
8376 }
8377}
8378
8379fn prune_dead_items(items: &mut Vec<Stmt>, reads: &[usize], dropped: &mut usize) {
8384 let dead = |v: u32| reads.get(v as usize).copied().unwrap_or(0) == 0;
8385 items.retain_mut(|x| match x {
8386 Stmt::ExprStmt(Expr::Assign { target, value, op, .. }) => {
8387 let dead_target = matches!(&**target, Expr::Local { var, .. } if dead(*var));
8388 if dead_target && matches!(*op, AssignOp::Plain) {
8389 *dropped += 1;
8390 if has_side_effects(value) {
8391 let e = std::mem::replace(value, Box::new(Expr::This));
8392 *x = Stmt::ExprStmt(*e);
8393 true
8394 } else {
8395 false
8396 }
8397 } else {
8398 true
8399 }
8400 }
8401 Stmt::LocalDef { var, init, .. } if dead(*var) => {
8402 *dropped += 1;
8403 if let Some(e) = init.take() {
8404 if has_side_effects(&e) {
8405 *x = Stmt::ExprStmt(e);
8406 return true;
8407 }
8408 }
8409 false
8410 }
8411 _ => true,
8412 });
8413}
8414
8415
8416fn stmt_shallow_vars<F: FnMut(u32)>(st: &Stmt, out: &mut F) {
8421 fn ex<F: FnMut(u32)>(e: &Expr, out: &mut F) {
8422 visit_exprs(e, &mut |x| {
8423 if let Expr::Local { var, .. } = x {
8424 out(*var);
8425 }
8426 });
8427 }
8428 match st {
8429 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
8430 ex(e, out)
8431 }
8432 Stmt::TernaryValue { e } => ex(e, out),
8433 Stmt::Return(Some(e)) => ex(e, out),
8434 Stmt::LocalDef { var, init, .. } => {
8435 out(*var);
8436 if let Some(e) = init {
8437 ex(e, out);
8438 }
8439 }
8440 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
8441 ex(cond, out)
8442 }
8443 Stmt::For { cond, update, .. } => {
8444 if let Some(c) = cond {
8445 ex(c, out);
8446 }
8447 for u in update {
8448 ex(u, out);
8449 }
8450 }
8451 Stmt::ForEach { iterable, .. } => ex(iterable, out),
8452 Stmt::Switch { selector, cases, .. } => {
8453 ex(selector, out);
8454 for cg in cases {
8455 if let Some(g) = &cg.guard {
8456 ex(g, out);
8457 }
8458 }
8459 }
8460 Stmt::Assert { cond, msg } => {
8461 ex(cond, out);
8462 if let Some(m) = msg {
8463 ex(m, out);
8464 }
8465 }
8466 Stmt::Synchronized { lock, .. } => ex(lock, out),
8467 _ => {}
8468 }
8469}
8470
8471fn scope_pass_a(st: &Stmt, cur: u32, next: &mut u32, def_block: &mut [u32], sizes: &mut Vec<u32>) {
8483 if let Stmt::LocalDef { var, .. } = st {
8484 let i = *var as usize;
8485 if i < def_block.len() && def_block[i] == u32::MAX {
8486 def_block[i] = cur;
8487 }
8488 }
8489 fn scoped(c: &Stmt, next: &mut u32, def_block: &mut [u32], sizes: &mut Vec<u32>) {
8490 let id = *next;
8491 *next += 1;
8492 sizes.push(0);
8493 scope_pass_a(c, id, next, def_block, sizes);
8494 sizes[id as usize] = *next - id;
8495 }
8496 match st {
8497 Stmt::Block(v) => {
8498 for x in v {
8499 scope_pass_a(x, cur, next, def_block, sizes);
8500 }
8501 }
8502 Stmt::If {
8503 then_stmt,
8504 else_stmt,
8505 ..
8506 } => {
8507 scoped(then_stmt, next, def_block, sizes);
8508 if let Some(e) = else_stmt {
8509 scoped(e, next, def_block, sizes);
8510 }
8511 }
8512 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => {
8513 scoped(body, next, def_block, sizes)
8514 }
8515 Stmt::For { init, body, .. } => {
8516 let id = *next;
8519 *next += 1;
8520 sizes.push(0);
8521 for x in init {
8522 scope_pass_a(x, id, next, def_block, sizes);
8523 }
8524 scoped(body, next, def_block, sizes);
8525 sizes[id as usize] = *next - id;
8526 }
8527 Stmt::ForEach { body, .. } => scoped(body, next, def_block, sizes),
8528 Stmt::Switch { cases, default, .. } => {
8529 for c in cases {
8530 let id = *next;
8535 *next += 1;
8536 sizes.push(0);
8537 for x in &c.body {
8538 if matches!(x, Stmt::Block(_)) {
8539 scoped(x, next, def_block, sizes);
8540 } else {
8541 scope_pass_a(x, id, next, def_block, sizes);
8542 }
8543 }
8544 sizes[id as usize] = *next - id;
8545 }
8546 if let Some(d) = default {
8547 scoped(d, next, def_block, sizes);
8548 }
8549 }
8550 Stmt::Try {
8551 body,
8552 catches,
8553 finally,
8554 } => {
8555 scoped(body, next, def_block, sizes);
8556 for c in catches {
8557 scoped(&c.body, next, def_block, sizes);
8558 }
8559 if let Some(f) = finally {
8560 scoped(f, next, def_block, sizes);
8561 }
8562 }
8563 Stmt::TryWithResources {
8564 resources,
8565 body,
8566 catches,
8567 finally,
8568 } => {
8569 let id = *next;
8571 *next += 1;
8572 sizes.push(0);
8573 for r in resources {
8574 scope_pass_a(r, id, next, def_block, sizes);
8575 }
8576 scoped(body, next, def_block, sizes);
8577 for c in catches {
8578 scoped(&c.body, next, def_block, sizes);
8579 }
8580 if let Some(f) = finally {
8581 scoped(f, next, def_block, sizes);
8582 }
8583 sizes[id as usize] = *next - id;
8584 }
8585 Stmt::Synchronized { body, .. } => scoped(body, next, def_block, sizes),
8586 Stmt::Labeled { body, .. } => scope_pass_a(body, cur, next, def_block, sizes),
8587 _ => {}
8588 }
8589}
8590
8591fn scope_pass_b(
8596 st: &Stmt,
8597 cur: u32,
8598 next: &mut u32,
8599 def_block: &[u32],
8600 sizes: &[u32],
8601 within: &mut [u32],
8602 total: &mut [u32],
8603) {
8604 fn cnt(
8605 v: u32,
8606 at: u32,
8607 def_block: &[u32],
8608 sizes: &[u32],
8609 within: &mut [u32],
8610 total: &mut [u32],
8611 ) {
8612 let i = v as usize;
8613 if i < total.len() {
8614 total[i] += 1;
8615 let d = def_block[i];
8616 if d != u32::MAX && d <= at && at < d + sizes[d as usize] {
8617 within[i] += 1;
8618 }
8619 }
8620 }
8621 fn scoped(
8622 c: &Stmt,
8623 next: &mut u32,
8624 def_block: &[u32],
8625 sizes: &[u32],
8626 within: &mut [u32],
8627 total: &mut [u32],
8628 ) {
8629 let id = *next;
8630 *next += 1;
8631 scope_pass_b(c, id, next, def_block, sizes, within, total);
8632 }
8633 fn ex_at(
8634 e: &Expr,
8635 at: u32,
8636 def_block: &[u32],
8637 sizes: &[u32],
8638 within: &mut [u32],
8639 total: &mut [u32],
8640 ) {
8641 visit_exprs(e, &mut |x| {
8642 if let Expr::Local { var, .. } = x {
8643 cnt(*var, at, def_block, sizes, within, total);
8644 }
8645 });
8646 }
8647 match st {
8648 Stmt::For {
8649 init,
8650 cond,
8651 update,
8652 body,
8653 } => {
8654 let id = *next;
8655 *next += 1;
8656 for x in init {
8657 scope_pass_b(x, id, next, def_block, sizes, within, total);
8658 }
8659 if let Some(c) = cond {
8660 ex_at(c, id, def_block, sizes, within, total);
8661 }
8662 for u in update {
8663 ex_at(u, id, def_block, sizes, within, total);
8664 }
8665 scoped(body, next, def_block, sizes, within, total);
8666 }
8667 Stmt::TryWithResources {
8668 resources,
8669 body,
8670 catches,
8671 finally,
8672 } => {
8673 let id = *next;
8674 *next += 1;
8675 for r in resources {
8676 scope_pass_b(r, id, next, def_block, sizes, within, total);
8677 }
8678 scoped(body, next, def_block, sizes, within, total);
8679 for c in catches {
8680 scoped(&c.body, next, def_block, sizes, within, total);
8681 }
8682 if let Some(f) = finally {
8683 scoped(f, next, def_block, sizes, within, total);
8684 }
8685 }
8686 _ => {
8687 stmt_shallow_vars(st, &mut |v| cnt(v, cur, def_block, sizes, within, total));
8688 match st {
8689 Stmt::Block(v) => {
8690 for x in v {
8691 scope_pass_b(x, cur, next, def_block, sizes, within, total);
8692 }
8693 }
8694 Stmt::If {
8695 then_stmt,
8696 else_stmt,
8697 ..
8698 } => {
8699 scoped(then_stmt, next, def_block, sizes, within, total);
8700 if let Some(e) = else_stmt {
8701 scoped(e, next, def_block, sizes, within, total);
8702 }
8703 }
8704 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => {
8705 scoped(body, next, def_block, sizes, within, total)
8706 }
8707 Stmt::ForEach { body, .. } => {
8708 scoped(body, next, def_block, sizes, within, total)
8709 }
8710 Stmt::Switch { cases, default, .. } => {
8711 for c in cases {
8712 let id = *next;
8713 *next += 1;
8714 for x in &c.body {
8715 if matches!(x, Stmt::Block(_)) {
8716 scoped(x, next, def_block, sizes, within, total);
8717 } else {
8718 scope_pass_b(x, id, next, def_block, sizes, within, total);
8719 }
8720 }
8721 }
8722 if let Some(d) = default {
8723 scoped(d, next, def_block, sizes, within, total);
8724 }
8725 }
8726 Stmt::Try {
8727 body,
8728 catches,
8729 finally,
8730 } => {
8731 scoped(body, next, def_block, sizes, within, total);
8732 for c in catches {
8733 scoped(&c.body, next, def_block, sizes, within, total);
8734 }
8735 if let Some(f) = finally {
8736 scoped(f, next, def_block, sizes, within, total);
8737 }
8738 }
8739 Stmt::Synchronized { body, .. } => {
8740 scoped(body, next, def_block, sizes, within, total)
8741 }
8742 Stmt::Labeled { body, .. } => {
8743 scope_pass_b(body, cur, next, def_block, sizes, within, total)
8744 }
8745 _ => {}
8746 }
8747 }
8748 }
8749}
8750
8751pub fn strip_enum_const_inits(s: &mut Stmt, class: &crate::PoolClass) {
8760 let const_fields: jdc_core::FxHashSet<&str> = class
8761 .static_fields
8762 .iter()
8763 .filter(|f| f.access & crate::access::ACC_ENUM != 0)
8764 .map(|f| f.name.as_str())
8765 .collect();
8766 if const_fields.is_empty() {
8767 return;
8768 }
8769 if let Stmt::Block(stmts) = s {
8770 stmts.retain(|st| {
8771 !matches!(
8772 st,
8773 Stmt::ExprStmt(Expr::Assign { target, value, .. })
8774 if matches!(&**target,
8775 Expr::Field { cls, name, is_static: true, .. }
8776 if cls.as_ref() == class.name
8777 && const_fields.contains(&**name))
8778 && matches!(&**value, Expr::New { cls: ncls, .. } if ncls.as_ref() == class.name)
8779 )
8780 });
8781 }
8782}
8783
8784fn contains_this_access(e: &Expr) -> bool {
8793 let mut hit = false;
8794 visit_exprs(e, &mut |x| {
8795 if hit {
8796 return;
8797 }
8798 match x {
8799 Expr::This => hit = true,
8800 Expr::Field { owner, is_static: false, .. } => {
8801 if owner.is_none() || matches!(&**owner.as_ref().unwrap(), Expr::This) {
8802 hit = true;
8803 }
8804 }
8805 Expr::Method { owner, is_static: false, .. } => {
8806 if owner.is_none() || matches!(&**owner.as_ref().unwrap(), Expr::This) {
8807 hit = true;
8808 }
8809 }
8810 _ => {}
8811 }
8812 });
8813 hit
8814}
8815
8816fn var_occurs_in(stmts: &[Stmt], var: u32) -> bool {
8831 stmts.iter().any(|st| {
8832 let mut hit = false;
8833 visit_stmt_exprs_ro(st, &mut |e| {
8834 if !hit {
8835 visit_exprs(e, &mut |x| {
8836 if let Expr::Local { var: v, .. } = x {
8837 if *v == var {
8838 hit = true;
8839 }
8840 }
8841 });
8842 }
8843 });
8844 hit
8845 })
8846}
8847
8848pub struct CtorHelper {
8858 pub name: String,
8859 pub ret: JavaType,
8860 pub params: Vec<(JavaType, String)>,
8861 pub body: Stmt,
8862 pub vt: VarTable,
8863}
8864
8865fn ctor_helpers() -> &'static std::sync::Mutex<std::collections::HashMap<String, Vec<CtorHelper>>> {
8866 static H: std::sync::OnceLock<std::sync::Mutex<std::collections::HashMap<String, Vec<CtorHelper>>>> =
8867 std::sync::OnceLock::new();
8868 H.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::default()))
8869}
8870
8871pub struct CtorRelay {
8876 pub formals: Vec<JavaType>,
8878 pub is_super: bool,
8879}
8880
8881type RelayMap = std::collections::HashMap<String, Vec<(String, CtorRelay)>>;
8882
8883fn ctor_relays() -> &'static std::sync::Mutex<RelayMap> {
8884 static H: std::sync::OnceLock<std::sync::Mutex<RelayMap>> = std::sync::OnceLock::new();
8885 H.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::default()))
8886}
8887
8888pub fn take_ctor_relays(class: &str) -> Vec<(String, CtorRelay)> {
8891 let mut m = ctor_relays().lock().unwrap_or_else(|e| e.into_inner());
8892 let mut v = m.remove(class).unwrap_or_default();
8893 v.sort_by(|a, b| a.0.cmp(&b.0));
8894 v
8895}
8896
8897fn array_init(args: Vec<Expr>) -> Expr {
8898 Expr::NewArray {
8899 elem: TypeRef::J(JavaType::Object(std::sync::Arc::from("java/lang/Object"))),
8900 dims: Vec::new(),
8901 trailing_dims: 0,
8902 init: Some(args),
8903 }
8904}
8905
8906fn helper_name(class: &str, desc: &str) -> String {
8909 let mut h: u64 = 0xcbf29ce484222325;
8910 for b in class.as_bytes().iter().chain(b"::").chain(desc.as_bytes()) {
8911 h ^= *b as u64;
8912 h = h.wrapping_mul(0x100000001b3);
8913 }
8914 format!("resolve${:x}", (h >> 16) & 0xffff_ffff)
8915}
8916
8917pub fn take_ctor_helpers(class: &str) -> Vec<CtorHelper> {
8920 let mut m = ctor_helpers().lock().unwrap_or_else(|e| e.into_inner());
8921 let mut v = m.remove(class).unwrap_or_default();
8922 v.sort_by(|a, b| a.name.cmp(&b.name));
8923 v
8924}
8925
8926fn strip_delegations_deep(stmts: &mut Vec<Stmt>) {
8927 stmts.retain(|s| !is_bare_ctor_call(s));
8928 for s in stmts.iter_mut() {
8929 strip_delegation_one(s);
8930 }
8931}
8932
8933fn strip_delegation_one(st: &mut Stmt) {
8934 if is_bare_ctor_call(st) {
8935 *st = Stmt::Block(Vec::new());
8936 return;
8937 }
8938 match st {
8939 Stmt::Block(v) => strip_delegations_deep(v),
8940 Stmt::If { then_stmt, else_stmt, .. } => {
8941 strip_delegation_one(then_stmt);
8942 if let Some(e) = else_stmt {
8943 strip_delegation_one(e);
8944 }
8945 }
8946 Stmt::While { body, .. }
8947 | Stmt::DoWhile { body, .. }
8948 | Stmt::ForEach { body, .. }
8949 | Stmt::Synchronized { body, .. }
8950 | Stmt::Labeled { body, .. } => strip_delegation_one(body),
8951 Stmt::For { init, body, .. } => {
8952 strip_delegations_deep(init);
8953 strip_delegation_one(body);
8954 }
8955 Stmt::Switch { cases, default, .. } => {
8956 for c in cases.iter_mut() {
8957 strip_delegations_deep(&mut c.body);
8958 }
8959 if let Some(d) = default {
8960 strip_delegation_one(d);
8961 }
8962 }
8963 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
8964 strip_delegation_one(body);
8965 for c in catches.iter_mut() {
8966 strip_delegation_one(&mut c.body);
8967 }
8968 if let Some(f) = finally {
8969 strip_delegation_one(f);
8970 }
8971 }
8972 _ => {}
8973 }
8974}
8975
8976#[allow(clippy::ptr_arg)]
8977fn rewrite_bare_returns(stmts: &mut Vec<Stmt>, val: &Expr) {
8978 for s in stmts.iter_mut() {
8979 rewrite_bare_returns_one(s, val);
8980 }
8981}
8982
8983fn rewrite_bare_returns_one(st: &mut Stmt, val: &Expr) {
8984 match st {
8985 Stmt::Return(None) => *st = Stmt::Return(Some(val.clone())),
8986 Stmt::Block(v) => rewrite_bare_returns(v, val),
8987 Stmt::If { then_stmt, else_stmt, .. } => {
8988 rewrite_bare_returns_one(then_stmt, val);
8989 if let Some(e) = else_stmt {
8990 rewrite_bare_returns_one(e, val);
8991 }
8992 }
8993 Stmt::While { body, .. }
8994 | Stmt::DoWhile { body, .. }
8995 | Stmt::ForEach { body, .. }
8996 | Stmt::Synchronized { body, .. }
8997 | Stmt::Labeled { body, .. } => rewrite_bare_returns_one(body, val),
8998 Stmt::For { init, body, .. } => {
8999 rewrite_bare_returns(init, val);
9000 rewrite_bare_returns_one(body, val);
9001 }
9002 Stmt::Switch { cases, default, .. } => {
9003 for c in cases.iter_mut() {
9004 rewrite_bare_returns(&mut c.body, val);
9005 }
9006 if let Some(d) = default {
9007 rewrite_bare_returns_one(d, val);
9008 }
9009 }
9010 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
9011 rewrite_bare_returns_one(body, val);
9012 for c in catches.iter_mut() {
9013 rewrite_bare_returns_one(&mut c.body, val);
9014 }
9015 if let Some(f) = finally {
9016 rewrite_bare_returns_one(f, val);
9017 }
9018 }
9019 _ => {}
9020 }
9021}
9022
9023fn collect_def_vars(stmts: &[Stmt], out: &mut jdc_core::FxHashSet<u32>) {
9024 for st in stmts {
9025 match st {
9026 Stmt::LocalDef { var, .. } => {
9027 out.insert(*var);
9028 }
9029 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
9030 if let Expr::Local { var, .. } = &**target {
9031 out.insert(*var);
9032 }
9033 }
9034 Stmt::Block(v) => collect_def_vars(v, out),
9035 Stmt::If { then_stmt, else_stmt, .. } => {
9036 collect_def_vars(std::slice::from_ref(then_stmt.as_ref()), out);
9037 if let Some(e) = else_stmt {
9038 collect_def_vars(std::slice::from_ref(e.as_ref()), out);
9039 }
9040 }
9041 Stmt::While { body, .. }
9042 | Stmt::DoWhile { body, .. }
9043 | Stmt::Synchronized { body, .. }
9044 | Stmt::Labeled { body, .. } => {
9045 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
9046 }
9047 Stmt::ForEach { var, body, .. } => {
9048 out.insert(*var);
9049 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
9050 }
9051 Stmt::For { init, body, .. } => {
9052 collect_def_vars(init, out);
9053 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
9054 }
9055 Stmt::Switch { cases, default, .. } => {
9056 for c in cases {
9057 collect_def_vars(&c.body, out);
9058 }
9059 if let Some(d) = default {
9060 collect_def_vars(std::slice::from_ref(d.as_ref()), out);
9061 }
9062 }
9063 Stmt::Try { body, catches, finally }
9064 | Stmt::TryWithResources { body, catches, finally, .. } => {
9065 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
9066 for c in catches {
9067 out.insert(c.var);
9068 collect_def_vars(std::slice::from_ref(c.body.as_ref()), out);
9069 }
9070 if let Some(f) = finally {
9071 collect_def_vars(std::slice::from_ref(f.as_ref()), out);
9072 }
9073 }
9074 _ => {}
9075 }
9076 }
9077}
9078
9079fn extraction_shape_ok(stmts: &[Stmt]) -> bool {
9082 stmts.iter().all(|st| match st {
9083 Stmt::Block(v) => extraction_shape_ok(v),
9084 Stmt::LocalDef { .. }
9085 | Stmt::ExprStmt(_)
9086 | Stmt::Return(_)
9087 | Stmt::Throw(_)
9088 | Stmt::Assert { .. }
9089 | Stmt::Break(_)
9090 | Stmt::Continue(_) => true,
9091 Stmt::If { then_stmt, else_stmt, .. } => {
9092 ok_one(then_stmt)
9093 && else_stmt.as_deref().map(ok_one).unwrap_or(true)
9094 }
9095 Stmt::While { body, .. }
9096 | Stmt::DoWhile { body, .. }
9097 | Stmt::ForEach { body, .. }
9098 | Stmt::Synchronized { body, .. } => ok_one(body),
9099 Stmt::Labeled { body, .. } => ok_one(body),
9100 Stmt::For { init, body, .. } => extraction_shape_ok(init) && ok_one(body),
9101 Stmt::Switch { cases, default, .. } => {
9102 cases.iter().all(|c| extraction_shape_ok(&c.body))
9103 && default.as_deref().map(ok_one).unwrap_or(true)
9104 }
9105 Stmt::Try { body, catches, finally }
9106 | Stmt::TryWithResources { body, catches, finally, .. } => {
9107 ok_one(body)
9108 && catches.iter().all(|c| ok_one(&c.body))
9109 && finally.as_deref().map(ok_one).unwrap_or(true)
9110 }
9111 _ => false,
9114 })
9115}
9116
9117fn ok_one(st: &Stmt) -> bool {
9118 extraction_shape_ok(std::slice::from_ref(st))
9119}
9120
9121fn static_safe(stmts: &[Stmt], this_id: Option<u32>) -> bool {
9122 let mut ok = true;
9123 for st in stmts {
9124 visit_stmt_exprs_ro(st, &mut |e| {
9125 visit_exprs(e, &mut |x| {
9126 match x {
9127 Expr::This => ok = false,
9128 Expr::Local { var, .. } => {
9134 if Some(*var) == this_id {
9135 ok = false;
9136 }
9137 }
9138 Expr::Field { owner: None, is_static: false, .. } => ok = false,
9140 Expr::Method { owner: None, is_static: false, .. } => ok = false,
9141 _ => {}
9142 }
9143 });
9144 });
9145 }
9146 ok
9147}
9148
9149pub fn strip_clinit_hotfix_guard(body: &mut Stmt) {
9160 if let Stmt::Block(vs) = body {
9164 flatten_top_blocks(vs);
9165 }
9166 let Stmt::Block(stmts) = body else { return };
9167 let mut gi = 0usize;
9170 while gi < stmts.len() && matches!(&stmts[gi], Stmt::LocalDef { init: None, .. }) {
9171 gi += 1;
9172 }
9173 let Some(first) = stmts.get(gi) else { return };
9174 let Stmt::If { then_stmt, else_stmt, .. } = first else {
9175 return;
9176 };
9177 if else_stmt.is_some() {
9178 return;
9179 }
9180 let mut has_ret = false;
9181 let mut has_proxy = false;
9182 let c = then_stmt.as_ref();
9183 walk_all(c, &mut |st| {
9184 if matches!(st, Stmt::Return(None)) {
9185 has_ret = true;
9186 }
9187 });
9188 let mut cc = c.clone();
9189 walk_stmt_exprs(&mut cc, &mut |e| {
9190 deep_rewrite(e, &mut |x| {
9191 if let Expr::Method { cls, .. } = x {
9192 if cls.contains("PatchProxy") {
9193 has_proxy = true;
9194 }
9195 }
9196 });
9197 });
9198 if has_ret && has_proxy {
9199 stmts.remove(gi);
9200 }
9201}
9202
9203pub(crate) fn first_leaf_stmt(s: &Stmt) -> &Stmt {
9224 match s {
9225 Stmt::Block(v) if !v.is_empty() => first_leaf_stmt(&v[0]),
9226 other => other,
9227 }
9228}
9229
9230pub fn strip_ctor_hotfix_guards(body: &mut Stmt) {
9249 fn marker_hit(e: &Expr) -> bool {
9250 let has = |n: &str| {
9251 let l = n.to_ascii_lowercase();
9252 l.contains("hotfix")
9253 || l.contains("instantrun")
9254 || l.contains("robust")
9255 || l.contains("titan")
9256 || l.contains("patchproxy")
9257 || l.contains("changequickredirect")
9258 || l.contains("constructorcode")
9259 };
9260 let mut hit = false;
9261 visit_exprs(e, &mut |x| {
9262 match x {
9263 Expr::Method { cls, .. } | Expr::New { cls, .. } | Expr::Field { cls, .. } => {
9264 if has(cls) {
9265 hit = true;
9266 }
9267 }
9268 _ => {}
9269 }
9270 if let Expr::Field { ty, .. } = x {
9271 if let TypeRef::J(JavaType::Object(n)) = ty {
9272 if has(n) {
9273 hit = true;
9274 }
9275 }
9276 }
9277 });
9278 hit
9279 }
9280 fn is_guard(st: &Stmt) -> bool {
9284 let mut cur = st;
9285 loop {
9286 match cur {
9287 Stmt::Block(v) if v.len() == 1 => cur = &v[0],
9288 Stmt::If { .. } => break,
9289 _ => return false,
9290 }
9291 }
9292 let mut has_del = false;
9293 walk_all(cur, &mut |s2| {
9294 if is_bare_ctor_call(s2) {
9295 has_del = true;
9296 }
9297 });
9298 if !has_del {
9299 return false;
9300 }
9301 let mut has_marker = false;
9302 visit_stmt_exprs_ro(cur, &mut |e| {
9303 if marker_hit(e) {
9304 has_marker = true;
9305 }
9306 });
9307 has_marker
9308 }
9309 fn strip_in(stmts: &mut Vec<Stmt>) {
9320 stmts.retain(|st| !is_guard(st));
9321 for st in stmts.iter_mut() {
9322 if let Stmt::Block(v) = st {
9323 strip_in(v);
9324 }
9325 }
9326 }
9327 let Stmt::Block(stmts) = body else { return };
9328 strip_in(stmts);
9329}
9330
9331pub fn hoist_branch_delegations(body: &mut Stmt, vt: &VarTable) {
9344 let Stmt::Block(stmts) = body else { return };
9345 if stmts.first().is_some_and(is_bare_ctor_call) {
9346 return; }
9348 let mut dels: Vec<Expr> = Vec::new();
9349 for st in stmts.iter() {
9350 visit_stmt_exprs_ro(st, &mut |e| {
9351 if is_delegation_expr(e) {
9352 dels.push(e.clone());
9353 }
9354 });
9355 }
9356 if dels.is_empty() {
9357 return;
9358 }
9359 if !dels.iter().all(|d| *d == dels[0]) {
9360 return;
9361 }
9362 let mut bad = false;
9363 visit_exprs(&dels[0], &mut |x| {
9364 if let Expr::Local { var, .. } = x {
9365 if (*var as usize) >= vt.vars.len() || !vt.vars[*var as usize].is_param {
9366 bad = true;
9367 }
9368 }
9369 });
9370 if bad {
9371 return;
9372 }
9373 let del = dels[0].clone();
9374 strip_delegations_deep(stmts);
9375 stmts.insert(0, Stmt::ExprStmt(del));
9376}
9377
9378pub fn extract_branched_delegation_helper(
9412 body: &mut Stmt,
9413 vt: &VarTable,
9414 pool: &DexPool,
9415 class: &crate::PoolClass,
9416 desc_str: &str,
9417) {
9418 if class.is_enum() {
9419 return;
9420 }
9421 if class.nesting.enclosing_class.is_some() && !crate::ctx::nested_is_static(pool, class) {
9426 return;
9427 }
9428 let Stmt::Block(stmts) = body else { return };
9429 let mut dels: Vec<Expr> = Vec::new();
9430 for st in stmts.iter() {
9431 visit_stmt_exprs_ro(st, &mut |e| {
9432 if is_delegation_expr(e) {
9433 dels.push(e.clone());
9434 }
9435 });
9436 }
9437 if dels.is_empty() {
9438 return;
9439 }
9440 if dels.len() == 1 && stmts.first().is_some_and(is_bare_ctor_call) {
9441 return; }
9443 let template = dels[0].clone();
9444 let Expr::Method { args: t_args, .. } = &template else {
9445 return;
9446 };
9447 enum Mode {
9451 Single(u32),
9452 Position(usize),
9453 Full,
9460 }
9461 let full_gate = (|| -> Option<()> {
9468 let Expr::Method {
9469 args: a0,
9470 desc: d0,
9471 is_super: s0,
9472 cls: c0,
9473 ..
9474 } = &dels[0]
9475 else {
9476 return None;
9477 };
9478 if a0.is_empty() {
9479 return None;
9480 }
9481 for d in &dels[1..] {
9482 match d {
9483 Expr::Method {
9484 args,
9485 desc,
9486 is_super,
9487 cls,
9488 ..
9489 } if args.len() == a0.len()
9490 && **desc == **d0
9491 && is_super == s0
9492 && cls == c0 => {}
9493 _ => return None,
9494 }
9495 }
9496 if class
9497 .all_methods()
9498 .any(|m| &*m.name == "<init>" && &*m.desc == "([Ljava/lang/Object;)V")
9499 {
9500 return None;
9501 }
9502 Some(())
9503 })()
9504 .is_some();
9505 let mode = if dels.iter().all(|d| *d == dels[0]) {
9506 let mut carrier: Option<u32> = None;
9507 let mut reads = 0usize;
9508 let mut multi = false;
9509 for a in t_args.iter() {
9510 let c = a.clone();
9511 visit_exprs(&c, &mut |x| {
9512 if let Expr::Local { var, .. } = x {
9513 if (*var as usize) < vt.vars.len() && !vt.vars[*var as usize].is_param {
9514 reads += 1;
9515 match carrier {
9516 Some(v) if v == *var => {}
9517 Some(_) => multi = true,
9518 None => carrier = Some(*var),
9519 }
9520 }
9521 }
9522 });
9523 }
9524 match carrier {
9525 Some(cv) if !multi && reads == 1 => Mode::Single(cv),
9526 _ if full_gate => Mode::Full,
9527 _ => return,
9528 }
9529 } else {
9530 let positioned: Option<Mode> = (|| {
9531 let arity = t_args.len();
9532 if dels
9536 .iter()
9537 .any(|d| !matches!(d, Expr::Method { args, .. } if args.len() == arity))
9538 {
9539 return None;
9540 }
9541 let mut diff: Vec<usize> = Vec::new();
9542 for j in 0..arity {
9543 if dels.iter().any(|d| match d {
9544 Expr::Method { args, .. } => args.get(j) != t_args.get(j),
9545 _ => true,
9546 }) {
9547 diff.push(j);
9548 }
9549 }
9550 if diff.len() != 1 {
9551 return None;
9552 }
9553 let k = diff[0];
9554 let mut base0 = dels[0].clone();
9558 if let Expr::Method { args, .. } = &mut base0 {
9559 args[k] = Expr::Const(ConstVal::Null);
9560 }
9561 for d in dels.iter() {
9562 let mut c = d.clone();
9563 if let Expr::Method { args, .. } = &mut c {
9564 args[k] = Expr::Const(ConstVal::Null);
9565 }
9566 if c != base0 {
9567 return None;
9568 }
9569 }
9570 let mut carrier_ty: Option<JavaType> = None;
9573 for d in dels.iter() {
9574 let Expr::Method { args, .. } = d else {
9575 return None;
9576 };
9577 let Expr::Local { var, .. } = &args[k] else {
9578 return None;
9579 };
9580 if (*var as usize) >= vt.vars.len() || vt.vars[*var as usize].is_param {
9581 return None;
9582 }
9583 let ty = vt.vars[*var as usize].ty.erased();
9584 match &carrier_ty {
9585 Some(t) if *t == ty => {}
9586 Some(_) => return None,
9587 None => carrier_ty = Some(ty),
9588 }
9589 for (j, a) in args.iter().enumerate() {
9595 if j == k {
9596 continue;
9597 }
9598 let mut bad = false;
9599 visit_exprs(a, &mut |x| {
9600 if let Expr::Local { var: v2, .. } = x {
9601 if (*v2 as usize) >= vt.vars.len() || !vt.vars[*v2 as usize].is_param {
9602 bad = true;
9603 }
9604 }
9605 });
9606 if bad {
9607 return None;
9608 }
9609 }
9610 }
9611 Some(Mode::Position(k))
9612 })();
9613 match positioned {
9614 Some(m) => m,
9615 None if full_gate => Mode::Full,
9616 None => return,
9617 }
9618 };
9619 let l = stmts.iter().rposition(is_bare_ctor_call);
9620 if l == Some(0) {
9621 return;
9622 }
9623 let (mut extracted, mut tail): (Vec<Stmt>, Vec<Stmt>) = match l {
9624 Some(l) => (stmts[..l].to_vec(), stmts[l + 1..].to_vec()),
9625 None => (stmts.clone(), Vec::new()),
9626 };
9627 if !extraction_shape_ok(&extracted) {
9628 return;
9629 }
9630 let mut site_tails: Vec<Vec<Stmt>> = Vec::new();
9637 {
9638 let mut probe = extracted.clone();
9639 site_tails_walk(&mut probe, &mut site_tails, false);
9640 }
9641 let hoist = !site_tails.is_empty() && {
9642 let n0 = normalize_tail(&site_tails[0]);
9643 site_tails.iter().all(|t| normalize_tail(t) == n0)
9644 && (l.is_none() || normalize_tail(&tail) == n0)
9645 };
9646 if hoist {
9647 site_tails_walk(&mut extracted, &mut Vec::new(), true);
9648 if l.is_none() {
9649 tail = normalize_tail(&site_tails[0]);
9650 }
9651 }
9652 let primary_del: Expr = match l {
9655 Some(l) => match &stmts[l] {
9656 Stmt::ExprStmt(e) => e.clone(),
9657 _ => return,
9658 },
9659 None => dels[0].clone(),
9660 };
9661 let ret = match mode {
9662 Mode::Single(cv) => vt.var(cv).ty.erased(),
9663 Mode::Position(k) => match &primary_del {
9664 Expr::Method { args, .. } => match args.get(k) {
9665 Some(Expr::Local { var, .. }) => vt.var(*var).ty.erased(),
9666 _ => return,
9667 },
9668 _ => return,
9669 },
9670 Mode::Full => JavaType::Array(Box::new(JavaType::Object(std::sync::Arc::from(
9671 "java/lang/Object",
9672 )))),
9673 };
9674 match mode {
9675 Mode::Single(cv) => {
9676 strip_delegations_deep(&mut extracted);
9677 let cv_local = Expr::Local {
9678 var: cv,
9679 ty: vt.var(cv).ty.clone(),
9680 };
9681 rewrite_bare_returns(&mut extracted, &cv_local);
9682 if !always_returns(&extracted) {
9683 extracted.push(Stmt::Return(Some(cv_local)));
9684 }
9685 }
9686 Mode::Position(k) => {
9687 fill_returns_by_position(&mut extracted, &|args: &[Expr]| args.get(k).cloned());
9691 let fallback = match &primary_del {
9692 Expr::Method { args, .. } => args[k].clone(),
9693 _ => return,
9694 };
9695 rewrite_bare_returns(&mut extracted, &fallback);
9696 if !always_returns(&extracted) {
9697 extracted.push(Stmt::Return(Some(fallback)));
9698 }
9699 }
9700 Mode::Full => {
9701 fill_returns_by_position(&mut extracted, &|args: &[Expr]| {
9708 Some(array_init(args.to_vec()))
9709 });
9710 if l.is_none() {
9711 if !always_returns(&extracted) {
9718 let cut = (1..=extracted.len())
9719 .find(|&i| always_returns(&extracted[..i]));
9720 match cut {
9721 Some(i) => extracted.truncate(i),
9722 None => return,
9723 }
9724 }
9725 }
9726 let fallback_args = match &primary_del {
9727 Expr::Method { args, .. } => args.clone(),
9728 _ => return,
9729 };
9730 let fallback = array_init(fallback_args);
9731 rewrite_bare_returns(&mut extracted, &fallback);
9732 if !always_returns(&extracted) {
9733 extracted.push(Stmt::Return(Some(fallback)));
9734 }
9735 }
9736 }
9737 let this_id = vt
9738 .vars
9739 .iter()
9740 .find(|v| v.is_param && v.name == "this")
9741 .map(|v| v.id);
9742 if !static_safe(&extracted, this_id) {
9743 return;
9744 }
9745 let mut defs: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
9746 collect_def_vars(&extracted, &mut defs);
9747 if defs.iter().any(|v| var_occurs_in(&tail, *v)) {
9748 return;
9749 }
9750 let params: Vec<(u32, JavaType, String)> = vt
9751 .vars
9752 .iter()
9753 .filter(|v| v.is_param && v.name != "this")
9754 .map(|v| (v.id, v.ty.erased(), v.name.clone()))
9755 .collect();
9756 let name = helper_name(&class.name, desc_str);
9757 let call = Expr::Method {
9758 owner: None,
9759 cls: std::sync::Arc::from(class.name.as_str()),
9760 name: std::sync::Arc::from(name.as_str()),
9761 desc: std::sync::Arc::new(jdc_core::types::MethodDescriptor {
9762 ret: ret.clone(),
9763 args: params.iter().map(|p| p.1.clone()).collect(),
9764 }),
9765 args: params
9766 .iter()
9767 .map(|(id, ty, _)| Expr::Local {
9768 var: *id,
9769 ty: TypeRef::J(ty.clone()),
9770 })
9771 .collect(),
9772 is_static: true,
9773 is_interface: false,
9774 is_special: false,
9775 is_super: false,
9776 is_dynamic: false,
9777 type_args: Vec::new(),
9778 };
9779 let mut new_del = primary_del.clone();
9780 match mode {
9781 Mode::Single(cv) => {
9782 deep_rewrite(&mut new_del, &mut |x| {
9783 if let Expr::Local { var, .. } = x {
9784 if *var == cv {
9785 *x = call.clone();
9786 }
9787 }
9788 });
9789 }
9790 Mode::Position(k) => {
9791 let Expr::Method { args, .. } = &mut new_del else {
9792 return;
9793 };
9794 if k >= args.len() {
9795 return;
9796 }
9797 args[k] = call.clone();
9798 }
9799 Mode::Full => {
9800 new_del = Expr::Method {
9804 owner: None,
9805 cls: std::sync::Arc::from(class.name.as_str()),
9806 name: "<init>".into(),
9807 desc: std::sync::Arc::new(jdc_core::types::MethodDescriptor {
9808 args: vec![JavaType::Array(Box::new(JavaType::Object(
9809 std::sync::Arc::from("java/lang/Object"),
9810 )))],
9811 ret: JavaType::Void,
9812 }),
9813 args: vec![call.clone()],
9814 is_static: false,
9815 is_interface: false,
9816 is_special: true,
9817 is_super: false,
9818 is_dynamic: false,
9819 type_args: Vec::new(),
9820 };
9821 }
9822 }
9823 {
9824 let mut m = ctor_helpers().lock().unwrap_or_else(|e| e.into_inner());
9825 let v = m.entry(class.name.clone()).or_default();
9826 if !v.iter().any(|h| h.name == name) {
9827 let mut hbody = Stmt::Block(extracted);
9831 invert_empty_thens(&mut hbody);
9832 v.push(CtorHelper {
9833 name,
9834 ret: ret.clone(),
9835 params: params.into_iter().map(|(_, ty, nm)| (ty, nm)).collect(),
9836 body: hbody,
9837 vt: vt.clone(),
9838 });
9839 if matches!(mode, Mode::Full) {
9840 let Expr::Method {
9841 desc: d0,
9842 is_super: s0,
9843 ..
9844 } = &primary_del
9845 else {
9846 return;
9847 };
9848 let mut rm = ctor_relays().lock().unwrap_or_else(|e| e.into_inner());
9849 let rv = rm.entry(class.name.clone()).or_default();
9850 if !rv.iter().any(|(d, _)| d == desc_str) {
9851 rv.push((
9852 desc_str.to_string(),
9853 CtorRelay {
9854 formals: d0.args.clone(),
9855 is_super: *s0,
9856 },
9857 ));
9858 }
9859 }
9860 } else {
9861 return; }
9863 }
9864 *stmts = std::iter::once(Stmt::ExprStmt(new_del)).chain(tail).collect();
9865}
9866
9867fn fill_returns_by_position(
9874 stmts: &mut Vec<Stmt>,
9875 pick: &dyn Fn(&[Expr]) -> Option<Expr>,
9876) {
9877 let mut i = 0;
9878 while i < stmts.len() {
9879 if is_bare_ctor_call(&stmts[i]) {
9880 let karg = match &stmts[i] {
9881 Stmt::ExprStmt(Expr::Method { args, .. }) => pick(args),
9882 _ => None,
9883 };
9884 if let Some(v) = karg {
9885 let mut j = i + 1;
9886 let mut found = None;
9887 while j < stmts.len() {
9888 if matches!(stmts[j], Stmt::Return(None)) {
9889 found = Some(j);
9890 break;
9891 }
9892 if is_bare_ctor_call(&stmts[j]) {
9893 break; }
9895 j += 1;
9896 }
9897 match found {
9898 Some(j) => {
9899 stmts[j] = Stmt::Return(Some(v));
9900 stmts.remove(i);
9901 continue;
9902 }
9903 None => {
9904 stmts[i] = Stmt::Return(Some(v));
9905 stmts.truncate(i + 1);
9906 break;
9907 }
9908 }
9909 }
9910 }
9911 fill_returns_one(&mut stmts[i], pick);
9912 i += 1;
9913 }
9914}
9915
9916fn fill_returns_one(st: &mut Stmt, pick: &dyn Fn(&[Expr]) -> Option<Expr>) {
9917 match st {
9918 Stmt::Block(v) => fill_returns_by_position(v, pick),
9919 Stmt::If { then_stmt, else_stmt, .. } => {
9920 fill_returns_stmt(then_stmt, pick);
9921 if let Some(e) = else_stmt {
9922 fill_returns_stmt(e, pick);
9923 }
9924 }
9925 Stmt::While { body, .. }
9926 | Stmt::DoWhile { body, .. }
9927 | Stmt::ForEach { body, .. }
9928 | Stmt::Synchronized { body, .. }
9929 | Stmt::Labeled { body, .. } => fill_returns_stmt(body, pick),
9930 Stmt::For { init, body, .. } => {
9931 fill_returns_by_position(init, pick);
9932 fill_returns_stmt(body, pick);
9933 }
9934 Stmt::Switch { cases, default, .. } => {
9935 for c in cases.iter_mut() {
9936 fill_returns_by_position(&mut c.body, pick);
9937 }
9938 if let Some(d) = default {
9939 fill_returns_stmt(d, pick);
9940 }
9941 }
9942 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
9943 fill_returns_stmt(body, pick);
9944 for c in catches.iter_mut() {
9945 fill_returns_stmt(&mut c.body, pick);
9946 }
9947 if let Some(f) = finally {
9948 fill_returns_stmt(f, pick);
9949 }
9950 }
9951 _ => {}
9952 }
9953}
9954
9955fn fill_returns_stmt(st: &mut Stmt, pick: &dyn Fn(&[Expr]) -> Option<Expr>) {
9956 if is_bare_ctor_call(st) {
9957 if let Stmt::ExprStmt(Expr::Method { args, .. }) = st {
9960 if let Some(v) = pick(args) {
9961 *st = Stmt::Return(Some(v));
9962 return;
9963 }
9964 }
9965 }
9966 fill_returns_one(st, pick);
9967}
9968
9969fn normalize_tail(t: &[Stmt]) -> Vec<Stmt> {
9970 let mut v = t.to_vec();
9971 while matches!(v.last(), Some(Stmt::Return(None))) {
9972 v.pop();
9973 }
9974 v
9975}
9976
9977fn site_tails_walk(stmts: &mut Vec<Stmt>, out: &mut Vec<Vec<Stmt>>, carve: bool) {
9982 let mut i = 0;
9983 while i < stmts.len() {
9984 if is_bare_ctor_call(&stmts[i]) {
9985 let mut j = i + 1;
9986 let mut end = stmts.len();
9987 while j < stmts.len() {
9988 if matches!(stmts[j], Stmt::Return(None)) {
9989 end = j;
9990 break;
9991 }
9992 if is_bare_ctor_call(&stmts[j]) {
9993 end = j;
9994 break;
9995 }
9996 j += 1;
9997 }
9998 out.push(stmts[i + 1..end].to_vec());
9999 if carve {
10000 stmts.splice(i + 1..end, []);
10001 }
10002 i += 1;
10003 continue;
10004 }
10005 site_tails_one(&mut stmts[i], out, carve);
10006 i += 1;
10007 }
10008}
10009
10010fn site_tails_one(st: &mut Stmt, out: &mut Vec<Vec<Stmt>>, carve: bool) {
10011 if is_bare_ctor_call(st) {
10012 out.push(Vec::new());
10013 return;
10014 }
10015 match st {
10016 Stmt::Block(v) => site_tails_walk(v, out, carve),
10017 Stmt::If { then_stmt, else_stmt, .. } => {
10018 site_tails_one(then_stmt, out, carve);
10019 if let Some(e) = else_stmt {
10020 site_tails_one(e, out, carve);
10021 }
10022 }
10023 Stmt::While { body, .. }
10024 | Stmt::DoWhile { body, .. }
10025 | Stmt::ForEach { body, .. }
10026 | Stmt::Synchronized { body, .. }
10027 | Stmt::Labeled { body, .. } => site_tails_one(body, out, carve),
10028 Stmt::For { init, body, .. } => {
10029 site_tails_walk(init, out, carve);
10030 site_tails_one(body, out, carve);
10031 }
10032 Stmt::Switch { cases, default, .. } => {
10033 for c in cases.iter_mut() {
10034 site_tails_walk(&mut c.body, out, carve);
10035 }
10036 if let Some(d) = default {
10037 site_tails_one(d, out, carve);
10038 }
10039 }
10040 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
10041 site_tails_one(body, out, carve);
10042 for c in catches.iter_mut() {
10043 site_tails_one(&mut c.body, out, carve);
10044 }
10045 if let Some(f) = finally {
10046 site_tails_one(f, out, carve);
10047 }
10048 }
10049 _ => {}
10050 }
10051}
10052
10053fn always_returns(stmts: &[Stmt]) -> bool {
10059 let Some(last) = stmts
10060 .iter()
10061 .rev()
10062 .find(|s| !matches!(s, Stmt::Block(v) if v.is_empty()))
10063 else {
10064 return false;
10065 };
10066 match last {
10067 Stmt::Return(Some(_)) | Stmt::Throw(_) => true,
10068 Stmt::Block(v) => always_returns(v),
10069 Stmt::If { then_stmt, else_stmt, .. } => match else_stmt {
10070 Some(e) => always_returns_one(then_stmt) && always_returns_one(e),
10071 None => false,
10072 },
10073 Stmt::Labeled { body, .. } | Stmt::Synchronized { body, .. } => {
10074 always_returns_one(body)
10075 }
10076 Stmt::Switch { cases, default, .. } => {
10077 default.as_ref().is_some_and(|d| always_returns_one(d))
10078 && cases.iter().all(|c| always_returns(&c.body))
10079 }
10080 Stmt::Try { body, catches, finally }
10081 | Stmt::TryWithResources { body, catches, finally, .. } => {
10082 finally.as_ref().is_some_and(|f| always_returns_one(f))
10083 || (always_returns_one(body)
10084 && catches.iter().all(|c| always_returns_one(&c.body)))
10085 }
10086 _ => false,
10087 }
10088}
10089
10090fn always_returns_one(st: &Stmt) -> bool {
10091 always_returns(std::slice::from_ref(st))
10092}
10093
10094fn inline_ctor_arg_defs(
10095 call: &mut Stmt,
10096 prefix: &[&Stmt],
10097 vt: &VarTable,
10098 consumed: &mut Vec<(u32, usize)>,
10099) -> bool {
10100 let Stmt::ExprStmt(cexpr) = call else {
10101 return false;
10102 };
10103 fn subst(e: &mut Expr, v: u32, val: &Option<Expr>) -> bool {
10106 let mut ok = true;
10107 deep_rewrite(e, &mut |x| {
10108 if let Expr::Local { var, .. } = x {
10109 if *var == v {
10110 match val {
10111 Some(e2) => *x = e2.clone(),
10112 None => ok = false,
10113 }
10114 }
10115 }
10116 });
10117 ok
10118 }
10119 let mut seen: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
10120 for _ in 0..32 {
10121 let mut need: Option<u32> = None;
10122 visit_exprs(cexpr, &mut |x| {
10123 if need.is_none() {
10124 if let Expr::Local { var, .. } = x {
10125 if (*var as usize) < vt.vars.len() && !vt.vars[*var as usize].is_param {
10126 need = Some(*var);
10127 }
10128 }
10129 }
10130 });
10131 let Some(v) = need else { break };
10132 if !seen.insert(v) {
10133 return false; }
10135 let mut val: Option<Expr> = None;
10147 let mut ok = true;
10148 for (si, st) in prefix.iter().enumerate() {
10149 match st {
10150 Stmt::LocalDef { var, init: None, .. } if *var == v => {}
10154 Stmt::LocalDef { var, init, .. } if *var == v => {
10155 let Some(e) = init else { ok = false; break };
10156 let mut e2 = (*e).clone();
10157 if !subst(&mut e2, v, &val) {
10158 ok = false;
10159 break;
10160 }
10161 val = Some(e2);
10162 consumed.push((v, si));
10163 }
10164 Stmt::ExprStmt(Expr::Assign { target, op, .. })
10165 if matches!(&**target, Expr::Local { var: tv, .. } if *tv == v) =>
10166 {
10167 if !matches!(op, AssignOp::Plain) {
10168 ok = false; break;
10170 }
10171 let Stmt::ExprStmt(Expr::Assign { value, .. }) = st else {
10172 unreachable!()
10173 };
10174 let mut e2 = value.as_ref().clone();
10175 if !subst(&mut e2, v, &val) {
10176 ok = false;
10177 break;
10178 }
10179 val = Some(e2);
10180 consumed.push((v, si));
10181 }
10182 _ => {
10183 let mut touched = false;
10184 visit_all_exprs(st, &mut |x| {
10185 if let Expr::Local { var, .. } = x {
10186 if *var == v {
10187 touched = true;
10188 }
10189 }
10190 });
10191 if touched {
10192 ok = false;
10193 break;
10194 }
10195 }
10196 }
10197 }
10198 if !ok {
10199 return false;
10200 }
10201 let Some(de) = val else { return false };
10202 if contains_this_access(&de) {
10203 return false;
10204 }
10205 let mut de_nodes = 0usize;
10207 visit_exprs(&de, &mut |_| de_nodes += 1);
10208 if de_nodes > 256 {
10209 return false;
10210 }
10211 deep_rewrite(cexpr, &mut |x| {
10212 if let Expr::Local { var, .. } = x {
10213 if *var == v {
10214 *x = de.clone();
10215 }
10216 }
10217 });
10218 let mut nodes = 0usize;
10223 visit_exprs(cexpr, &mut |_| nodes += 1);
10224 if nodes > 2_048 {
10225 return false;
10226 }
10227 }
10228 true
10229}
10230
10231fn fold_sb_chain(stmts: &mut Vec<Stmt>, pos: usize) -> usize {
10244 let mut sbv: Option<u32> = None;
10246 let mut def_i: Option<usize> = None;
10247 let mut def_init: Option<Expr> = None;
10248 for (i, st) in stmts[..pos].iter().enumerate() {
10249 let cand: Option<(u32, &Expr)> = match st {
10250 Stmt::LocalDef {
10251 var,
10252 init: Some(e),
10253 ..
10254 } => Some((*var, e)),
10255 Stmt::ExprStmt(Expr::Assign {
10256 target,
10257 value,
10258 op: AssignOp::Plain,
10259 ..
10260 }) => match &**target {
10261 Expr::Local { var, .. } => Some((*var, value.as_ref())),
10262 _ => None,
10263 },
10264 _ => None,
10265 };
10266 if let Some((v, init)) = cand {
10267 if let Expr::New { cls, .. } = init {
10268 if cls.as_ref() == "java/lang/StringBuilder" {
10269 sbv = Some(v);
10270 def_i = Some(i);
10271 def_init = Some(init.clone());
10272 break;
10273 }
10274 }
10275 }
10276 }
10277 let (Some(sbv), Some(def_i), Some(init)) = (sbv, def_i, def_init) else {
10278 return pos;
10279 };
10280 if contains_this_access(&init) {
10281 return pos;
10282 }
10283 let mut appends: Vec<usize> = Vec::new();
10285 for (i, st) in stmts[..pos].iter().enumerate() {
10286 if let Stmt::ExprStmt(Expr::Method {
10287 owner: Some(o),
10288 cls,
10289 name,
10290 ..
10291 }) = st
10292 {
10293 if matches!(&**o, Expr::Local { var, .. } if *var == sbv)
10294 && cls.as_ref() == "java/lang/StringBuilder"
10295 && &**name == "append"
10296 {
10297 appends.push(i);
10298 }
10299 }
10300 }
10301 let mut post_read = false;
10303 for st in stmts.iter().skip(pos + 1) {
10304 visit_all_exprs(st, &mut |e| {
10305 if let Expr::Local { var, .. } = e {
10306 if *var == sbv {
10307 post_read = true;
10308 }
10309 }
10310 });
10311 }
10312 if post_read {
10313 return pos;
10314 }
10315 for (i, st) in stmts[..pos].iter().enumerate() {
10318 if i == def_i || appends.contains(&i) {
10319 if let Stmt::ExprStmt(Expr::Method { args, .. }) = st {
10321 if args.iter().any(contains_this_access) {
10322 return pos;
10323 }
10324 }
10325 continue;
10326 }
10327 let mut leak = false;
10328 visit_all_exprs(st, &mut |e| {
10329 if let Expr::Local { var, .. } = e {
10330 if *var == sbv {
10331 leak = true;
10332 }
10333 }
10334 });
10335 if leak {
10336 return pos;
10337 }
10338 }
10339 let mut local_reads = 0usize;
10341 let mut tostring_reads = 0usize;
10342 visit_all_exprs(&stmts[pos], &mut |e| match e {
10343 Expr::Local { var, .. } if *var == sbv => local_reads += 1,
10344 Expr::Method {
10345 owner: Some(o),
10346 name,
10347 args,
10348 ..
10349 } if args.is_empty()
10350 && &**name == "toString"
10351 && matches!(&**o, Expr::Local { var, .. } if *var == sbv) =>
10352 {
10353 tostring_reads += 1;
10354 }
10355 _ => {}
10356 });
10357 if local_reads != 1 || tostring_reads != 1 {
10358 return pos;
10359 }
10360 let mut chain = init;
10362 for ai in &appends {
10363 if let Stmt::ExprStmt(Expr::Method {
10364 owner: _,
10365 cls,
10366 name,
10367 desc,
10368 args,
10369 is_static,
10370 is_interface,
10371 is_special,
10372 is_super,
10373 is_dynamic,
10374 type_args,
10375 ..
10376 }) = &stmts[*ai]
10377 {
10378 chain = Expr::Method {
10379 owner: Some(Box::new(chain)),
10380 cls: cls.clone(),
10381 name: name.clone(),
10382 desc: desc.clone(),
10383 args: args.clone(),
10384 is_static: *is_static,
10385 is_interface: *is_interface,
10386 is_special: *is_special,
10387 is_super: *is_super,
10388 is_dynamic: *is_dynamic,
10389 type_args: type_args.clone(),
10390 };
10391 }
10392 }
10393 let mut replaced = false;
10394 walk_stmt_exprs(&mut stmts[pos], &mut |e| {
10395 deep_rewrite(e, &mut |x| {
10396 if let Expr::Method {
10397 owner: Some(o),
10398 name,
10399 args,
10400 ..
10401 } = x
10402 {
10403 if args.is_empty()
10404 && &**name == "toString"
10405 && matches!(&**o, Expr::Local { var, .. } if *var == sbv)
10406 {
10407 **o = chain.clone();
10408 replaced = true;
10409 }
10410 }
10411 });
10412 });
10413 if !replaced {
10414 return pos; }
10416 let mut removed: Vec<usize> = appends.clone();
10418 removed.push(def_i);
10419 removed.sort_unstable();
10420 removed.dedup();
10421 for i in removed.iter().rev() {
10422 stmts.remove(*i);
10423 }
10424 pos - removed.len()
10425}
10426
10427pub fn fix_ctor_super_first(body: &mut Stmt, vt: &VarTable) {
10428 let Stmt::Block(stmts) = body else { return };
10429 if stmts.is_empty() || is_bare_ctor_call(stmts.first().unwrap()) {
10430 return;
10431 }
10432 if let Some(pos0) = stmts.iter().position(is_bare_ctor_call) {
10434 let pos = fold_sb_chain(stmts, pos0);
10435 let mut call = stmts[pos].clone();
10436 let prefix: Vec<&Stmt> = stmts[..pos].iter().collect();
10437 let mut consumed: Vec<(u32, usize)> = Vec::new();
10438 if !inline_ctor_arg_defs(&mut call, &prefix, vt, &mut consumed) {
10439 return;
10440 }
10441 stmts.remove(pos);
10442 let mut drop_idx: Vec<usize> = consumed
10449 .iter()
10450 .filter(|(v, _)| !var_occurs_in(&stmts[pos..], *v))
10451 .map(|(_, k)| *k)
10452 .collect();
10453 drop_idx.sort_unstable();
10454 drop_idx.dedup();
10455 for k in drop_idx.iter().rev() {
10456 stmts.remove(*k);
10457 }
10458 stmts.insert(0, call);
10459 return;
10460 }
10461 let mut found: Option<(usize, usize)> = None;
10471 for (i, st) in stmts.iter().enumerate() {
10472 if let Stmt::Block(inner) = st {
10473 if let Some(p) = inner.iter().position(is_bare_ctor_call) {
10474 if inner[..p]
10475 .iter()
10476 .all(|s| matches!(s, Stmt::LocalDef { .. } | Stmt::ExprStmt(_)))
10477 {
10478 found = Some((i, p));
10479 break;
10480 }
10481 }
10482 }
10483 }
10484 if let Some((i, p)) = found {
10485 let mut call = match &stmts[i] {
10486 Stmt::Block(inner) => inner[p].clone(),
10487 _ => unreachable!(),
10488 };
10489 let mut prefix: Vec<&Stmt> = stmts[..i].iter().collect();
10490 if let Stmt::Block(inner) = &stmts[i] {
10491 prefix.extend(inner[..p].iter());
10492 }
10493 let mut consumed: Vec<(u32, usize)> = Vec::new();
10494 if !inline_ctor_arg_defs(&mut call, &prefix, vt, &mut consumed) {
10495 return;
10496 }
10497 let mut ks: Vec<usize> = Vec::new();
10504 {
10505 let inner_tail: &[Stmt] = match &stmts[i] {
10506 Stmt::Block(inner) if p < inner.len() => &inner[p + 1..],
10507 _ => &[],
10508 };
10509 for (v, k) in &consumed {
10510 if !var_occurs_in(inner_tail, *v) && !var_occurs_in(&stmts[i + 1..], *v) {
10511 ks.push(*k);
10512 }
10513 }
10514 }
10515 ks.sort_unstable();
10516 ks.dedup();
10517 if let Stmt::Block(inner) = &mut stmts[i] {
10518 inner.remove(p);
10519 for k in ks.iter().rev().filter(|k| **k >= i) {
10520 inner.remove(k - i);
10521 }
10522 }
10523 for k in ks.iter().rev().filter(|k| **k < i) {
10524 stmts.remove(*k);
10525 }
10526 stmts.insert(0, call);
10527 }
10528}
10529
10530fn is_delegation_expr(e: &Expr) -> bool {
10539 matches!(
10540 e,
10541 Expr::Method { name, is_special: true, owner, .. }
10542 if &**name == "<init>"
10543 && (owner.is_none() || matches!(owner.as_deref(), Some(Expr::This)))
10544 )
10545}
10546
10547pub(crate) fn is_bare_ctor_call(s: &Stmt) -> bool {
10548 matches!(s, Stmt::ExprStmt(e) if is_delegation_expr(e))
10549}
10550
10551pub(crate) fn local_is_written(body: &Stmt, var: u32) -> bool {
10555 let mut hit = false;
10556 visit_stmt_exprs_ro(body, &mut |e| {
10557 if hit {
10558 return;
10559 }
10560 visit_exprs(e, &mut |x| {
10561 if hit {
10562 return;
10563 }
10564 match x {
10565 Expr::Assign { target, .. }
10566 if matches!(&**target, Expr::Local { var: v, .. } if *v == var) =>
10567 {
10568 hit = true;
10569 }
10570 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. }
10571 if matches!(&**e, Expr::Local { var: v, .. } if *v == var) =>
10572 {
10573 hit = true;
10574 }
10575 _ => {}
10576 }
10577 });
10578 });
10579 hit
10580}
10581
10582pub(crate) fn rewrite_inner_ctor_outer_param(
10592 body: &mut Stmt,
10593 param0: u32,
10594 outer_display: &str,
10595 outer_ty: &TypeRef,
10596 eligible: &[String],
10597) {
10598 walk_stmt_exprs(body, &mut |e| {
10602 if let Expr::Method { name, cls, args, .. } = e {
10603 if &**name == "<init>"
10604 && eligible.iter().any(|c| c.as_str() == cls.as_ref())
10605 && args.first().is_some_and(|a| matches!(a, Expr::Local { var: v, .. } if *v == param0))
10606 {
10607 args.remove(0);
10608 }
10609 }
10610 });
10611 let text = format!("{outer_display}.this");
10613 let ty = outer_ty.clone();
10614 walk_stmt_exprs(body, &mut |e| {
10615 deep_rewrite_reads(e, &mut |x| {
10616 if let Expr::Local { var: v, .. } = x {
10617 if *v == param0 {
10618 *x = Expr::RawT(text.clone(), ty.clone());
10619 }
10620 }
10621 });
10622 });
10623}
10624
10625fn stmts_count_writes(stmts: &[Stmt], var: u32) -> usize {
10673 let mut n = 0usize;
10674 for s in stmts {
10675 visit_stmt_exprs_ro(s, &mut |e| {
10676 visit_exprs(e, &mut |x| match x {
10677 Expr::Assign { target, .. }
10678 if matches!(&**target, Expr::Local { var: v, .. } if *v == var) =>
10679 {
10680 n += 1;
10681 }
10682 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. }
10683 if matches!(&**e, Expr::Local { var: v, .. } if *v == var) =>
10684 {
10685 n += 1;
10686 }
10687 _ => {}
10688 });
10689 });
10690 }
10691 n
10692}
10693
10694fn prefix_written_vars(stmts: &[Stmt]) -> jdc_core::FxHashSet<u32> {
10698 let mut set = jdc_core::FxHashSet::default();
10699 for s in stmts {
10700 let mut c = s.clone();
10701 walk_stmt_exprs(&mut c, &mut |e| {
10702 deep_rewrite(e, &mut |x| match x {
10703 Expr::Assign { target, .. } => {
10704 if let Expr::Local { var, .. } = &**target {
10705 set.insert(*var);
10706 }
10707 }
10708 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => {
10709 if let Expr::Local { var, .. } = &**e {
10710 set.insert(*var);
10711 }
10712 }
10713 _ => {}
10714 });
10715 });
10716 if let Stmt::LocalDef { var, .. } = s {
10717 set.insert(*var);
10718 }
10719 }
10720 set
10721}
10722
10723fn pre_this_safe(e: &Expr, prefix_vars: &jdc_core::FxHashSet<u32>) -> bool {
10727 match e {
10728 Expr::Local { var, .. } => !prefix_vars.contains(var),
10729 Expr::Const(_) => true,
10730 Expr::Cast { e, .. } => pre_this_safe(e, prefix_vars),
10731 Expr::Bin { l, r, .. } => {
10732 pre_this_safe(l, prefix_vars) && pre_this_safe(r, prefix_vars)
10733 }
10734 Expr::Cond { c, t, f } => {
10735 pre_this_safe(c, prefix_vars)
10736 && pre_this_safe(t, prefix_vars)
10737 && pre_this_safe(f, prefix_vars)
10738 }
10739 Expr::Field { owner, is_static, .. } => {
10740 *is_static
10741 && owner
10742 .as_ref()
10743 .is_none_or(|o| pre_this_safe(o, prefix_vars))
10744 }
10745 Expr::Method {
10746 owner,
10747 args,
10748 is_static,
10749 ..
10750 } => {
10751 args.iter().all(|a| pre_this_safe(a, prefix_vars))
10752 && match owner {
10753 Some(o) => pre_this_safe(o, prefix_vars),
10754 None => *is_static,
10755 }
10756 }
10757 Expr::New { args, .. } => args.iter().all(|a| pre_this_safe(a, prefix_vars)),
10765 Expr::NewArray { dims, init, .. } => {
10766 dims.iter().all(|d| pre_this_safe(d, prefix_vars))
10767 && init
10768 .as_ref()
10769 .map(|l| l.iter().all(|x| pre_this_safe(x, prefix_vars)))
10770 .unwrap_or(true)
10771 }
10772 Expr::Un { e, .. } | Expr::InstanceOf { e, .. } => pre_this_safe(e, prefix_vars),
10774 Expr::ArrayIndex { array, index } => {
10775 pre_this_safe(array, prefix_vars) && pre_this_safe(index, prefix_vars)
10776 }
10777 _ => false,
10778 }
10779}
10780
10781fn single_assign_to_v(s: &Stmt, v: u32) -> Option<Expr> {
10785 match s {
10786 Stmt::ExprStmt(Expr::Assign {
10787 target,
10788 op: AssignOp::Plain,
10789 value,
10790 }) if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v) => {
10791 Some((**value).clone())
10792 }
10793 Stmt::Block(xs) if xs.len() == 1 => single_assign_to_v(&xs[0], v),
10794 _ => None,
10795 }
10796}
10797
10798pub(crate) fn flatten_top_blocks(stmts: &mut Vec<Stmt>) {
10820 fn rec(s: Stmt, out: &mut Vec<Stmt>) {
10821 match s {
10822 Stmt::Block(inner) => {
10823 for x in inner {
10824 rec(x, out);
10825 }
10826 }
10827 other => out.push(other),
10828 }
10829 }
10830 if !stmts.iter().any(|s| matches!(s, Stmt::Block(_))) {
10831 return;
10832 }
10833 let mut out: Vec<Stmt> = Vec::with_capacity(stmts.len());
10834 for s in std::mem::take(stmts) {
10835 rec(s, &mut out);
10836 }
10837 *stmts = out;
10838}
10839
10840fn fold_default_arg_bridge(stmts: &mut Vec<Stmt>) -> bool {
10841 flatten_top_blocks(stmts);
10842 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
10843 return false;
10844 };
10845 if pos == 0 {
10846 return false;
10847 }
10848 let Stmt::ExprStmt(Expr::Method { args, .. }) = &stmts[pos] else {
10849 return false;
10850 };
10851 let mut arg_vars: Vec<u32> = Vec::new();
10852 let mut reads = 0usize;
10853 for a in args {
10854 let mut c = a.clone();
10855 deep_rewrite_reads(&mut c, &mut |x| {
10856 if let Expr::Local { var, .. } = x {
10857 reads += 1;
10858 if !arg_vars.contains(var) {
10859 arg_vars.push(*var);
10860 }
10861 }
10862 });
10863 }
10864 if arg_vars.is_empty() || reads != arg_vars.len() {
10865 return false; }
10867 for &v in &arg_vars {
10870 if stmts_read_var(&stmts[pos + 1..], v) != 0
10871 || stmts_count_writes(&stmts[pos + 1..], v) != 0
10872 {
10873 return false;
10874 }
10875 }
10876 let prefix_vars = prefix_written_vars(&stmts[..pos]);
10877 let mut repl: Vec<(u32, Expr)> = Vec::new();
10878 let mut drop: Vec<usize> = Vec::new();
10879 for &v in &arg_vars {
10880 let mut base: Option<(usize, Expr)> = None;
10882 let mut decl: Option<usize> = None;
10883 let mut condf: Option<(usize, Expr, Option<Expr>, Option<Expr>)> = None;
10884 let mut ok = true;
10885 for (i, s) in stmts[..pos].iter().enumerate() {
10886 match s {
10887 Stmt::LocalDef { var, init, .. } if *var == v => match init {
10888 None => {
10889 if decl.is_some() {
10890 ok = false;
10891 break;
10892 }
10893 decl = Some(i);
10894 }
10895 Some(e) => {
10896 if base.is_some() {
10897 ok = false;
10898 break;
10899 }
10900 base = Some((i, e.clone()));
10901 }
10902 },
10903 Stmt::ExprStmt(Expr::Assign {
10904 target,
10905 op: AssignOp::Plain,
10906 value,
10907 }) if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v) => {
10908 if base.is_some() {
10909 ok = false;
10910 break;
10911 }
10912 base = Some((i, (**value).clone()));
10913 }
10914 Stmt::If {
10915 cond,
10916 then_stmt,
10917 else_stmt,
10918 } => {
10919 let t = single_assign_to_v(then_stmt, v);
10920 let f = else_stmt.as_ref().and_then(|b| single_assign_to_v(b, v));
10921 let then_touches = t.is_some()
10922 || stmts_read_var(std::slice::from_ref(&**then_stmt), v) != 0
10923 || stmts_count_writes(std::slice::from_ref(&**then_stmt), v) != 0;
10924 let else_touches = else_stmt.as_ref().is_some_and(|b| {
10925 f.is_some()
10926 || stmts_read_var(std::slice::from_ref(b), v) != 0
10927 || stmts_count_writes(std::slice::from_ref(b), v) != 0
10928 });
10929 if !then_touches && !else_touches {
10930 continue; }
10932 if condf.is_some()
10937 || base.is_none()
10938 || (then_touches && t.is_none())
10939 || (else_touches && f.is_none())
10940 {
10941 ok = false;
10942 break;
10943 }
10944 let cond_stmt = Stmt::ExprStmt(cond.clone());
10945 if stmts_read_var(std::slice::from_ref(&cond_stmt), v) != 0 {
10946 ok = false;
10947 break;
10948 }
10949 condf = Some((i, cond.clone(), t, f));
10950 }
10951 _ => {}
10952 }
10953 }
10954 if !ok {
10955 continue;
10956 }
10957 let Some((bi, be)) = base else { continue };
10958 let want_writes = 1
10961 + condf
10962 .as_ref()
10963 .map(|(_, _, t, f)| t.is_some() as usize + f.is_some() as usize)
10964 .unwrap_or(0);
10965 if stmts_count_writes(&stmts[..pos], v) != want_writes
10966 || stmts_read_var(&stmts[..pos], v) != 0
10967 {
10968 continue;
10969 }
10970 let rep = match condf {
10971 Some((ci, c, t, f)) => {
10972 drop.push(ci);
10973 match (t, f) {
10974 (Some(t), Some(f)) => Expr::Cond {
10975 c: Box::new(c),
10976 t: Box::new(t),
10977 f: Box::new(f),
10978 },
10979 (Some(t), None) => Expr::Cond {
10980 c: Box::new(c),
10981 t: Box::new(t),
10982 f: Box::new(be.clone()),
10983 },
10984 (None, Some(f)) => Expr::Cond {
10985 c: Box::new(c),
10986 t: Box::new(be.clone()),
10987 f: Box::new(f),
10988 },
10989 (None, None) => unreachable!("condf requires a branch assign"),
10990 }
10991 }
10992 None => be.clone(),
10993 };
10994 if !pre_this_safe(&rep, &prefix_vars) {
10995 continue;
10996 }
10997 drop.push(bi);
10998 if let Some(d) = decl {
10999 drop.push(d);
11000 }
11001 repl.push((v, rep));
11002 }
11003 if repl.is_empty() {
11004 return false;
11005 }
11006 if let Stmt::ExprStmt(Expr::Method { args, .. }) = &mut stmts[pos] {
11007 for a in args.iter_mut() {
11008 deep_rewrite_reads(a, &mut |x| {
11009 if let Expr::Local { var, .. } = x {
11010 if let Some((_, e)) = repl.iter().find(|(vv, _)| vv == var) {
11011 *x = e.clone();
11012 }
11013 }
11014 });
11015 }
11016 }
11017 drop.sort_unstable();
11018 drop.dedup();
11019 for i in drop.into_iter().rev() {
11020 stmts.remove(i);
11021 }
11022 true
11023}
11024
11025pub fn rewrite_kotlin_facades(body: &mut Stmt, pool: &crate::DexPool) {
11033 let map = pool.kotlin_facade_map();
11034 if map.is_empty() {
11035 return;
11036 }
11037 walk_stmt_exprs(body, &mut |e| {
11038 match e {
11039 Expr::Method {
11040 cls,
11041 name,
11042 desc,
11043 is_static,
11044 ..
11045 } if *is_static => {
11046 if let Some((base, cover)) = map.get(cls.as_ref()) {
11047 let hit = match cover {
11048 None => true,
11049 Some(c) => c.0.contains(&(name.to_string(), desc.to_string())),
11050 };
11051 if hit {
11052 *cls = std::sync::Arc::from(base.as_str());
11053 }
11054 }
11055 }
11056 Expr::Field {
11057 cls,
11058 name,
11059 is_static,
11060 ..
11061 } if *is_static => {
11062 if let Some((base, cover)) = map.get(cls.as_ref()) {
11063 let hit = match cover {
11064 None => true,
11065 Some(c) => c.1.contains(name.as_ref()),
11066 };
11067 if hit {
11068 *cls = std::sync::Arc::from(base.as_str());
11069 }
11070 }
11071 }
11072 _ => {}
11073 }
11074 });
11075}
11076
11077pub fn fold_enum_default_arg_bridge(body: &mut Stmt) {
11085 let Stmt::Block(stmts) = body else { return };
11086 fold_default_arg_bridge(stmts);
11087}
11088
11089pub fn fix_ctor_delegation_arg_defs(body: &mut Stmt) {
11090 let Stmt::Block(stmts) = body else { return };
11091 fold_default_arg_bridge(stmts);
11094 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
11098 return;
11099 };
11100 if pos == 0 {
11101 return;
11102 }
11103 let mut arg_vars: Vec<u32> = Vec::new();
11106 let mut total_reads = 0usize;
11107 {
11108 let Stmt::ExprStmt(Expr::Method { args, .. }) = &stmts[pos] else {
11109 return;
11110 };
11111 for a in args {
11112 let mut c = a.clone();
11113 deep_rewrite_reads(&mut c, &mut |x| {
11114 if let Expr::Local { var: v, .. } = x {
11115 total_reads += 1;
11116 if !arg_vars.contains(v) {
11117 arg_vars.push(*v);
11118 }
11119 }
11120 });
11121 }
11122 }
11123 if arg_vars.is_empty() || total_reads != arg_vars.len() {
11124 return;
11125 }
11126 let prefix_defs: Vec<u32> = stmts[..pos]
11129 .iter()
11130 .filter_map(|s| match s {
11131 Stmt::LocalDef { var, init: Some(_), .. } => Some(*var),
11132 _ => None,
11133 })
11134 .collect();
11135 let mut plan: Vec<(u32, usize, Option<usize>)> = Vec::new();
11140 for v in arg_vars {
11141 let defs: Vec<usize> = stmts[..pos]
11142 .iter()
11143 .enumerate()
11144 .filter(|(i, _)| {
11145 matches!(&stmts[*i], Stmt::LocalDef { var, init: Some(_), .. } if *var == v)
11146 })
11147 .map(|(i, _)| i)
11148 .collect();
11149 if defs.len() > 1 {
11150 return; }
11152 let Some(&q) = defs.first() else {
11153 continue; };
11155 let init = match &stmts[q] {
11156 Stmt::LocalDef { init: Some(e), .. } => e.clone(),
11157 _ => return,
11158 };
11159 if stmts_read_var(&stmts[q + 1..pos], v) != 0 {
11160 return; }
11162 let mut init_vars: Vec<u32> = Vec::new();
11165 deep_rewrite_reads(&mut { init.clone() }, &mut |x| {
11166 if let Expr::Local { var: vv, .. } = x {
11167 if !init_vars.contains(vv) {
11168 init_vars.push(*vv);
11169 }
11170 }
11171 });
11172 if init_vars.iter().any(|iv| prefix_defs.contains(iv)) {
11173 return;
11174 }
11175 match stmts[pos + 1..]
11176 .iter()
11177 .position(|s| {
11178 matches!(s, Stmt::ExprStmt(Expr::Assign { target, .. })
11179 if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v))
11180 }) {
11181 Some(w) => {
11182 if stmts_read_var(&stmts[pos + 1..pos + 1 + w], v) != 0 {
11184 return;
11185 }
11186 plan.push((v, q, Some(pos + 1 + w)));
11187 }
11188 None => {
11189 if stmts_read_var(&stmts[pos + 1..], v) != 0 {
11191 return;
11192 }
11193 plan.push((v, q, None));
11194 }
11195 }
11196 }
11197 if plan.is_empty() {
11198 return;
11199 }
11200 let values: Vec<(u32, Expr)> = plan
11202 .iter()
11203 .map(|(_v, q, _)| match &stmts[*q] {
11204 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e.clone()),
11205 _ => unreachable!("plan entries carry a LocalDef with init"),
11206 })
11207 .collect();
11208 if let Stmt::ExprStmt(Expr::Method { args, .. }) = &mut stmts[pos] {
11209 for a in args {
11210 deep_rewrite_reads(a, &mut |x| {
11211 if let Expr::Local { var: v, .. } = x {
11212 if let Some((_, e)) = values.iter().find(|(vv, _)| vv == v) {
11213 *x = e.clone();
11214 }
11215 }
11216 });
11217 }
11218 }
11219 for (v, _, w) in &plan {
11223 if let Some(w) = w {
11224 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = &mut stmts[*w] {
11225 if matches!(&**target, Expr::Local { var: vv, .. } if vv == v) {
11226 let taken = std::mem::replace(&mut **value, Expr::Const(ConstVal::Null));
11227 stmts[*w] = Stmt::LocalDef {
11228 var: *v,
11229 init: Some(taken),
11230 is_final: false,
11231 force_type: false,
11232 };
11233 }
11234 }
11235 }
11236 }
11237 let mut order: Vec<usize> = plan.iter().map(|(_, q, _)| *q).collect();
11238 order.sort_unstable();
11239 order.reverse();
11240 for q in order {
11241 stmts.remove(q);
11242 }
11243}
11244
11245pub fn fix_bool_returns(vt: &VarTable, body: &mut Stmt) {
11256 walk_mut_deep(body, &mut |st| {
11257 if let Stmt::Return(Some(e)) = st {
11258 let numeric = match &*e {
11259 Expr::Local { var, ty } => {
11260 let t = if (*var as usize) < vt.vars.len() {
11261 vt.var(*var).ty.erased()
11262 } else {
11263 ty.erased()
11264 };
11265 matches!(
11266 t,
11267 JavaType::Int
11268 | JavaType::Long
11269 | JavaType::Short
11270 | JavaType::Byte
11271 | JavaType::Char
11272 )
11273 }
11274 other => matches!(
11275 other.type_ref().erased(),
11276 JavaType::Int | JavaType::Long | JavaType::Short | JavaType::Byte
11277 | JavaType::Char
11278 ),
11279 };
11280 if numeric {
11281 let taken = e.clone();
11282 *e = Expr::Bin {
11283 op: BinOp::Ne,
11284 l: Box::new(taken),
11285 r: Box::new(Expr::Const(ConstVal::Int(0))),
11286 ty: Some(TypeRef::J(JavaType::Boolean)),
11287 };
11288 }
11289 }
11290 });
11291}
11292
11293pub fn fix_int_returns(vt: &VarTable, body: &mut Stmt) {
11294 walk_mut_deep(body, &mut |st| {
11295 if let Stmt::Return(Some(e)) = st {
11296 let is_bool = match e {
11297 Expr::Local { var, .. } => {
11298 (*var as usize) < vt.vars.len()
11299 && vt.vars[*var as usize].ty.erased() == JavaType::Boolean
11300 }
11301 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
11302 _ => false,
11303 };
11304 if is_bool {
11305 *e = Expr::Cond {
11306 c: Box::new(e.clone()),
11307 t: Box::new(Expr::Const(ConstVal::Int(1))),
11308 f: Box::new(Expr::Const(ConstVal::Int(0))),
11309 };
11310 }
11311 }
11312 });
11313}
11314
11315pub fn split_generations(vt: &mut VarTable, body: &mut Stmt, pool: &DexPool) {
11331 let mut counter: u32 = 0;
11332 let mut gen: std::collections::HashMap<u32, u32> = std::collections::HashMap::default();
11333 split_walk_stmt(body, vt, &mut gen, &mut counter, pool);
11334}
11335
11336fn split_needed(vt: &VarTable, target: u32, value: &Expr, pool: &DexPool) -> bool {
11338 let n = vt.vars.len();
11339 if target as usize >= n {
11340 return false;
11341 }
11342 let declared = vt.vars[target as usize].ty.erased();
11343 let vt_is_ref = |t: &JavaType| matches!(t, JavaType::Object(_) | JavaType::Array(_));
11344 fn side_bool(e: &Expr, vt: &VarTable) -> bool {
11353 match e {
11354 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
11355 matches!(vt.var(*var).ty.erased(), JavaType::Boolean)
11356 }
11357 Expr::Const(ConstVal::Int(0 | 1)) => true,
11358 Expr::Bin { op: BinOp::Or | BinOp::And | BinOp::Xor, l, r, .. } => {
11359 side_bool(l, vt) && side_bool(r, vt)
11360 }
11361 other => matches!(other.type_ref().erased(), JavaType::Boolean),
11362 }
11363 }
11364 let value_ty = match value {
11365 Expr::Local { var, ty } if (*var as usize) < vt.vars.len() => vt.var(*var).ty.erased(),
11366 Expr::Bin {
11367 op: BinOp::Or | BinOp::And | BinOp::Xor,
11368 l,
11369 r,
11370 ..
11371 } if side_bool(l, vt) && side_bool(r, vt) => JavaType::Boolean,
11372 other => other.type_ref().erased(),
11373 };
11374 if vt_is_ref(&declared) && vt_is_ref(&value_ty) {
11375 let obj = JavaType::Object("java/lang/Object".into());
11385 if declared == value_ty || declared == obj || value_ty == obj {
11386 return false;
11387 }
11388 if let (JavaType::Object(v), JavaType::Object(d)) = (&value_ty, &declared) {
11401 if matches!(value, Expr::New { .. })
11402 && (pool.get(v).is_none() || pool.get(d).is_none())
11403 {
11404 return false;
11405 }
11406 }
11407 true
11408 } else {
11409 if vt_is_ref(&declared) != vt_is_ref(&value_ty) {
11413 return true;
11414 }
11415 let d_bool = matches!(declared, JavaType::Boolean);
11423 let v_bool = matches!(value_ty, JavaType::Boolean);
11424 let zero_one = matches!(value, Expr::Const(ConstVal::Int(0 | 1)));
11425 d_bool != v_bool && !zero_one
11426 }
11427}
11428
11429fn rewrite_gen_reads(e: &mut Expr, gen: &std::collections::HashMap<u32, u32>, vt: &VarTable) {
11430 deep_rewrite(e, &mut |x| {
11431 if let Expr::Local { var, ty } = x {
11432 if let Some(&to) = gen.get(var) {
11433 if to != *var {
11434 *var = to;
11435 *ty = vt.var(to).ty.clone();
11436 }
11437 }
11438 }
11439 });
11440}
11441
11442fn do_split(
11443 vt: &mut VarTable,
11444 target: &mut Expr,
11445 old: u32,
11446 value_ty: &JavaType,
11447 gen: &mut std::collections::HashMap<u32, u32>,
11448 counter: &mut u32,
11449) {
11450 *counter += 1;
11451 let info = &vt.vars[old as usize];
11452 let id = vt.vars.len() as u32;
11453 let name = format!("{}_g{}", info.name, counter);
11454 let slot = info.slot;
11455 let is_param = false;
11463 vt.vars.push(jdc_core::var::VarInfo {
11464 id,
11465 slot,
11466 name,
11467 ty: TypeRef::J(value_ty.clone()),
11468 is_param,
11469 range_start: 0,
11470 range_end: u16::MAX,
11471 synthetic_name: true,
11472 });
11473 while vt.by_slot.len() <= slot as usize {
11474 vt.by_slot.push(Vec::new());
11475 }
11476 vt.by_slot[slot as usize].push((0, u16::MAX, id));
11477 gen.insert(old, id);
11478 if let Expr::Local { var, ty, .. } = target {
11479 *var = id;
11480 *ty = TypeRef::J(value_ty.clone());
11481 }
11482}
11483
11484fn rename_gen_back(s: &mut Stmt, from: u32, to: u32, vt: &VarTable) {
11496 if from == to || (to as usize) >= vt.vars.len() {
11497 return;
11498 }
11499 let to_ty = vt.vars[to as usize].ty.clone();
11500 walk_mut_deep(s, &mut |st| {
11501 if let Stmt::LocalDef { var, .. } = st {
11502 if *var == from {
11503 *var = to;
11504 }
11505 }
11506 });
11507 rewrite_exprs(s, &mut |e| {
11508 deep_rewrite(e, &mut |x| {
11509 if let Expr::Local { var, ty } = x {
11510 if *var == from {
11511 *var = to;
11512 *ty = to_ty.clone();
11513 }
11514 }
11515 });
11516 });
11517}
11518
11519fn drop_gen_slot(vt: &mut VarTable, dead: u32) {
11523 if (dead as usize) >= vt.vars.len() {
11524 return;
11525 }
11526 let slot = vt.vars[dead as usize].slot as usize;
11527 if let Some(seg) = vt.by_slot.get_mut(slot) {
11528 seg.retain(|&(_, _, id)| id != dead);
11529 }
11530}
11531
11532fn undo_type_safe(vt: &VarTable, k: u32, v: u32) -> bool {
11541 if (k as usize) >= vt.vars.len() || (v as usize) >= vt.vars.len() {
11542 return false;
11543 }
11544 let kt = vt.vars[k as usize].ty.erased();
11545 let vt_ty = vt.vars[v as usize].ty.erased();
11546 if kt == vt_ty {
11547 return true;
11548 }
11549 let is_prim = |t: &JavaType| {
11550 matches!(
11551 t,
11552 JavaType::Int
11553 | JavaType::Long
11554 | JavaType::Short
11555 | JavaType::Byte
11556 | JavaType::Char
11557 | JavaType::Float
11558 | JavaType::Double
11559 | JavaType::Boolean
11560 )
11561 };
11562 is_prim(&kt) && is_prim(&vt_ty)
11563}
11564
11565fn split_walk_stmt(
11566 st: &mut Stmt,
11567 vt: &mut VarTable,
11568 gen: &mut std::collections::HashMap<u32, u32>,
11569 counter: &mut u32,
11570 pool: &DexPool,
11571) {
11572 match st {
11573 Stmt::Block(v) => {
11574 for x in v.iter_mut() {
11575 split_walk_stmt(x, vt, gen, counter, pool);
11576 }
11577 }
11578 Stmt::ExprStmt(e) => {
11579 rewrite_gen_reads(e, gen, vt);
11580 if let Expr::Assign {
11581 target,
11582 value,
11583 op: AssignOp::Plain,
11584 ..
11585 } = e
11586 {
11587 if let Expr::Local { var, .. } = &**target {
11588 let old = *var;
11589 if split_needed(vt, old, value, pool) {
11590 let value_ty = value.type_ref().erased();
11591 do_split(vt, target, old, &value_ty, gen, counter);
11592 }
11593 }
11594 }
11595 }
11596 Stmt::LocalDef { var, init, .. } => {
11597 if let Some(e) = init {
11598 rewrite_gen_reads(e, gen, vt);
11599 if split_needed(vt, *var, e, pool) {
11600 let value_ty = e.type_ref().erased();
11604 let old = *var;
11605 let mut fake = Expr::Local {
11606 var: old,
11607 ty: vt.vars[old as usize].ty.clone(),
11608 };
11609 do_split(vt, &mut fake, old, &value_ty, gen, counter);
11610 if let Expr::Local { var: nv, .. } = fake {
11611 *var = nv;
11612 }
11613 }
11614 }
11615 }
11616 Stmt::If {
11617 cond,
11618 then_stmt,
11619 else_stmt,
11620 ..
11621 } => {
11622 rewrite_gen_reads(cond, gen, vt);
11623 let then_floor = vt.vars.len() as u32;
11624 let mut g_then = gen.clone();
11625 split_walk_stmt(then_stmt, vt, &mut g_then, counter, pool);
11626 let else_floor = vt.vars.len() as u32;
11627 let mut g_else = gen.clone();
11628 if let Some(e) = else_stmt {
11629 split_walk_stmt(e, vt, &mut g_else, counter, pool);
11630 }
11631 let mut undo_then: Vec<(u32, u32)> = g_then
11655 .iter()
11656 .filter(|(&k, &v)| {
11657 v >= then_floor
11658 && v < else_floor
11659 && g_else.get(&k).copied() == gen.get(&k).copied()
11660 && undo_type_safe(vt, k, v)
11661 })
11662 .map(|(&k, &v)| (v, k))
11663 .collect();
11664 if else_floor > then_floor && !undo_then.is_empty() {
11665 let vars: Vec<u32> = undo_then.iter().map(|&(v, _)| v).collect();
11666 let mut hits = vec![false; vars.len()];
11667 mark_read_vars(std::slice::from_ref(&**then_stmt), &vars, &mut hits);
11668 let mut i = 0usize;
11669 undo_then.retain(|_| {
11670 let keep = !hits[i];
11671 i += 1;
11672 keep
11673 });
11674 }
11675 undo_then.sort_unstable_by_key(|&(v, _)| std::cmp::Reverse(v));
11676 for &(v, k) in &undo_then {
11677 rename_gen_back(then_stmt, v, k, vt);
11678 drop_gen_slot(vt, v);
11679 }
11680 if let Some(e) = else_stmt.as_deref_mut() {
11681 let mut undo_else: Vec<(u32, u32)> = g_else
11682 .iter()
11683 .filter(|(&k, &v)| {
11684 v >= else_floor
11685 && g_then.get(&k).copied() == gen.get(&k).copied()
11686 && undo_type_safe(vt, k, v)
11687 })
11688 .map(|(&k, &v)| (v, k))
11689 .collect();
11690 if (vt.vars.len() as u32) > else_floor && !undo_else.is_empty() {
11691 let vars: Vec<u32> = undo_else.iter().map(|&(v, _)| v).collect();
11692 let mut hits = vec![false; vars.len()];
11693 mark_read_vars(std::slice::from_ref(&*e), &vars, &mut hits);
11694 let mut i = 0usize;
11695 undo_else.retain(|_| {
11696 let keep = !hits[i];
11697 i += 1;
11698 keep
11699 });
11700 }
11701 undo_else.sort_unstable_by_key(|&(v, _)| std::cmp::Reverse(v));
11702 for &(v, k) in &undo_else {
11703 rename_gen_back(e, v, k, vt);
11704 drop_gen_slot(vt, v);
11705 }
11706 }
11707 gen.retain(|k, v| g_then.get(k) == Some(v) && g_else.get(k) == Some(v));
11710 }
11711 Stmt::While { cond, body, .. } | Stmt::DoWhile { cond, body, .. } => {
11712 let entry = gen.clone();
11716 for _ in 0..3 {
11717 rewrite_gen_reads(cond, gen, vt);
11718 split_walk_stmt(body, vt, gen, counter, pool);
11719 }
11720 *gen = entry;
11723 }
11724 Stmt::For { init, body, .. } => {
11725 for x in init.iter_mut() {
11726 split_walk_stmt(x, vt, gen, counter, pool);
11727 }
11728 let entry = gen.clone();
11729 for _ in 0..3 {
11730 split_walk_stmt(body, vt, gen, counter, pool);
11731 }
11732 *gen = entry;
11733 }
11734 Stmt::ForEach { body, .. } => {
11735 let entry = gen.clone();
11736 for _ in 0..3 {
11737 split_walk_stmt(body, vt, gen, counter, pool);
11738 }
11739 *gen = entry;
11740 }
11741 Stmt::Switch { cases, default, .. } => {
11742 let entry = gen.clone();
11743 for c in cases.iter_mut() {
11744 let mut g_case = entry.clone();
11745 for x in c.body.iter_mut() {
11746 split_walk_stmt(x, vt, &mut g_case, counter, pool);
11747 }
11748 gen.retain(|k, v| g_case.get(k) == Some(v));
11749 }
11750 if let Some(d) = default {
11751 let mut g_def = entry.clone();
11752 split_walk_stmt(d, vt, &mut g_def, counter, pool);
11753 gen.retain(|k, v| g_def.get(k) == Some(v));
11754 }
11755 }
11756 Stmt::Try {
11757 body,
11758 catches,
11759 finally,
11760 } => {
11761 let entry = gen.clone();
11762 let mut g_try = entry.clone();
11763 split_walk_stmt(body, vt, &mut g_try, counter, pool);
11764 let mut g_all = g_try.clone();
11765 for c in catches.iter_mut() {
11766 let mut g_c = entry.clone();
11769 split_walk_stmt(&mut c.body, vt, &mut g_c, counter, pool);
11770 g_all.retain(|k, v| g_c.get(k) == Some(v));
11771 }
11772 *gen = g_all;
11773 if let Some(f) = finally {
11774 split_walk_stmt(f, vt, gen, counter, pool);
11775 }
11776 }
11777 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => {
11778 split_walk_stmt(body, vt, gen, counter, pool);
11779 }
11780 Stmt::Return(Some(e)) | Stmt::Throw(e) => {
11781 rewrite_gen_reads(e, gen, vt);
11782 }
11783 _ => {}
11784 }
11785}
11786
11787fn fw_super(c: &str) -> Option<&'static str> {
11794 crate::fwdb::super_of(c)
11795}
11796
11797fn exc_subtype(sub: &str, sup: &str, pool: &DexPool) -> bool {
11803 if sub == sup {
11804 return true;
11805 }
11806 if pool.is_subtype(sub, sup) {
11807 return true;
11808 }
11809 let mut cur = sub;
11810 for _ in 0..24 {
11811 let Some(next) = fw_super(cur) else { break };
11812 if next == sup {
11813 return true;
11814 }
11815 cur = next;
11816 }
11817 false
11818}
11819
11820pub fn dedupe_multicatch(s: &mut Stmt, pool: &DexPool) {
11826 dedupe_multicatch_walk(s, pool);
11827}
11828
11829fn dedupe_multicatch_walk(s: &mut Stmt, pool: &DexPool) {
11830 match s {
11831 Stmt::Try {
11832 body,
11833 catches,
11834 finally,
11835 }
11836 | Stmt::TryWithResources {
11837 body,
11838 catches,
11839 finally,
11840 ..
11841 } => {
11842 dedupe_multicatch_walk(body, pool);
11843 for c in catches.iter_mut() {
11844 if c.exc.len() > 1 {
11845 let mut keep: Vec<std::sync::Arc<str>> = Vec::with_capacity(c.exc.len());
11846 for e in c.exc.iter() {
11847 let shadowed = c.exc.iter().any(|o| {
11848 o.as_ref() != e.as_ref() && exc_subtype(e, o, pool)
11849 });
11850 if !shadowed && !keep.iter().any(|k| k.as_ref() == e.as_ref()) {
11851 keep.push(e.clone());
11852 }
11853 }
11854 c.exc = keep;
11855 }
11856 dedupe_multicatch_walk(&mut c.body, pool);
11857 }
11858 if let Some(f) = finally {
11859 dedupe_multicatch_walk(f, pool);
11860 }
11861 }
11862 Stmt::Block(v) => {
11863 for x in v.iter_mut() {
11864 dedupe_multicatch_walk(x, pool);
11865 }
11866 }
11867 Stmt::If {
11868 then_stmt,
11869 else_stmt,
11870 ..
11871 } => {
11872 dedupe_multicatch_walk(then_stmt, pool);
11873 if let Some(e) = else_stmt {
11874 dedupe_multicatch_walk(e, pool);
11875 }
11876 }
11877 Stmt::While { body, .. }
11878 | Stmt::DoWhile { body, .. }
11879 | Stmt::For { body, .. }
11880 | Stmt::ForEach { body, .. }
11881 | Stmt::Labeled { body, .. }
11882 | Stmt::Synchronized { body, .. } => dedupe_multicatch_walk(body, pool),
11883 Stmt::Switch { cases, default, .. } => {
11884 for c in cases.iter_mut() {
11885 for x in c.body.iter_mut() {
11886 dedupe_multicatch_walk(x, pool);
11887 }
11888 }
11889 if let Some(d) = default {
11890 dedupe_multicatch_walk(d, pool);
11891 }
11892 }
11893 _ => {}
11894 }
11895}
11896
11897pub fn resolve_dangling_gotos(s: &mut Stmt) {
11909 let mut labels: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
11910 walk_all(s, &mut |st| {
11913 if let Stmt::Label(id) = st {
11914 labels.insert(*id);
11915 }
11916 });
11917 resolve_gotos_walk(s, &labels, 0);
11918}
11919
11920fn resolve_gotos_walk(s: &mut Stmt, labels: &jdc_core::FxHashSet<u32>, loop_depth: u32) {
11921 let depth = match s {
11922 Stmt::While { .. } | Stmt::DoWhile { .. } | Stmt::For { .. } | Stmt::ForEach { .. } => {
11923 loop_depth + 1
11924 }
11925 _ => loop_depth,
11926 };
11927 match s {
11928 Stmt::Goto(id) => {
11929 if labels.contains(id) {
11930 return; }
11932 if depth > 0 {
11933 *s = Stmt::Break(None);
11934 } else {
11935 *s = Stmt::Comment(format!(
11936 "$DDC: unresolved jump to block {} (irreducible control flow)",
11937 id
11938 ));
11939 }
11940 }
11941 Stmt::Block(v) => {
11942 let mut i = 0usize;
11943 while i < v.len() {
11944 let dangling_here =
11949 depth == 0 && matches!(&v[i], Stmt::Goto(id) if !labels.contains(id));
11950 resolve_gotos_walk(&mut v[i], labels, depth);
11951 if dangling_here {
11952 v.truncate(i + 1);
11953 break;
11954 }
11955 i += 1;
11956 }
11957 }
11958 Stmt::If { then_stmt, else_stmt, .. } => {
11959 resolve_gotos_walk(then_stmt, labels, depth);
11960 if let Some(e) = else_stmt {
11961 resolve_gotos_walk(e, labels, depth);
11962 }
11963 }
11964 Stmt::While { body, .. }
11965 | Stmt::DoWhile { body, .. }
11966 | Stmt::For { body, .. }
11967 | Stmt::ForEach { body, .. }
11968 | Stmt::Labeled { body, .. }
11969 | Stmt::Synchronized { body, .. } => resolve_gotos_walk(body, labels, depth),
11970 Stmt::Try {
11971 body,
11972 catches,
11973 finally,
11974 } => {
11975 resolve_gotos_walk(body, labels, depth);
11976 for c in catches.iter_mut() {
11977 resolve_gotos_walk(&mut c.body, labels, depth);
11978 }
11979 if let Some(f) = finally {
11980 resolve_gotos_walk(f, labels, depth);
11981 }
11982 }
11983 Stmt::Switch { cases, default, .. } => {
11984 for c in cases.iter_mut() {
11985 for x in c.body.iter_mut() {
11986 resolve_gotos_walk(x, labels, depth);
11987 }
11988 }
11989 if let Some(d) = default {
11990 resolve_gotos_walk(d, labels, depth);
11991 }
11992 }
11993 _ => {}
11994 }
11995}
11996
11997fn mark_read_vars(stmts: &[Stmt], vars: &[u32], hits: &mut [bool]) {
12009 let mut remaining = vars.len();
12010 for s in stmts {
12011 if remaining == 0 {
12012 break;
12013 }
12014 visit_stmt_exprs_ro(s, &mut |e| {
12015 if remaining == 0 {
12016 return;
12017 }
12018 visit_exprs_reads(e, &mut |x| {
12019 if remaining == 0 {
12020 return;
12021 }
12022 if let Expr::Local { var, .. } = x {
12023 for (i, v) in vars.iter().enumerate() {
12024 if *v == *var && !hits[i] {
12025 hits[i] = true;
12026 remaining -= 1;
12027 }
12028 }
12029 }
12030 });
12031 });
12032 }
12033}
12034
12035fn stmts_read_var(stmts: &[Stmt], v: u32) -> usize {
12036 let mut n = 0usize;
12037 for s in stmts {
12038 visit_stmt_exprs_ro(s, &mut |e| {
12039 visit_exprs_reads(e, &mut |x| {
12040 if let Expr::Local { var: vv, .. } = x {
12041 if *vv == v {
12042 n += 1;
12043 }
12044 }
12045 });
12046 });
12047 }
12048 n
12049}
12050
12051pub fn fix_ctor_conditional_super(body: &mut Stmt) {
12052 if !matches!(body, Stmt::Block(_)) {
12055 let inner = std::mem::replace(body, Stmt::Block(Vec::new()));
12056 *body = Stmt::Block(vec![inner]);
12057 }
12058 let Stmt::Block(stmts) = body else { return };
12059 while stmts.len() == 1 {
12063 let Stmt::Block(inner) = &stmts[0] else { break };
12064 *stmts = inner.clone();
12065 }
12066 if stmts.is_empty() {
12067 return;
12068 }
12069 if is_bare_ctor_call(stmts.first().unwrap()) {
12070 dedupe_ctor_delegations(body);
12071 return;
12072 }
12073 if try_linear_super_inline(stmts) {
12074 return;
12075 }
12076 try_merge_branched_super(stmts);
12077}
12078
12079pub fn strip_secondary_delegations(body: &mut Stmt) {
12103 let Stmt::Block(stmts) = body else { return };
12104 if !stmts.first().is_some_and(is_bare_ctor_call) {
12105 return;
12106 }
12107 fn strip_in(s: &mut Stmt) {
12108 if is_bare_ctor_call(s) {
12109 *s = Stmt::Block(Vec::new());
12110 return;
12111 }
12112 match s {
12113 Stmt::Block(v) => v.iter_mut().for_each(strip_in),
12114 Stmt::If {
12115 then_stmt,
12116 else_stmt,
12117 ..
12118 } => {
12119 strip_in(then_stmt);
12120 if let Some(e) = else_stmt {
12121 strip_in(e);
12122 }
12123 }
12124 Stmt::While { body, .. }
12125 | Stmt::DoWhile { body, .. }
12126 | Stmt::ForEach { body, .. }
12127 | Stmt::Synchronized { body, .. }
12128 | Stmt::Labeled { body, .. } => strip_in(body),
12129 Stmt::For { init, body, .. } => {
12130 init.iter_mut().for_each(strip_in);
12131 strip_in(body);
12132 }
12133 Stmt::Switch { cases, default, .. } => {
12134 for c in cases.iter_mut() {
12135 c.body.iter_mut().for_each(strip_in);
12136 }
12137 if let Some(d) = default {
12138 strip_in(d);
12139 }
12140 }
12141 Stmt::Try {
12142 body,
12143 catches,
12144 finally,
12145 }
12146 | Stmt::TryWithResources {
12147 body,
12148 catches,
12149 finally,
12150 ..
12151 } => {
12152 strip_in(body);
12153 for c in catches.iter_mut() {
12154 strip_in(&mut c.body);
12155 }
12156 if let Some(f) = finally {
12157 strip_in(f);
12158 }
12159 }
12160 _ => {}
12161 }
12162 }
12163 for st in stmts.iter_mut().skip(1) {
12164 strip_in(st);
12165 }
12166}
12167
12168pub fn dedupe_ctor_delegations(body: &mut Stmt) {
12169 let Stmt::Block(stmts) = body else { return };
12170 let Some(d0) = stmts.first().and_then(|s| match s {
12171 Stmt::ExprStmt(e) if is_delegation_expr(e) => Some(e.clone()),
12172 _ => None,
12173 }) else {
12174 return;
12175 };
12176 fn rec(s: &mut Stmt, d0: &Expr) {
12177 match s {
12178 Stmt::Block(v) => {
12179 v.retain(|x| {
12180 !matches!(x, Stmt::ExprStmt(e) if is_delegation_expr(e) && same_delegation(e, d0))
12181 });
12182 for x in v.iter_mut() {
12183 rec(x, d0);
12184 }
12185 }
12186 Stmt::If { then_stmt, else_stmt, .. } => {
12187 rec(then_stmt, d0);
12188 if let Some(e) = else_stmt {
12189 rec(e, d0);
12190 }
12191 }
12192 _ => {}
12193 }
12194 }
12195 {
12199 let d0r = &d0;
12200 let mut idx = 0usize;
12201 stmts.retain(|s| {
12202 idx += 1;
12203 idx == 1
12204 || !(matches!(s, Stmt::ExprStmt(e)
12205 if is_delegation_expr(e) && same_delegation(e, d0r)))
12206 });
12207 }
12208 for s in stmts.iter_mut().skip(1) {
12209 rec(s, &d0);
12210 }
12211}
12212
12213fn same_delegation(a: &Expr, b: &Expr) -> bool {
12217 match (a, b) {
12218 (
12219 Expr::Method { name: n1, cls: c1, is_super: s1, args: a1, .. },
12220 Expr::Method { name: n2, cls: c2, is_super: s2, args: a2, .. },
12221 ) => {
12222 &**n1 == "<init>"
12223 && &**n2 == "<init>"
12224 && c1 == c2
12225 && s1 == s2
12226 && (a1 == a2 || (a1.is_empty() && a2.is_empty()))
12227 }
12228 _ => false,
12229 }
12230}
12231
12232fn ctor_call_expr(s: &Stmt) -> Option<&Expr> {
12234 match s {
12235 Stmt::ExprStmt(e) if is_delegation_expr(e) => Some(e),
12236 _ => None,
12237 }
12238}
12239
12240fn same_ctor_kind(a: &Expr, b: &Expr) -> bool {
12242 match (a, b) {
12243 (
12244 Expr::Method { cls: c1, is_super: s1, owner: o1, .. },
12245 Expr::Method { cls: c2, is_super: s2, owner: o2, .. },
12246 ) => c1 == c2 && s1 == s2 && o1 == o2,
12247 _ => false,
12248 }
12249}
12250
12251fn flat_list(s: &Stmt) -> Vec<&Stmt> {
12256 fn rec<'a>(s: &'a Stmt, out: &mut Vec<&'a Stmt>) {
12257 match s {
12258 Stmt::Block(v) => {
12259 for x in v {
12260 rec(x, out);
12261 }
12262 }
12263 other => out.push(other),
12264 }
12265 }
12266 let mut out = Vec::new();
12267 rec(s, &mut out);
12268 out
12269}
12270
12271fn strip_lost_alloc_owners(e: &mut Expr) {
12277 deep_rewrite(e, &mut |x| {
12278 if let Expr::Method { name, is_special: true, owner: Some(o), .. } = x {
12279 if &**name == "<init>" && !matches!(**o, Expr::This) {
12280 **o = Expr::Const(jdc_core::ir::expr::ConstVal::Int(0));
12281 }
12282 }
12283 });
12284}
12285
12286fn count_locals_expr(e: &Expr) -> jdc_core::FxHashMap<u32, usize> {
12289 let mut m = jdc_core::FxHashMap::default();
12290 count_expr_ro(e, &mut m);
12291 m
12292}
12293
12294pub(crate) fn count_locals_stmts(ss: &[Stmt]) -> jdc_core::FxHashMap<u32, usize> {
12295 let mut m = jdc_core::FxHashMap::default();
12296 for s in ss {
12297 visit_stmt_exprs_ro(s, &mut |e| count_expr_ro(e, &mut m));
12298 }
12299 m
12300}
12301
12302fn cnt(m: &jdc_core::FxHashMap<u32, usize>, v: u32) -> usize {
12303 m.get(&v).copied().unwrap_or(0)
12304}
12305
12306fn inline_locals(
12310 e: &Expr,
12311 map: &std::collections::HashMap<u32, Expr>,
12312 depth: u32,
12313) -> Option<Expr> {
12314 if depth > 4 {
12315 return None;
12316 }
12317 let mut out = e.clone();
12318 let mut fail = false;
12319 deep_rewrite(&mut out, &mut |x| {
12320 if let Expr::Local { var, .. } = x {
12321 if let Some(def) = map.get(var) {
12322 match inline_locals(def, map, depth + 1) {
12323 Some(r) => *x = r,
12324 None => fail = true,
12325 }
12326 }
12327 }
12328 });
12329 if fail {
12330 None
12331 } else {
12332 Some(out)
12333 }
12334}
12335
12336fn def_of(s: &Stmt) -> Option<(u32, &Expr)> {
12338 match s {
12339 Stmt::LocalDef { var, init: Some(e), .. } => Some((*var, e)),
12340 Stmt::ExprStmt(Expr::Assign {
12341 target,
12342 value,
12343 op: jdc_core::ir::expr::AssignOp::Plain,
12344 ..
12345 }) => match &**target {
12346 Expr::Local { var, .. } => Some((*var, value)),
12347 _ => None,
12348 },
12349 _ => None,
12350 }
12351}
12352
12353fn try_linear_super_inline(stmts: &mut Vec<Stmt>) -> bool {
12355 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
12356 return false;
12357 };
12358 if pos == 0 {
12359 return false; }
12361 if !stmts[..pos]
12363 .iter()
12364 .all(|s| matches!(s, Stmt::LocalDef { .. } | Stmt::ExprStmt(_)))
12365 {
12366 return false;
12367 }
12368 let Some(call) = ctor_call_expr(&stmts[pos]) else {
12369 return false;
12370 };
12371 let Expr::Method { args, .. } = call else {
12372 return false;
12373 };
12374 let mut map: std::collections::HashMap<u32, Expr> =
12376 std::collections::HashMap::new();
12377 let mut def_idx: jdc_core::FxHashMap<u32, usize> =
12378 jdc_core::FxHashMap::default();
12379 for (i, s) in stmts[..pos].iter().enumerate() {
12380 if let Some((v, e)) = def_of(s) {
12381 map.insert(v, e.clone());
12382 def_idx.insert(v, i);
12383 }
12384 }
12385 let mut needed: Vec<u32> = Vec::new();
12387 for a in args {
12388 let mut probe = a.clone();
12389 strip_lost_alloc_owners(&mut probe);
12390 deep_rewrite(&mut probe, &mut |x| {
12391 if let Expr::Local { var, .. } = x {
12392 if map.contains_key(var) && !needed.contains(var) {
12393 needed.push(*var);
12394 }
12395 }
12396 });
12397 }
12398 if needed.is_empty() {
12399 return false; }
12401 let mut consumed: Vec<usize> = Vec::new();
12404 let use_counts = count_locals_stmts(stmts);
12405 for &v in &needed {
12406 let def = &map[&v];
12407 let uses = cnt(&use_counts, v);
12408 if jdc_core::ir::build::has_side_effects(def) && uses != 1 {
12409 return false;
12410 }
12411 if uses == 1 {
12412 consumed.push(def_idx[&v]);
12413 }
12414 }
12415 let mut new_args: Vec<Expr> = Vec::with_capacity(args.len());
12417 for a in args {
12418 match inline_locals(a, &map, 0) {
12419 Some(r) => new_args.push(r),
12420 None => return false,
12421 }
12422 }
12423 let mut call_stmt = stmts.remove(pos);
12426 if let Stmt::ExprStmt(Expr::Method { args: slot, .. }) = &mut call_stmt {
12427 *slot = new_args;
12428 }
12429 let mut rest: Vec<Stmt> = Vec::with_capacity(stmts.len() + 1);
12430 rest.push(call_stmt);
12431 for (i, s) in stmts.drain(..).enumerate() {
12432 let orig = if i >= pos { i + 1 } else { i };
12435 if !consumed.contains(&orig) {
12436 rest.push(s);
12437 }
12438 }
12439 *stmts = rest;
12440 true
12441}
12442
12443fn try_merge_branched_super(stmts: &mut Vec<Stmt>) -> bool {
12445 let if_positions: Vec<usize> = stmts
12449 .iter()
12450 .enumerate()
12451 .filter(|(_, s)| matches!(s, Stmt::If { .. }))
12452 .map(|(i, _)| i)
12453 .collect();
12454 for if_pos in if_positions {
12455 let gate = match &stmts[if_pos] {
12461 Stmt::If { then_stmt, else_stmt, .. } => {
12462 contains_delegation(then_stmt)
12463 || else_stmt.as_ref().is_some_and(|e| contains_delegation(e))
12464 }
12465 _ => false,
12466 };
12467 if gate && merge_at(stmts, if_pos) {
12468 return true;
12469 }
12470 }
12471 false
12472}
12473
12474fn contains_delegation(s: &Stmt) -> bool {
12477 match s {
12478 Stmt::ExprStmt(e) => is_delegation_expr(e),
12479 Stmt::Block(v) => v.iter().any(contains_delegation),
12480 Stmt::If { then_stmt, else_stmt, .. } => {
12481 contains_delegation(then_stmt)
12482 || else_stmt.as_ref().is_some_and(|e| contains_delegation(e))
12483 }
12484 _ => false,
12485 }
12486}
12487
12488
12489
12490fn merge_at(stmts: &mut Vec<Stmt>, if_pos: usize) -> bool {
12503 enum Br {
12508 Call(usize, Vec<(u32, Expr)>, Vec<usize>),
12510 Clean,
12511 Dirty,
12512 }
12513 fn split_branch(list: &[&Stmt]) -> Br {
12514 let call_pos = list.iter().position(|s| ctor_call_expr(s).is_some());
12515 let Some(c) = call_pos else {
12516 return if list.iter().any(|s| contains_delegation(s)) {
12519 Br::Dirty
12520 } else {
12521 Br::Clean
12522 };
12523 };
12524 if list.iter().skip(c + 1).any(|s| ctor_call_expr(s).is_some()) {
12525 return Br::Dirty; }
12527 let mut defs: Vec<(u32, Expr)> = Vec::new();
12528 let mut def_idxs: Vec<usize> = Vec::new();
12529 for (i, s) in list.iter().enumerate().take(c) {
12530 match def_of(s) {
12531 Some((v, e)) => {
12532 defs.push((v, e.clone()));
12533 def_idxs.push(i);
12534 }
12535 None => match s {
12536 Stmt::ExprStmt(_) | Stmt::LocalDef { .. } => {}
12539 _ => return Br::Dirty, },
12541 }
12542 }
12543 Br::Call(c, defs, def_idxs)
12544 }
12545 fn cond_def_of(s: &Stmt, scope: &[Stmt]) -> Option<(u32, Expr)> {
12547 let Stmt::If { cond, then_stmt, else_stmt: Some(e), .. } = s else {
12548 return None;
12549 };
12550 fn one<'x>(b: &'x Stmt, scope: &[Stmt]) -> Option<(u32, &'x Expr)> {
12551 let l = flat_list(b);
12552 if l.len() == 1 {
12553 return def_of(l[0]);
12554 }
12555 if l.len() == 2 {
12564 let (w, ex) = def_of(l[0])?;
12565 let Stmt::ExprStmt(Expr::Assign {
12566 target,
12567 op: AssignOp::Plain,
12568 value,
12569 }) = l[1]
12570 else {
12571 return None;
12572 };
12573 let Expr::Local { var: tv, .. } = &**target else {
12574 return None;
12575 };
12576 let Expr::Local { var: rv, .. } = &**value else {
12577 return None;
12578 };
12579 if *rv != w || *tv == w {
12580 return None;
12581 }
12582 let mut ldef_writes = 0usize;
12586 for st in scope {
12587 walk_all(st, &mut |x| {
12588 if let Stmt::LocalDef {
12589 var,
12590 init: Some(_),
12591 ..
12592 } = x
12593 {
12594 if *var == w {
12595 ldef_writes += 1;
12596 }
12597 }
12598 });
12599 }
12600 if stmts_read_var(scope, w) != 1
12601 || stmts_count_writes(scope, w) + ldef_writes != 1
12602 {
12603 return None;
12604 }
12605 return Some((*tv, ex));
12606 }
12607 None
12608 }
12609 let (v1, e1) = one(then_stmt, scope)?;
12610 let (v2, e2) = one(e, scope)?;
12611 if v1 != v2 {
12612 return None;
12613 }
12614 Some((
12615 v1,
12616 Expr::Cond {
12617 c: Box::new(cond.clone()),
12618 t: Box::new(e1.clone()),
12619 f: Box::new(e2.clone()),
12620 },
12621 ))
12622 }
12623
12624 let mut pmap: std::collections::HashMap<u32, Expr> =
12626 std::collections::HashMap::new();
12627 let mut pidx: jdc_core::FxHashMap<u32, usize> =
12628 jdc_core::FxHashMap::default();
12629 for (i, s) in stmts[..if_pos].iter().enumerate() {
12630 match s {
12631 Stmt::LocalDef { .. } | Stmt::ExprStmt(_) => {
12632 if let Some((v, e)) = def_of(s) {
12633 pmap.insert(v, e.clone());
12634 pidx.insert(v, i);
12635 }
12636 }
12637 Stmt::If { .. } => match cond_def_of(s, stmts) {
12638 Some((v, e)) => {
12639 pmap.insert(v, e);
12640 pidx.insert(v, i);
12641 }
12642 None => return false,
12643 },
12644 _ => return false,
12645 }
12646 }
12647 let (cond, then_v, else_v, had_else) = match &stmts[if_pos] {
12648 Stmt::If { cond, then_stmt, else_stmt, .. } => (
12649 cond.clone(),
12650 then_stmt.as_ref().clone(),
12651 else_stmt.as_ref().map_or_else(|| Stmt::Block(Vec::new()), |e| e.as_ref().clone()),
12652 else_stmt.is_some(),
12653 ),
12654 _ => return false,
12655 };
12656 fn normalize_branch(b: &mut Stmt) {
12662 let slot: &mut Stmt = match b {
12667 Stmt::Block(v) if v.len() == 1 => &mut v[0],
12668 other => other,
12669 };
12670 if !matches!(slot, Stmt::If { .. }) || !contains_delegation(slot) {
12671 return;
12672 }
12673 let taken = std::mem::replace(slot, Stmt::Block(Vec::new()));
12674 let mut tmp = vec![taken];
12675 if merge_at(&mut tmp, 0) {
12676 *slot = Stmt::Block(tmp);
12677 } else {
12678 *slot = tmp.pop().unwrap();
12679 }
12680 }
12681 let mut then_v = then_v;
12682 let mut else_v = else_v;
12683 normalize_branch(&mut then_v);
12684 normalize_branch(&mut else_v);
12685 let then_list = flat_list(&then_v);
12686 let else_list = flat_list(&else_v);
12687
12688 enum Side {
12691 Call {
12692 at: usize,
12693 defs: Vec<(u32, Expr)>,
12694 def_idxs: Vec<usize>,
12695 },
12696 Clean,
12697 }
12698 let (t_side, e_side, two_sided) = match (split_branch(&then_list), split_branch(&else_list)) {
12699 (Br::Call(tc, td, ti), Br::Call(ec, ed, ei)) => (
12700 Side::Call { at: tc, defs: td, def_idxs: ti },
12701 Side::Call { at: ec, defs: ed, def_idxs: ei },
12702 true,
12703 ),
12704 (Br::Call(tc, td, ti), Br::Clean) => (
12705 Side::Call { at: tc, defs: td, def_idxs: ti },
12706 Side::Clean,
12707 false,
12708 ),
12709 (Br::Clean, Br::Call(ec, ed, ei)) => (
12710 Side::Clean,
12711 Side::Call { at: ec, defs: ed, def_idxs: ei },
12712 false,
12713 ),
12714 _ => return false,
12715 };
12716 let (t_call, e_call) = match (&t_side, &e_side) {
12718 (Side::Call { at: ta, .. }, Side::Call { at: ea, .. }) => (
12719 ctor_call_expr(then_list[*ta]),
12720 ctor_call_expr(else_list[*ea]),
12721 ),
12722 (Side::Call { at: ta, .. }, Side::Clean) => {
12723 (ctor_call_expr(then_list[*ta]), None)
12724 }
12725 (Side::Clean, Side::Call { at: ea, .. }) => {
12726 (None, ctor_call_expr(else_list[*ea]))
12727 }
12728 _ => return false,
12729 };
12730 let (call_list, call_at) = match &t_side {
12732 Side::Call { at, .. } => (&then_list, *at),
12733 Side::Clean => match &e_side {
12734 Side::Call { at, .. } => (&else_list, *at),
12735 Side::Clean => return false,
12736 },
12737 };
12738 let (base_args, other_args) = if two_sided {
12739 let (Some(tc), Some(ec)) = (t_call, e_call) else {
12740 return false;
12741 };
12742 if !same_ctor_kind(tc, ec) {
12743 return false;
12744 }
12745 let (Expr::Method { args: a1, .. }, Expr::Method { args: a2, .. }) = (tc, ec) else {
12746 return false;
12747 };
12748 if a1.len() != a2.len() {
12749 return false;
12750 }
12751 (a1, Some(a2))
12752 } else {
12753 let c = t_call.or(e_call);
12754 let Some(c) = c else { return false };
12755 let Expr::Method { args, .. } = c else {
12756 return false;
12757 };
12758 (args, None)
12759 };
12760
12761 let mut tmap: std::collections::HashMap<u32, Expr> = pmap.clone();
12763 let mut emap: std::collections::HashMap<u32, Expr> = pmap.clone();
12764 if let Side::Call { defs, .. } = &t_side {
12765 for (v, e) in defs {
12766 tmap.insert(*v, e.clone());
12767 }
12768 }
12769 if let Side::Call { defs, .. } = &e_side {
12770 for (v, e) in defs {
12771 emap.insert(*v, e.clone());
12772 }
12773 }
12774 let Some(cond_i) = inline_locals(&cond, &pmap, 0) else {
12775 return false;
12776 };
12777 let mut then_args: Vec<Expr> = Vec::with_capacity(base_args.len());
12779 let mut else_args: Vec<Expr> = Vec::new();
12780 if two_sided {
12781 for a in base_args {
12782 match inline_locals(a, &tmap, 0) {
12783 Some(r) => then_args.push(r),
12784 None => return false,
12785 }
12786 }
12787 for a in other_args.unwrap() {
12788 match inline_locals(a, &emap, 0) {
12789 Some(r) => else_args.push(r),
12790 None => return false,
12791 }
12792 }
12793 } else {
12794 let from_then = matches!(&t_side, Side::Call { .. });
12797 for a in base_args {
12798 let r = if from_then {
12799 inline_locals(a, &tmap, 0)
12800 } else {
12801 inline_locals(a, &emap, 0)
12802 };
12803 match r {
12804 Some(r) => then_args.push(r),
12805 None => return false,
12806 }
12807 }
12808 }
12809 let primary_args = base_args;
12810 let mut merged: Vec<Expr> = Vec::with_capacity(then_args.len());
12812 let mut differ = 0usize;
12813 for (j, t) in then_args.iter().enumerate() {
12814 if two_sided {
12815 let e = &else_args[j];
12816 if t == e {
12817 merged.push(t.clone());
12818 } else {
12819 differ += 1;
12820 merged.push(Expr::Cond {
12821 c: Box::new(cond_i.clone()),
12822 t: Box::new(t.clone()),
12823 f: Box::new(e.clone()),
12824 });
12825 }
12826 } else {
12827 merged.push(t.clone());
12828 }
12829 }
12830
12831 let vars_in = |e: &Expr| -> Vec<u32> {
12833 let mut vs = Vec::new();
12834 let mut probe = e.clone();
12835 strip_lost_alloc_owners(&mut probe);
12836 deep_rewrite(&mut probe, &mut |x| {
12837 if let Expr::Local { var, .. } = x {
12838 if !vs.contains(var) {
12839 vs.push(*var);
12840 }
12841 }
12842 });
12843 vs
12844 };
12845 let branch_remainder = |list: &[&Stmt], side: &Side| -> Vec<Stmt> {
12853 match side {
12854 Side::Clean => list.iter().map(|s| (*s).clone()).collect(),
12855 Side::Call { at, def_idxs, .. } => {
12856 list.iter()
12857 .enumerate()
12858 .filter(|(i, _)| *i != *at && !def_idxs.contains(i))
12859 .map(|(_, s)| (*s).clone())
12860 .collect()
12861 }
12862 }
12863 };
12864 let tail_then_v = branch_remainder(&then_list, &t_side);
12865 let tail_else_v = branch_remainder(&else_list, &e_side);
12866 let post = &stmts[if_pos + 1..];
12867 let mut inlined: Vec<u32> = vars_in(&cond);
12868 for a in primary_args.iter().chain(else_args.iter()) {
12869 for v in vars_in(a) {
12870 if !inlined.contains(&v) {
12871 inlined.push(v);
12872 }
12873 }
12874 }
12875 inlined.retain(|v| pmap.contains_key(v) || tmap.contains_key(v) || emap.contains_key(v));
12876 for &v in &inlined {
12878 let in_branch = match (&t_side, &e_side) {
12879 (Side::Call { defs: td, .. }, Side::Call { defs: ed, .. }) => {
12880 td.iter().any(|(d, _)| *d == v) || ed.iter().any(|(d, _)| *d == v)
12881 }
12882 (Side::Call { defs, .. }, Side::Clean)
12883 | (Side::Clean, Side::Call { defs, .. }) => {
12884 defs.iter().any(|(d, _)| *d == v)
12885 }
12886 _ => false,
12887 };
12888 if pmap.contains_key(&v) && in_branch {
12889 return false;
12890 }
12891 }
12892 type Counts = jdc_core::FxHashMap<u32, usize>;
12893 type GuardCounts = (
12894 Counts,
12895 Vec<Counts>,
12896 Vec<Counts>,
12897 Counts,
12898 Counts,
12899 Counts,
12900 Vec<(u32, Counts)>,
12901 );
12902 let (cond_counts, a1_counts, a2_counts, tail1_counts, tail2_counts, post_counts, def_counts): GuardCounts =
12903 if inlined.is_empty() {
12904 Default::default()
12905 } else {
12906 let all_defs: Vec<(u32, &Expr)> = pmap
12907 .iter()
12908 .map(|(k, v)| (*k, v))
12909 .chain(match &t_side {
12910 Side::Call { defs, .. } => defs.iter().map(|(k, v)| (*k, v)).collect(),
12911 Side::Clean => Vec::new(),
12912 })
12913 .chain(match &e_side {
12914 Side::Call { defs, .. } => defs.iter().map(|(k, v)| (*k, v)).collect(),
12915 Side::Clean => Vec::new(),
12916 })
12917 .collect();
12918 (
12919 count_locals_expr(&cond),
12920 primary_args.iter().map(count_locals_expr).collect(),
12921 else_args.iter().map(count_locals_expr).collect(),
12922 count_locals_stmts(&tail_then_v),
12923 count_locals_stmts(&tail_else_v),
12924 count_locals_stmts(post),
12925 all_defs.into_iter().map(|(v, e)| (v, count_locals_expr(e))).collect(),
12926 )
12927 };
12928 for &v in &inlined {
12929 let in_t = matches!(&t_side, Side::Call { defs, .. } if defs.iter().any(|(d, _)| *d == v));
12930 let in_e = matches!(&e_side, Side::Call { defs, .. } if defs.iter().any(|(d, _)| *d == v));
12931 let effectful = [
12932 in_t.then(|| tmap.get(&v)).flatten(),
12933 in_e.then(|| emap.get(&v)).flatten(),
12934 pmap.get(&v),
12935 ]
12936 .into_iter()
12937 .flatten()
12938 .any(jdc_core::ir::build::has_side_effects);
12939 if !effectful {
12940 continue;
12941 }
12942 let u_cond = cnt(&cond_counts, v);
12943 let u_post = cnt(&post_counts, v);
12944 let u_t1 = cnt(&tail1_counts, v);
12945 let u_t2 = cnt(&tail2_counts, v);
12946 let mut u_odef = 0usize;
12947 for (dv, dc) in &def_counts {
12948 if *dv != v && inlined.contains(dv) {
12949 u_odef += cnt(dc, v);
12950 }
12951 }
12952 let mut emit = u_cond * (1 + differ);
12953 let mut uses_args = 0usize;
12954 for j in 0..primary_args.len() {
12955 let c1 = cnt(&a1_counts[j], v);
12956 let c2 = if two_sided && j < a2_counts.len() {
12957 cnt(&a2_counts[j], v)
12958 } else {
12959 0
12960 };
12961 uses_args += c1 + c2;
12962 emit += if two_sided && then_args[j] != else_args[j] {
12963 c1 + c2
12964 } else {
12965 c1.max(c2)
12966 };
12967 }
12968 let ok = if in_t || in_e {
12969 u_cond == 0
12970 && u_post == 0
12971 && u_t1 == 0
12972 && u_t2 == 0
12973 && u_odef == 0
12974 && a1_counts.iter().map(|c| cnt(c, v)).sum::<usize>() <= 1
12975 && a2_counts.iter().map(|c| cnt(c, v)).sum::<usize>() <= 1
12976 && emit <= 2
12977 && (in_t || a1_counts.iter().all(|c| cnt(c, v) == 0))
12978 && (in_e || a2_counts.iter().all(|c| cnt(c, v) == 0))
12979 } else {
12980 u_post == 0
12981 && u_t1 == 0
12982 && u_t2 == 0
12983 && u_odef == 0
12984 && ((u_cond == 1 && uses_args == 0 && emit <= 2)
12985 || (u_cond == 0 && emit <= 1))
12986 };
12987 if !ok {
12988 return false;
12989 }
12990 }
12991
12992 let mut call_stmt = call_list[call_at].clone();
12994 if let Stmt::ExprStmt(Expr::Method { args: slot, .. }) = &mut call_stmt {
12995 *slot = merged;
12996 }
12997 let residual = |v: u32| -> usize {
12998 cnt(&tail1_counts, v) + cnt(&tail2_counts, v) + cnt(&post_counts, v)
12999 };
13000 let mut kept_prelude: Vec<Stmt> = Vec::new();
13001 for (i, s) in stmts[..if_pos].iter().enumerate() {
13002 let consumed = match s {
13003 Stmt::LocalDef { var, .. } => Some(*var),
13004 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
13005 Expr::Local { var, .. } => Some(*var),
13006 _ => None,
13007 },
13008 Stmt::If { .. } => cond_def_of(s, stmts).map(|(v, _)| v),
13009 _ => None,
13010 };
13011 if let Some(v) = consumed {
13012 if inlined.contains(&v) && pidx.get(&v) == Some(&i) && residual(v) == 0 {
13013 continue;
13014 }
13015 }
13016 kept_prelude.push(s.clone());
13017 }
13018 let build_keep = |list: &[&Stmt], side: &Side| -> Vec<Stmt> {
13019 match side {
13020 Side::Clean => list.iter().map(|s| (*s).clone()).collect(),
13021 Side::Call { at, .. } => {
13022 let mut out = Vec::new();
13023 for (i, s) in list.iter().enumerate() {
13024 if i == *at {
13025 continue;
13026 }
13027 if i < *at {
13028 if let Some((v, _)) = def_of(s) {
13029 if inlined.contains(&v) && residual(v) == 0 {
13030 continue;
13031 }
13032 }
13033 }
13034 out.push((*s).clone());
13035 }
13036 out
13037 }
13038 }
13039 };
13040 let then_keep = build_keep(&then_list, &t_side);
13041 let else_keep = build_keep(&else_list, &e_side);
13042 let else_part = if else_keep.is_empty() && !had_else {
13043 None
13044 } else {
13045 Some(Box::new(Stmt::Block(else_keep)))
13046 };
13047 let new_if = Stmt::If {
13048 cond: cond_i,
13049 then_stmt: Box::new(Stmt::Block(then_keep)),
13050 else_stmt: else_part,
13051 };
13052 let if_empty = match &new_if {
13053 Stmt::If { then_stmt, else_stmt, .. } => {
13054 let be = |s: &Stmt| matches!(s, Stmt::Block(v) if v.is_empty());
13055 be(then_stmt) && else_stmt.as_ref().is_none_or(|e| be(e))
13056 }
13057 _ => false,
13058 };
13059 let mut out: Vec<Stmt> = Vec::with_capacity(stmts.len());
13060 out.push(call_stmt);
13061 out.extend(kept_prelude);
13062 if !if_empty {
13063 out.push(new_if);
13064 }
13065 out.extend(stmts[if_pos + 1..].to_vec());
13066 *stmts = out;
13067 true
13068}
13069
13070
13071pub fn strip_enum_ctor_super(body: &mut Stmt) {
13072 fn is_enum_super(s: &Stmt) -> bool {
13073 matches!(
13074 s,
13075 Stmt::ExprStmt(Expr::Method { name, is_super: true, .. }) if &**name == "<init>"
13076 )
13077 }
13078 fn strip_deep(stmts: &mut Vec<Stmt>) {
13088 stmts.retain(|s| !is_enum_super(s));
13089 for st in stmts.iter_mut() {
13090 strip_one(st);
13091 }
13092 }
13093 fn strip_one(st: &mut Stmt) {
13094 match st {
13095 Stmt::Block(v) => strip_deep(v),
13096 Stmt::If { then_stmt, else_stmt, .. } => {
13097 strip_one(then_stmt);
13098 if let Some(e) = else_stmt {
13099 strip_one(e);
13100 }
13101 }
13102 Stmt::While { body, .. }
13103 | Stmt::DoWhile { body, .. }
13104 | Stmt::ForEach { body, .. }
13105 | Stmt::Synchronized { body, .. }
13106 | Stmt::Labeled { body, .. } => strip_one(body),
13107 Stmt::For { init, body, .. } => {
13108 strip_deep(init);
13109 strip_one(body);
13110 }
13111 Stmt::Switch { cases, default, .. } => {
13112 for c in cases.iter_mut() {
13113 strip_deep(&mut c.body);
13114 }
13115 if let Some(d) = default {
13116 strip_one(d);
13117 }
13118 }
13119 Stmt::Try { body, catches, finally }
13120 | Stmt::TryWithResources { body, catches, finally, .. } => {
13121 strip_one(body);
13122 for c in catches.iter_mut() {
13123 strip_one(&mut c.body);
13124 }
13125 if let Some(f) = finally {
13126 strip_one(f);
13127 }
13128 }
13129 _ => {}
13130 }
13131 }
13132 let Stmt::Block(stmts) = body else {
13133 return;
13134 };
13135 strip_deep(stmts);
13136}
13137
13138pub fn ensure_declared(body: &mut Stmt, vt: &VarTable) {
13139 let n = vt.vars.len().max(1);
13143
13144 let mut seen: HashSet<u32> = HashSet::default();
13147 demote_dup_decls(body, &mut seen, vt);
13148
13149 let mut def_block: Vec<u32> = vec![u32::MAX; n];
13157 let mut sizes: Vec<u32> = vec![0];
13158 let mut next_id: u32 = 1;
13159 scope_pass_a(body, 0, &mut next_id, &mut def_block, &mut sizes);
13160 sizes[0] = next_id;
13161 let mut within: Vec<u32> = vec![0; n];
13162 let mut total: Vec<u32> = vec![0; n];
13163 let mut next_b: u32 = 1;
13164 scope_pass_b(body, 0, &mut next_b, &def_block, &sizes, &mut within, &mut total);
13165 let mut hoist: HashSet<u32> = HashSet::default();
13166 for v in 0..n {
13167 if def_block[v] != u32::MAX && def_block[v] != 0 && within[v] < total[v] {
13168 hoist.insert(v as u32);
13169 }
13170 }
13171 if !hoist.is_empty() {
13172 let mut seed: HashSet<u32> = hoist.iter().copied().collect();
13173 demote_dup_decls(body, &mut seed, vt);
13174 }
13175
13176 let mut declared: Vec<bool> = vec![false; n];
13177 let mut is_param: Vec<bool> = vec![false; n];
13178 for v in &vt.vars {
13179 if v.is_param && (v.id as usize) < n {
13180 is_param[v.id as usize] = true;
13181 }
13182 }
13183 let mut is_catch: Vec<bool> = vec![false; n];
13189 walk_all(body, &mut |st| {
13190 if let Stmt::Try { catches, .. } = st {
13191 for c in catches {
13192 if (c.var as usize) < n {
13193 is_catch[c.var as usize] = true;
13194 }
13195 }
13196 }
13197 });
13198 walk_all(body, &mut |st| {
13199 if let Stmt::LocalDef { var, .. } = st {
13200 if (*var as usize) < n {
13201 declared[*var as usize] = true;
13202 }
13203 }
13204 });
13205 let mut used: HashSet<u32> = HashSet::default();
13206 stmt_collect_vars(body, &mut used, true);
13210
13211 let mut needs_decl: Vec<u32> = used
13214 .into_iter()
13215 .filter(|v| {
13216 let i = *v as usize;
13217 i >= n
13218 || ((!declared[i] && !is_param[i] && !is_catch[i]) || hoist.contains(v))
13219 })
13220 .collect();
13221 needs_decl.sort_unstable();
13222 needs_decl.dedup();
13223
13224 if !needs_decl.is_empty() {
13225 let mut decls: Vec<Stmt> = needs_decl
13226 .into_iter()
13227 .map(|v| Stmt::LocalDef {
13228 var: v,
13229 init: None,
13230 is_final: false,
13231 force_type: true,
13232 })
13233 .collect();
13234 decls.reverse();
13235 match body {
13236 Stmt::Block(v) => {
13237 for d in decls {
13238 v.insert(0, d);
13239 }
13240 }
13241 other => {
13242 let inner = std::mem::replace(other, Stmt::Block(vec![]));
13243 *other = Stmt::Block({
13244 let mut vs = decls;
13245 vs.push(inner);
13246 vs
13247 });
13248 }
13249 }
13250 }
13251}
13252
13253fn demote_dup_decls(s: &mut Stmt, seen: &mut HashSet<u32>, vt: &VarTable) {
13254 match s {
13255 Stmt::Block(v) => {
13258 for x in v.iter_mut() {
13259 demote_dup_decls(x, seen, vt);
13260 }
13261 }
13262 Stmt::LocalDef { var, init, .. } => {
13263 if seen.contains(var) {
13264 let ty = vt.var(*var).ty.clone();
13265 if let Some(e) = init.take() {
13266 *s = Stmt::ExprStmt(Expr::Assign {
13267 target: Box::new(Expr::Local { var: *var, ty }),
13268 op: AssignOp::Plain,
13269 value: Box::new(e),
13270 });
13271 } else {
13272 *s = Stmt::Block(vec![]);
13273 }
13274 } else {
13275 seen.insert(*var);
13276 }
13277 }
13278 other => {
13279 walk_mut(other, &mut |x| demote_dup_decls(x, seen, vt));
13280 }
13281 }
13282}
13283
13284#[allow(dead_code)]
13285fn unused(_: &Vec<CaseGroup>, _: &Catch) {}
13286
13287pub fn apply_local_names(vt: &mut VarTable, body: &Stmt) {
13301 use std::collections::{HashMap, HashSet};
13302
13303 let mut by_call: HashMap<u32, Vec<String>> = HashMap::new();
13306 let mut by_intrinsics: HashMap<u32, String> = HashMap::new();
13307
13308 visit_all_exprs(body, &mut |e| {
13309 if let Expr::Method { cls, name, args, .. } = e {
13310 if (cls.as_ref() == "kotlin/jvm/internal/Intrinsics"
13311 || cls.as_ref() == "kotlin/jvm/internal/IntrinsicsKt")
13312 && matches!(name.as_ref(), "checkNotNullParameter" | "checkParameterIsNotNull")
13313 && args.len() == 2
13314 {
13315 if let (Expr::Local { var, .. }, Expr::Const(ConstVal::Str(s))) = (&args[0], &args[1])
13316 {
13317 by_intrinsics
13318 .entry(*var)
13319 .or_insert_with(|| sanitize_name(s).unwrap_or_default());
13320 }
13321 }
13322 }
13323 });
13324
13325 walk_all(body, &mut |st| {
13326 let (var, value) = match st {
13327 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e),
13328 Stmt::ExprStmt(Expr::Assign { target, value, .. })
13329 if matches!(&**target, Expr::Local { .. }) =>
13330 {
13331 let Expr::Local { var, .. } = &**target else { return };
13332 (*var, value.as_ref())
13333 }
13334 _ => return,
13335 };
13336 if let Some(n) = defining_call_name(value) {
13337 by_call.entry(var).or_default().push(n);
13338 }
13339 });
13340
13341 let sanit = |n: &str| crate::classdec::java_ident(n).into_owned();
13352 let mut taken: HashSet<String> = HashSet::default();
13353 for v in vt.vars.iter() {
13358 if v.synthetic_name {
13363 taken.insert(sanit(&v.name));
13364 }
13365 }
13366 for v in vt.vars.iter_mut() {
13367 if v.synthetic_name {
13368 continue;
13369 }
13370 if !taken.insert(sanit(&v.name)) {
13371 let base = sanit(&v.name);
13372 for i in 2.. {
13373 let cand = format!("{base}{i}");
13374 if taken.insert(cand.clone()) {
13375 v.name = cand;
13376 break;
13377 }
13378 }
13379 }
13380 }
13381 let claim = |taken: &mut HashSet<String>, want: &str| -> String {
13382 let w = crate::classdec::java_ident(want).into_owned();
13383 if taken.insert(w.clone()) {
13384 return w;
13385 }
13386 for i in 2.. {
13387 let cand = format!("{w}{i}");
13388 if taken.insert(cand.clone()) {
13389 return cand;
13390 }
13391 }
13392 w
13393 };
13394
13395 for info in vt.vars.iter_mut() {
13396 if !info.synthetic_name {
13397 continue;
13398 }
13399 if let Some(n) = by_intrinsics.get(&info.id) {
13402 if !n.is_empty() {
13403 info.name = claim(&mut taken, n);
13404 continue;
13405 }
13406 }
13407 if let Some(names) = by_call.get(&info.id) {
13409 let unique: HashSet<&String> = names.iter().collect();
13410 if unique.len() == 1 {
13411 let n = unique.into_iter().next().unwrap();
13412 if !is_java_keyword(n) {
13413 info.name = claim(&mut taken, n);
13414 continue;
13415 }
13416 }
13417 }
13418 let want = type_alias(&info.ty).map(str::to_string).or_else(|| simple_type_name(&info.ty));
13421 if let Some(n) = want {
13422 info.name = claim(&mut taken, &n);
13423 }
13424 }
13425
13426 let mut seen: HashSet<String> = HashSet::default();
13431 for v in vt.vars.iter_mut() {
13432 if !seen.insert(sanit(&v.name)) {
13433 let base = sanit(&v.name);
13434 for i in 2.. {
13435 let cand = format!("{base}{i}");
13436 if seen.insert(cand.clone()) {
13437 v.name = cand;
13438 break;
13439 }
13440 }
13441 }
13442 }
13443}
13444
13445fn simple_type_name(ty: &TypeRef) -> Option<String> {
13447 let TypeRef::J(JavaType::Object(n)) = ty else {
13448 return None;
13449 };
13450 let simple = n.rsplit('/').next().unwrap_or(n);
13451 let simple = simple.rsplit('$').next().unwrap_or(simple);
13452 let mut chars = simple.chars();
13455 let mut out = String::with_capacity(simple.len());
13456 let mut lowered = false;
13457 for c in chars.by_ref() {
13458 if c.is_ascii_alphabetic() && !lowered {
13459 out.push(c.to_ascii_lowercase());
13460 lowered = true;
13461 } else {
13462 out.push(c);
13463 }
13464 if lowered {
13465 break;
13466 }
13467 }
13468 out.extend(chars);
13469 sanitize_name(&out)
13470}
13471
13472fn defining_call_name(e: &Expr) -> Option<String> {
13476 match e {
13477 Expr::Method { name, .. } => {
13478 let base = name
13479 .strip_prefix("get")
13480 .or_else(|| name.strip_prefix("is"))
13481 .filter(|rest| rest.chars().next().is_some_and(|c| c.is_ascii_uppercase()))
13482 .unwrap_or(name);
13483 let first = base.chars().next()?;
13484 let mut out = String::with_capacity(base.len());
13485 out.push(first.to_ascii_lowercase());
13486 out.extend(base.chars().skip(1));
13487 sanitize_name(&out)
13488 }
13489 Expr::New { cls, .. } => {
13490 let simple = cls.rsplit('/').next().unwrap_or(cls);
13491 let simple = simple.rsplit('$').next().unwrap_or(simple);
13492 let first = simple.chars().next()?;
13493 let mut out = String::with_capacity(simple.len());
13494 out.push(first.to_ascii_lowercase());
13495 out.extend(simple.chars().skip(1));
13496 sanitize_name(&out)
13497 }
13498 _ => None,
13499 }
13500}
13501
13502fn sanitize_name(s: &str) -> Option<String> {
13504 let mut out = String::with_capacity(s.len());
13505 for c in s.chars() {
13506 if c.is_ascii_alphanumeric() || c == '_' || c == '$' {
13507 out.push(c);
13508 } else {
13509 out.push('_');
13510 }
13511 }
13512 if out.len() < 2
13513 || out.chars().next().is_some_and(|c| c.is_ascii_digit())
13514 || is_java_keyword(&out)
13515 {
13516 return None;
13517 }
13518 Some(out)
13519}
13520
13521fn type_alias(ty: &TypeRef) -> Option<&'static str> {
13524 let TypeRef::J(JavaType::Object(n)) = ty else {
13525 return None;
13526 };
13527 Some(match n.as_ref() {
13528 "java/lang/String" | "kotlin/String" => "str",
13529 "java/lang/Class" => "cls",
13530 "java/lang/Throwable" => "th",
13531 "java/lang/Object" | "kotlin/Any" => "obj",
13532 "java/util/Iterator" | "kotlin/collections/Iterator" => "it",
13533 "java/lang/Boolean" => "bool",
13534 "java/lang/Integer" => "num",
13535 "java/lang/Character" => "ch",
13536 "java/lang/Byte" => "b",
13537 "java/lang/Short" => "sh",
13538 "java/lang/Float" => "f",
13539 "java/lang/Double" => "d",
13540 "java/lang/Long" => "i",
13541 "java/lang/StringBuilder" => "sb",
13542 "java/util/ArrayList" => "list",
13543 "java/util/HashMap" => "map",
13544 "android/content/Context" => "context",
13545 "android/content/Intent" => "intent",
13546 "android/os/Bundle" => "bundle",
13547 "android/view/View" => "view",
13548 "android/graphics/Bitmap" => "bitmap",
13549 "android/view/ViewGroup" => "viewGroup",
13550 _ => return None,
13551 })
13552}
13553
13554pub(crate) fn visit_all_exprs<F: FnMut(&Expr)>(s: &Stmt, f: &mut F) {
13556 walk_all(s, &mut |st| {
13557 let exprs: Vec<&Expr> = match st {
13558 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
13559 let mut v: Vec<&Expr> = vec![value];
13560 if !matches!(&**target, Expr::Local { .. }) {
13561 v.push(target);
13562 }
13563 v
13564 }
13565 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
13566 vec![e]
13567 }
13568 Stmt::Return(Some(e)) => vec![e],
13569 Stmt::LocalDef { init: Some(e), .. } => vec![e],
13570 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
13571 vec![cond]
13572 }
13573 _ => Vec::new(),
13574 };
13575 for e in exprs {
13576 visit_exprs(e, f);
13577 }
13578 });
13579}
13580
13581pub fn platform_constants(body: &mut Stmt) {
13586 if !crate::platform::installed() {
13587 return;
13588 }
13589 rewrite_exprs(body, &mut |e| {
13590 if let Expr::Method { cls, name, desc, args, .. } = e {
13591 for a in args.iter_mut() {
13592 let (value, is_long) = match a {
13593 Expr::Const(ConstVal::Int(v)) => (*v as i64, false),
13594 Expr::Const(ConstVal::Long(v)) => (*v, true),
13595 _ => continue,
13596 };
13597 if let Some(named) =
13598 crate::platform::named_constant(cls, name, &desc_to_string(desc), 0, value)
13599 {
13600 let _ = is_long;
13601 *a = Expr::Raw(named);
13602 }
13603 }
13604 }
13605 });
13606}
13607
13608fn desc_to_string(d: &jdc_core::types::MethodDescriptor) -> String {
13610 let mut out = String::from("(");
13611 for a in &d.args {
13612 type_to_desc(a, &mut out);
13613 }
13614 out.push(')');
13615 type_to_desc(&d.ret, &mut out);
13616 out
13617}
13618
13619fn type_to_desc(t: &jdc_core::types::JavaType, out: &mut String) {
13620 match t {
13621 JavaType::Void => out.push('V'),
13622 JavaType::Boolean => out.push('Z'),
13623 JavaType::Byte => out.push('B'),
13624 JavaType::Char => out.push('C'),
13625 JavaType::Short => out.push('S'),
13626 JavaType::Int => out.push('I'),
13627 JavaType::Float => out.push('F'),
13628 JavaType::Long => out.push('J'),
13629 JavaType::Double => out.push('D'),
13630 JavaType::Object(n) => {
13631 out.push('L');
13632 out.push_str(n);
13633 out.push(';');
13634 }
13635 JavaType::Array(inner) => {
13636 out.push('[');
13637 type_to_desc(inner, out);
13638 }
13639 }
13640}
13641
13642pub fn remove_kotlin_checks(s: &mut Stmt) {
13648 walk_mut_deep(s, &mut |st| {
13649 let Stmt::Block(v) = st else { return };
13650 v.retain(|x| !is_kotlin_check(x));
13651 });
13652}
13653
13654fn is_kotlin_check(s: &Stmt) -> bool {
13655 let Stmt::ExprStmt(e) = s else { return false };
13656 let Expr::Method { cls, name, .. } = e else { return false };
13657 (cls.as_ref() == "kotlin/jvm/internal/Intrinsics" || cls.as_ref() == "kotlin/jvm/internal/IntrinsicsKt__Jdk7Kt")
13658 && matches!(
13659 name.as_ref(),
13660 "checkNotNullParameter"
13661 | "checkParameterIsNotNull"
13662 | "checkExpressionValueIsNotNull"
13663 | "checkNotNullExpressionValue"
13664 | "checkReturnedValueIsNotNull"
13665 | "checkFieldIsNotNull"
13666 | "checkNotNull"
13667 )
13668}
13669
13670fn inline_ref_ok(pool: &DexPool, host: &str, target: &str, member: Option<(&str, bool)>) -> bool {
13690 let hpkg = match host.rfind('/') {
13691 Some(i) => &host[..i],
13692 None => "",
13693 };
13694 let mut cur = target;
13695 loop {
13696 let tpkg = match cur.rfind('/') {
13697 Some(i) => &cur[..i],
13698 None => "",
13699 };
13700 if tpkg != hpkg {
13701 if let Some(pc) = pool.get(cur) {
13702 if pc.access & crate::access::ACC_PUBLIC == 0 {
13703 return false;
13704 }
13705 if cur == target {
13706 if let Some((mname, is_method)) = member {
13707 let ok = if is_method {
13708 pc.all_methods().any(|m| {
13709 &*m.name == mname
13710 && m.access & crate::access::ACC_PUBLIC != 0
13711 })
13712 } else {
13713 pc.static_fields
13714 .iter()
13715 .chain(pc.instance_fields.iter())
13716 .any(|f| {
13717 f.name == mname
13718 && f.access & crate::access::ACC_PUBLIC != 0
13719 })
13720 };
13721 if !ok
13726 && (if is_method {
13727 pc.all_methods().any(|m| &*m.name == mname)
13728 } else {
13729 pc.static_fields
13730 .iter()
13731 .chain(pc.instance_fields.iter())
13732 .any(|f| f.name == mname)
13733 })
13734 {
13735 return false;
13736 }
13737 }
13738 }
13739 }
13740 }
13741 match cur.rfind('$') {
13742 Some(i) if i > 0 => cur = &cur[..i],
13743 _ => break,
13744 }
13745 }
13746 true
13747}
13748
13749pub fn inline_accessors(s: &mut Stmt, pool: &DexPool, host: &str) {
13750 rewrite_exprs(s, &mut |e| {
13751 let Expr::Method { cls, name, desc, args, is_static, .. } = e else { return };
13752 if !*is_static || args.is_empty() {
13753 return;
13754 }
13755 let Some(target) = pool.get(cls) else { return };
13756 let desc_s = desc_to_string(desc);
13757 let Some(m) = target.find_method(name, &desc_s) else { return };
13758 if !(m.is_static() && m.access & access::ACC_SYNTHETIC != 0) {
13759 return;
13760 }
13761 let Some(dex) = pool.dex(m.dex_idx) else { return };
13762 let code = match pool.accessor_code(m.dex_idx, m.code_off) {
13767 Some(bytes) => ddc_dex::CodeItem::parse(&bytes, 0),
13768 None => dex.code_at(m.code_off),
13769 };
13770 let Some(code) = code else { return };
13771 let insns: Vec<&Insn> =
13772 code.insns.iter().filter(|i| !matches!(i.kind, InsnKind::Nop)).collect();
13773 let core: Vec<&Insn> = insns
13775 .iter()
13776 .copied()
13777 .filter(|i| !is_const_only_trace(&i.kind, &insns))
13778 .collect();
13779 match accessor_shape(&dex, &core, &code) {
13780 Some(Shape::Identity(arg_i)) => {
13781 if let Some(a) = args.get(arg_i) {
13782 *e = a.clone();
13783 }
13784 }
13785 Some(Shape::FieldRead { arg, cls: field_cls, field, field_ty }) => {
13786 if !inline_ref_ok(pool, host, &field_cls, Some((&field, false))) {
13787 return;
13788 }
13789 if let Some(owner) = args.get(arg).cloned().map(Box::new) {
13790 let ty = TypeRef::J(desc_type(&field_ty));
13791 let name = jdc_core::rename::field_display(
13797 &field_cls,
13798 &field,
13799 &field_ty,
13800 )
13801 .map(std::sync::Arc::from)
13802 .unwrap_or_else(|| field.into());
13803 *e = Expr::Field {
13804 owner: Some(owner),
13805 cls: field_cls.into(),
13806 name,
13807 ty,
13808 is_static: false,
13809 };
13810 }
13811 }
13812 Some(Shape::Forward { cls: tcls, name: tname, desc: tdesc, instance }) => {
13813 if !inline_ref_ok(pool, host, &tcls, Some((&tname, true))) {
13814 return;
13815 }
13816 let disp = jdc_core::rename::field_display(
13820 &tcls,
13821 &tname,
13822 &tdesc.to_string(),
13823 )
13824 .map(std::sync::Arc::from)
13825 .unwrap_or_else(|| tname.into());
13826 *cls = tcls.into();
13827 *name = disp;
13828 *desc = std::sync::Arc::new(tdesc);
13829 if instance && !args.is_empty() {
13830 let recv = args.remove(0);
13832 if let Expr::Method { owner, .. } = e {
13833 *owner = Some(Box::new(recv));
13834 }
13835 }
13836 }
13837 None => {}
13838 }
13839 });
13840}
13841
13842enum Shape {
13844 Identity(usize),
13846 FieldRead { arg: usize, cls: String, field: String, field_ty: String },
13848 Forward { cls: String, name: String, desc: MethodDescriptor, instance: bool },
13851}
13852
13853fn field_of(dex: &ddc_dex::DexFile, field_idx: u32) -> Option<(String, String, String)> {
13855 let f = dex.field(field_idx);
13856 Some((
13857 dex.class_name(f.class_idx),
13860 dex.string(f.name_idx).to_string(),
13861 dex.type_name(f.type_idx).to_string(),
13863 ))
13864}
13865
13866fn param_index(code: &ddc_dex::CodeItem, reg: u16) -> Option<usize> {
13869 let rs = code.registers_size as usize;
13870 let ins = code.ins_size as usize;
13871 let r = reg as usize;
13872 let first = rs.checked_sub(ins)?;
13873 (r >= first && r < rs).then(|| r - first)
13874}
13875
13876fn accessor_shape(
13877 dex: &ddc_dex::DexFile,
13878 core: &[&Insn],
13879 code: &ddc_dex::CodeItem,
13880) -> Option<Shape> {
13881 let reads = |rest: &[&Insn], r: u16| {
13891 rest.iter()
13892 .any(|i| crate::lift::src_regs(&i.kind).contains(&r))
13893 };
13894 let core: &[&Insn] = match core.split_first() {
13895 Some((first, rest))
13896 if matches!(first.kind, InsnKind::Const { dst, .. } if !reads(rest, dst)) =>
13897 {
13898 rest
13899 }
13900 _ => core,
13901 };
13902 let core: &[&Insn] = match core.split_last() {
13903 Some((last, rest))
13904 if matches!(last.kind, InsnKind::Const { dst, .. } if !reads(rest, dst)) =>
13905 {
13906 rest
13907 }
13908 _ => core,
13909 };
13910 match core {
13911 [Insn { kind: InsnKind::Return { src }, .. }] => {
13913 let idx = param_index(code, *src)?;
13914 Some(Shape::Identity(idx))
13915 }
13916 [
13918 Insn { kind: InsnKind::IGet { dst, obj, field_idx }, .. },
13919 Insn { kind: InsnKind::Return { src }, .. },
13920 ] if dst == src => {
13921 let arg = param_index(code, *obj)?;
13922 let (cls, field, field_ty) = field_of(dex, *field_idx)?;
13923 Some(Shape::FieldRead { arg, cls, field, field_ty })
13924 }
13925 [Insn { kind: InsnKind::Invoke { method_idx, regs, kind, .. }, .. }, rest @ ..] if rest.len() <= 2 => {
13927 if !regs.iter().all(|r| param_index(code, *r).is_some()) || regs.is_empty() {
13929 return None;
13930 }
13931 if rest.len() == 2 {
13934 let ok = matches!(rest[0].kind, InsnKind::MoveResult { .. })
13935 && matches!(rest[1].kind, InsnKind::Return { .. } | InsnKind::ReturnVoid);
13936 if !ok {
13937 return None;
13938 }
13939 }
13940 let mid = dex.method(*method_idx);
13941 let cls = dex.class_name(mid.class_idx);
13944 let name = dex.string(mid.name_idx).to_string();
13945 let mut s = String::from("(");
13946 for t in dex.proto_params(mid.proto_idx) {
13947 s.push_str(dex.type_name(*t));
13948 }
13949 s.push(')');
13950 s.push_str(dex.type_name(dex.proto(mid.proto_idx).return_type_idx));
13951 let desc = parse_method_descriptor(&s)?;
13952 Some(Shape::Forward { cls, name, desc, instance: !matches!(kind, InvokeKind::Static) })
13953 }
13954 _ => None,
13955 }
13956}
13957
13958
13959fn is_const_only_trace(kind: &InsnKind, insns: &[&Insn]) -> bool {
13965 let InsnKind::Invoke { kind, regs, .. } = kind else { return false };
13966 if !matches!(kind, InvokeKind::Static) || regs.is_empty() {
13967 return false;
13968 }
13969 regs.iter().all(|r| const_loaded(*r, insns, 0))
13970}
13971
13972fn const_loaded(r: u16, insns: &[&Insn], depth: u8) -> bool {
13975 if depth > 3 {
13976 return false;
13977 }
13978 insns.iter().any(|i| match &i.kind {
13979 InsnKind::Const { dst, .. } => *dst == r,
13980 InsnKind::Move { dst, src } => *dst == r && const_loaded(*src, insns, depth + 1),
13981 _ => false,
13982 })
13983}