1#![allow(clippy::collapsible_match)]
14
15use jdc_core::FxHashSet as HashSet;
16
17use jdc_core::ir::build::has_side_effects;
21use jdc_core::emit::is_java_keyword;
22use jdc_core::ir::expr::{AssignOp, BinOp, ConcatPart, ConstVal, Expr, TypeRef, UnOp};
23use jdc_core::ir::stmt::{CaseGroup, Catch, Stmt};
24use jdc_core::types::{JavaType, MethodDescriptor};
25use jdc_core::var::VarTable;
26
27use crate::lift::MethodEnv;
28use crate::{access, desc_type, DexPool};
29use ddc_dex::insn::{Insn, InsnKind, InvokeKind};
30use jdc_core::types::parse_method_descriptor;
31
32pub fn rewrite_exprs<F: FnMut(&mut Expr)>(s: &mut Stmt, f: &mut F) {
38 walk_stmt_exprs(s, f);
39}
40
41pub(crate) fn walk_stmt_exprs<F: FnMut(&mut Expr)>(s: &mut Stmt, f: &mut F) {
42 match s {
43 Stmt::Block(v) => {
44 for x in v.iter_mut() {
45 walk_stmt_exprs(x, f);
46 }
47 }
48 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
49 f(e);
50 }
51 Stmt::Return(Some(e)) => f(e),
52 Stmt::LocalDef { init, .. } => {
53 if let Some(e) = init {
54 f(e);
55 }
56 }
57 Stmt::If {
58 cond,
59 then_stmt,
60 else_stmt,
61 } => {
62 f(cond);
63 walk_stmt_exprs(then_stmt, f);
64 if let Some(e) = else_stmt {
65 walk_stmt_exprs(e, f);
66 }
67 }
68 Stmt::While { cond, body } => {
69 f(cond);
70 walk_stmt_exprs(body, f);
71 }
72 Stmt::DoWhile { body, cond } => {
73 walk_stmt_exprs(body, f);
74 f(cond);
75 }
76 Stmt::For {
77 init,
78 cond,
79 update,
80 body,
81 } => {
82 for x in init.iter_mut() {
83 walk_stmt_exprs(x, f);
84 }
85 if let Some(c) = cond {
86 f(c);
87 }
88 for u in update.iter_mut() {
89 f(u);
90 }
91 walk_stmt_exprs(body, f);
92 }
93 Stmt::ForEach { iterable, body, .. } => {
94 f(iterable);
95 walk_stmt_exprs(body, f);
96 }
97 Stmt::Switch {
98 selector,
99 cases,
100 default,
101 ..
102 } => {
103 f(selector);
104 for c in cases {
105 for x in c.body.iter_mut() {
106 walk_stmt_exprs(x, f);
107 }
108 if let Some(g) = &mut c.guard {
109 f(g);
110 }
111 }
112 if let Some(d) = default {
113 walk_stmt_exprs(d, f);
114 }
115 }
116 Stmt::Try {
117 body,
118 catches,
119 finally,
120 } => {
121 walk_stmt_exprs(body, f);
122 for c in catches {
123 walk_stmt_exprs(&mut c.body, f);
124 }
125 if let Some(fl) = finally {
126 walk_stmt_exprs(fl, f);
127 }
128 }
129 Stmt::TryWithResources {
130 resources,
131 body,
132 catches,
133 finally,
134 } => {
135 for x in resources.iter_mut() {
136 walk_stmt_exprs(x, f);
137 }
138 walk_stmt_exprs(body, f);
139 for c in catches {
140 walk_stmt_exprs(&mut c.body, f);
141 }
142 if let Some(fl) = finally {
143 walk_stmt_exprs(fl, f);
144 }
145 }
146 Stmt::Assert { cond, msg } => {
147 f(cond);
148 if let Some(m) = msg {
149 f(m);
150 }
151 }
152 Stmt::Synchronized { lock, body } => {
153 f(lock);
154 walk_stmt_exprs(body, f);
155 }
156 Stmt::TernaryValue { e } => f(e),
157 Stmt::Labeled { body, .. } => walk_stmt_exprs(body, f),
158 Stmt::Goto(_) | Stmt::Label(_) | Stmt::Break(_) | Stmt::Continue(_) => {}
159 _ => {}
160 }
161}
162
163pub(crate) fn visit_stmt_exprs_ro<F: FnMut(&Expr)>(s: &Stmt, f: &mut F) {
168 match s {
169 Stmt::Block(v) => {
170 for x in v {
171 visit_stmt_exprs_ro(x, f);
172 }
173 }
174 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
175 f(e);
176 }
177 Stmt::Return(Some(e)) => f(e),
178 Stmt::LocalDef { init: Some(e), .. } => f(e),
179 Stmt::If {
180 cond,
181 then_stmt,
182 else_stmt,
183 } => {
184 f(cond);
185 visit_stmt_exprs_ro(then_stmt, f);
186 if let Some(e) = else_stmt {
187 visit_stmt_exprs_ro(e, f);
188 }
189 }
190 Stmt::While { cond, body } => {
191 f(cond);
192 visit_stmt_exprs_ro(body, f);
193 }
194 Stmt::DoWhile { body, cond } => {
195 visit_stmt_exprs_ro(body, f);
196 f(cond);
197 }
198 Stmt::For {
199 init,
200 cond,
201 update,
202 body,
203 } => {
204 for x in init {
205 visit_stmt_exprs_ro(x, f);
206 }
207 if let Some(c) = cond {
208 f(c);
209 }
210 for u in update {
211 f(u);
212 }
213 visit_stmt_exprs_ro(body, f);
214 }
215 Stmt::ForEach { iterable, body, .. } => {
216 f(iterable);
217 visit_stmt_exprs_ro(body, f);
218 }
219 Stmt::Switch {
220 selector,
221 cases,
222 default,
223 ..
224 } => {
225 f(selector);
226 for c in cases {
227 for x in &c.body {
228 visit_stmt_exprs_ro(x, f);
229 }
230 if let Some(g) = &c.guard {
231 f(g);
232 }
233 }
234 if let Some(d) = default {
235 visit_stmt_exprs_ro(d, f);
236 }
237 }
238 Stmt::Try {
239 body,
240 catches,
241 finally,
242 } => {
243 visit_stmt_exprs_ro(body, f);
244 for c in catches {
245 visit_stmt_exprs_ro(&c.body, f);
246 }
247 if let Some(fl) = finally {
248 visit_stmt_exprs_ro(fl, f);
249 }
250 }
251 Stmt::TryWithResources {
252 resources,
253 body,
254 catches,
255 finally,
256 ..
257 } => {
258 for x in resources {
259 visit_stmt_exprs_ro(x, f);
260 }
261 visit_stmt_exprs_ro(body, f);
262 for c in catches {
263 visit_stmt_exprs_ro(&c.body, f);
264 }
265 if let Some(fl) = finally {
266 visit_stmt_exprs_ro(fl, f);
267 }
268 }
269 Stmt::Assert { cond, msg } => {
270 f(cond);
271 if let Some(m) = msg {
272 f(m);
273 }
274 }
275 Stmt::Synchronized { lock, body } => {
276 f(lock);
277 visit_stmt_exprs_ro(body, f);
278 }
279 Stmt::TernaryValue { e } => f(e),
280 Stmt::Labeled { body, .. } => visit_stmt_exprs_ro(body, f),
281 _ => {}
282 }
283}
284
285fn count_expr_ro(e: &Expr, m: &mut jdc_core::FxHashMap<u32, usize>) {
291 if let Expr::Local { var, .. } = e {
292 *m.entry(*var).or_insert(0) += 1;
293 return;
294 }
295 if let Expr::Method {
296 name,
297 is_special: true,
298 owner: Some(o),
299 args,
300 ..
301 } = e
302 {
303 if &**name == "<init>" && !matches!(**o, Expr::This) {
304 for a in args {
305 count_expr_ro(a, m);
306 }
307 return;
308 }
309 }
310 for_each_child(e, &mut |c| count_expr_ro(c, m));
311}
312
313fn visit_exprs<F: FnMut(&Expr)>(e: &Expr, f: &mut F) {
315 f(e);
316 for_each_child(e, &mut |c| visit_exprs(c, f));
317}
318
319fn for_each_child<F: FnMut(&Expr)>(e: &Expr, f: &mut F) {
320 match e {
321 Expr::Un { e, .. } | Expr::Cast { e, .. } | Expr::InstanceOf { e, .. } => f(e),
322 Expr::Bin { l, r, .. } => {
323 f(l);
324 f(r);
325 }
326 Expr::Cond { c, t, f: fe } => {
327 f(c);
328 f(t);
329 f(fe);
330 }
331 Expr::Assign { target, value, .. } => {
332 f(target);
333 f(value);
334 }
335 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => f(e),
336 Expr::Field { owner, .. } => {
337 if let Some(o) = owner {
338 f(o);
339 }
340 }
341 Expr::Method { owner, args, .. } => {
342 if let Some(o) = owner {
343 f(o);
344 }
345 for a in args {
346 f(a);
347 }
348 }
349 Expr::ArrayIndex { array, index } => {
350 f(array);
351 f(index);
352 }
353 Expr::New { args, .. } => {
354 for a in args {
355 f(a);
356 }
357 }
358 Expr::NewArray { dims, init, .. } => {
359 for d in dims {
360 f(d);
361 }
362 if let Some(v) = init {
363 for x in v {
364 f(x);
365 }
366 }
367 }
368 Expr::NewMultiArray { dims, .. } => {
369 for d in dims {
370 f(d);
371 }
372 }
373 Expr::StringConcat(parts) => {
374 for p in parts {
375 if let ConcatPart::Str(x) = p {
376 f(x);
377 }
378 }
379 }
380 Expr::Invokedynamic { args, .. } => {
381 for a in args {
382 f(a);
383 }
384 }
385 Expr::AnonNew { args, .. } => {
386 for a in args {
387 f(a);
388 }
389 }
390 _ => {}
391 }
392}
393
394fn for_each_child_mut<F: FnMut(&mut Expr)>(e: &mut Expr, f: &mut F) {
396 match e {
397 Expr::Un { e, .. } | Expr::Cast { e, .. } | Expr::InstanceOf { e, .. } => f(e),
398 Expr::Bin { l, r, .. } => {
399 f(l);
400 f(r);
401 }
402 Expr::Cond { c, t, f: fe } => {
403 f(c);
404 f(t);
405 f(fe);
406 }
407 Expr::Assign { target, value, .. } => {
408 f(target);
409 f(value);
410 }
411 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => f(e),
412 Expr::Field { owner, .. } => {
413 if let Some(o) = owner {
414 f(o);
415 }
416 }
417 Expr::Method { owner, args, .. } => {
418 if let Some(o) = owner {
419 f(o);
420 }
421 for a in args.iter_mut() {
422 f(a);
423 }
424 }
425 Expr::ArrayIndex { array, index } => {
426 f(array);
427 f(index);
428 }
429 Expr::New { args, .. } => {
430 for a in args.iter_mut() {
431 f(a);
432 }
433 }
434 Expr::NewArray { dims, init, .. } => {
435 for d in dims.iter_mut() {
436 f(d);
437 }
438 if let Some(v) = init {
439 for x in v.iter_mut() {
440 f(x);
441 }
442 }
443 }
444 Expr::NewMultiArray { dims, .. } => {
445 for d in dims.iter_mut() {
446 f(d);
447 }
448 }
449 Expr::StringConcat(parts) => {
450 for p in parts {
451 if let ConcatPart::Str(x) = p {
452 f(x);
453 }
454 }
455 }
456 Expr::Invokedynamic { args, .. } => {
457 for a in args.iter_mut() {
458 f(a);
459 }
460 }
461 Expr::AnonNew { args, .. } => {
462 for a in args.iter_mut() {
463 f(a);
464 }
465 }
466 _ => {}
467 }
468}
469
470pub(crate) fn deep_rewrite<F: FnMut(&mut Expr)>(e: &mut Expr, f: &mut F) {
472 f(e);
473 for_each_child_mut(e, &mut |c| deep_rewrite(c, f));
474}
475
476fn visit_exprs_reads<F: FnMut(&Expr)>(e: &Expr, f: &mut F) {
488 f(e);
489 match e {
490 Expr::Assign { target, value, .. } => {
491 if !matches!(**target, Expr::Local { .. }) {
492 visit_exprs_reads(target, f);
493 }
494 visit_exprs_reads(value, f);
495 }
496 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
497 if !matches!(**inner, Expr::Local { .. }) {
498 visit_exprs_reads(inner, f);
499 }
500 }
501 _ => for_each_child(e, &mut |c| visit_exprs_reads(c, f)),
502 }
503}
504
505fn deep_rewrite_reads<F: FnMut(&mut Expr)>(e: &mut Expr, f: &mut F) {
506 f(e);
507 match e {
508 Expr::Assign { target, value, .. } => {
509 if !matches!(**target, Expr::Local { .. }) {
510 deep_rewrite_reads(target, f);
511 }
512 deep_rewrite_reads(value, f);
513 }
514 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
515 if !matches!(**inner, Expr::Local { .. }) {
516 deep_rewrite_reads(inner, f);
517 }
518 }
519 _ => for_each_child_mut(e, &mut |c| deep_rewrite_reads(c, f)),
520 }
521}
522
523fn collect_vars(e: &Expr, out: &mut HashSet<u32>) {
524 visit_exprs(e, &mut |x| {
525 if let Expr::Local { var, .. } = x {
526 out.insert(*var);
527 }
528 });
529}
530
531fn stmt_collect_vars(s: &Stmt, out: &mut HashSet<u32>, assignments: bool) {
536 match s {
537 Stmt::Block(v) => {
538 for x in v {
539 stmt_collect_vars(x, out, assignments);
540 }
541 }
542 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
543 if let Expr::Local { var, .. } = &**target {
544 if assignments {
545 out.insert(*var);
546 }
547 } else {
548 collect_vars(target, out);
549 }
550 collect_vars(value, out);
551 }
552 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
553 collect_vars(e, out)
554 }
555 Stmt::Return(Some(e)) => collect_vars(e, out),
556 Stmt::LocalDef { var, init, .. } => {
557 if assignments {
558 out.insert(*var);
559 }
560 if let Some(e) = init {
561 collect_vars(e, out);
562 }
563 }
564 Stmt::If {
565 cond,
566 then_stmt,
567 else_stmt,
568 } => {
569 collect_vars(cond, out);
570 stmt_collect_vars(then_stmt, out, assignments);
571 if let Some(e) = else_stmt {
572 stmt_collect_vars(e, out, assignments);
573 }
574 }
575 Stmt::While { cond, body } => {
576 collect_vars(cond, out);
577 stmt_collect_vars(body, out, assignments);
578 }
579 Stmt::DoWhile { body, cond } => {
580 stmt_collect_vars(body, out, assignments);
581 collect_vars(cond, out);
582 }
583 Stmt::For {
584 init,
585 cond,
586 update,
587 body,
588 } => {
589 for x in init {
590 stmt_collect_vars(x, out, assignments);
591 }
592 if let Some(c) = cond {
593 collect_vars(c, out);
594 }
595 for u in update {
596 collect_vars(u, out);
597 }
598 stmt_collect_vars(body, out, assignments);
599 }
600 Stmt::ForEach {
601 var,
602 iterable,
603 body,
604 ..
605 } => {
606 if assignments {
607 out.insert(*var);
608 }
609 collect_vars(iterable, out);
610 stmt_collect_vars(body, out, assignments);
611 }
612 Stmt::Switch {
613 selector,
614 cases,
615 default,
616 ..
617 } => {
618 collect_vars(selector, out);
619 for c in cases {
620 for x in &c.body {
621 stmt_collect_vars(x, out, assignments);
622 }
623 }
624 if let Some(d) = default {
625 stmt_collect_vars(d, out, assignments);
626 }
627 }
628 Stmt::Try {
629 body,
630 catches,
631 finally,
632 } => {
633 stmt_collect_vars(body, out, assignments);
634 for c in catches {
635 stmt_collect_vars(&c.body, out, assignments);
636 }
637 if let Some(f) = finally {
638 stmt_collect_vars(f, out, assignments);
639 }
640 }
641 Stmt::TryWithResources {
642 resources,
643 body,
644 catches,
645 finally,
646 } => {
647 for x in resources {
648 stmt_collect_vars(x, out, assignments);
649 }
650 stmt_collect_vars(body, out, assignments);
651 for c in catches {
652 stmt_collect_vars(&c.body, out, assignments);
653 }
654 if let Some(f) = finally {
655 stmt_collect_vars(f, out, assignments);
656 }
657 }
658 Stmt::Synchronized { lock, body } => {
659 collect_vars(lock, out);
660 stmt_collect_vars(body, out, assignments);
661 }
662 Stmt::Assert { cond, msg } => {
663 collect_vars(cond, out);
664 if let Some(m) = msg {
665 collect_vars(m, out);
666 }
667 }
668 Stmt::Labeled { body, .. } => stmt_collect_vars(body, out, assignments),
669 _ => {}
670 }
671}
672
673pub(crate) fn walk_all<F: FnMut(&Stmt)>(s: &Stmt, f: &mut F) {
674 f(s);
675 match s {
676 Stmt::Block(v) => {
677 for x in v {
678 walk_all(x, f);
679 }
680 }
681 Stmt::If {
682 then_stmt,
683 else_stmt,
684 ..
685 } => {
686 walk_all(then_stmt, f);
687 if let Some(e) = else_stmt {
688 walk_all(e, f);
689 }
690 }
691 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => walk_all(body, f),
692 Stmt::For { init, body, .. } => {
693 for x in init {
694 walk_all(x, f);
695 }
696 walk_all(body, f);
697 }
698 Stmt::ForEach { body, .. } => walk_all(body, f),
699 Stmt::Switch { cases, default, .. } => {
700 for c in cases {
701 for x in &c.body {
702 walk_all(x, f);
703 }
704 }
705 if let Some(d) = default {
706 walk_all(d, f);
707 }
708 }
709 Stmt::Try {
710 body,
711 catches,
712 finally,
713 } => {
714 walk_all(body, f);
715 for c in catches {
716 walk_all(&c.body, f);
717 }
718 if let Some(fl) = finally {
719 walk_all(fl, f);
720 }
721 }
722 Stmt::TryWithResources {
723 resources,
724 body,
725 catches,
726 finally,
727 } => {
728 for x in resources {
729 walk_all(x, f);
730 }
731 walk_all(body, f);
732 for c in catches {
733 walk_all(&c.body, f);
734 }
735 if let Some(fl) = finally {
736 walk_all(fl, f);
737 }
738 }
739 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => walk_all(body, f),
740 _ => {}
741 }
742}
743
744pub fn bind_catches(s: &mut Stmt, vt: &mut VarTable) {
751 let mut defined: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
756 for v in &vt.vars {
757 if v.is_param {
758 defined.insert(v.id);
759 }
760 }
761 collect_defined_locals(s, &mut defined);
762 let reads_all = count_locals_stmts(std::slice::from_ref(s));
763 let mut assigned: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
767 collect_assigned_locals(s, &mut assigned);
768 let mut fallback_bound: Vec<u32> = Vec::new();
769 bind_catches_walk(s, vt, &defined, &reads_all, &assigned, &mut fallback_bound);
770 if !fallback_bound.is_empty() {
773 remove_bare_decls(s, &fallback_bound);
774 }
775}
776
777fn collect_defined_locals(s: &Stmt, out: &mut jdc_core::FxHashSet<u32>) {
778 crate::passes::walk_all(s, &mut |st| {
779 match st {
780 Stmt::LocalDef { var, .. } => {
781 out.insert(*var);
782 }
783 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
784 if let Expr::Local { var, .. } = &**target {
785 out.insert(*var);
786 }
787 }
788 _ => {}
789 }
790 });
791}
792
793fn collect_bare_decls(s: &Stmt, out: &mut Vec<u32>) {
796 walk_all(s, &mut |st| {
797 if let Stmt::LocalDef { var, init: None, .. } = st {
798 if !out.contains(var) {
799 out.push(*var);
800 }
801 }
802 });
803}
804
805
806fn collect_assigned_locals(s: &Stmt, out: &mut jdc_core::FxHashSet<u32>) {
811 crate::passes::walk_all(s, &mut |st| {
812 match st {
813 Stmt::LocalDef { var, init: Some(_), .. } => {
814 out.insert(*var);
815 }
816 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
817 if let Expr::Local { var, .. } = &**target {
818 out.insert(*var);
819 }
820 }
821 _ => {}
822 }
823 });
824}
825
826fn first_thrown_unassigned_local(
829 body: &Stmt,
830 vt: &VarTable,
831 assigned: &jdc_core::FxHashSet<u32>,
832) -> Option<u32> {
833 let mut found: Option<u32> = None;
834 let c = body.clone();
835 walk_all(&c, &mut |st| {
836 if let Stmt::Throw(th) = st {
837 if let Expr::Local { var, .. } = th {
838 let v = *var;
839 if !assigned.contains(&v) {
840 if let JavaType::Object(o) = vt.var(v).ty.erased() {
841 if o.as_ref() == "java/lang/Throwable" {
842 found = Some(v);
843 }
844 }
845 }
846 }
847 }
848 });
849 found
850}
851
852fn remove_first_leaf(s: &mut Stmt) {
856 if let Stmt::Block(v) = s {
857 if let Some(f) = v.first_mut() {
858 remove_first_leaf(f);
859 }
860 while matches!(v.first(), Some(Stmt::Block(b)) if b.is_empty()) {
861 v.remove(0);
862 }
863 } else {
864 *s = Stmt::Block(vec![]);
865 }
866}
867
868fn remove_bare_decls(s: &mut Stmt, vars: &[u32]) {
871 let vars: jdc_core::FxHashSet<u32> = vars.iter().copied().collect();
872 walk_mut_deep(s, &mut |st| {
873 if let Stmt::Block(v) = st {
874 v.retain(|x| {
875 !matches!(x, Stmt::LocalDef { var, init: None, .. } if vars.contains(var))
876 });
877 }
878 });
879}
880
881fn first_undefined_local_read(
882 body: &Stmt,
883 defined: &jdc_core::FxHashSet<u32>,
884) -> Option<u32> {
885 let mut found: Option<u32> = None;
886 visit_stmt_exprs_ro(body, &mut |e| {
887 if found.is_none() {
888 visit_exprs(e, &mut |x| {
889 if found.is_none() {
890 if let Expr::Local { var, .. } = x {
891 if !defined.contains(var) {
892 found = Some(*var);
893 }
894 }
895 }
896 });
897 }
898 });
899 found
900}
901
902#[allow(clippy::too_many_arguments)]
903fn bind_catches_walk(
904 s: &mut Stmt,
905 vt: &mut VarTable,
906 defined: &jdc_core::FxHashSet<u32>,
907 reads_all: &jdc_core::FxHashMap<u32, usize>,
908 assigned: &jdc_core::FxHashSet<u32>,
909 fallback_bound: &mut Vec<u32>,
910) {
911 match s {
912 Stmt::Try {
913 body,
914 catches,
915 finally,
916 } => {
917 bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound);
918 for c in catches.iter_mut() {
919 if c.var == u32::MAX {
920 let stored = match first_leaf_stmt(c.body.as_ref()) {
927 Stmt::LocalDef { var, init: None, .. } => Some(*var),
939 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
943 match (&**target, &**value) {
944 (Expr::Local { var, .. }, Expr::Local { var: src, .. })
945 if !defined.contains(src) =>
946 {
947 Some(*var)
948 }
949 _ => None,
950 }
951 }
952 _ => None,
953 };
954 if let Some(v) = stored {
955 let exc = c
956 .exc
957 .first()
958 .cloned()
959 .unwrap_or_else(|| "java/lang/Throwable".into());
960 let slot = vt.var(v).slot;
961 let name = "e".to_string();
962 let new_var =
963 vt.add_catch_var(slot, name, TypeRef::J(JavaType::Object(exc)));
964 remove_first_leaf(c.body.as_mut());
965 rewrite_local_refs(c.body.as_mut(), v, new_var);
966 c.var = new_var;
967 } else if let Some(v) = first_undefined_local_read(c.body.as_ref(), defined)
968 .filter(|v| {
969 let in_catch = count_locals_stmts(std::slice::from_ref(c.body.as_ref()));
975 reads_all.get(v).copied().unwrap_or(0)
976 == in_catch.get(v).copied().unwrap_or(0)
977 })
978 .filter(|v| {
979 let ty = vt.var(*v).ty.erased();
987 let exc = c
988 .exc
989 .first()
990 .cloned()
991 .unwrap_or_else(|| "java/lang/Throwable".into());
992 matches!(&ty, JavaType::Object(o)
993 if o.as_ref() == exc.as_ref()
994 || o.as_ref() == "java/lang/Throwable")
995 })
996 {
997 c.var = v;
1007 fallback_bound.push(v);
1008 } else if let Some(v) =
1009 first_thrown_unassigned_local(c.body.as_ref(), vt, assigned)
1010 .filter(|v| {
1011 let in_catch =
1012 count_locals_stmts(std::slice::from_ref(c.body.as_ref()));
1013 reads_all.get(v).copied().unwrap_or(0)
1014 == in_catch.get(v).copied().unwrap_or(0)
1015 })
1016 .filter(|v| {
1017 let ty = vt.var(*v).ty.erased();
1021 let exc = c
1022 .exc
1023 .first()
1024 .cloned()
1025 .unwrap_or_else(|| "java/lang/Throwable".into());
1026 matches!(&ty, JavaType::Object(o)
1027 if o.as_ref() == exc.as_ref()
1028 || o.as_ref() == "java/lang/Throwable")
1029 })
1030 {
1031 c.var = v;
1037 fallback_bound.push(v);
1038 }
1039 }
1040 if c.var != u32::MAX {
1041 let in_catch =
1056 count_locals_stmts(std::slice::from_ref(c.body.as_ref()));
1057 let mut twins: Vec<u32> = Vec::new();
1058 collect_bare_decls(c.body.as_ref(), &mut twins);
1059 let mut swept: Vec<u32> = Vec::new();
1060 for x in twins {
1061 if x == c.var || assigned.contains(&x) {
1062 continue;
1063 }
1064 if reads_all.get(&x).copied().unwrap_or(0)
1065 != in_catch.get(&x).copied().unwrap_or(0)
1066 {
1067 continue;
1068 }
1069 if !matches!(vt.var(x).ty.erased(),
1070 JavaType::Object(o) if o.as_ref() == "java/lang/Throwable")
1071 {
1072 continue;
1073 }
1074 rewrite_local_refs(c.body.as_mut(), x, c.var);
1075 swept.push(x);
1076 }
1077 if !swept.is_empty() {
1078 remove_bare_decls(c.body.as_mut(), &swept);
1079 }
1080 }
1081 bind_catches_walk(&mut c.body, vt, defined, reads_all, assigned, fallback_bound);
1082 }
1083 if let Some(f) = finally {
1084 bind_catches_walk(f.as_mut(), vt, defined, reads_all, assigned, fallback_bound);
1085 }
1086 }
1087 Stmt::Block(v) => {
1088 for x in v.iter_mut() {
1089 bind_catches_walk(x, vt, defined, reads_all, assigned, fallback_bound);
1090 }
1091 }
1092 Stmt::If {
1093 then_stmt,
1094 else_stmt,
1095 ..
1096 } => {
1097 bind_catches_walk(then_stmt, vt, defined, reads_all, assigned, fallback_bound);
1098 if let Some(e) = else_stmt {
1099 bind_catches_walk(e, vt, defined, reads_all, assigned, fallback_bound);
1100 }
1101 }
1102 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound),
1103 Stmt::For { init, body, .. } => {
1104 for x in init.iter_mut() {
1105 bind_catches_walk(x, vt, defined, reads_all, assigned, fallback_bound);
1106 }
1107 bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound);
1108 }
1109 Stmt::ForEach { body, .. }
1110 | Stmt::Labeled { body, .. }
1111 | Stmt::Synchronized { body, .. } => bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound),
1112 Stmt::Switch { cases, default, .. } => {
1113 for c in cases.iter_mut() {
1114 for x in c.body.iter_mut() {
1115 bind_catches_walk(x, vt, defined, reads_all, assigned, fallback_bound);
1116 }
1117 }
1118 if let Some(d) = default {
1119 bind_catches_walk(d, vt, defined, reads_all, assigned, fallback_bound);
1120 }
1121 }
1122 _ => {}
1123 }
1124}
1125
1126fn rewrite_local_refs(s: &mut Stmt, from: u32, to: u32) {
1127 rewrite_exprs(s, &mut |e| {
1128 deep_rewrite(e, &mut |x| {
1129 if let Expr::Local { var, .. } = x {
1130 if *var == from {
1131 *var = to;
1132 }
1133 }
1134 });
1135 });
1136}
1137
1138pub fn cleanup(s: &mut Stmt) {
1140 jdc_core::ir::stmt::flatten(s);
1141 prune_dead_tails(s);
1142 strip_empty(s);
1143 merge_singleton_stmts(s);
1144}
1145
1146fn seq_terminates(s: &Stmt) -> bool {
1157 match s {
1158 Stmt::Return(_) | Stmt::Throw(_) | Stmt::Break(_) | Stmt::Continue(_) => {
1159 true
1160 }
1161 Stmt::Block(v) => v.iter().any(seq_terminates),
1162 Stmt::If {
1163 then_stmt,
1164 else_stmt,
1165 ..
1166 } => {
1167 seq_terminates(then_stmt)
1168 && else_stmt
1169 .as_ref()
1170 .map(|e| seq_terminates(e))
1171 .unwrap_or(false)
1172 }
1173 Stmt::Try {
1174 body,
1175 catches,
1176 finally,
1177 }
1178 | Stmt::TryWithResources {
1179 body,
1180 catches,
1181 finally,
1182 ..
1183 } => {
1184 if let Some(f) = finally {
1185 if seq_terminates(f) {
1186 return true;
1187 }
1188 }
1189 seq_terminates(body) && catches.iter().all(|c| seq_terminates(&c.body))
1190 }
1191 Stmt::Labeled { body, .. } | Stmt::Synchronized { body, .. } => {
1192 seq_terminates(body)
1193 }
1194 Stmt::While { .. } | Stmt::DoWhile { .. } => {
1199 jdc_core::analysis::dead_end_infinite_while(s)
1200 }
1201 _ => false,
1202 }
1203}
1204
1205fn prune_seq(v: &mut Vec<Stmt>) {
1206 if v.iter()
1207 .any(|x| matches!(x, Stmt::Label(_) | Stmt::Goto(_)))
1208 {
1209 return;
1210 }
1211 if let Some(i) = v.iter().position(seq_terminates) {
1212 v.truncate(i + 1);
1213 }
1214}
1215
1216pub fn prune_dead_tails(s: &mut Stmt) {
1217 match s {
1218 Stmt::Block(v) => {
1219 for x in v.iter_mut() {
1220 prune_dead_tails(x);
1221 }
1222 prune_seq(v);
1223 }
1224 Stmt::If {
1225 then_stmt,
1226 else_stmt,
1227 ..
1228 } => {
1229 prune_dead_tails(then_stmt);
1230 if let Some(e) = else_stmt {
1231 prune_dead_tails(e);
1232 }
1233 }
1234 Stmt::While { body, .. }
1235 | Stmt::DoWhile { body, .. }
1236 | Stmt::ForEach { body, .. }
1237 | Stmt::Labeled { body, .. }
1238 | Stmt::Synchronized { body, .. } => prune_dead_tails(body),
1239 Stmt::For { init, body, .. } => {
1240 for x in init.iter_mut() {
1241 prune_dead_tails(x);
1242 }
1243 prune_seq(init);
1244 prune_dead_tails(body);
1245 }
1246 Stmt::Switch {
1247 cases, default, ..
1248 } => {
1249 for c in cases {
1250 for x in c.body.iter_mut() {
1251 prune_dead_tails(x);
1252 }
1253 prune_seq(&mut c.body);
1254 }
1255 if let Some(d) = default {
1256 prune_dead_tails(d);
1257 }
1258 }
1259 Stmt::Try {
1260 body,
1261 catches,
1262 finally,
1263 }
1264 | Stmt::TryWithResources {
1265 body,
1266 catches,
1267 finally,
1268 ..
1269 } => {
1270 prune_dead_tails(body);
1271 for c in catches {
1272 prune_dead_tails(&mut c.body);
1273 }
1274 if let Some(f) = finally {
1275 prune_dead_tails(f);
1276 }
1277 }
1278 _ => {}
1279 }
1280}
1281
1282pub fn strip_phantom_field_writes(body: &mut Stmt, pool: &DexPool) {
1298 fn phantom(pool: &DexPool, cls: &str, name: &str) -> bool {
1299 let Some(pc) = pool.get(cls) else {
1303 return false;
1304 };
1305 fn declares(pc: &crate::PoolClass, cls: &str, name: &str) -> bool {
1313 pc.static_fields
1314 .iter()
1315 .chain(pc.instance_fields.iter())
1316 .any(|f| {
1317 f.name.as_ref() == name
1318 || jdc_core::rename::field_display(cls, &f.name, &f.desc)
1319 .is_some_and(|d| d == name)
1320 })
1321 }
1322 if declares(pc, cls, name) {
1323 return false;
1324 }
1325 let mut cur: Option<String> = pc.super_name.clone();
1335 let mut hops = 0u32;
1336 while let Some(s) = cur {
1337 hops += 1;
1338 if hops > 64 {
1339 return false;
1340 }
1341 if let Some(sup) = pool.get(&s) {
1342 if declares(sup, &s, name) {
1343 return false;
1344 }
1345 cur = sup.super_name.clone();
1346 } else {
1347 if !crate::fwdb::exists(&s) {
1348 return false;
1349 }
1350 let mut found = false;
1351 crate::fwdb::for_each_field(&s, |f, _| {
1352 if f == name {
1353 found = true;
1354 }
1355 });
1356 if found {
1357 return false;
1358 }
1359 cur = crate::fwdb::super_of(&s).map(|x| x.to_string());
1361 }
1362 }
1363 true
1364 }
1365 fn droppable_value(pool: &DexPool, e: &Expr) -> bool {
1366 match e {
1367 Expr::Const(_) | Expr::Local { .. } => true,
1368 Expr::Field { cls, name, .. } => phantom(pool, cls, name),
1369 Expr::Cast { e, .. } => droppable_value(pool, e),
1370 _ => false,
1371 }
1372 }
1373 fn is_phantom_write(pool: &DexPool, st: &Stmt) -> bool {
1374 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = st {
1375 if let Expr::Field { cls, name, .. } = &**target {
1376 return phantom(pool, cls, name) && droppable_value(pool, value);
1377 }
1378 }
1379 false
1380 }
1381 walk_mut_deep(body, &mut |st| match st {
1385 Stmt::Block(v) => v.retain(|x| !is_phantom_write(pool, x)),
1386 Stmt::Switch { cases, .. } => {
1387 for c in cases.iter_mut() {
1388 c.body.retain(|x| !is_phantom_write(pool, x));
1389 }
1390 }
1391 Stmt::For { init, .. } => init.retain(|x| !is_phantom_write(pool, x)),
1392 Stmt::TryWithResources { resources, .. } => {
1393 resources.retain(|x| !is_phantom_write(pool, x))
1394 }
1395 _ => {}
1396 });
1397 if is_phantom_write(pool, body) {
1398 *body = Stmt::Block(vec![]);
1399 }
1400}
1401
1402pub fn init_bare_decls(body: &mut Stmt, vt: &VarTable) {
1414 walk_mut_deep(body, &mut |st| {
1415 if let Stmt::LocalDef { var, init, .. } = st {
1416 if init.is_some() {
1417 return;
1418 }
1419 let lit = match vt.var(*var).ty.erased() {
1420 JavaType::Boolean => "false",
1421 JavaType::Int
1422 | JavaType::Byte
1423 | JavaType::Short
1424 | JavaType::Char => "0",
1425 JavaType::Long => "0L",
1426 JavaType::Float => "0.0f",
1427 JavaType::Double => "0.0d",
1428 JavaType::Void => return,
1429 JavaType::Object(_) | JavaType::Array(_) => "null",
1430 };
1431 *init = Some(Expr::Raw(lit.to_string()));
1432 }
1433 });
1434}
1435
1436fn strip_empty(s: &mut Stmt) {
1437 match s {
1438 Stmt::Block(v) => {
1439 v.retain(|x| !x.is_empty_block());
1440 for x in v.iter_mut() {
1441 strip_empty(x);
1442 }
1443 }
1444 Stmt::If {
1445 then_stmt,
1446 else_stmt,
1447 ..
1448 } => {
1449 strip_empty(then_stmt);
1450 if let Some(e) = else_stmt {
1451 strip_empty(e);
1452 if e.is_empty_block() {
1453 *else_stmt = None;
1454 }
1455 }
1456 }
1457 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => strip_empty(body),
1458 Stmt::For { init, body, .. } => {
1459 init.retain(|x| !x.is_empty_block());
1460 strip_empty(body);
1461 }
1462 Stmt::ForEach { body, .. } => strip_empty(body),
1463 Stmt::Switch { cases, default, .. } => {
1464 for c in cases {
1465 c.body.retain(|x| !x.is_empty_block());
1466 }
1467 if let Some(d) = default {
1468 strip_empty(d);
1469 }
1470 }
1471 Stmt::Try {
1472 body,
1473 catches,
1474 finally,
1475 } => {
1476 strip_empty(body);
1477 for c in catches {
1478 strip_empty(&mut c.body);
1479 }
1480 if let Some(f) = finally {
1481 strip_empty(f);
1482 }
1483 }
1484 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => strip_empty(body),
1485 _ => {}
1486 }
1487}
1488
1489fn merge_singleton_stmts(s: &mut Stmt) {
1490 if let Stmt::Block(v) = s {
1491 for x in v.iter_mut() {
1492 merge_singleton_stmts(x);
1493 }
1494 }
1495}
1496
1497pub fn prune_unreachable(s: &mut Stmt) {
1499 if let Stmt::Block(v) = s {
1500 let mut cut: Option<usize> = None;
1501 for (i, x) in v.iter().enumerate() {
1502 if matches!(x, Stmt::Return(_) | Stmt::Throw(_)) {
1503 cut = Some(i + 1);
1504 break;
1505 }
1506 }
1507 if let Some(c) = cut {
1508 if c < v.len() {
1509 v.truncate(c);
1510 }
1511 }
1512 for x in v.iter_mut() {
1513 prune_unreachable(x);
1514 }
1515 } else {
1516 walk_mut(s, &mut prune_unreachable);
1517 }
1518}
1519
1520fn walk_mut<F: FnMut(&mut Stmt)>(s: &mut Stmt, f: &mut F) {
1521 match s {
1522 Stmt::Block(v) => {
1523 for x in v.iter_mut() {
1524 f(x);
1525 }
1526 }
1527 Stmt::If {
1528 then_stmt,
1529 else_stmt,
1530 ..
1531 } => {
1532 f(then_stmt);
1533 if let Some(e) = else_stmt {
1534 f(e);
1535 }
1536 }
1537 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => f(body),
1538 Stmt::For { init, body, .. } => {
1539 for x in init.iter_mut() {
1540 f(x);
1541 }
1542 f(body);
1543 }
1544 Stmt::ForEach { body, .. } => f(body),
1545 Stmt::Switch { cases, default, .. } => {
1546 for c in cases {
1547 for x in c.body.iter_mut() {
1548 f(x);
1549 }
1550 }
1551 if let Some(d) = default {
1552 f(d);
1553 }
1554 }
1555 Stmt::Try {
1556 body,
1557 catches,
1558 finally,
1559 } => {
1560 f(body);
1561 for c in catches {
1562 f(&mut c.body);
1563 }
1564 if let Some(fl) = finally {
1565 f(fl);
1566 }
1567 }
1568 Stmt::TryWithResources {
1569 resources,
1570 body,
1571 catches,
1572 finally,
1573 } => {
1574 for r in resources.iter_mut() {
1575 f(r);
1576 }
1577 f(body);
1578 for c in catches {
1579 f(&mut c.body);
1580 }
1581 if let Some(fl) = finally {
1582 f(fl);
1583 }
1584 }
1585 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => f(body),
1586 _ => {}
1587 }
1588}
1589
1590pub fn count_localdefs(s: &Stmt, out: &mut usize) {
1592 if matches!(s, Stmt::LocalDef { .. }) {
1593 *out += 1;
1594 }
1595 walk_all(s, &mut |x| {
1596 if matches!(x, Stmt::LocalDef { .. }) {
1597 *out += 1;
1598 }
1599 });
1600}
1601
1602pub fn invert_empty_thens(s: &mut Stmt) {
1607 walk_mut_deep(s, &mut |st| {
1608 if let Stmt::If {
1609 cond,
1610 then_stmt,
1611 else_stmt,
1612 } = st
1613 {
1614 let then_empty = matches!(&**then_stmt, Stmt::Block(v) if v.is_empty());
1615 if then_empty {
1616 if let Some(els) = else_stmt.take() {
1617 let c = std::mem::replace(cond, Expr::Const(ConstVal::Int(0)));
1618 *cond = jdc_core::convert::negate(c);
1619 *then_stmt = els;
1620 }
1621 }
1622 }
1623 });
1624}
1625
1626pub fn fold_short_circuits(s: &mut Stmt) {
1637 for _ in 0..8 {
1638 let mut changed = false;
1639 walk_mut_deep(s, &mut |st| {
1640 if try_fold_diamond(st) {
1641 changed = true;
1642 }
1643 });
1644 if !changed {
1645 break;
1646 }
1647 }
1648}
1649
1650fn try_fold_diamond(st: &mut Stmt) -> bool {
1651 enum Shape {
1652 ThenAnd,
1653 ThenAndNot,
1654 ElseOr,
1655 ElseOrNot,
1656 BareAnd,
1657 BareAndNot,
1658 }
1659 let shape = match &*st {
1663 Stmt::If {
1666 then_stmt,
1667 else_stmt: None,
1668 ..
1669 } => match &**then_stmt {
1670 Stmt::If {
1671 then_stmt: t2,
1672 else_stmt: None,
1673 ..
1674 } if !matches!(&**t2, Stmt::Block(v) if v.is_empty()) => Some(Shape::BareAnd),
1675 Stmt::If {
1676 then_stmt: t2,
1677 else_stmt: Some(_),
1678 ..
1679 } if matches!(&**t2, Stmt::Block(v) if v.is_empty()) => Some(Shape::BareAndNot),
1680 _ => None,
1681 },
1682 Stmt::If {
1683 then_stmt,
1684 else_stmt: Some(els),
1685 ..
1686 } => match (&**then_stmt, &**els) {
1687 (Stmt::If { else_stmt: Some(f2), .. }, _) if f2.as_ref() == els.as_ref() => {
1688 Some(Shape::ThenAnd)
1689 }
1690 (Stmt::If { then_stmt: t2, else_stmt: Some(_), .. }, _)
1691 if t2.as_ref() == els.as_ref() =>
1692 {
1693 Some(Shape::ThenAndNot)
1694 }
1695 (_, Stmt::If { then_stmt: t2, else_stmt: Some(_), .. })
1696 if t2.as_ref() == then_stmt.as_ref() =>
1697 {
1698 Some(Shape::ElseOr)
1699 }
1700 (_, Stmt::If { else_stmt: Some(f2), .. })
1701 if f2.as_ref() == then_stmt.as_ref() =>
1702 {
1703 Some(Shape::ElseOrNot)
1704 }
1705 _ => None,
1706 },
1707 _ => None,
1708 };
1709 let Some(shape) = shape else {
1710 return false;
1711 };
1712
1713 let taken = std::mem::replace(st, Stmt::Block(vec![]));
1714 if matches!(shape, Shape::BareAnd | Shape::BareAndNot) {
1716 let Stmt::If {
1717 cond: c1,
1718 then_stmt,
1719 else_stmt: None,
1720 } = taken
1721 else {
1722 unreachable!()
1723 };
1724 let Stmt::If {
1725 cond: c2,
1726 then_stmt: t2,
1727 else_stmt: f2,
1728 } = *then_stmt
1729 else {
1730 unreachable!()
1731 };
1732 let sc = |op: BinOp, l: Expr, r: Expr| Expr::Bin {
1733 op,
1734 l: Box::new(l),
1735 r: Box::new(r),
1736 ty: None,
1737 };
1738 match shape {
1739 Shape::BareAnd => {
1741 *st = Stmt::If {
1742 cond: sc(BinOp::LogAnd, c1, c2),
1743 then_stmt: t2,
1744 else_stmt: None,
1745 };
1746 }
1747 _ => {
1749 let body = f2.unwrap();
1750 *st = Stmt::If {
1751 cond: sc(
1752 BinOp::LogAnd,
1753 c1,
1754 Expr::Un {
1755 op: UnOp::Not,
1756 e: Box::new(c2),
1757 },
1758 ),
1759 then_stmt: body,
1760 else_stmt: None,
1761 };
1762 }
1763 }
1764 return true;
1765 }
1766 let Stmt::If {
1767 cond: c1,
1768 then_stmt,
1769 else_stmt: Some(els),
1770 } = taken
1771 else {
1772 unreachable!("shape probe guaranteed an If with else")
1773 };
1774 let sc = |op: BinOp, l: Expr, r: Expr| Expr::Bin {
1775 op,
1776 l: Box::new(l),
1777 r: Box::new(r),
1778 ty: None,
1779 };
1780 let not = |e: Expr| Expr::Un {
1781 op: UnOp::Not,
1782 e: Box::new(e),
1783 };
1784 match shape {
1785 Shape::BareAnd | Shape::BareAndNot => unreachable!("handled above"),
1786 Shape::ThenAnd | Shape::ThenAndNot => {
1787 let Stmt::If {
1788 cond: c2,
1789 then_stmt: t2,
1790 else_stmt: Some(f2),
1791 } = *then_stmt
1792 else {
1793 unreachable!()
1794 };
1795 let (nc, body, alt) = match shape {
1796 Shape::ThenAnd => (sc(BinOp::LogAnd, c1, c2), t2, f2),
1798 _ => (sc(BinOp::LogAnd, c1, not(c2)), f2, t2),
1800 };
1801 *st = Stmt::If {
1802 cond: nc,
1803 then_stmt: body,
1804 else_stmt: Some(alt),
1805 };
1806 }
1807 Shape::ElseOr | Shape::ElseOrNot => {
1808 let Stmt::If {
1809 cond: c2,
1810 then_stmt: t2,
1811 else_stmt: Some(f2),
1812 } = *els
1813 else {
1814 unreachable!()
1815 };
1816 let body = then_stmt;
1819 let (nc, alt) = match shape {
1820 Shape::ElseOr => (sc(BinOp::LogOr, c1, c2), f2),
1822 _ => (sc(BinOp::LogOr, c1, not(c2)), t2),
1824 };
1825 *st = Stmt::If {
1826 cond: nc,
1827 then_stmt: body,
1828 else_stmt: Some(alt),
1829 };
1830 }
1831 }
1832 true
1833}
1834
1835pub fn strip_clinit_returns(s: &mut Stmt) {
1841 walk_mut_deep(s, &mut |st| {
1842 if let Stmt::Block(v) = st {
1843 v.retain(|x| !matches!(x, Stmt::Return(None)));
1844 }
1845 });
1846}
1847
1848pub fn prepend_comment(s: &mut Stmt, text: String) {
1849 let stmt = Stmt::Comment(text);
1850 match s {
1851 Stmt::Block(v) => v.insert(0, stmt),
1852 other => {
1853 let inner = std::mem::replace(other, Stmt::Block(vec![]));
1854 *other = Stmt::Block(vec![stmt, inner]);
1855 }
1856 }
1857}
1858
1859fn is_obj_expr(e: &Expr, obj_var: &dyn Fn(u32) -> bool) -> bool {
1867 match e {
1868 Expr::Local { var, .. } => obj_var(*var),
1869 Expr::Const(_) => false,
1870 _ => e.type_ref().erased().is_reference(),
1871 }
1872}
1873
1874fn null_side_rewrite(x: &mut Expr, obj_var: &dyn Fn(u32) -> bool) {
1879 if let Expr::Bin { op, l, r, .. } = x {
1880 if !matches!(op, BinOp::Eq | BinOp::Ne) {
1881 return;
1882 }
1883 let l_zero = matches!(&**l, Expr::Const(ConstVal::Int(0)));
1884 let r_zero = matches!(&**r, Expr::Const(ConstVal::Int(0)));
1885 if l_zero && !r_zero && is_obj_expr(r, obj_var) {
1886 **l = Expr::Const(ConstVal::Null);
1887 } else if r_zero && !l_zero && is_obj_expr(l, obj_var) {
1888 **r = Expr::Const(ConstVal::Null);
1889 }
1890 }
1891}
1892
1893pub fn fused_expr_rewrites(s: &mut Stmt, vt: &VarTable) {
1894 let mut obj_vars: Vec<bool> = Vec::with_capacity(vt.vars.len());
1896 for v in &vt.vars {
1897 obj_vars.push(v.ty.erased().is_reference());
1898 }
1899 rewrite_exprs(s, &mut |e| {
1900 deep_rewrite(e, &mut |x| {
1901 if let Expr::Invokedynamic { name, args, .. } = x {
1903 if name.starts_with('\0') && args.len() == 2 {
1904 let (cls, ty) = match name.as_str() {
1905 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
1906 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
1907 _ => ("java/lang/Double", JavaType::Double),
1908 };
1909 let desc = MethodDescriptor {
1910 args: vec![ty.clone(), ty],
1911 ret: JavaType::Int,
1912 };
1913 *x = Expr::Method {
1914 owner: None,
1915 cls: cls.into(),
1916 name: "compare".into(),
1917 desc: std::sync::Arc::new(desc),
1918 args: args.clone(),
1919 is_static: true,
1920 is_interface: false,
1921 is_special: false,
1922 is_super: false,
1923 is_dynamic: false,
1924 type_args: vec![],
1925 };
1926 return;
1927 }
1928 }
1929 null_side_rewrite(x, &|v| obj_vars.get(v as usize).copied().unwrap_or(false));
1932 if let Expr::Bin { op, l, r, .. } = x {
1934 if matches!(
1935 op,
1936 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
1937 ) {
1938 let l_const = matches!(&**l, Expr::Const(_));
1939 let r_var = matches!(&**r, Expr::Local { .. });
1940 if l_const && r_var {
1941 std::mem::swap(l, r);
1942 }
1943 }
1944 }
1945 });
1946 });
1947 walk_mut_deep(s, &mut |st| {
1955 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = st {
1956 let tgt_is_obj = match &**target {
1957 Expr::Local { var, .. } => {
1958 obj_vars.get(*var as usize).copied().unwrap_or(false)
1959 }
1960 other => other.type_ref().erased().is_reference(),
1961 };
1962 if tgt_is_obj {
1963 if let Expr::Const(ConstVal::Int(0)) = &**value {
1964 **value = Expr::Const(ConstVal::Null);
1965 }
1966 }
1967 } else if let Stmt::LocalDef { var, init, .. } = st {
1968 if obj_vars.get(*var as usize).copied().unwrap_or(false) {
1969 if let Some(iv) = init {
1970 if let Expr::Const(ConstVal::Int(0)) = iv {
1971 *iv = Expr::Const(ConstVal::Null);
1972 }
1973 }
1974 }
1975 }
1976 });
1977}
1978
1979#[allow(dead_code)]
1982pub fn desugar_cmp_residuals(s: &mut Stmt) {
1983 rewrite_exprs(s, &mut |e| {
1984 deep_rewrite(e, &mut |x| {
1985 if let Expr::Invokedynamic { name, args, .. } = x {
1986 if name.starts_with('\0') && args.len() == 2 {
1987 let (cls, ty) = match name.as_str() {
1988 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
1989 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
1990 _ => ("java/lang/Double", JavaType::Double),
1991 };
1992 let desc = jdc_core::types::MethodDescriptor {
1993 args: vec![ty.clone(), ty],
1994 ret: JavaType::Int,
1995 };
1996 *x = Expr::Method {
1997 owner: None,
1998 cls: cls.into(),
1999 name: "compare".into(),
2000 desc: std::sync::Arc::new(desc),
2001 args: args.clone(),
2002 is_static: true,
2003 is_interface: false,
2004 is_special: false,
2005 is_super: false,
2006 is_dynamic: false,
2007 type_args: vec![],
2008 };
2009 }
2010 }
2011 });
2012 });
2013}
2014
2015pub fn fold_string_builders(s: &mut Stmt, vt: &VarTable) {
2022 let n_vars = vt.vars.len().max(1);
2024 let mut counts = vec![0usize; n_vars];
2025 count_assignments(s, &mut counts);
2026 let mut values: Vec<Option<Expr>> = vec![None; n_vars];
2027 record_assignments(s, &counts, &mut values);
2028
2029 let mut appended_stmts: HashSet<u32> = HashSet::default();
2031 walk_all(s, &mut |st| {
2032 if let Stmt::ExprStmt(Expr::Method {
2033 cls,
2034 name,
2035 owner,
2036 args,
2037 ..
2038 }) = st
2039 {
2040 if name.as_ref() == "append" && is_string_builder(cls) && args.len() == 1 {
2041 if let Some(Expr::Local { var, .. }) = owner.as_deref() {
2042 appended_stmts.insert(*var);
2043 }
2044 }
2045 }
2046 });
2047
2048 rewrite_exprs(s, &mut |e| {
2049 fold_concat_in_expr(e, &values, &appended_stmts);
2050 });
2051
2052 for _ in 0..8 {
2056 let mut reads: HashSet<u32> = HashSet::default();
2057 stmt_collect_vars(s, &mut reads, false);
2058 if drop_unused_assigns(s, &reads) == 0 {
2059 break;
2060 }
2061 }
2062}
2063
2064pub fn count_stmts_deep(s: &Stmt, out: &mut usize) {
2066 *out += 1;
2067 walk_all(s, &mut |_| {
2068 *out += 1;
2069 });
2070}
2071
2072struct VarAnalysis {
2074 assigns: Vec<usize>,
2075 reads: Vec<usize>,
2076 values: Vec<Option<Expr>>,
2078}
2079
2080fn analyze_vars(s: &Stmt, a: &mut VarAnalysis) {
2081 walk_all(s, &mut |st| {
2082 let exprs: Vec<&Expr> = match st {
2083 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
2084 if let Expr::Local { var, .. } = &**target {
2085 grow_to(&mut a.assigns, *var);
2086 let idx = *var as usize;
2087 a.assigns[idx] += 1;
2088 if a.assigns[idx] == 1 {
2089 if idx >= a.values.len() {
2090 a.values.resize(idx + 1, None);
2091 }
2092 a.values[idx] = Some((**value).clone());
2093 } else if idx < a.values.len() {
2094 a.values[idx] = None;
2095 }
2096 }
2097 let mut v: Vec<&Expr> = vec![value];
2098 if !matches!(&**target, Expr::Local { .. }) {
2099 v.push(target);
2100 }
2101 v
2102 }
2103 Stmt::ExprStmt(e @ (Expr::PreIncDec { .. } | Expr::PostIncDec { .. })) => {
2104 let inner = match e {
2109 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => e,
2110 _ => unreachable!(),
2111 };
2112 if let Expr::Local { var, .. } = &**inner {
2113 grow_to(&mut a.assigns, *var);
2114 let idx = *var as usize;
2115 a.assigns[idx] += 1;
2116 if idx < a.values.len() {
2117 a.values[idx] = None;
2118 }
2119 return;
2120 }
2121 vec![e]
2122 }
2123 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
2124 vec![e]
2125 }
2126 Stmt::Return(Some(e)) => vec![e],
2127 Stmt::LocalDef {
2128 var, init: Some(e), ..
2129 } => {
2130 grow_to(&mut a.assigns, *var);
2131 let idx = *var as usize;
2132 a.assigns[idx] += 1;
2133 if a.assigns[idx] == 1 {
2134 if idx >= a.values.len() {
2135 a.values.resize(idx + 1, None);
2136 }
2137 a.values[idx] = Some(e.clone());
2138 } else if idx < a.values.len() {
2139 a.values[idx] = None;
2140 }
2141 vec![e]
2142 }
2143 Stmt::If { cond, .. } => vec![cond],
2144 Stmt::While { cond, .. } => vec![cond],
2145 Stmt::DoWhile { cond, .. } => vec![cond],
2146 _ => Vec::new(),
2147 };
2148 for e in exprs {
2149 visit_exprs(e, &mut |x| {
2150 if let Expr::Local { var, .. } = x {
2151 grow_to(&mut a.reads, *var);
2152 a.reads[*var as usize] += 1;
2153 }
2154 });
2155 }
2156 });
2157 let n = a.assigns.len().max(a.reads.len()).max(a.values.len());
2159 a.assigns.resize(n, 0);
2160 a.reads.resize(n, 0);
2161 a.values.resize(n, None);
2162}
2163
2164fn grow_to(v: &mut Vec<usize>, var: u32) {
2165 if var as usize >= v.len() {
2166 v.resize(var as usize + 1, 0);
2167 }
2168}
2169
2170fn count_assignments(s: &Stmt, counts: &mut Vec<usize>) {
2171 walk_all(s, &mut |st| match st {
2172 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
2173 if let Expr::Local { var, .. } = &**target {
2174 grow_to(counts, *var);
2175 counts[*var as usize] += 1;
2176 }
2177 }
2178 Stmt::LocalDef {
2179 var, init: Some(_), ..
2180 } => {
2181 grow_to(counts, *var);
2182 counts[*var as usize] += 1;
2183 }
2184 _ => {}
2185 });
2186}
2187
2188fn record_assignments(s: &Stmt, counts: &[usize], out: &mut Vec<Option<Expr>>) {
2189 walk_all(s, &mut |st| match st {
2190 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
2191 if let Expr::Local { var, .. } = &**target {
2192 if counts.get(*var as usize).copied().unwrap_or(0) == 1 {
2193 if *var as usize >= out.len() {
2194 out.resize(*var as usize + 1, None);
2195 }
2196 out[*var as usize] = Some((**value).clone());
2197 }
2198 }
2199 }
2200 Stmt::LocalDef {
2201 var, init: Some(e), ..
2202 } if counts.get(*var as usize).copied().unwrap_or(0) == 1 => {
2203 if *var as usize >= out.len() {
2204 out.resize(*var as usize + 1, None);
2205 }
2206 out[*var as usize] = Some(e.clone());
2207 }
2208 _ => {}
2209 });
2210}
2211
2212fn fold_concat_in_expr(e: &mut Expr, values: &[Option<Expr>], appended: &HashSet<u32>) {
2213 deep_rewrite(e, &mut |x| {
2214 if let Expr::Method {
2215 cls,
2216 name,
2217 args,
2218 owner,
2219 ..
2220 } = x
2221 {
2222 if name.as_ref() == "toString" && args.is_empty() && is_string_builder(cls) {
2223 if let Some(o) = owner {
2224 let mut has_stmt_appends = false;
2227 visit_exprs(o, &mut |y| {
2228 if let Expr::Local { var, .. } = y {
2229 if appended.contains(var) {
2230 has_stmt_appends = true;
2231 }
2232 }
2233 });
2234 if has_stmt_appends {
2235 return;
2236 }
2237 let resolved = resolve_local(o, values);
2238 if let Some(parts) = collect_sb_parts(&resolved, values, 0) {
2239 if !parts.is_empty() {
2248 *x = Expr::StringConcat(parts);
2249 }
2250 }
2251 }
2252 }
2253 }
2254 });
2255}
2256
2257fn is_string_builder(cls: &str) -> bool {
2258 cls == "java/lang/StringBuilder" || cls == "java/lang/StringBuffer"
2259}
2260
2261fn resolve_local(e: &Expr, values: &[Option<Expr>]) -> Expr {
2262 match e {
2263 Expr::Local { var, ty } => values
2264 .get(*var as usize)
2265 .and_then(|o| o.clone())
2266 .unwrap_or_else(|| Expr::Local {
2267 var: *var,
2268 ty: ty.clone(),
2269 }),
2270 other => other.clone(),
2271 }
2272}
2273
2274fn collect_sb_parts(e: &Expr, values: &[Option<Expr>], depth: u32) -> Option<Vec<ConcatPart>> {
2276 if depth > 16 {
2277 return None;
2278 }
2279 match e {
2280 Expr::Method {
2281 cls,
2282 name,
2283 args,
2284 owner,
2285 ..
2286 } if name.as_ref() == "append" && is_string_builder(cls) && args.len() == 1 => {
2287 let mut parts =
2288 collect_sb_parts(&resolve_local(owner.as_deref()?, values), values, depth + 1)?;
2289 parts.push(ConcatPart::Str(args[0].clone()));
2290 Some(parts)
2291 }
2292 Expr::New {
2293 cls,
2294 args,
2295 raw: false,
2296 ..
2297 } if is_string_builder(cls) => {
2298 let mut parts = Vec::new();
2299 for a in args {
2300 if let Expr::Const(ConstVal::Str(sv)) = a {
2301 parts.push(ConcatPart::Const(sv.to_string()));
2302 } else {
2303 parts.push(ConcatPart::Str(a.clone()));
2304 }
2305 }
2306 Some(parts)
2307 }
2308 _ => None,
2309 }
2310}
2311
2312fn drop_unused_assigns(s: &mut Stmt, reads: &HashSet<u32>) -> usize {
2313 let mut dropped = 0usize;
2314 walk_mut_deep(s, &mut |st| {
2315 if let Stmt::Block(v) = st {
2316 let before = v.len();
2317 v.retain(|x| match x {
2318 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
2321 Expr::Local { var, .. } => reads.contains(var) || !is_sbish(value),
2322 _ => true,
2323 },
2324 Stmt::LocalDef { var, init, .. } => {
2325 reads.contains(var) || !init.as_ref().map(is_sbish).unwrap_or(false)
2326 }
2327 _ => true,
2328 });
2329 dropped += before - v.len();
2330 }
2331 });
2332 dropped
2333}
2334
2335fn is_sbish(e: &Expr) -> bool {
2336 match e {
2337 Expr::New {
2338 cls, raw: false, ..
2339 } => is_string_builder(cls),
2340 Expr::Method { name, cls, .. } => name.as_ref() == "append" && is_string_builder(cls),
2341 _ => false,
2342 }
2343}
2344
2345fn walk_mut_deep<F: FnMut(&mut Stmt)>(s: &mut Stmt, f: &mut F) {
2346 f(s);
2347 walk_mut(s, &mut |x| walk_mut_deep(x, f));
2348}
2349
2350pub fn drop_empty_finallies(body: &mut Stmt) {
2366 fn is_empty_block(s: &Stmt) -> bool {
2367 match s {
2368 Stmt::Block(v) => v.iter().all(is_empty_block),
2369 _ => false,
2370 }
2371 }
2372 walk_mut_deep(body, &mut |st| {
2373 let Stmt::Try {
2374 body: b,
2375 catches,
2376 finally,
2377 } = st
2378 else {
2379 return;
2380 };
2381 if !catches.is_empty() {
2382 return;
2383 }
2384 match finally {
2385 None => {}
2389 Some(f) if is_empty_block(f) => {}
2390 Some(_) => return,
2391 }
2392 let inner = std::mem::replace(&mut **b, Stmt::Block(vec![]));
2393 *st = inner;
2394 });
2395}
2396
2397pub fn drop_pure_value_stmts(body: &mut Stmt) {
2402 fn is_pure(s: &Stmt) -> bool {
2405 let Stmt::ExprStmt(e) = s else {
2406 return false;
2407 };
2408 !has_side_effects(e)
2409 }
2410 walk_mut_deep(body, &mut |st| {
2411 if is_pure(st) {
2414 *st = Stmt::Block(vec![]);
2415 return;
2416 }
2417 match st {
2418 Stmt::Switch { cases, .. } => {
2419 for c in cases.iter_mut() {
2420 c.body.retain(|x| !is_pure(x));
2421 }
2422 }
2423 Stmt::For { init, .. } => init.retain(|x| !is_pure(x)),
2424 _ => {}
2425 }
2426 });
2427}
2428
2429pub fn drop_dead_raw_news(body: &mut Stmt) {
2430 fn is_orphan(s: &Stmt) -> bool {
2431 matches!(s, Stmt::ExprStmt(Expr::New { raw: true, .. }))
2432 }
2433 walk_mut_deep(body, &mut |st| match st {
2434 Stmt::Block(v) => v.retain(|x| !is_orphan(x)),
2435 Stmt::Switch { cases, .. } => {
2436 for c in cases.iter_mut() {
2437 c.body.retain(|x| !is_orphan(x));
2438 }
2439 }
2440 Stmt::For { init, .. } => init.retain(|x| !is_orphan(x)),
2441 Stmt::TryWithResources { resources, .. } => resources.retain(|x| !is_orphan(x)),
2442 _ => {}
2443 });
2444}
2445
2446pub fn fold_synchronized(s: &mut Stmt) {
2450 fold_sync_walk(s);
2451}
2452
2453
2454fn fold_sync_walk(s: &mut Stmt) {
2455 walk_mut_deep(s, &mut |st| {
2456 let Stmt::Block(v) = st else { return };
2457 let mut i = 0;
2458 while i < v.len() {
2459 #[allow(unused_assignments)]
2463 let mut advance = true;
2464 if i + 1 < v.len() {
2465 if let Some((repl, consumed)) = try_sync_at(v, i) {
2466 advance = !matches!(repl.first(), Some(Stmt::Synchronized { .. }));
2467 let _ = v.drain(i..i + consumed);
2468 let n = repl.len();
2469 v.splice(i..i, repl);
2470 if advance {
2471 i += n;
2472 }
2473 continue;
2474 }
2475 }
2476 if let Some((repl, consumed)) = try_sync_in_try(v, i) {
2477 let _ = v.drain(i..i + consumed);
2478 let n = repl.len();
2479 v.splice(i..i, repl);
2480 i += n;
2481 continue;
2482 }
2483 if let Some((repl, consumed)) = try_sync_deep(v, i) {
2484 let _ = v.drain(i..i + consumed);
2485 let n = repl.len();
2486 v.splice(i..i, repl);
2487 i += n;
2488 continue;
2489 }
2490 if let Some((repl, consumed)) = try_sync_absorb_after(v, i) {
2491 let _ = v.drain(i..i + consumed);
2492 let n = repl.len();
2493 v.splice(i..i, repl);
2494 i += n;
2495 continue;
2496 }
2497 i += 1;
2498 }
2499 });
2500}
2501
2502fn is_pure_read_expr(e: &Expr) -> bool {
2506 let mut pure = true;
2507 visit_exprs(e, &mut |x| match x {
2508 Expr::Const(_)
2509 | Expr::Local { .. }
2510 | Expr::This
2511 | Expr::Field { .. } => {}
2512 _ => pure = false,
2513 });
2514 pure
2515}
2516
2517fn resolve_lock(e: &Expr, defs: &jdc_core::FxHashMap<u32, Expr>, depth: u32) -> Expr {
2522 if depth >= 4 {
2523 return e.clone();
2524 }
2525 if let Expr::Local { var, .. } = e {
2526 if let Some(d) = defs.get(var) {
2527 return resolve_lock(d, defs, depth + 1);
2528 }
2529 }
2530 e.clone()
2531}
2532
2533fn lock_same(a: &Expr, b: &Expr) -> bool {
2534 match (a, b) {
2535 (Expr::This, Expr::This) => true,
2536 (Expr::Local { var: x, .. }, Expr::Local { var: y, .. }) => x == y,
2537 (
2538 Expr::Field {
2539 owner: oa,
2540 cls: ca,
2541 name: na,
2542 ..
2543 },
2544 Expr::Field {
2545 owner: ob,
2546 cls: cb,
2547 name: nb,
2548 ..
2549 },
2550 ) => {
2551 ca == cb
2552 && na == nb
2553 && match (oa, ob) {
2554 (None, None) => true,
2555 (Some(ra), Some(rb)) => lock_same(ra, rb),
2556 _ => false,
2557 }
2558 }
2559 (Expr::Const(x), Expr::Const(y)) => x == y,
2560 _ => false,
2561 }
2562}
2563
2564fn lock_equiv(a: &Expr, b: &Expr, defs: &jdc_core::FxHashMap<u32, Expr>) -> bool {
2565 lock_same(&resolve_lock(a, defs, 0), &resolve_lock(b, defs, 0))
2566}
2567
2568fn flatten_leaves<'a>(s: &'a Stmt, out: &mut Vec<&'a Stmt>) {
2569 match s {
2570 Stmt::Block(v) => {
2571 for x in v {
2572 flatten_leaves(x, out);
2573 }
2574 }
2575 _ => out.push(s),
2576 }
2577}
2578
2579fn strip_lock_exits(s: &mut Stmt, lock: &Expr, defs: &jdc_core::FxHashMap<u32, Expr>) {
2583 walk_mut_deep(s, &mut |st| {
2584 let Stmt::Block(v) = st else { return };
2585 v.retain(|x| !matches!(x, Stmt::MonitorExit(e) if lock_equiv(e, lock, defs)));
2586 });
2587}
2588
2589fn try_sync_at(v: &[Stmt], i: usize) -> Option<(Vec<Stmt>, usize)> {
2590 let mut defs: jdc_core::FxHashMap<u32, Expr> = jdc_core::FxHashMap::default();
2592 for s in &v[..i] {
2593 if let Stmt::LocalDef { var, init: Some(e), .. } = s {
2594 defs.insert(*var, e.clone());
2595 }
2596 }
2597 let (pre, enter_e, mut j, mut absorbed): (Vec<Stmt>, &Expr, usize, Vec<Stmt>) = match &v[i] {
2601 Stmt::MonitorEnter(e) => (Vec::new(), e, i + 1, Vec::new()),
2602 Stmt::Block(b) => {
2603 let split = b
2604 .iter()
2605 .position(|x| matches!(x, Stmt::MonitorEnter(_)))?;
2606 let e = match &b[split] {
2607 Stmt::MonitorEnter(e) => e,
2608 _ => unreachable!(),
2609 };
2610 let mut absorbed: Vec<Stmt> = Vec::new();
2611 let mut k = split + 1;
2612 while let Some(Stmt::LocalDef { var, init: Some(ex), .. }) = b.get(k) {
2613 if !is_pure_read_expr(ex) {
2614 break;
2615 }
2616 defs.insert(*var, ex.clone());
2617 absorbed.push(b[k].clone());
2618 k += 1;
2619 }
2620 if k != b.len() {
2623 return None;
2624 }
2625 let pre: Vec<Stmt> = b[..split].to_vec();
2626 for s in &pre {
2627 if let Stmt::LocalDef { var, init: Some(e2), .. } = s {
2628 defs.insert(*var, e2.clone());
2629 }
2630 }
2631 (pre, e, i + 1, absorbed)
2632 }
2633 _ => return None,
2634 };
2635 let mut absorbed_vars: Vec<u32> = Vec::new();
2639 while let Some(Stmt::LocalDef { var, init: Some(e), .. }) = v.get(j) {
2640 if !is_pure_read_expr(e) {
2641 break;
2642 }
2643 defs.insert(*var, e.clone());
2644 absorbed_vars.push(*var);
2645 absorbed.push(v[j].clone());
2646 j += 1;
2647 }
2648 let Stmt::Try {
2649 body,
2650 catches,
2651 finally,
2652 } = v.get(j)?
2653 else {
2654 return None;
2655 };
2656 if finally.is_some() || catches.len() != 1 {
2657 return None;
2658 }
2659 let c = &catches[0];
2660 if !c.exc.is_empty() {
2661 return None;
2662 }
2663 let mut flat: Vec<&Stmt> = Vec::new();
2666 flatten_leaves(c.body.as_ref(), &mut flat);
2667 if flat.len() < 2 {
2668 return None;
2669 }
2670 let Stmt::MonitorExit(exit_e) = flat[0] else {
2671 return None;
2672 };
2673 if !lock_equiv(enter_e, exit_e, &defs) {
2674 return None;
2675 }
2676 if !matches!(flat[flat.len() - 1], Stmt::Throw(_)) {
2677 return None;
2678 }
2679 for mid in &flat[1..flat.len() - 1] {
2680 if !matches!(mid, Stmt::MonitorEnter(_) | Stmt::MonitorExit(_)) {
2681 return None;
2682 }
2683 }
2684 if !absorbed_vars.is_empty() {
2687 let outside = count_locals_stmts(&v[j + 1..]);
2688 if absorbed_vars
2689 .iter()
2690 .any(|av| outside.get(av).copied().unwrap_or(0) > 0)
2691 {
2692 return None;
2693 }
2694 }
2695 let mut body_stmt = (**body).clone();
2696 strip_lock_exits(&mut body_stmt, enter_e, &defs);
2697 absorbed.push(body_stmt);
2698 let mut consumed = j - i + 1;
2699 if let Some(Stmt::MonitorExit(e)) = v.get(j + 1) {
2702 if lock_equiv(e, enter_e, &defs) {
2703 consumed += 1;
2704 }
2705 }
2706 let mut repl = pre;
2707 repl.push(Stmt::Synchronized {
2708 lock: enter_e.clone(),
2709 body: Box::new(Stmt::Block(absorbed)),
2710 });
2711 Some((repl, consumed))
2712}
2713
2714fn try_sync_in_try(v: &[Stmt], i: usize) -> Option<(Vec<Stmt>, usize)> {
2720 let Stmt::Try {
2721 body,
2722 catches,
2723 finally,
2724 } = &v[i]
2725 else {
2726 return None;
2727 };
2728 if finally.is_some() || catches.len() != 1 {
2729 return None;
2730 }
2731 let c = &catches[0];
2732 if !c.exc.is_empty() {
2733 return None;
2734 }
2735 let mut flat: Vec<&Stmt> = Vec::new();
2736 flatten_leaves(c.body.as_ref(), &mut flat);
2737 if flat.len() < 2 {
2738 return None;
2739 }
2740 let Stmt::MonitorExit(_) = flat[0] else {
2741 return None;
2742 };
2743 if !matches!(flat[flat.len() - 1], Stmt::Throw(_)) {
2744 return None;
2745 }
2746 for mid in &flat[1..flat.len() - 1] {
2747 if !matches!(mid, Stmt::MonitorEnter(_) | Stmt::MonitorExit(_)) {
2748 return None;
2749 }
2750 }
2751 let Stmt::Block(bv) = body.as_ref() else {
2755 return None;
2756 };
2757 let mut defs: jdc_core::FxHashMap<u32, Expr> = jdc_core::FxHashMap::default();
2758 for s in &v[..i] {
2759 if let Stmt::LocalDef { var, init: Some(e), .. } = s {
2760 defs.insert(*var, e.clone());
2761 }
2762 }
2763 let mut pre: Vec<Stmt> = Vec::new();
2764 let mut enter_e: Option<Expr> = None;
2765 let mut absorbed: Vec<Stmt> = Vec::new();
2766 let mut absorbed_vars: Vec<u32> = Vec::new();
2767 let mut rest_start: Option<usize> = None;
2768 for (p, el) in bv.iter().enumerate() {
2769 match el {
2770 Stmt::MonitorEnter(e) => {
2771 enter_e = Some(e.clone());
2772 rest_start = Some(p + 1);
2773 break;
2774 }
2775 Stmt::Block(inner) => {
2776 let split = inner
2777 .iter()
2778 .position(|x| matches!(x, Stmt::MonitorEnter(_)));
2779 if let Some(sp) = split {
2780 let mut k = sp + 1;
2781 let mut ok = true;
2782 let mut tail: Vec<Stmt> = Vec::new();
2783 while let Some(Stmt::LocalDef { var: _, init: Some(ex), .. }) = inner.get(k) {
2784 if !is_pure_read_expr(ex) {
2785 ok = false;
2786 break;
2787 }
2788 tail.push(inner[k].clone());
2789 k += 1;
2790 }
2791 if ok && k == inner.len() {
2792 for s in &inner[..sp] {
2793 if let Stmt::LocalDef { var, init: Some(ex), .. } = s {
2794 defs.insert(*var, ex.clone());
2795 }
2796 }
2797 pre.extend(inner[..sp].iter().cloned());
2798 enter_e = Some(match &inner[sp] {
2799 Stmt::MonitorEnter(e) => e.clone(),
2800 _ => unreachable!(),
2801 });
2802 absorbed = tail;
2803 rest_start = Some(p + 1);
2804 break;
2805 }
2806 }
2807 for s in inner.iter() {
2809 if let Stmt::LocalDef { var, init: Some(ex), .. } = s {
2810 defs.insert(*var, ex.clone());
2811 }
2812 }
2813 pre.push(el.clone());
2814 }
2815 other => {
2816 if let Stmt::LocalDef { var, init: Some(ex), .. } = other {
2817 defs.insert(*var, ex.clone());
2818 }
2819 pre.push(other.clone());
2820 }
2821 }
2822 }
2823 let enter_e = enter_e?;
2824 let rs = rest_start?;
2825 let mut q = rs;
2827 while let Some(Stmt::LocalDef { var, init: Some(ex), .. }) = bv.get(q) {
2828 if !is_pure_read_expr(ex) {
2829 break;
2830 }
2831 defs.insert(*var, ex.clone());
2832 absorbed_vars.push(*var);
2833 absorbed.push(bv[q].clone());
2834 q += 1;
2835 }
2836 let rest: Vec<Stmt> = bv[q..].to_vec();
2837 if rest.is_empty() {
2838 return None;
2839 }
2840 if pre
2843 .iter()
2844 .any(|s| !matches!(s, Stmt::LocalDef { init: Some(e), .. } if is_pure_read_expr(e)))
2845 {
2846 return None;
2847 }
2848 let Stmt::MonitorExit(exit_e) = flat[0] else {
2850 return None;
2851 };
2852 if !lock_equiv(&enter_e, exit_e, &defs) {
2853 return None;
2854 }
2855 let outside_vars: Vec<u32> = pre
2857 .iter()
2858 .chain(absorbed.iter())
2859 .filter_map(|s| match s {
2860 Stmt::LocalDef { var, .. } => Some(*var),
2861 _ => None,
2862 })
2863 .collect();
2864 if !outside_vars.is_empty() {
2865 let outside = count_locals_stmts(&v[i + 1..]);
2866 if outside_vars
2867 .iter()
2868 .any(|av| outside.get(av).copied().unwrap_or(0) > 0)
2869 {
2870 return None;
2871 }
2872 }
2873 let mut body_stmts = pre;
2874 body_stmts.extend(absorbed);
2875 body_stmts.extend(rest);
2876 let mut sync_body = Stmt::Block(body_stmts);
2877 strip_lock_exits(&mut sync_body, &enter_e, &defs);
2878 let mut consumed = 1;
2879 if let Some(Stmt::MonitorExit(e)) = v.get(i + 1) {
2880 if lock_equiv(e, &enter_e, &defs) {
2881 consumed += 1;
2882 }
2883 }
2884 Some((
2885 vec![Stmt::Synchronized {
2886 lock: enter_e,
2887 body: Box::new(sync_body),
2888 }],
2889 consumed,
2890 ))
2891}
2892
2893fn try_sync_absorb_after(v: &[Stmt], i: usize) -> Option<(Vec<Stmt>, usize)> {
2903 let Stmt::Synchronized { lock, body } = &v[i] else {
2904 return None;
2905 };
2906 let Stmt::Try {
2907 body: tbody,
2908 catches,
2909 finally,
2910 } = v.get(i + 1)?
2911 else {
2912 return None;
2913 };
2914 if finally.is_some() {
2915 return None;
2916 }
2917 let defs = defs_probe(v, i, body);
2923 let mut absorbed_mid: Vec<Stmt> = Vec::new();
2924 let mut rest_after: Option<Stmt> = None;
2925 let mut found = false;
2926 let mut scan = i + 2;
2927 'scan: while scan < v.len() && scan < i + 14 {
2928 match &v[scan] {
2929 Stmt::MonitorExit(e) => {
2930 if !lock_equiv(e, lock, &defs) {
2931 return None;
2932 }
2933 found = true;
2934 scan += 1;
2935 break;
2936 }
2937 Stmt::Block(bv) => {
2938 for (k, s) in bv.iter().enumerate() {
2939 match s {
2940 Stmt::MonitorExit(e) => {
2941 if !lock_equiv(e, lock, &defs) {
2942 return None;
2943 }
2944 absorbed_mid.push(Stmt::Block(bv[..k].to_vec()));
2945 let rest = &bv[k + 1..];
2946 rest_after = (!rest.is_empty())
2947 .then(|| Stmt::Block(rest.to_vec()));
2948 found = true;
2949 scan += 1;
2950 break 'scan;
2951 }
2952 Stmt::ExprStmt(e) if mid_safe(e) || is_local_commit(e) => {}
2953 _ => break 'scan,
2954 }
2955 }
2956 absorbed_mid.push(v[scan].clone());
2957 scan += 1;
2958 }
2959 Stmt::ExprStmt(e) if mid_safe(e) || is_local_commit(e) => {
2960 absorbed_mid.push(v[scan].clone());
2961 scan += 1;
2962 }
2963 _ => break,
2964 }
2965 }
2966 if !found {
2967 return None;
2968 }
2969 let consumed = scan - i;
2970 let mut t = (**tbody).clone();
2973 let defs = defs_probe(v, i, body);
2974 strip_lock_exits(&mut t, lock, &defs);
2975 let mut cs = Vec::with_capacity(catches.len());
2976 for c in catches.iter() {
2977 let mut cb = (*c.body).clone();
2978 strip_lock_exits(&mut cb, lock, &defs);
2979 let bare_rethrow = c.exc.is_empty() && {
2983 let mut leaves: Vec<&Stmt> = Vec::new();
2984 flatten_leaves(&cb, &mut leaves);
2985 matches!(leaves.as_slice(), [Stmt::Throw(Expr::Local { var, .. })] if *var == c.var)
2986 };
2987 if bare_rethrow {
2988 continue;
2989 }
2990 let mut c2 = c.clone();
2991 *c2.body = cb;
2992 cs.push(c2);
2993 }
2994 let mut new_body = (**body).clone();
2995 let adopted = Stmt::Try {
2996 body: Box::new(t),
2997 catches: cs,
2998 finally: None,
2999 };
3000 match &mut new_body {
3001 Stmt::Block(bv) => bv.push(adopted),
3002 other => {
3003 let inner = std::mem::replace(other, Stmt::Block(vec![]));
3004 *other = Stmt::Block(vec![inner, adopted]);
3005 }
3006 }
3007 if let Stmt::Block(bv) = &mut new_body {
3008 bv.extend(absorbed_mid);
3009 }
3010 let sync = Stmt::Synchronized {
3011 lock: lock.clone(),
3012 body: Box::new(new_body),
3013 };
3014 let mut repl = vec![sync];
3015 if let Some(r) = rest_after {
3016 repl.push(r);
3017 }
3018 Some((repl, consumed))
3019}
3020
3021fn mid_safe(e: &Expr) -> bool {
3025 !has_side_effects(e)
3026}
3027
3028fn is_local_commit(e: &Expr) -> bool {
3030 match e {
3031 Expr::Assign { target, op: AssignOp::Plain, .. } => {
3032 matches!(&**target, Expr::Local { .. })
3033 }
3034 _ => false,
3035 }
3036}
3037
3038fn defs_probe<'a>(
3041 v: &'a [Stmt],
3042 i: usize,
3043 body: &'a Stmt,
3044) -> jdc_core::FxHashMap<u32, Expr> {
3045 let mut defs: jdc_core::FxHashMap<u32, Expr> = jdc_core::FxHashMap::default();
3046 for s in &v[..i] {
3047 if let Stmt::LocalDef { var, init: Some(e), .. } = s {
3048 defs.insert(*var, e.clone());
3049 }
3050 }
3051 if let Stmt::Block(bv) = body {
3052 for s in bv {
3053 if let Stmt::LocalDef { var, init: Some(e), .. } = s {
3054 defs.insert(*var, e.clone());
3055 }
3056 }
3057 }
3058 defs
3059}
3060
3061
3062fn try_sync_deep(v: &[Stmt], i: usize) -> Option<(Vec<Stmt>, usize)> {
3071 let Stmt::Try {
3072 body,
3073 catches,
3074 finally,
3075 } = &v[i]
3076 else {
3077 return None;
3078 };
3079 if finally.is_some() || catches.len() != 1 {
3080 return None;
3081 }
3082 let c = &catches[0];
3083 if !c.exc.is_empty() {
3084 return None;
3085 }
3086 let mut flat: Vec<&Stmt> = Vec::new();
3087 flatten_leaves(c.body.as_ref(), &mut flat);
3088 if flat.len() < 2 {
3089 return None;
3090 }
3091 let catch_exit = match flat[0] {
3092 Stmt::MonitorExit(e) => e.clone(),
3093 _ => return None,
3094 };
3095 if !matches!(flat[flat.len() - 1], Stmt::Throw(_)) {
3096 return None;
3097 }
3098 for mid in &flat[1..flat.len() - 1] {
3099 if !matches!(mid, Stmt::MonitorEnter(_) | Stmt::MonitorExit(_)) {
3100 return None;
3101 }
3102 }
3103 let mut defs0: jdc_core::FxHashMap<u32, Expr> = jdc_core::FxHashMap::default();
3104 for s in &v[..i] {
3105 if let Stmt::LocalDef { var, init: Some(e), .. } = s {
3106 defs0.insert(*var, e.clone());
3107 }
3108 }
3109 let mut body_mut = (**body).clone();
3110 let folded = match &mut body_mut {
3111 Stmt::Block(b) => try_fold_spine(b, &catch_exit, &defs0),
3112 Stmt::If {
3113 then_stmt,
3114 else_stmt,
3115 ..
3116 } => {
3117 let mut hit = false;
3118 if let Stmt::Block(b) = &mut **then_stmt {
3119 hit |= try_fold_spine(b, &catch_exit, &defs0);
3120 }
3121 if !hit {
3122 if let Some(els) = else_stmt {
3123 if let Stmt::Block(b) = &mut **els {
3124 hit |= try_fold_spine(b, &catch_exit, &defs0);
3125 }
3126 }
3127 }
3128 hit
3129 }
3130 _ => false,
3131 };
3132 if !folded {
3133 return None;
3134 }
3135 let throw = flat[flat.len() - 1].clone();
3137 let mut catches_out = catches.clone();
3138 *catches_out[0].body = Stmt::Block(vec![throw]);
3139 let mut consumed = 1;
3140 if let Some(Stmt::MonitorExit(e)) = v.get(i + 1) {
3141 if lock_equiv(e, &catch_exit, &defs0) {
3142 consumed += 1;
3143 }
3144 }
3145 Some((
3146 vec![Stmt::Try {
3147 body: Box::new(body_mut),
3148 catches: catches_out,
3149 finally: None,
3150 }],
3151 consumed,
3152 ))
3153}
3154
3155fn try_fold_spine(
3161 seq: &mut Vec<Stmt>,
3162 catch_exit: &Expr,
3163 defs_in: &jdc_core::FxHashMap<u32, Expr>,
3164) -> bool {
3165 for p in (0..seq.len()).rev() {
3166 if let Stmt::MonitorEnter(e0) = &seq[p] {
3168 let mut defs = defs_in.clone();
3169 for s in &seq[..p] {
3170 if let Stmt::LocalDef { var, init: Some(ex), .. } = s {
3171 defs.insert(*var, ex.clone());
3172 }
3173 }
3174 for s in &seq[p + 1..] {
3177 match s {
3178 Stmt::LocalDef { var, init: Some(ex), .. } if is_pure_read_expr(ex) => {
3179 defs.insert(*var, ex.clone());
3180 }
3181 _ => break,
3182 }
3183 }
3184 if !lock_equiv(e0, catch_exit, &defs) {
3185 return false;
3186 }
3187 let enter_e = e0.clone();
3188 let rest: Vec<Stmt> = seq.split_off(p + 1);
3189 let mut body = Stmt::Block(rest);
3190 strip_lock_exits(&mut body, &enter_e, &defs);
3191 seq[p] = Stmt::Synchronized {
3192 lock: enter_e,
3193 body: Box::new(body),
3194 };
3195 return true;
3196 }
3197 let is_last = p + 1 == seq.len();
3200 let mut trial = seq[p].clone();
3201 let hit = match &mut trial {
3202 Stmt::Block(b) => try_fold_spine(b, catch_exit, defs_in),
3203 Stmt::If {
3204 then_stmt,
3205 else_stmt,
3206 ..
3207 } => {
3208 let mut hit = false;
3209 if let Stmt::Block(b) = &mut **then_stmt {
3210 hit |= try_fold_spine(b, catch_exit, defs_in);
3211 }
3212 if !hit {
3213 if let Some(els) = else_stmt {
3214 if let Stmt::Block(b) = &mut **els {
3215 hit |= try_fold_spine(b, catch_exit, defs_in);
3216 }
3217 }
3218 }
3219 hit
3220 }
3221 _ => false,
3222 };
3223 if hit {
3224 if is_last {
3225 seq[p] = trial;
3226 return true;
3227 }
3228 return false;
3229 }
3230 }
3231 false
3232}
3233
3234pub fn forward_single_use(s: &mut Stmt, _vt: &VarTable) {
3239 let mut analysis = VarAnalysis {
3243 assigns: Vec::new(),
3244 reads: Vec::new(),
3245 values: Vec::new(),
3246 };
3247 analyze_vars(s, &mut analysis);
3248 let mut assigns = analysis.assigns;
3249 let mut reads = analysis.reads;
3250 let n_vars = assigns.len().max(_vt.vars.len()).max(1);
3253 assigns.resize(n_vars, 0);
3254 reads.resize(n_vars, 0);
3255 let mut values = analysis.values;
3256 values.resize(n_vars, None);
3257
3258 let mut cand = vec![false; n_vars];
3263 let mut edges: Vec<Vec<u32>> = vec![Vec::new(); n_vars];
3264 for (v, cand_v) in cand.iter_mut().enumerate() {
3265 if assigns.get(v).copied().unwrap_or(0) != 1 || reads.get(v).copied().unwrap_or(0) != 1 {
3266 continue;
3267 }
3268 if let Some(val) = values.get(v).and_then(|o| o.as_ref()) {
3269 let mut refs = HashSet::default();
3270 collect_vars(val, &mut refs);
3271 if refs.iter().any(|r| assigns[*r as usize] > 1) {
3272 continue;
3273 }
3274 edges[v] = refs.into_iter().collect();
3278 }
3279 *cand_v = true;
3280 }
3281
3282 let mut color = vec![0u8; n_vars]; let mut bad = vec![false; n_vars];
3300 for root in 0..n_vars {
3301 if color[root] != 0 || edges[root].is_empty() {
3302 continue;
3303 }
3304 color[root] = 1;
3305 let mut stack: Vec<(usize, usize)> = vec![(root, 0)];
3306 while let Some(top) = stack.last_mut() {
3307 let v = top.0;
3308 let i = top.1;
3309 top.1 += 1;
3310 if i < edges[v].len() {
3311 let r = edges[v][i] as usize;
3312 if color[r] == 1 {
3313 for &(pn, _) in &stack {
3314 bad[pn] = true;
3315 }
3316 } else if color[r] == 0 {
3317 color[r] = 1;
3318 stack.push((r, 0));
3319 } else if bad[r] {
3320 for &(pn, _) in &stack {
3321 bad[pn] = true;
3322 }
3323 }
3324 } else {
3325 color[v] = 2;
3326 stack.pop();
3327 }
3328 }
3329 }
3330 let single: Vec<bool> = (0..n_vars).map(|v| cand[v] && !bad[v]).collect();
3331 if !single.iter().any(|&b| b) {
3332 return;
3333 }
3334 let mut pure: Vec<bool> = (0..n_vars)
3335 .map(|v| {
3336 single[v]
3337 && values
3338 .get(v)
3339 .and_then(|o| o.as_ref())
3340 .map(|e| !has_side_effects(e))
3341 .unwrap_or(false)
3342 })
3343 .collect();
3344 let mut impure: Vec<bool> = (0..n_vars).map(|v| single[v] && !pure[v]).collect();
3345
3346 {
3359 type FieldKey = (std::sync::Arc<str>, std::sync::Arc<str>, bool);
3360 let mut field_writes: jdc_core::FxHashMap<FieldKey, Vec<usize>> =
3361 jdc_core::FxHashMap::default();
3362 let mut def_pos: Vec<usize> = vec![usize::MAX; n_vars];
3363 let mut use_pos: Vec<usize> = vec![usize::MAX; n_vars];
3364 let mut pos = 0usize;
3365 walk_all(s, &mut |st| {
3366 let here = pos;
3367 pos += 1;
3368 for e in stmt_read_exprs(st) {
3369 visit_exprs(e, &mut |x| {
3370 if let Expr::Local { var, .. } = x {
3371 let v = *var as usize;
3372 if v < use_pos.len() && use_pos[v] == usize::MAX {
3373 use_pos[v] = here;
3374 }
3375 }
3376 });
3377 }
3378 match st {
3379 Stmt::LocalDef { var, .. } => {
3380 let v = *var as usize;
3381 if v < def_pos.len() {
3382 def_pos[v] = here;
3383 }
3384 }
3385 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
3386 Expr::Local { var, .. } => {
3387 let v = *var as usize;
3388 if v < def_pos.len() {
3389 def_pos[v] = here;
3390 }
3391 }
3392 Expr::Field { cls, name, is_static, .. } => {
3393 field_writes
3394 .entry((cls.clone(), name.clone(), *is_static))
3395 .or_default()
3396 .push(here);
3397 }
3398 _ => {}
3399 },
3400 Stmt::ExprStmt(Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. }) => {
3401 if let Expr::Field { cls, name, is_static, .. } = &**e {
3402 field_writes
3403 .entry((cls.clone(), name.clone(), *is_static))
3404 .or_default()
3405 .push(here);
3406 }
3407 }
3408 _ => {}
3409 }
3410 });
3411 let mut hazard = vec![false; n_vars];
3412 for v in 0..n_vars {
3413 if !pure[v] {
3414 continue;
3415 }
3416 let Some(Some(val)) = values.get(v).map(|o| o.as_ref()) else {
3417 continue;
3418 };
3419 let (d, u) = (def_pos[v], use_pos[v]);
3420 if d == usize::MAX || u == usize::MAX || u <= d {
3421 continue;
3422 }
3423 visit_exprs(val, &mut |x| {
3424 if let Expr::Field { cls, name, is_static, .. } = x {
3425 if let Some(ws) = field_writes.get(&(cls.clone(), name.clone(), *is_static)) {
3426 if ws.iter().any(|w| *w > d && *w < u) {
3427 hazard[v] = true;
3428 }
3429 }
3430 }
3431 });
3432 }
3433 for v in 0..n_vars {
3434 if hazard[v] {
3435 pure[v] = false;
3436 impure[v] = single[v];
3437 }
3438 }
3439 }
3440
3441 if pure.iter().any(|&b| b) {
3443 let vals: Vec<Option<Expr>> = (0..n_vars)
3444 .map(|v| {
3445 if pure[v] {
3446 values.get(v).cloned().flatten()
3447 } else {
3448 None
3449 }
3450 })
3451 .collect();
3452 rewrite_exprs(s, &mut |e| {
3453 deep_rewrite_reads(e, &mut |x| {
3454 if let Expr::Local { var, .. } = x {
3455 if let Some(Some(v)) = vals.get(*var as usize) {
3456 *x = v.clone();
3457 }
3458 }
3459 });
3460 });
3461 drop_defs(s, &pure);
3462 }
3463
3464 if impure.iter().any(|&b| b) {
3467 forward_adjacent_impure(s, &values, &impure);
3468 }
3469}
3470
3471fn stmt_read_exprs(st: &Stmt) -> Vec<&Expr> {
3477 match st {
3478 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
3479 let mut v: Vec<&Expr> = vec![value];
3480 if !matches!(&**target, Expr::Local { .. }) {
3481 v.push(target);
3482 }
3483 v
3484 }
3485 Stmt::ExprStmt(e @ (Expr::PreIncDec { .. } | Expr::PostIncDec { .. })) => {
3488 let inner = match e {
3489 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => e,
3490 _ => unreachable!(),
3491 };
3492 if matches!(**inner, Expr::Local { .. }) {
3493 Vec::new()
3494 } else {
3495 vec![e]
3496 }
3497 }
3498 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
3499 vec![e]
3500 }
3501 Stmt::Return(Some(e)) => vec![e],
3502 Stmt::LocalDef { init: Some(e), .. } => vec![e],
3503 Stmt::If { cond, .. } => vec![cond],
3504 Stmt::While { cond, .. } => vec![cond],
3505 Stmt::DoWhile { cond, .. } => vec![cond],
3506 Stmt::Switch { selector, .. } => vec![selector],
3512 Stmt::ForEach { iterable, .. } => vec![iterable],
3513 Stmt::Synchronized { lock, .. } => vec![lock],
3514 Stmt::For { cond, update, .. } => {
3515 let mut v: Vec<&Expr> = Vec::with_capacity(update.len() + 1);
3516 if let Some(c) = cond {
3517 v.push(c);
3518 }
3519 v.extend(update.iter());
3520 v
3521 }
3522 Stmt::Assert { cond, msg } => {
3523 let mut v: Vec<&Expr> = vec![cond];
3524 if let Some(m) = msg {
3525 v.push(m);
3526 }
3527 v
3528 }
3529 _ => Vec::new(),
3530 }
3531}
3532
3533fn count_reads(s: &Stmt, out: &mut Vec<usize>) {
3535 walk_all(s, &mut |st| {
3536 for e in stmt_read_exprs(st) {
3537 visit_exprs(e, &mut |x| {
3538 if let Expr::Local { var, .. } = x {
3539 grow_to(out, *var);
3540 out[*var as usize] += 1;
3541 }
3542 });
3543 }
3544 });
3545}
3546
3547
3548fn drop_defs(s: &mut Stmt, vars: &[bool]) {
3549 walk_mut_deep(s, &mut |st| {
3550 if let Stmt::Block(v) = st {
3551 v.retain(|x| match x {
3552 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
3553 Expr::Local { var, .. } => !vars.get(*var as usize).copied().unwrap_or(false),
3554 _ => true,
3555 },
3556 Stmt::LocalDef { var, .. } => !vars.get(*var as usize).copied().unwrap_or(false),
3557 _ => true,
3558 });
3559 }
3560 });
3561}
3562
3563fn forward_adjacent_impure(s: &mut Stmt, values: &[Option<Expr>], impure: &[bool]) {
3565 if let Stmt::Block(v) = s {
3566 let mut i = 0;
3567 while i + 1 < v.len() {
3568 let def_var = match &v[i] {
3569 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
3570 Expr::Local { var, .. } => Some(*var),
3571 _ => None,
3572 },
3573 Stmt::LocalDef { var, .. } => Some(*var),
3574 _ => None,
3575 };
3576 if let Some(var) = def_var {
3577 if impure.get(var as usize).copied().unwrap_or(false) {
3578 let mut reads_here = vec![0usize; values.len().max(1)];
3580 count_reads(&v[i + 1], &mut reads_here);
3581 if reads_here.get(var as usize).copied().unwrap_or(0) == 1 {
3582 let val = match &v[i] {
3586 Stmt::ExprStmt(Expr::Assign { value, .. }) => (**value).clone(),
3587 Stmt::LocalDef { init: Some(e), .. } => (*e).clone(),
3588 _ => {
3589 i += 1;
3590 continue;
3591 }
3592 };
3593 rewrite_exprs(&mut v[i + 1], &mut |e| {
3594 deep_rewrite_reads(e, &mut |x| {
3595 if let Expr::Local { var: v2, .. } = x {
3596 if *v2 == var {
3597 *x = val.clone();
3598 }
3599 }
3600 });
3601 });
3602 v.remove(i);
3603 i = i.saturating_sub(1);
3606 continue;
3607 }
3608 }
3609 }
3610 i += 1;
3611 }
3612 }
3613 walk_mut(s, &mut |x| forward_adjacent_impure(x, values, impure));
3614}
3615
3616pub fn infer_types(vt: &mut VarTable, body: &mut Stmt, ret: &JavaType, env: &MethodEnv) {
3622 let n = vt.vars.len();
3623 let mut evidence: Vec<Vec<(JavaType, bool)>> = vec![Vec::new(); n];
3631 let ev = |evidence: &mut Vec<Vec<(JavaType, bool)>>, var: u32, t: JavaType, strong: bool| {
3632 if (var as usize) < evidence.len() {
3633 evidence[var as usize].push((t, strong));
3634 }
3635 };
3636
3637 walk_all(body, &mut |st| match st {
3638 Stmt::LocalDef { var, init, .. } => {
3639 if let Some(e) = init {
3640 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
3641 let strong = match e {
3649 Expr::Const(_) => false,
3650 Expr::Local { var: src, .. } => {
3651 let src_ty = vt.vars.get(*src as usize).map(|v| v.ty.erased());
3652 let tgt_ty = vt.vars.get(*var as usize).map(|v| v.ty.erased());
3653 match (src_ty, tgt_ty) {
3654 (Some(s), Some(g)) => {
3655 (s.is_numeric() && g.is_numeric())
3656 || (s.is_reference() && g.is_reference())
3657 }
3658 _ => false,
3659 }
3660 }
3661 _ => true,
3662 };
3663 ev(&mut evidence, *var, e.type_ref().erased(), strong);
3664 }
3665 }
3666 Stmt::ExprStmt(e) => {
3667 if let Expr::Assign { target, value, .. } = e {
3674 if let Expr::Field { owner: Some(o), cls, is_static: false, .. } = &**target {
3681 if cls.is_empty() {
3682 } else if let Expr::Local { var, .. } = &**o {
3684 ev(
3685 &mut evidence,
3686 *var,
3687 JavaType::Object(cls.clone()),
3688 true,
3689 );
3690 }
3691 }
3692 if let Expr::Local { var, .. } = &**target {
3693 let strong = match &**value {
3694 Expr::Const(_) => false,
3695 Expr::Local { var: src, .. } => {
3696 let src_ty = vt.vars.get(*src as usize).map(|v| v.ty.erased());
3703 let tgt_ty = vt.vars.get(*var as usize).map(|v| v.ty.erased());
3704 match (src_ty, tgt_ty) {
3705 (Some(s), Some(g)) => {
3706 (s.is_numeric() && g.is_numeric())
3707 || (s.is_reference() && g.is_reference())
3708 }
3709 _ => false,
3710 }
3711 }
3712 _ => true,
3713 };
3714 ev(&mut evidence, *var, value.type_ref().erased(), strong);
3715 }
3716 }
3717 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
3718 }
3719 Stmt::Throw(e) => {
3720 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
3721 if let Expr::Local { var, .. } = e {
3722 ev(
3723 &mut evidence,
3724 *var,
3725 JavaType::Object("java/lang/Throwable".into()),
3726 true,
3727 );
3728 }
3729 }
3730 Stmt::Return(Some(e)) => {
3731 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
3732 if let Expr::Local { var, .. } = e {
3733 ev(&mut evidence, *var, ret.clone(), true);
3734 }
3735 }
3736 Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
3737 if let Expr::Local { var, .. } = e {
3738 ev(
3739 &mut evidence,
3740 *var,
3741 JavaType::Object("java/lang/Object".into()),
3742 true,
3743 );
3744 }
3745 }
3746 Stmt::ForEach {
3747 var,
3748 iterable,
3749 is_array,
3750 ..
3751 } => {
3752 if let Expr::Local { var: v0, .. } = iterable {
3753 if *is_array {
3754 ev(&mut evidence, *v0, JavaType::Array(Box::new(JavaType::Int)), true);
3755 } else {
3756 ev(
3757 &mut evidence,
3758 *v0,
3759 JavaType::Object("java/lang/Object".into()),
3760 true,
3761 );
3762 }
3763 }
3764 let _ = var;
3765 }
3766 _ => {}
3767 });
3768 let _ = env;
3769
3770 let mut derefed: Vec<bool> = vec![false; vt.vars.len()];
3780 mark_derefs(body, &mut derefed);
3781 let mut arr_derefed: Vec<bool> = vec![false; vt.vars.len()];
3786 visit_all_exprs(body, &mut |x| {
3787 if let Expr::ArrayIndex { array, .. } = x {
3788 if let Expr::Local { var, .. } = &**array {
3789 if (*var as usize) < arr_derefed.len() {
3790 arr_derefed[*var as usize] = true;
3791 }
3792 }
3793 }
3794 if let Expr::Bin { op, l, r, .. } = x {
3802 if matches!(op, BinOp::Eq | BinOp::Ne)
3803 && (matches!(&**r, Expr::Const(ConstVal::Null))
3804 || matches!(&**l, Expr::Const(ConstVal::Null)))
3805 {
3806 for side in [l, r] {
3807 if let Expr::Local { var, .. } = &**side {
3808 ev(
3809 &mut evidence,
3810 *var,
3811 JavaType::Object("java/lang/Object".into()),
3812 true,
3813 );
3814 }
3815 }
3816 }
3817 }
3818 });
3819 const FINAL_JDK: &[&str] = &[
3820 "java/lang/Boolean",
3821 "java/lang/String",
3822 "java/lang/Integer",
3823 "java/lang/Long",
3824 "java/lang/Short",
3825 "java/lang/Byte",
3826 "java/lang/Character",
3827 "java/lang/Float",
3828 "java/lang/Double",
3829 ];
3830 for (i, evs) in evidence.iter().enumerate() {
3831 let info = &vt.vars[i];
3832 if info.is_param {
3833 continue;
3834 }
3835 let strong: Vec<JavaType> = evs
3836 .iter()
3837 .filter(|(_, s)| *s)
3838 .map(|(t, _)| t.clone())
3839 .collect();
3840 let all: Vec<JavaType> = evs.iter().map(|(t, _)| t.clone()).collect();
3841 if info.ty.erased().is_reference() {
3842 if strong.is_empty() {
3848 let refs: std::collections::HashSet<&str> = all
3849 .iter()
3850 .filter_map(|t| match t {
3851 JavaType::Object(n) if n.as_ref() != "java/lang/Object" => {
3852 Some(n.as_ref())
3853 }
3854 _ => None,
3855 })
3856 .collect();
3857 let mixed = all.iter().any(|t| t.is_numeric());
3858 if refs.len() >= 2 || (mixed && !refs.is_empty()) || (mixed && all.iter().any(|t| matches!(t, JavaType::Array(_)))) {
3859 vt.vars[i].ty =
3860 TypeRef::J(JavaType::Object("java/lang/Object".into()));
3861 continue;
3862 }
3863 }
3864 if arr_derefed[i] {
3867 let arr = strong
3868 .iter()
3869 .chain(all.iter())
3870 .find(|t| matches!(t, JavaType::Array(_)))
3871 .cloned();
3872 if let Some(t) = arr {
3873 vt.vars[i].ty = TypeRef::J(t);
3874 continue;
3875 }
3876 }
3877 let deref_pick = || {
3881 if !derefed[i] {
3882 return None;
3883 }
3884 strong
3885 .iter()
3886 .find(|t| {
3887 matches!(t, JavaType::Object(n)
3888 if !n.is_empty()
3889 && n.as_ref() != "java/lang/Object"
3890 && !FINAL_JDK.contains(&n.as_ref()))
3891 })
3892 .cloned()
3893 };
3894 if let Some(t) = deref_pick()
3895 .or_else(|| pick_object(&strong))
3896 .or_else(|| pick_object(&all))
3897 {
3898 vt.vars[i].ty = TypeRef::J(t);
3899 }
3900 } else if let Some(t) = pick_object(&strong) {
3901 vt.vars[i].ty = TypeRef::J(t);
3905 } else if strong.contains(&JavaType::Boolean) {
3906 vt.vars[i].ty = TypeRef::J(JavaType::Boolean);
3910 } else if !all.is_empty() && all.iter().all(|t| t.is_numeric()) {
3911 if let Some(t) = pick_numeric(&all) {
3912 vt.vars[i].ty = TypeRef::J(t);
3913 }
3914 } else {
3915 let refs = all.iter().filter(|t| t.is_reference()).count();
3919 let nums = all.iter().filter(|t| t.is_numeric()).count();
3920 if refs > 0 && nums > 0 {
3921 vt.vars[i].ty =
3922 TypeRef::J(JavaType::Object("java/lang/Object".into()));
3923 } else if let Some(t) = pick_object(&strong).or_else(|| pick_object(&all)) {
3924 vt.vars[i].ty = TypeRef::J(t);
3925 }
3926 }
3927 }
3928
3929 let types: Vec<&TypeRef> = vt.vars.iter().map(|v| &v.ty).collect();
3934 rewrite_exprs(body, &mut |e| {
3935 deep_rewrite(e, &mut |x| {
3936 if let Expr::Local { var, ty } = x {
3937 if let Some(want) = types.get(*var as usize) {
3938 if ty != *want {
3939 *ty = (*want).clone();
3940 }
3941 }
3942 }
3943 });
3944 });
3945
3946 coerce_num_consts(body, &types);
3949}
3950
3951fn coerce_num_consts(body: &mut Stmt, types: &[&TypeRef]) {
3952 walk_mut_deep(body, &mut |st| {
3953 let (var, val): (u32, &mut Expr) = match st {
3954 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &mut **target {
3955 Expr::Local { var, .. } => (*var, value),
3956 _ => return,
3957 },
3958 Stmt::LocalDef {
3959 var, init: Some(e), ..
3960 } => (*var, e),
3961 _ => return,
3962 };
3963 let want = match types.get(var as usize) {
3964 Some(TypeRef::J(t)) => t.clone(),
3965 _ => return,
3966 };
3967 let rewritten = match &*val {
3968 Expr::Const(ConstVal::Long(l)) => match &want {
3976 JavaType::Double => Some(Expr::Const(ConstVal::Double(f64::from_bits(*l as u64)))),
3977 JavaType::Float => Some(Expr::Const(ConstVal::Float(f32::from_bits(*l as u32)))),
3978 _ => None,
3979 },
3980 Expr::Const(ConstVal::Int(i)) => match &want {
3981 JavaType::Double => Some(Expr::Const(ConstVal::Double(*i as f64))),
3982 JavaType::Float => Some(Expr::Const(ConstVal::Float(f32::from_bits(*i as u32)))),
3983 _ => None,
3984 },
3985 _ => None,
3986 };
3987 if let Some(e) = rewritten {
3988 *val = e;
3989 }
3990 });
3991}
3992
3993fn pick_object(evs: &[JavaType]) -> Option<JavaType> {
3994 evs.iter()
3995 .find(|t| {
3996 t.is_reference()
3997 && !matches!(t, JavaType::Object(n) if n.is_empty() || n.as_ref() == "java/lang/Object")
3998 })
3999 .cloned()
4000}
4001
4002fn pick_numeric(evs: &[JavaType]) -> Option<JavaType> {
4003 if evs.is_empty() {
4004 return None;
4005 }
4006 let mut t = evs[0].clone();
4007 for x in &evs[1..] {
4008 t = join_numeric(&t, x);
4009 }
4010 Some(t)
4011}
4012
4013fn join_numeric(a: &JavaType, b: &JavaType) -> JavaType {
4014 if a == b {
4015 return a.clone();
4016 }
4017 match (a, b) {
4018 (JavaType::Double, _) | (_, JavaType::Double) => JavaType::Double,
4019 (JavaType::Float, _) | (_, JavaType::Float) => JavaType::Float,
4020 (JavaType::Long, _) | (_, JavaType::Long) => JavaType::Long,
4021 (JavaType::Int, x) if x.is_integral() => JavaType::Int,
4022 (x, JavaType::Int) if x.is_integral() => JavaType::Int,
4023 (x, y) if x.is_integral() && y.is_integral() => JavaType::Int,
4024 _ => JavaType::Int,
4025 }
4026}
4027
4028fn expr_evidence<F: FnMut(u32, JavaType)>(e: &Expr, f: &mut F) {
4030 visit_exprs(e, &mut |x| match x {
4031 Expr::Method {
4032 owner,
4033 args,
4034 desc,
4035 is_static,
4036 cls,
4037 ..
4038 } => {
4039 if !*is_static {
4040 if let Some(o) = owner {
4041 if let Expr::Local { var, .. } = &**o {
4042 f(*var, JavaType::Object(cls.clone()));
4043 }
4044 }
4045 }
4046 for (i, a) in args.iter().enumerate() {
4047 if let Expr::Local { var, .. } = a {
4048 if let Some(t) = desc.args.get(i) {
4049 f(*var, t.clone());
4050 }
4051 }
4052 }
4053 }
4054 Expr::Field {
4055 owner,
4056 cls,
4057 ty,
4058 is_static,
4059 ..
4060 } => {
4061 if !*is_static && !cls.is_empty() {
4062 if let Some(o) = owner {
4063 if let Expr::Local { var, .. } = &**o {
4064 f(*var, JavaType::Object(cls.clone()));
4074 }
4075 }
4076 }
4077 let _ = ty;
4078 }
4079 Expr::Bin { op, l, r, .. }
4084 if matches!(op, BinOp::Eq | BinOp::Ne)
4085 && (matches!(&**r, Expr::Const(ConstVal::Null))
4086 || matches!(&**l, Expr::Const(ConstVal::Null))) =>
4087 {
4088 for side in [l, r] {
4089 if let Expr::Local { var, .. } = &**side {
4090 f(*var, JavaType::Object("java/lang/Object".into()));
4091 }
4092 }
4093 }
4094 Expr::ArrayIndex { array, .. } => {
4095 if let Expr::Local { var, ty, .. } = &**array {
4096 let t = ty.erased();
4101 if matches!(t, JavaType::Array(_)) {
4102 f(*var, t);
4103 } else {
4104 f(*var, JavaType::Array(Box::new(JavaType::Int)));
4105 }
4106 }
4107 }
4108 Expr::Assign { .. } => {
4109 }
4112 Expr::Cast { e: inner, .. } => {
4113 let _ = inner;
4119 }
4120 Expr::InstanceOf { e: inner, .. } => {
4121 let _ = inner;
4122 }
4123 _ => {}
4124 });
4125}
4126
4127pub fn mark_field_owner_concrete(vt: &mut VarTable, body: &mut Stmt) {
4139 let mut owners: HashSet<u32> = HashSet::default();
4140 rewrite_exprs(body, &mut |e| {
4141 visit_exprs(e, &mut |x| {
4142 if let Expr::Field {
4143 owner: Some(o),
4144 is_static: false,
4145 ..
4146 } = x
4147 {
4148 if let Expr::Local { var, .. } = &**o {
4149 owners.insert(*var);
4150 }
4151 }
4152 });
4153 });
4154 for var in owners {
4155 let i = var as usize;
4156 if i >= vt.vars.len() {
4157 continue;
4158 }
4159 if let JavaType::Object(cls) = vt.vars[i].ty.erased() {
4160 if let Some(last) = cls.rsplit('$').next() {
4161 if !last.is_empty() && last.chars().all(|c| c.is_ascii_digit()) {
4162 vt.force_concrete_vars.insert(var);
4163 }
4164 }
4165 }
4166 }
4167 let concrete: Vec<u32> = vt
4176 .vars
4177 .iter()
4178 .filter(|v| {
4179 if let JavaType::Object(cls) = v.ty.erased() {
4180 cls.rsplit('$')
4181 .next()
4182 .is_some_and(|l| !l.is_empty() && l.chars().all(|c| c.is_ascii_digit()))
4183 } else {
4184 false
4185 }
4186 })
4187 .map(|v| v.id)
4188 .collect();
4189 for var in concrete {
4190 vt.force_concrete_vars.insert(var);
4191 }
4192}
4193
4194pub fn insert_object_narrowing_casts(
4206 vt: &VarTable,
4207 body: &mut Stmt,
4208 pool: &DexPool,
4209 ret: &JavaType,
4210) {
4211 let value_ty = |e: &Expr| -> JavaType {
4218 match e {
4219 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4220 other => other.type_ref().erased(),
4221 }
4222 };
4223 let is_top_object_ty = |v: &JavaType| -> bool {
4224 matches!(v, JavaType::Object(c) if c.as_ref() == "java/lang/Object")
4225 };
4226 let specific_ref = |ty: &TypeRef| -> Option<TypeRef> {
4231 match ty {
4232 TypeRef::J(JavaType::Object(c)) if c.as_ref() != "java/lang/Object" => {
4233 Some(ty.clone())
4234 }
4235 TypeRef::J(JavaType::Array(_)) => Some(ty.clone()),
4236 _ => match ty.erased() {
4237 JavaType::Object(c) if c.as_ref() != "java/lang/Object" => Some(ty.clone()),
4238 JavaType::Array(_) => Some(ty.clone()),
4239 _ => None,
4240 },
4241 }
4242 };
4243 let non_cast_shape = |e: &Expr| -> bool {
4244 !matches!(e, Expr::Const(_) | Expr::Cast { .. } | Expr::InstanceOf { .. })
4245 };
4246 let needs_downcast = |vty: &JavaType, tt: &JavaType| -> bool {
4256 let (JavaType::Object(vn), JavaType::Object(tn)) = (vty, tt) else {
4257 return false;
4258 };
4259 if vn.as_ref() == "java/lang/Object" || tn.as_ref() == "java/lang/Object" {
4260 return false;
4261 }
4262 vn != tn && !pool.is_subtype(vn, tn)
4263 };
4264 let wants_cast = |e: &Expr, vty: &JavaType, tt: &JavaType| -> bool {
4266 non_cast_shape(e) && (is_top_object_ty(vty) || needs_downcast(vty, tt))
4267 };
4268 walk_mut_deep(body, &mut |st| match st {
4269 Stmt::ExprStmt(Expr::Assign {
4270 target,
4271 value,
4272 op: AssignOp::Plain,
4273 ..
4274 }) => {
4275 let tgt_erased: JavaType;
4279 let tgt_ref: Option<&TypeRef>;
4280 match &**target {
4281 Expr::Local { var, .. } => {
4282 let ty = &vt.var(*var).ty;
4283 tgt_erased = ty.erased();
4284 tgt_ref = Some(ty);
4285 }
4286 Expr::Field { ty, .. } => {
4287 tgt_erased = ty.erased();
4288 tgt_ref = Some(ty);
4289 }
4290 Expr::ArrayIndex { array, .. } => {
4294 let aty = match &**array {
4295 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4296 other => other.type_ref().erased(),
4297 };
4298 match aty {
4299 JavaType::Array(inner) => {
4300 tgt_erased = *inner;
4301 tgt_ref = None;
4302 }
4303 _ => return,
4304 }
4305 }
4306 _ => return,
4307 }
4308 let specific = match tgt_ref {
4312 Some(tr) => specific_ref(tr),
4313 None => specific_ref(&TypeRef::J(tgt_erased.clone())),
4314 };
4315 let Some(t) = specific else {
4316 return;
4317 };
4318 let vty = value_ty(value);
4319 if wants_cast(value, &vty, &tgt_erased) {
4320 let v = std::mem::replace(value, Box::new(Expr::This));
4321 **value = Expr::Cast { ty: t, e: v };
4322 }
4323 }
4324 Stmt::LocalDef { var, init: Some(value), .. } => {
4325 if let Some(t) = specific_ref(&vt.var(*var).ty) {
4326 let tt = vt.var(*var).ty.erased();
4327 let vty = value_ty(value);
4328 if wants_cast(value, &vty, &tt) {
4329 let v = std::mem::replace(value, Expr::This);
4330 *value = Expr::Cast {
4331 ty: t,
4332 e: Box::new(v),
4333 };
4334 }
4335 }
4336 }
4337 Stmt::Return(Some(value)) => {
4338 if let Some(t) = specific_ref(&TypeRef::J(ret.clone())) {
4339 let vty = value_ty(value);
4340 if wants_cast(value, &vty, ret) {
4341 let v = std::mem::replace(value, Expr::This);
4342 *value = Expr::Cast { ty: t, e: Box::new(v) };
4343 }
4344 }
4345 }
4346 _ => {}
4347 });
4348 walk_stmt_exprs(body, &mut |e| {
4355 deep_rewrite(e, &mut |x| {
4356 let (args, arg_tys): (&mut Vec<Expr>, &[JavaType]) = match x {
4357 Expr::Method { desc, args, is_dynamic: false, .. } => {
4358 (args, desc.args.as_slice())
4359 }
4360 Expr::New { arg_tys, args, .. } => (args, arg_tys.as_slice()),
4364 _ => return,
4365 };
4366 for (i, a) in args.iter_mut().enumerate() {
4367 let Some(formal) = arg_tys.get(i) else {
4368 continue;
4369 };
4370 let Some(t) = specific_ref(&TypeRef::J(formal.clone())) else {
4371 continue;
4372 };
4373 let aty = value_ty(a);
4374 if !wants_cast(a, &aty, formal) {
4375 continue;
4376 }
4377 let v = std::mem::replace(a, Expr::Const(ConstVal::Null));
4378 *a = Expr::Cast { ty: t, e: Box::new(v) };
4379 }
4380 });
4381 });
4382}
4383
4384pub fn fix_primitive_assign_casts(vt: &VarTable, body: &mut Stmt, ret_ty: &JavaType) {
4393 fn numlike(t: &JavaType) -> bool {
4394 matches!(
4395 t,
4396 JavaType::Byte
4397 | JavaType::Short
4398 | JavaType::Int
4399 | JavaType::Char
4400 | JavaType::Long
4401 | JavaType::Float
4402 | JavaType::Double
4403 )
4404 }
4405 fn val_ty(e: &Expr, vt: &VarTable) -> JavaType {
4406 match e {
4407 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
4408 vt.var(*var).ty.erased()
4409 }
4410 other => other.type_ref().erased(),
4411 }
4412 }
4413 fn wrap(ty: &JavaType, value: &mut Expr) {
4414 if matches!(value, Expr::Cast { .. }) {
4415 return;
4416 }
4417 if val_ty_static(value) == *ty {
4418 return;
4419 }
4420 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
4421 *value = Expr::Cast {
4422 ty: TypeRef::J(ty.clone()),
4423 e: Box::new(taken),
4424 };
4425 }
4426 fn val_ty_static(e: &Expr) -> JavaType {
4427 e.type_ref().erased()
4428 }
4429 fn coerce(tt: &JavaType, value: &mut Expr, vt: &VarTable) {
4437 match (&*value, tt) {
4442 (Expr::Const(ConstVal::Int(b)), JavaType::Float) => {
4443 *value = Expr::Const(ConstVal::Float(f32::from_bits(*b as u32)));
4444 return;
4445 }
4446 (Expr::Const(ConstVal::Long(b)), JavaType::Double) => {
4447 *value = Expr::Const(ConstVal::Double(f64::from_bits(*b as u64)));
4448 return;
4449 }
4450 (Expr::Const(ConstVal::Float(f)), JavaType::Int) => {
4451 *value = Expr::Const(ConstVal::Int(f.to_bits() as i32));
4452 return;
4453 }
4454 (Expr::Const(ConstVal::Double(d)), JavaType::Long) => {
4455 *value = Expr::Const(ConstVal::Long(d.to_bits() as i64));
4456 return;
4457 }
4458 _ => {}
4459 }
4460 let vt_val = val_ty(value, vt);
4461 let tt_bool = matches!(tt, JavaType::Boolean);
4462 let val_bool = matches!(vt_val, JavaType::Boolean);
4463 if tt_bool && !val_bool && numlike(&vt_val) {
4464 if matches!(
4465 value,
4466 Expr::Const(_)
4467 | Expr::Bin { op: BinOp::Eq | BinOp::Ne, .. }
4468 | Expr::Un { op: UnOp::Not, .. }
4469 ) {
4470 return;
4475 }
4476 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
4477 *value = Expr::Bin {
4478 op: BinOp::Ne,
4479 l: Box::new(taken),
4480 r: Box::new(Expr::Const(ConstVal::Int(0))),
4481 ty: Some(TypeRef::J(JavaType::Boolean)),
4482 };
4483 } else if !tt_bool && val_bool && numlike(tt) {
4484 if matches!(value, Expr::Cast { .. }) {
4485 return;
4486 }
4487 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
4488 *value = Expr::Cast {
4489 ty: TypeRef::J(tt.clone()),
4490 e: Box::new(Expr::Cond {
4491 c: Box::new(taken),
4492 t: Box::new(Expr::Const(ConstVal::Int(1))),
4493 f: Box::new(Expr::Const(ConstVal::Int(0))),
4494 }),
4495 };
4496 } else if numlike(tt) && numlike(&vt_val) {
4497 wrap(tt, value);
4498 }
4499 }
4500 walk_mut_deep(body, &mut |st| match st {
4501 Stmt::ExprStmt(Expr::Assign {
4502 target,
4503 op: AssignOp::Plain,
4504 value,
4505 }) => {
4506 let tt = match &**target {
4507 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
4508 vt.var(*var).ty.erased()
4509 }
4510 Expr::Field { ty, .. } => ty.erased(),
4511 Expr::ArrayIndex { array, .. } => match array.type_ref().erased() {
4515 JavaType::Array(inner) => *inner,
4516 _ => return,
4517 },
4518 _ => return,
4519 };
4520 coerce(&tt, value, vt);
4521 }
4522 Stmt::LocalDef {
4523 var,
4524 init: Some(value),
4525 ..
4526 } => {
4527 if (*var as usize) >= vt.vars.len() {
4528 return;
4529 }
4530 let tt = vt.var(*var).ty.erased();
4531 coerce(&tt, value, vt);
4532 }
4533 Stmt::Return(Some(e)) => {
4534 if !numlike(ret_ty) || !numlike(&val_ty(e, vt)) {
4535 return;
4536 }
4537 wrap(ret_ty, e);
4538 }
4539 _ => {}
4540 });
4541}
4542
4543pub fn fix_shadowed_super_fields(
4567 body: &mut Stmt,
4568 vt: &VarTable,
4569 pool: &DexPool,
4570 class_name: &str,
4571) {
4572 fn display(pool: &DexPool, cls: &str, name: &str) -> Option<String> {
4576 let pc = pool.get(cls)?;
4577 let f = pc
4578 .static_fields
4579 .iter()
4580 .chain(pc.instance_fields.iter())
4581 .find(|f| f.name.as_ref() == name)?;
4582 Some(
4583 jdc_core::rename::field_display(cls, name, &f.desc)
4584 .map(|d| d.to_string())
4585 .unwrap_or_else(|| name.to_string()),
4586 )
4587 }
4588 fn owner_ct(
4589 o: &Option<Box<Expr>>,
4590 vt: &VarTable,
4591 class_name: &str,
4592 ) -> Option<std::sync::Arc<str>> {
4593 match o.as_deref() {
4594 None | Some(Expr::This) => Some(std::sync::Arc::from(class_name)),
4595 Some(Expr::Local { var, .. }) => match vt.var(*var).ty.erased() {
4596 JavaType::Object(n) => Some(n),
4597 _ => None,
4598 },
4599 Some(other) => match other.type_ref().erased() {
4600 JavaType::Object(n) => Some(n),
4601 _ => None,
4602 },
4603 }
4604 }
4605 walk_stmt_exprs(body, &mut |e| {
4606 deep_rewrite(e, &mut |x| {
4607 let Expr::Field {
4608 owner,
4609 cls,
4610 name,
4611 is_static: false,
4612 ..
4613 } = x
4614 else {
4615 return;
4616 };
4617 let Some(ot) = owner_ct(owner, vt, class_name) else {
4618 return;
4619 };
4620 if cls.as_ref() == ot.as_ref() {
4621 return;
4622 }
4623 let Some(ref_display) = display(pool, cls, name) else {
4624 return;
4625 };
4626 let mut cur = ot.to_string();
4630 let mut shadowed = false;
4631 let mut found = false;
4632 for _ in 0..64 {
4633 if display(pool, &cur, name).as_deref() == Some(ref_display.as_str()) {
4634 shadowed = true;
4635 }
4636 let Some(pc) = pool.get(&cur) else {
4637 return;
4638 };
4639 let Some(sup) = pc.super_name.clone() else {
4640 return;
4641 };
4642 if sup.as_str() == cls.as_ref() {
4643 found = true;
4644 break;
4645 }
4646 cur = sup;
4647 }
4648 if !found || !shadowed {
4649 return;
4650 }
4651 let ty = TypeRef::J(JavaType::Object(cls.clone()));
4652 match owner {
4653 Some(o) => {
4654 if let Expr::Cast { ty: ct, .. } = &mut **o {
4655 *ct = ty;
4656 } else {
4657 let taken = std::mem::replace(&mut **o, Expr::This);
4658 **o = Expr::Cast {
4659 ty,
4660 e: Box::new(taken),
4661 };
4662 }
4663 }
4664 None => {
4665 *owner = Some(Box::new(Expr::Cast {
4666 ty,
4667 e: Box::new(Expr::This),
4668 }))
4669 }
4670 }
4671 });
4672 });
4673}
4674
4675pub fn fix_field_owner_downcasts(body: &mut Stmt, vt: &VarTable, pool: &DexPool) {
4694 fn owner_internal(o: &Expr, vt: &VarTable) -> Option<std::sync::Arc<str>> {
4695 let t = match o {
4696 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4697 other => other.type_ref().erased(),
4698 };
4699 match t {
4700 JavaType::Object(n) => Some(n),
4701 _ => None,
4702 }
4703 }
4704 fn declares_up(pool: &DexPool, class: &str, name: &str) -> bool {
4705 let mut cur = Some(class.to_string());
4706 let mut hops = 0;
4707 while let Some(c) = cur {
4708 hops += 1;
4709 if hops > 64 {
4710 return false;
4711 }
4712 let Some(pc) = pool.get(&c) else { return false };
4713 if pc
4714 .instance_fields
4715 .iter()
4716 .chain(pc.static_fields.iter())
4717 .any(|f| f.name.as_ref() == name)
4718 {
4719 return true;
4720 }
4721 cur = pc.super_name.clone();
4722 }
4723 false
4724 }
4725 walk_stmt_exprs(body, &mut |e| {
4726 deep_rewrite(e, &mut |x| {
4727 if let Expr::Field {
4728 owner: Some(o),
4729 cls,
4730 name,
4731 is_static: false,
4732 ..
4733 } = x
4734 {
4735 let Some(ot) = owner_internal(o, vt) else {
4736 return;
4737 };
4738 if ot.as_ref() == cls.as_ref() || !pool.is_subtype(cls, &ot) {
4739 return;
4740 }
4741 if declares_up(pool, &ot, name) || !declares_up(pool, cls, name) {
4742 return;
4743 }
4744 let ty = TypeRef::J(JavaType::Object(cls.clone()));
4745 if let Expr::Cast { ty: ct, .. } = &mut **o {
4746 *ct = ty;
4747 } else {
4748 let taken = std::mem::replace(&mut **o, Expr::This);
4749 **o = Expr::Cast { ty, e: Box::new(taken) };
4750 }
4751 }
4752 let (tgt_ty, value) = match x {
4754 Expr::Assign { target, op: AssignOp::Plain, value } => {
4755 let t = match &**target {
4756 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4757 Expr::Field { ty, .. } => ty.erased(),
4758 _ => return,
4759 };
4760 (t, value)
4761 }
4762 _ => return,
4763 };
4764 let JavaType::Object(sn) = tgt_ty else { return };
4765 if let Expr::Cast { ty, .. } = &mut **value {
4766 if let TypeRef::J(JavaType::Object(tn)) = ty {
4767 if tn.as_ref() != sn.as_ref() && pool.is_subtype(&sn, tn) {
4768 *ty = TypeRef::J(JavaType::Object(sn.clone()));
4769 }
4770 }
4771 }
4772 });
4773 });
4774 walk_mut_deep(body, &mut |st| {
4776 if let Stmt::LocalDef { var, init: Some(value), .. } = st {
4777 let tt = if (*var as usize) < vt.vars.len() {
4778 vt.vars[*var as usize].ty.erased()
4779 } else {
4780 return;
4781 };
4782 let JavaType::Object(sn) = tt else { return };
4783 if let Expr::Cast { ty, .. } = value {
4784 if let TypeRef::J(JavaType::Object(tn)) = ty {
4785 if tn.as_ref() != sn.as_ref() && pool.is_subtype(&sn, tn) {
4786 *ty = TypeRef::J(JavaType::Object(sn.clone()));
4787 }
4788 }
4789 }
4790 }
4791 });
4792}
4793
4794pub fn fix_incomparable_equality(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
4801 fn side_ty(e: &Expr, vt: &VarTable) -> JavaType {
4802 match e {
4803 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
4804 vt.var(*var).ty.erased()
4805 }
4806 other => other.type_ref().erased(),
4807 }
4808 }
4809 let incomparable = |a: &JavaType, b: &JavaType| -> bool {
4810 use JavaType::{Array, Object};
4811 match (a, b) {
4812 (Object(x), Object(y)) => {
4813 if x == y || x.as_ref() == "java/lang/Object" || y.as_ref() == "java/lang/Object"
4814 {
4815 return false;
4816 }
4817 let xi = pool.get(x).map(|c| c.is_interface()).unwrap_or(true);
4818 let yi = pool.get(y).map(|c| c.is_interface()).unwrap_or(true);
4819 if xi || yi {
4823 return false;
4824 }
4825 !pool.is_subtype(x, y) && !pool.is_subtype(y, x)
4826 }
4827 (Array(_), Object(y)) | (Object(y), Array(_)) => {
4828 y.as_ref() != "java/lang/Object"
4829 && y.as_ref() != "java/lang/Cloneable"
4830 && y.as_ref() != "java/io/Serializable"
4831 && !pool.get(y).map(|c| c.is_interface()).unwrap_or(false)
4832 }
4833 _ => false,
4834 }
4835 };
4836 let is_ref = |t: &JavaType| matches!(t, JavaType::Object(_) | JavaType::Array(_));
4837 walk_stmt_exprs(body, &mut |e| {
4838 deep_rewrite(e, &mut |x| {
4839 if let Expr::Bin {
4840 op: BinOp::Eq | BinOp::Ne,
4841 l,
4842 r,
4843 ..
4844 } = x
4845 {
4846 let lt = side_ty(l, vt);
4847 let rt = side_ty(r, vt);
4848 if is_ref(<) && matches!(&**r, Expr::Const(ConstVal::Int(0))) {
4854 **r = Expr::Const(ConstVal::Null);
4855 return;
4856 }
4857 if is_ref(&rt) && matches!(&**l, Expr::Const(ConstVal::Int(0))) {
4858 **l = Expr::Const(ConstVal::Null);
4859 return;
4860 }
4861 if incomparable(<, &rt) {
4862 let taken = std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
4863 **l = Expr::Cast {
4864 ty: TypeRef::J(JavaType::Object("java/lang/Object".into())),
4865 e: taken,
4866 };
4867 }
4868 }
4869 });
4870 });
4871}
4872
4873pub fn fix_primitive_arg_bridges(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
4887 fn val_ty(e: &Expr, vt: &VarTable) -> JavaType {
4888 match e {
4889 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4890 other => other.type_ref().erased(),
4891 }
4892 }
4893 #[allow(clippy::type_complexity)]
4894 let is_bool = |t: &JavaType| matches!(t, JavaType::Boolean);
4895 let is_num = |t: &JavaType| {
4896 matches!(
4897 t,
4898 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
4899 | JavaType::Long | JavaType::Float | JavaType::Double
4900 )
4901 };
4902 fn bridge(actual: &JavaType, formal: &JavaType, arg: &Expr) -> Option<Expr> {
4907 match (arg, formal) {
4917 (Expr::Const(ConstVal::Int(b)), JavaType::Float) => {
4918 return Some(Expr::Const(ConstVal::Float(f32::from_bits(*b as u32))));
4919 }
4920 (Expr::Const(ConstVal::Long(b)), JavaType::Double) => {
4921 return Some(Expr::Const(ConstVal::Double(f64::from_bits(*b as u64))));
4922 }
4923 (Expr::Const(ConstVal::Float(f)), JavaType::Int) => {
4924 return Some(Expr::Const(ConstVal::Int(f.to_bits() as i32)));
4925 }
4926 (Expr::Const(ConstVal::Double(d)), JavaType::Long) => {
4927 return Some(Expr::Const(ConstVal::Long(d.to_bits() as i64)));
4928 }
4929 _ => {}
4930 }
4931 if matches!(actual, JavaType::Boolean)
4932 && matches!(
4933 formal,
4934 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
4935 | JavaType::Long | JavaType::Float | JavaType::Double
4936 )
4937 {
4938 Some(Expr::Cast {
4939 ty: TypeRef::J(formal.clone()),
4940 e: Box::new(Expr::Cond {
4941 c: Box::new(arg.clone()),
4942 t: Box::new(Expr::Const(ConstVal::Int(1))),
4943 f: Box::new(Expr::Const(ConstVal::Int(0))),
4944 }),
4945 })
4946 } else if matches!(formal, JavaType::Boolean)
4947 && matches!(
4948 actual,
4949 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
4950 | JavaType::Long | JavaType::Float | JavaType::Double
4951 )
4952 {
4953 Some(Expr::Bin {
4954 op: BinOp::Ne,
4955 l: Box::new(arg.clone()),
4956 r: Box::new(Expr::Const(ConstVal::Int(0))),
4957 ty: Some(TypeRef::J(JavaType::Boolean)),
4958 })
4959 } else if matches!(
4960 actual,
4961 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
4962 | JavaType::Long | JavaType::Float | JavaType::Double
4963 ) && matches!(
4964 formal,
4965 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
4966 | JavaType::Long | JavaType::Float | JavaType::Double
4967 ) && !implicit_widening(actual, formal)
4968 {
4969 Some(Expr::Cast {
4978 ty: TypeRef::J(formal.clone()),
4979 e: Box::new(arg.clone()),
4980 })
4981 } else {
4982 None
4983 }
4984 }
4985 fn fw_resolvable(n: &str) -> bool {
5001 crate::fwdb::exists(n)
5002 }
5003 fn null_arg_cast(a: &mut Expr, f: &JavaType, pool: &DexPool) -> bool {
5004 if !matches!(&*a, Expr::Const(ConstVal::Null)) {
5005 return false;
5006 }
5007 if let JavaType::Object(n) = f {
5008 let resolvable = pool.get(n).is_some() || fw_resolvable(n);
5014 if n.as_ref() != "java/lang/Object" && resolvable {
5015 *a = Expr::Cast {
5016 ty: TypeRef::J(f.clone()),
5017 e: Box::new(Expr::Const(ConstVal::Null)),
5018 };
5019 return true;
5020 }
5021 } else if let JavaType::Array(_) = f {
5022 let mut e: &JavaType = f;
5029 while let JavaType::Array(inner) = e {
5030 e = &**inner;
5031 }
5032 let resolvable = match e {
5033 JavaType::Object(n) => pool.get(n).is_some() || fw_resolvable(n),
5034 _ => true, };
5036 if resolvable {
5037 *a = Expr::Cast {
5038 ty: TypeRef::J(f.clone()),
5039 e: Box::new(Expr::Const(ConstVal::Null)),
5040 };
5041 return true;
5042 }
5043 }
5044 false
5047 }
5048 fn implicit_widening(a: &JavaType, f: &JavaType) -> bool {
5051 use JavaType::*;
5052 match (a, f) {
5053 (Byte, Short) | (Byte, Int) | (Byte, Long) | (Byte, Float) | (Byte, Double) => true,
5054 (Short, Int) | (Short, Long) | (Short, Float) | (Short, Double) => true,
5055 (Char, Int) | (Char, Long) | (Char, Float) | (Char, Double) => true,
5056 (Int, Long) | (Int, Float) | (Int, Double) => true,
5057 (Long, Float) | (Long, Double) => true,
5058 (Float, Double) => true,
5059 (x, y) => x == y,
5060 }
5061 }
5062 let overload_competes = |cls: &str, name: &str, args: &[JavaType]| -> bool {
5071 if let Some(c) = pool.get(cls) {
5072 return c.all_methods().any(|m| {
5073 &*m.name == name
5074 && m.access & (crate::access::ACC_BRIDGE | crate::access::ACC_SYNTHETIC) == 0
5080 && m.parsed_desc()
5081 .is_some_and(|d| d.args.len() == args.len() && d.args != args)
5082 });
5083 }
5084 let _ = args;
5093 false
5094 };
5095 let ctor_formals = |cls: &str, n: usize| -> Option<Vec<JavaType>> {
5098 let c = pool.get(cls)?;
5099 let mut out: Option<Vec<JavaType>> = None;
5100 for m in c.all_methods() {
5101 if &*m.name != "<init>" {
5102 continue;
5103 }
5104 let Some(d) = m.parsed_desc() else { continue };
5105 if d.args.len() != n {
5106 continue;
5107 }
5108 match &out {
5109 None => out = Some(d.args.clone()),
5110 Some(prev) if *prev != d.args => return None,
5111 Some(_) => {}
5112 }
5113 }
5114 out
5115 };
5116 walk_stmt_exprs(body, &mut |e| {
5117 deep_rewrite(e, &mut |x| match x {
5118 Expr::Method {
5119 cls,
5120 name,
5121 desc,
5122 args,
5123 is_static,
5124 ..
5125 } => {
5126 if args.len() != desc.args.len() {
5127 return;
5128 }
5129 let _ = is_static;
5130 let mut competes: Option<bool> = None;
5131 for (a, f) in args.iter_mut().zip(desc.args.iter()) {
5132 if null_arg_cast(a, f, pool) {
5133 continue;
5134 }
5135 if matches!(a, Expr::Cast { .. }) {
5136 continue;
5137 }
5138 let subtype_arg = match (&val_ty(a, vt), f) {
5149 (JavaType::Object(an), JavaType::Object(fn_)) => {
5150 (an != fn_ && pool.is_subtype(an, fn_))
5155 || (fn_.as_ref() == "java/lang/Object"
5156 && an.as_ref() != "java/lang/Object")
5157 }
5158 (JavaType::Array(_), JavaType::Object(fn_))
5159 if fn_.as_ref() == "java/lang/Object" =>
5160 {
5161 true
5162 }
5163 _ => false,
5164 };
5165 let null_obj =
5179 matches!(a, Expr::Const(ConstVal::Null)) && matches!(f, JavaType::Object(_));
5180 let framework_owner = pool.get(cls.as_ref()).is_none();
5181 let fw_null_ambiguous = framework_owner
5182 && matches!(
5183 (cls.as_ref(), name.as_ref()),
5184 ("java/lang/StringBuilder", "append")
5185 | ("java/lang/StringBuffer", "append")
5186 | ("java/io/PrintStream", "println")
5187 | ("java/io/PrintStream", "print")
5188 | ("java/io/PrintWriter", "println")
5189 | ("java/io/PrintWriter", "print")
5190 | ("java/lang/String", "valueOf")
5191 );
5192 let fw_subtype_pinnable = framework_owner
5203 && subtype_arg
5204 && !matches!(f, JavaType::Object(fn_) if fn_.as_ref() == "java/lang/Object")
5205 && matches!(
5206 (cls.as_ref(), name.as_ref()),
5207 ("android/content/Intent", "putExtra")
5208 | ("java/util/concurrent/ScheduledExecutorService", "schedule")
5209 | ("java/util/concurrent/ExecutorService", "submit")
5210 | ("java/util/concurrent/ExecutorService", "invokeAll")
5211 );
5212 if fw_subtype_pinnable
5213 || (subtype_arg
5214 && *competes.get_or_insert_with(|| {
5215 overload_competes(cls, name, &desc.args)
5216 }))
5217 || (null_obj
5218 && (fw_null_ambiguous
5219 || *competes.get_or_insert_with(|| {
5220 overload_competes(cls, name, &desc.args)
5221 })))
5222 {
5223 let old =
5224 std::mem::replace(a, Expr::Const(ConstVal::Null));
5225 *a = Expr::Cast {
5226 ty: TypeRef::J(f.clone()),
5227 e: Box::new(old),
5228 };
5229 continue;
5230 }
5231 if !is_bool(&val_ty(a, vt)) && !is_num(&val_ty(a, vt)) {
5232 continue;
5233 }
5234 if !is_bool(f) && !is_num(f) {
5235 continue;
5236 }
5237 if let Some(b) = bridge(&val_ty(a, vt), f, a) {
5238 *a = b;
5239 }
5240 }
5241 }
5242 Expr::New { cls, args, arg_tys, .. } => {
5243 let formals_owned = if arg_tys.len() == args.len() && !arg_tys.is_empty() {
5244 Some(arg_tys.clone())
5245 } else {
5246 ctor_formals(cls, args.len())
5247 };
5248 if let Some(formals) = formals_owned {
5249 for (a, f) in args.iter_mut().zip(formals.iter()) {
5250 if null_arg_cast(a, f, pool) {
5251 continue;
5252 }
5253 if matches!(a, Expr::Cast { .. }) {
5254 continue;
5255 }
5256 if !is_bool(f) && !is_num(f) {
5257 continue;
5258 }
5259 if let Some(b) = bridge(&val_ty(a, vt), f, a) {
5260 *a = b;
5261 }
5262 }
5263 }
5264 }
5265 _ => {}
5266 });
5267 });
5268}
5269
5270pub fn deshadow_locals(vt: &mut VarTable, pool: &crate::DexPool, class_name: &str, body: &Stmt) {
5282 let segs = pool.root_pkg_segs();
5283 let obscured = crate::classdec::obscured_simples_snapshot();
5287 let self_pkg = class_name.rfind('/').map(|i| &class_name[..i]).unwrap_or("");
5294 let mut same_pkg_simples: jdc_core::FxHashSet<String> = jdc_core::FxHashSet::default();
5295 visit_all_exprs(body, &mut |x| {
5296 let cls = match x {
5297 Expr::Field { cls, is_static: true, .. } => Some(cls.as_ref()),
5298 Expr::Method { cls, is_static: true, .. } => Some(cls.as_ref()),
5299 _ => None,
5300 };
5301 if let Some(c) = cls {
5302 let pkg = c.rfind('/').map(|i| &c[..i]).unwrap_or("");
5303 if pkg == self_pkg {
5304 let simple = &c[c.rfind('/').map(|i| i + 1).unwrap_or(0)..];
5305 let head = simple.split('$').next().unwrap_or(simple);
5306 if !head.is_empty() {
5307 same_pkg_simples.insert(head.to_string());
5308 }
5309 }
5310 }
5311 });
5312 let hit = |name: &str| {
5313 segs.contains(name) || obscured.contains(name) || same_pkg_simples.contains(name)
5314 };
5315 if (segs.is_empty() && obscured.is_empty() && same_pkg_simples.is_empty())
5316 || !vt.vars.iter().any(|v| hit(&v.name))
5317 {
5318 return;
5319 }
5320 let mut taken: jdc_core::FxHashSet<String> =
5321 vt.vars.iter().map(|v| v.name.clone()).collect();
5322 for v in vt.vars.iter_mut() {
5323 if !hit(&v.name) {
5324 continue;
5325 }
5326 let mut k = 0u32;
5327 let cand = loop {
5328 k += 1;
5329 let c = if k == 1 {
5330 format!("{}x", v.name)
5331 } else {
5332 format!("{}x{}", v.name, k)
5333 };
5334 if !taken.contains(&c) {
5335 break c;
5336 }
5337 };
5338 taken.insert(cand.clone());
5339 v.name = cand;
5340 }
5341}
5342
5343pub fn fix_ctor_this_aliases(body: &mut Stmt, vt: &VarTable) {
5369 let Some(this_var) = vt
5370 .vars
5371 .iter()
5372 .find(|v| v.is_param && v.name == "this")
5373 .map(|v| v.id)
5374 else {
5375 return;
5376 };
5377 let mut analysis = VarAnalysis {
5378 assigns: Vec::new(),
5379 reads: Vec::new(),
5380 values: Vec::new(),
5381 };
5382 analyze_vars(body, &mut analysis);
5383 enum Src {
5394 This,
5395 Loc(u32),
5396 Other,
5397 }
5398 let mut sources: jdc_core::FxHashMap<u32, Vec<Src>> = jdc_core::FxHashMap::default();
5399 walk_all(body, &mut |st| {
5400 let (tgt, val): (u32, &Expr) = match st {
5401 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
5402 Expr::Local { var, .. } => (*var, &**value),
5403 _ => return,
5404 },
5405 Stmt::LocalDef { var, init: Some(value), .. } => (*var, value),
5406 _ => return,
5407 };
5408 let s = match val {
5409 Expr::This => Src::This,
5410 Expr::Local { var: src, .. } if *src == this_var => Src::This,
5411 Expr::Local { var: src, .. } => Src::Loc(*src),
5412 _ => Src::Other,
5413 };
5414 sources.entry(tgt).or_default().push(s);
5415 });
5416 let n = vt
5417 .vars
5418 .len()
5419 .max(analysis.assigns.len())
5420 .max(sources.keys().map(|k| *k as usize + 1).max().unwrap_or(0));
5421 let mut alias = vec![false; n];
5422 loop {
5423 let mut changed = false;
5424 for (v, srcs) in &sources {
5425 let vi = *v as usize;
5426 if vi >= n || alias[vi] || srcs.is_empty() {
5427 continue;
5428 }
5429 let ok = srcs.iter().all(|s| match s {
5430 Src::This => true,
5431 Src::Loc(w) => (*w as usize) < n && alias[*w as usize],
5432 Src::Other => false,
5433 });
5434 if ok {
5435 alias[vi] = true;
5436 changed = true;
5437 }
5438 }
5439 if !changed {
5440 break;
5441 }
5442 }
5443 if !alias.iter().any(|&b| b) {
5444 return;
5445 }
5446 walk_stmt_exprs(body, &mut |e| {
5447 deep_rewrite_reads(e, &mut |x| {
5448 if let Expr::Local { var, .. } = x {
5449 if (*var as usize) < n && alias[*var as usize] {
5450 *x = Expr::This;
5451 }
5452 }
5453 });
5454 });
5455}
5456
5457pub fn fix_this0_owners(body: &mut Stmt, class_name: &str) {
5458 walk_stmt_exprs(body, &mut |e| {
5459 deep_rewrite(e, &mut |x| {
5460 if let Expr::Field { owner: Some(_), cls, name, .. } = x {
5461 if name.as_ref() == "this$0" && cls.as_ref() == class_name {
5462 if let Expr::Field { owner, .. } = x {
5463 *owner = None;
5464 }
5465 }
5466 }
5467 });
5468 });
5469}
5470
5471pub fn rescue_primitive_receivers(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
5472 fn prim(t: &JavaType) -> bool {
5473 match t {
5474 JavaType::Int
5475 | JavaType::Long
5476 | JavaType::Short
5477 | JavaType::Byte
5478 | JavaType::Char
5479 | JavaType::Float
5480 | JavaType::Double
5481 | JavaType::Boolean => true,
5482 JavaType::Object(n) => matches!(
5486 n.as_ref(),
5487 "java/lang/Boolean"
5488 | "java/lang/String"
5489 | "java/lang/Integer"
5490 | "java/lang/Long"
5491 | "java/lang/Short"
5492 | "java/lang/Byte"
5493 | "java/lang/Character"
5494 | "java/lang/Float"
5495 | "java/lang/Double"
5496 ),
5497 _ => false,
5498 }
5499 }
5500 walk_stmt_exprs(body, &mut |e| {
5501 deep_rewrite(e, &mut |x| {
5502 let (recv_var, recv_ty) = {
5504 let o: &Expr = match &*x {
5505 Expr::Method { owner: Some(o), .. } => o,
5506 Expr::Field { owner: Some(o), is_static: false, .. } => o,
5507 _ => return,
5508 };
5509 let Expr::Local { var, ty } = o else {
5510 return;
5511 };
5512 let vt_ty = if (*var as usize) < vt.vars.len() {
5513 vt.var(*var).ty.erased()
5514 } else {
5515 ty.erased()
5516 };
5517 if !prim(&vt_ty) {
5518 return;
5519 }
5520 (*var, vt_ty)
5521 };
5522 let cls: std::sync::Arc<str> = match &*x {
5523 Expr::Method { cls, .. } | Expr::Field { cls, .. } => cls.clone(),
5524 _ => return,
5525 };
5526 if let JavaType::Object(n) = &recv_ty {
5535 if n.as_ref() == cls.as_ref() || pool.is_subtype(n.as_ref(), cls.as_ref()) {
5536 return;
5537 }
5538 }
5539 let mut cands: Vec<u32> = Vec::new();
5541 for v in &vt.vars {
5542 if let JavaType::Object(n) = v.ty.erased() {
5543 if n.as_ref() == cls.as_ref()
5544 || pool.is_subtype(n.as_ref(), cls.as_ref())
5545 {
5546 cands.push(v.id);
5547 }
5548 }
5549 }
5550 let id = if cands.len() == 1 {
5551 cands[0]
5552 } else if cands.len() > 1 {
5553 let slot = if (recv_var as usize) < vt.vars.len() {
5558 Some(vt.var(recv_var).slot)
5559 } else {
5560 None
5561 };
5562 let mut hits: Vec<u32> = match slot {
5563 Some(sl) => cands
5564 .iter()
5565 .copied()
5566 .filter(|&c| {
5567 c != recv_var
5568 && (c as usize) < vt.vars.len()
5569 && vt.var(c).slot == sl
5570 })
5571 .collect(),
5572 None => Vec::new(),
5573 };
5574 if hits.is_empty() {
5575 hits = cands
5578 .iter()
5579 .copied()
5580 .filter(|&c| c != recv_var)
5581 .collect();
5582 }
5583 hits.sort_unstable();
5584 hits.dedup();
5585 if hits.is_empty() {
5586 return;
5587 }
5588 *hits.last().unwrap()
5589 } else {
5590 if cands.is_empty() {
5597 return;
5598 }
5599 let slot = if (recv_var as usize) < vt.vars.len() {
5600 vt.var(recv_var).slot
5601 } else {
5602 return;
5603 };
5604 let mut hits: Vec<u32> = cands
5605 .into_iter()
5606 .filter(|&c| {
5607 c != recv_var
5608 && (c as usize) < vt.vars.len()
5609 && vt.var(c).slot == slot
5610 })
5611 .collect();
5612 hits.sort_unstable();
5613 hits.dedup();
5614 if hits.len() != 1 {
5615 return;
5616 }
5617 hits[0]
5618 };
5619 let o: &mut Box<Expr> = match x {
5621 Expr::Method { owner: Some(o), .. } => o,
5622 Expr::Field { owner: Some(o), is_static: false, .. } => o,
5623 _ => return,
5624 };
5625 if let Expr::Local { var, ty } = &mut **o {
5626 if *var == recv_var {
5627 *var = id;
5628 *ty = vt.vars[id as usize].ty.clone();
5629 }
5630 }
5631 });
5632 });
5633
5634}
5635
5636pub fn rescue_arg_return_swaps(
5642 body: &mut Stmt,
5643 vt: &VarTable,
5644 pool: &crate::DexPool,
5645 ret_ty: &JavaType,
5646) {
5647 let mut top_ids: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
5655 for v in &vt.vars {
5656 if v.is_param {
5657 top_ids.insert(v.id);
5658 }
5659 }
5660 if let Stmt::Block(stmts) = body {
5661 for st in stmts.iter() {
5662 if let Stmt::LocalDef { var, .. } = st {
5663 top_ids.insert(*var);
5664 }
5665 }
5666 }
5667 let ret_wants_ref = match ret_ty {
5668 JavaType::Object(n) => n.as_ref() != "java/lang/Object",
5669 JavaType::Array(_) => true,
5670 _ => false,
5671 };
5672 let try_swap = |a: &mut Expr, want: &JavaType| {
5673 let Expr::Local { var, ty } = &mut *a else {
5674 return;
5675 };
5676 if (*var as usize) >= vt.vars.len() {
5677 return;
5678 }
5679 let avt = vt.var(*var).ty.erased();
5680 let avt_ref = matches!(avt, JavaType::Object(_) | JavaType::Array(_));
5681 let want_arr = matches!(want, JavaType::Array(_));
5682 if avt_ref && !(avt == JavaType::Object("java/lang/Object".into())) {
5685 return;
5686 }
5687 if avt_ref && !matches!(want, JavaType::Object(_)) {
5688 return;
5689 }
5690 let slot = vt.var(*var).slot;
5691 let mut hits: Vec<u32> = Vec::new();
5692 for v in &vt.vars {
5693 if v.slot != slot || !top_ids.contains(&v.id) || v.id == *var {
5694 continue;
5695 }
5696 let t = v.ty.erased();
5697 let ok = match (&t, want) {
5698 (JavaType::Object(n), JavaType::Object(w)) => {
5699 n.as_ref() == w.as_ref()
5700 || pool.is_subtype(n.as_ref(), w.as_ref())
5701 }
5702 (JavaType::Array(_), JavaType::Array(_)) => want_arr,
5703 _ => false,
5704 };
5705 if ok {
5706 hits.push(v.id);
5707 }
5708 }
5709 hits.sort_unstable();
5710 hits.dedup();
5711 if hits.is_empty() {
5712 return;
5713 }
5714 let id = *hits.last().unwrap();
5720 *var = id;
5721 *ty = vt.vars[id as usize].ty.clone();
5722 };
5723 walk_stmt_exprs(body, &mut |e| {
5724 deep_rewrite(e, &mut |x| {
5725 match x {
5726 Expr::Method { desc, args, is_dynamic: false, .. } => {
5727 for (i, a) in args.iter_mut().enumerate() {
5728 if let Some(f) = desc.args.get(i) {
5729 if matches!(f, JavaType::Object(n) if n.as_ref() != "java/lang/Object")
5730 || matches!(f, JavaType::Array(_))
5731 {
5732 try_swap(a, f);
5733 }
5734 }
5735 }
5736 }
5737 Expr::New { arg_tys, args, .. } => {
5738 for (i, a) in args.iter_mut().enumerate() {
5739 if let Some(f) = arg_tys.get(i) {
5740 if matches!(f, JavaType::Object(n) if n.as_ref() != "java/lang/Object")
5741 || matches!(f, JavaType::Array(_))
5742 {
5743 try_swap(a, f);
5744 }
5745 }
5746 }
5747 }
5748 _ => {}
5749 }
5750 });
5751 });
5752 if ret_wants_ref {
5753 walk_mut_deep(body, &mut |st| {
5754 if let Stmt::Return(Some(e)) = st {
5755 try_swap(e, ret_ty);
5756 }
5757 });
5758 }
5759}
5760
5761pub fn fix_ref_null_assigns(vt: &VarTable, body: &mut Stmt) {
5776 let is_ref_var = |var: u32| -> bool {
5777 (var as usize) < vt.vars.len()
5778 && matches!(
5779 vt.vars[var as usize].ty.erased(),
5780 JavaType::Object(_) | JavaType::Array(_)
5781 )
5782 };
5783 walk_mut_deep(body, &mut |st| match st {
5784 Stmt::ExprStmt(Expr::Assign {
5785 target,
5786 value,
5787 op: AssignOp::Plain,
5788 ..
5789 }) => {
5790 if let Expr::Local { var, .. } = &**target {
5791 if is_ref_var(*var) && matches!(&**value, Expr::Const(ConstVal::Int(0))) {
5792 **value = Expr::Const(ConstVal::Null);
5793 }
5794 }
5795 }
5796 Stmt::LocalDef {
5797 var,
5798 init: Some(e),
5799 ..
5800 } => {
5801 if is_ref_var(*var) && matches!(e, Expr::Const(ConstVal::Int(0))) {
5802 *e = Expr::Const(ConstVal::Null);
5803 }
5804 }
5805 _ => {}
5806 });
5807}
5808
5809pub fn fix_ref_array_null_consts(body: &mut Stmt) {
5810 walk_stmt_exprs(body, &mut |e| {
5811 deep_rewrite(e, &mut |x| {
5812 if let Expr::NewArray { elem, init: Some(list), .. } = x {
5813 if matches!(elem.erased(), JavaType::Object(_) | JavaType::Array(_)) {
5814 for slot in list.iter_mut() {
5815 if matches!(&*slot, Expr::Const(ConstVal::Int(0))) {
5816 *slot = Expr::Const(ConstVal::Null);
5817 }
5818 }
5819 }
5820 }
5821 });
5822 });
5823}
5824
5825pub fn fix_null_sentinels(body: &mut Stmt, vt: &VarTable, ret: &JavaType) {
5864 #[derive(Clone, Copy, PartialEq)]
5865 enum WK {
5866 Zero,
5867 Copy(u32),
5868 Other,
5869 }
5870 fn is_ref_jt(t: &JavaType) -> bool {
5871 matches!(t, JavaType::Object(_) | JavaType::Array(_))
5872 }
5873 fn cls_write(e: &Expr) -> WK {
5874 match e {
5875 Expr::Const(ConstVal::Int(0)) => WK::Zero,
5876 Expr::Local { var, .. } => WK::Copy(*var),
5877 _ => WK::Other,
5878 }
5879 }
5880 fn expr_writes(e: &Expr, writes: &mut jdc_core::FxHashMap<u32, Vec<WK>>) {
5881 visit_exprs(e, &mut |x| match x {
5882 Expr::Assign { target, op, value } => {
5883 if let Expr::Local { var, .. } = &**target {
5884 let k = if matches!(op, AssignOp::Plain) {
5885 cls_write(value)
5886 } else {
5887 WK::Other
5888 };
5889 writes.entry(*var).or_default().push(k);
5890 }
5891 }
5892 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
5893 if let Expr::Local { var, .. } = &**inner {
5894 writes.entry(*var).or_default().push(WK::Other);
5895 }
5896 }
5897 _ => {}
5898 });
5899 }
5900 fn collect_writes(
5901 s: &Stmt,
5902 writes: &mut jdc_core::FxHashMap<u32, Vec<WK>>,
5903 has_zero: &mut bool,
5904 ) {
5905 fn note(e: &Expr, has_zero: &mut bool, writes: &mut jdc_core::FxHashMap<u32, Vec<WK>>) {
5906 visit_exprs(e, &mut |x| {
5907 if let Expr::Const(ConstVal::Int(0)) = x {
5908 *has_zero = true;
5909 }
5910 });
5911 expr_writes(e, writes);
5912 }
5913 match s {
5914 Stmt::Block(v) => {
5915 for x in v {
5916 collect_writes(x, writes, has_zero);
5917 }
5918 }
5919 Stmt::LocalDef { var, init, .. } => {
5920 if let Some(e) = init {
5921 if matches!(e, Expr::Const(ConstVal::Int(0))) {
5922 *has_zero = true;
5923 }
5924 writes.entry(*var).or_default().push(cls_write(e));
5925 }
5926 }
5927 Stmt::ExprStmt(e) => note(e, has_zero, writes),
5928 Stmt::Return(e) => {
5929 if let Some(x) = e {
5930 note(x, has_zero, writes);
5931 }
5932 }
5933 Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
5934 note(e, has_zero, writes);
5935 }
5936 Stmt::TernaryValue { e } => note(e, has_zero, writes),
5937 Stmt::If { cond, then_stmt, else_stmt } => {
5938 note(cond, has_zero, writes);
5939 collect_writes(then_stmt, writes, has_zero);
5940 if let Some(b) = else_stmt {
5941 collect_writes(b, writes, has_zero);
5942 }
5943 }
5944 Stmt::While { cond, body } | Stmt::DoWhile { body, cond } => {
5945 note(cond, has_zero, writes);
5946 collect_writes(body, writes, has_zero);
5947 }
5948 Stmt::For { init, cond, update, body } => {
5949 for x in init {
5950 collect_writes(x, writes, has_zero);
5951 }
5952 if let Some(c) = cond {
5953 note(c, has_zero, writes);
5954 }
5955 for u in update {
5956 note(u, has_zero, writes);
5957 }
5958 collect_writes(body, writes, has_zero);
5959 }
5960 Stmt::ForEach { var, iterable, body, .. } => {
5961 writes.entry(*var).or_default().push(WK::Other);
5962 note(iterable, has_zero, writes);
5963 collect_writes(body, writes, has_zero);
5964 }
5965 Stmt::Switch { selector, cases, default, .. } => {
5966 note(selector, has_zero, writes);
5967 for c in cases {
5968 if let Some(g) = &c.guard {
5969 note(g, has_zero, writes);
5970 }
5971 for x in &c.body {
5972 collect_writes(x, writes, has_zero);
5973 }
5974 }
5975 if let Some(d) = default {
5976 collect_writes(d, writes, has_zero);
5977 }
5978 }
5979 Stmt::Try { body, catches, finally } => {
5980 collect_writes(body, writes, has_zero);
5981 for c in catches {
5982 writes.entry(c.var).or_default().push(WK::Other);
5983 collect_writes(&c.body, writes, has_zero);
5984 }
5985 if let Some(f) = finally {
5986 collect_writes(f, writes, has_zero);
5987 }
5988 }
5989 Stmt::TryWithResources { resources, body, catches, finally, .. } => {
5990 for r in resources {
5991 collect_writes(r, writes, has_zero);
5992 }
5993 collect_writes(body, writes, has_zero);
5994 for c in catches {
5995 writes.entry(c.var).or_default().push(WK::Other);
5996 collect_writes(&c.body, writes, has_zero);
5997 }
5998 if let Some(f) = finally {
5999 collect_writes(f, writes, has_zero);
6000 }
6001 }
6002 Stmt::Assert { cond, msg } => {
6003 note(cond, has_zero, writes);
6004 if let Some(m) = msg {
6005 note(m, has_zero, writes);
6006 }
6007 }
6008 Stmt::Synchronized { lock, body } => {
6009 note(lock, has_zero, writes);
6010 collect_writes(body, writes, has_zero);
6011 }
6012 Stmt::Labeled { body, .. } => collect_writes(body, writes, has_zero),
6013 _ => {}
6014 }
6015 }
6016
6017 let mut writes: jdc_core::FxHashMap<u32, Vec<WK>> =
6018 jdc_core::FxHashMap::default();
6019 let mut has_zero = false;
6020 collect_writes(body, &mut writes, &mut has_zero);
6021
6022 let mut eligible: HashSet<u32> = HashSet::default();
6024 let int_typed = |v: u32| {
6025 (v as usize) < vt.vars.len() && vt.vars[v as usize].ty.erased() == JavaType::Int
6026 };
6027 loop {
6028 let mut changed = false;
6029 for (&v, ws) in writes.iter() {
6030 if eligible.contains(&v) || ws.is_empty() || !int_typed(v) {
6031 continue;
6032 }
6033 if ws.iter().all(|k| match k {
6034 WK::Zero => true,
6035 WK::Copy(u) => *u != v && eligible.contains(u),
6036 WK::Other => false,
6037 }) {
6038 eligible.insert(v);
6039 changed = true;
6040 }
6041 }
6042 if !changed {
6043 break;
6044 }
6045 }
6046 if eligible.is_empty() && !has_zero {
6047 return; }
6049
6050 #[derive(Clone, Copy, PartialEq)]
6051 enum Mode {
6052 Scan,
6053 Rewrite,
6054 }
6055 struct St<'a> {
6056 mode: Mode,
6057 vt: &'a VarTable,
6058 eligible: &'a HashSet<u32>,
6059 ref_read: HashSet<u32>,
6060 dead: HashSet<u32>,
6061 sentinels: HashSet<u32>,
6062 n_lit: usize,
6063 n_var: usize,
6064 }
6065 fn arg_ctx(t: &JavaType) -> Option<JavaType> {
6069 match t {
6070 JavaType::Object(n) if n.as_ref() == "java/lang/Object" => None,
6071 t if is_ref_jt(t) => Some(t.clone()),
6072 _ => None,
6073 }
6074 }
6075 fn walk_expr(e: &mut Expr, ctx: Option<JavaType>, st: &mut St) {
6076 walk_expr_c(e, ctx, false, st);
6077 }
6078 fn walk_expr_c(e: &mut Expr, ctx: Option<JavaType>, cast_arg: bool, st: &mut St) {
6084 let slot_ref = ctx.as_ref().is_some_and(is_ref_jt);
6085 fn mk_null(ctx: &Option<JavaType>, cast_arg: bool) -> Expr {
6086 match (cast_arg, ctx) {
6087 (true, Some(t)) => Expr::Cast {
6088 ty: TypeRef::J(t.clone()),
6089 e: Box::new(Expr::Const(ConstVal::Null)),
6090 },
6091 _ => Expr::Const(ConstVal::Null),
6092 }
6093 }
6094 match e {
6095 Expr::Const(ConstVal::Int(0)) if slot_ref => {
6096 if st.mode == Mode::Rewrite {
6097 *e = mk_null(&ctx, cast_arg);
6098 st.n_lit += 1;
6099 }
6100 return;
6101 }
6102 Expr::Local { var, .. } => {
6103 if st.eligible.contains(var) {
6104 if slot_ref {
6105 st.ref_read.insert(*var);
6106 } else {
6107 st.dead.insert(*var);
6108 }
6109 }
6110 if st.mode == Mode::Rewrite && slot_ref && st.sentinels.contains(var) {
6111 *e = mk_null(&ctx, cast_arg);
6112 st.n_var += 1;
6113 }
6114 return;
6115 }
6116 _ => {}
6117 }
6118 match e {
6119 Expr::Method { owner, desc, args, .. } => {
6120 if let Some(o) = owner {
6121 walk_expr(o, None, st);
6122 }
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::Invokedynamic { desc, args, .. } => {
6129 for (i, a) in args.iter_mut().enumerate() {
6130 let c = desc.args.get(i).and_then(arg_ctx);
6131 walk_expr_c(a, c, true, st);
6132 }
6133 }
6134 Expr::New { args, arg_tys, .. } => {
6135 for (i, a) in args.iter_mut().enumerate() {
6136 let c = arg_tys.get(i).and_then(arg_ctx);
6137 walk_expr_c(a, c, true, st);
6138 }
6139 }
6140 Expr::AnonNew { args, .. } => {
6141 for a in args.iter_mut() {
6142 walk_expr(a, None, st);
6143 }
6144 }
6145 Expr::Assign { target, value, .. } => {
6146 if let Expr::Local { var: u, .. } = &**target {
6150 if st.eligible.contains(u) {
6151 return;
6152 }
6153 }
6154 let vctx = match &**target {
6155 Expr::Field { ty, .. } => {
6156 let t = ty.erased();
6157 is_ref_jt(&t).then_some(t)
6158 }
6159 Expr::ArrayIndex { array, .. } => match array.type_ref().erased() {
6160 JavaType::Array(inner) if is_ref_jt(&inner) => Some(*inner),
6161 _ => None,
6162 },
6163 _ => None,
6164 };
6165 walk_expr(target, None, st);
6166 walk_expr(value, vctx, st);
6167 }
6168 Expr::Cast { ty, e: inner } => {
6169 let t = ty.erased();
6170 let c = is_ref_jt(&t).then_some(t);
6171 walk_expr(inner, c, st);
6172 }
6173 Expr::InstanceOf { e: inner, .. } => {
6174 walk_expr(inner, Some(JavaType::Object("java/lang/Object".into())), st);
6175 }
6176 Expr::Cond { c, t, f } => {
6177 walk_expr(c, None, st);
6178 let tc = ctx.clone();
6179 walk_expr(t, tc, st);
6180 walk_expr(f, ctx, st);
6181 }
6182 Expr::NewArray { elem, dims, init, .. } => {
6183 for d in dims.iter_mut() {
6184 walk_expr(d, None, st);
6185 }
6186 if let Some(list) = init {
6187 let et = elem.erased();
6188 let c = is_ref_jt(&et).then_some(et);
6189 for slot in list.iter_mut() {
6190 walk_expr(slot, c.clone(), st);
6191 }
6192 }
6193 }
6194 Expr::NewMultiArray { dims, .. } => {
6195 for d in dims.iter_mut() {
6196 walk_expr(d, None, st);
6197 }
6198 }
6199 Expr::ArrayIndex { array, index } => {
6200 walk_expr(array, None, st);
6201 walk_expr(index, None, st);
6202 }
6203 Expr::Field { owner, .. } => {
6204 if let Some(o) = owner {
6205 walk_expr(o, None, st);
6206 }
6207 }
6208 Expr::Bin { l, r, .. } => {
6209 walk_expr(l, None, st);
6210 walk_expr(r, None, st);
6211 }
6212 Expr::Un { e: inner, .. } => walk_expr(inner, None, st),
6213 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
6214 walk_expr(inner, None, st);
6215 }
6216 Expr::StringConcat(parts) => {
6217 for p in parts.iter_mut() {
6218 if let ConcatPart::Str(x) = p {
6219 walk_expr(x, None, st);
6220 }
6221 }
6222 }
6223 Expr::Lambda(l) => {
6224 for c in l.captures.iter_mut() {
6225 walk_expr(c, None, st);
6226 }
6227 }
6228 _ => {}
6229 }
6230 }
6231 fn walk_stmt(s: &mut Stmt, st: &mut St, ret: &JavaType) {
6232 match s {
6233 Stmt::Block(v) => {
6234 for x in v.iter_mut() {
6235 walk_stmt(x, st, ret);
6236 }
6237 }
6238 Stmt::ExprStmt(e) => walk_expr(e, None, st),
6239 Stmt::LocalDef { var, init, .. } => {
6240 if let Some(e) = init {
6241 let ctx = if (*var as usize) < st.vt.vars.len() {
6242 let t = st.vt.vars[*var as usize].ty.erased();
6243 is_ref_jt(&t).then_some(t)
6244 } else {
6245 None
6246 };
6247 walk_expr(e, ctx, st);
6248 }
6249 }
6250 Stmt::Return(e) => {
6251 if let Some(x) = e {
6252 let c = is_ref_jt(ret).then(|| ret.clone());
6253 walk_expr(x, c, st);
6254 }
6255 }
6256 Stmt::Throw(e) => {
6257 walk_expr(e, Some(JavaType::Object("java/lang/Throwable".into())), st);
6258 }
6259 Stmt::If { cond, then_stmt, else_stmt } => {
6260 walk_expr(cond, None, st);
6261 walk_stmt(then_stmt, st, ret);
6262 if let Some(b) = else_stmt {
6263 walk_stmt(b, st, ret);
6264 }
6265 }
6266 Stmt::While { cond, body } => {
6267 walk_expr(cond, None, st);
6268 walk_stmt(body, st, ret);
6269 }
6270 Stmt::DoWhile { body, cond } => {
6271 walk_stmt(body, st, ret);
6272 walk_expr(cond, None, st);
6273 }
6274 Stmt::For { init, cond, update, body } => {
6275 for x in init.iter_mut() {
6276 walk_stmt(x, st, ret);
6277 }
6278 if let Some(c) = cond {
6279 walk_expr(c, None, st);
6280 }
6281 for u in update.iter_mut() {
6282 walk_expr(u, None, st);
6283 }
6284 walk_stmt(body, st, ret);
6285 }
6286 Stmt::ForEach { iterable, body, .. } => {
6287 walk_expr(iterable, None, st);
6288 walk_stmt(body, st, ret);
6289 }
6290 Stmt::Switch { selector, cases, default, .. } => {
6291 walk_expr(selector, None, st);
6292 for c in cases.iter_mut() {
6293 if let Some(g) = &mut c.guard {
6294 walk_expr(g, None, st);
6295 }
6296 for x in c.body.iter_mut() {
6297 walk_stmt(x, st, ret);
6298 }
6299 }
6300 if let Some(d) = default {
6301 walk_stmt(d, st, ret);
6302 }
6303 }
6304 Stmt::Try { body, catches, finally } => {
6305 walk_stmt(body, st, ret);
6306 for c in catches.iter_mut() {
6307 walk_stmt(&mut c.body, st, ret);
6308 }
6309 if let Some(f) = finally {
6310 walk_stmt(f, st, ret);
6311 }
6312 }
6313 Stmt::TryWithResources { resources, body, catches, finally, .. } => {
6314 for r in resources.iter_mut() {
6315 walk_stmt(r, st, ret);
6316 }
6317 walk_stmt(body, st, ret);
6318 for c in catches.iter_mut() {
6319 walk_stmt(&mut c.body, st, ret);
6320 }
6321 if let Some(f) = finally {
6322 walk_stmt(f, st, ret);
6323 }
6324 }
6325 Stmt::Assert { cond, msg } => {
6326 walk_expr(cond, None, st);
6327 if let Some(m) = msg {
6328 walk_expr(m, None, st);
6329 }
6330 }
6331 Stmt::Synchronized { lock, body } => {
6332 walk_expr(lock, Some(JavaType::Object("java/lang/Object".into())), st);
6333 walk_stmt(body, st, ret);
6334 }
6335 Stmt::TernaryValue { e } => walk_expr(e, None, st),
6336 Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
6337 walk_expr(e, Some(JavaType::Object("java/lang/Object".into())), st);
6338 }
6339 Stmt::Labeled { body, .. } => walk_stmt(body, st, ret),
6340 _ => {}
6341 }
6342 }
6343
6344 let mut st = St {
6345 mode: Mode::Scan,
6346 vt,
6347 eligible: &eligible,
6348 ref_read: HashSet::default(),
6349 dead: HashSet::default(),
6350 sentinels: HashSet::default(),
6351 n_lit: 0,
6352 n_var: 0,
6353 };
6354 if !eligible.is_empty() {
6355 walk_stmt(body, &mut st, ret);
6356 st.sentinels = eligible
6357 .iter()
6358 .copied()
6359 .filter(|v| st.ref_read.contains(v) && !st.dead.contains(v))
6360 .collect();
6361 }
6362 if st.sentinels.is_empty() && !has_zero {
6363 return;
6364 }
6365 st.mode = Mode::Rewrite;
6366 walk_stmt(body, &mut st, ret);
6367 if std::env::var_os("DDC_SENT").is_some() {
6368 eprintln!(
6369 "[sent] elig={} confirmed={} lit={} var={}",
6370 eligible.len(),
6371 st.sentinels.len(),
6372 st.n_lit,
6373 st.n_var
6374 );
6375 }
6376}
6377
6378pub fn fix_int_operand_bridges(body: &mut Stmt, vt: &VarTable) {
6387 fn side_bool(e: &Expr, vt: &VarTable) -> bool {
6388 match e {
6389 Expr::Local { var, .. } => {
6390 matches!(vt.var(*var).ty.erased(), JavaType::Boolean)
6391 }
6392 Expr::Bin {
6403 op: BinOp::And | BinOp::Or | BinOp::Xor,
6404 l,
6405 r,
6406 ..
6407 } => side_bool(l, vt) && side_bool(r, vt),
6408 other => matches!(other.type_ref().erased(), JavaType::Boolean),
6409 }
6410 }
6411 fn side_int(e: &Expr, vt: &VarTable) -> bool {
6412 if side_bool(e, vt) {
6416 return false;
6417 }
6418 matches!(
6419 match e {
6420 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
6421 other => other.type_ref().erased(),
6422 },
6423 JavaType::Int | JavaType::Short | JavaType::Byte | JavaType::Char | JavaType::Long
6424 )
6425 }
6426 fn wrap(e: Box<Expr>) -> Box<Expr> {
6427 Box::new(Expr::Cond {
6428 c: e,
6429 t: Box::new(Expr::Const(ConstVal::Int(1))),
6430 f: Box::new(Expr::Const(ConstVal::Int(0))),
6431 })
6432 }
6433 walk_stmt_exprs(body, &mut |e| {
6434 deep_rewrite(e, &mut |x| {
6435 if let Expr::Assign { target, value, op: AssignOp::Plain } = x {
6442 let tgt_ty: Option<JavaType> = match &**target {
6443 Expr::Field { ty, .. } => Some(ty.erased()),
6444 Expr::ArrayIndex { array, .. } => {
6445 match array.type_ref().erased() {
6446 JavaType::Array(el) => Some(el.as_ref().clone()),
6447 _ => None,
6448 }
6449 }
6450 Expr::Local { var, .. } => {
6455 if (*var as usize) < vt.vars.len() {
6456 Some(vt.var(*var).ty.erased())
6457 } else {
6458 None
6459 }
6460 }
6461 _ => None,
6462 };
6463 if let Some(t) = tgt_ty {
6464 let tgt_int = matches!(
6465 t,
6466 JavaType::Int
6467 | JavaType::Short
6468 | JavaType::Byte
6469 | JavaType::Char
6470 | JavaType::Long
6471 );
6472 if tgt_int && side_bool(value, vt) && !side_int(value, vt) {
6473 let taken = std::mem::replace(
6474 value,
6475 Box::new(Expr::Const(ConstVal::Null)),
6476 );
6477 let mut wrapped = wrap(taken);
6478 if !matches!(t, JavaType::Int | JavaType::Long) {
6479 wrapped = Box::new(Expr::Cast {
6480 ty: TypeRef::J(t.clone()),
6481 e: wrapped,
6482 });
6483 }
6484 *value = wrapped;
6485 } else if matches!(t, JavaType::Boolean)
6486 && side_int(value, vt)
6487 && !side_bool(value, vt)
6488 {
6489 let taken = std::mem::replace(
6490 value,
6491 Box::new(Expr::Const(ConstVal::Null)),
6492 );
6493 **value = Expr::Bin {
6494 op: BinOp::Ne,
6495 l: taken,
6496 r: Box::new(Expr::Const(ConstVal::Int(0))),
6497 ty: Some(TypeRef::J(JavaType::Boolean)),
6498 };
6499 }
6500 }
6501 }
6502 if let Expr::Cast { ty, e } = x {
6508 let t = ty.erased();
6509 let t_num = matches!(
6510 t,
6511 JavaType::Int
6512 | JavaType::Short
6513 | JavaType::Byte
6514 | JavaType::Char
6515 | JavaType::Long
6516 | JavaType::Float
6517 | JavaType::Double
6518 );
6519 if t_num && side_bool(e, vt) && !side_int(e, vt) {
6520 let taken = std::mem::replace(e, Box::new(Expr::Const(ConstVal::Null)));
6521 **e = Expr::Cond {
6522 c: taken,
6523 t: Box::new(Expr::Const(ConstVal::Int(1))),
6524 f: Box::new(Expr::Const(ConstVal::Int(0))),
6525 };
6526 } else if matches!(t, JavaType::Boolean)
6527 && side_int(e, vt)
6528 && !side_bool(e, vt)
6529 {
6530 let taken = std::mem::replace(e, Box::new(Expr::Const(ConstVal::Null)));
6531 *x = Expr::Bin {
6532 op: BinOp::Ne,
6533 l: taken,
6534 r: Box::new(Expr::Const(ConstVal::Int(0))),
6535 ty: Some(TypeRef::J(JavaType::Boolean)),
6536 };
6537 }
6538 }
6539 if let Expr::Bin { op, l, r, .. } = x {
6548 if matches!(op, BinOp::Eq | BinOp::Ne) {
6549 if matches!(&**r, Expr::Const(ConstVal::Null))
6550 && side_int(l, vt)
6551 && !side_bool(l, vt)
6552 {
6553 **r = Expr::Const(ConstVal::Int(0));
6554 } else if matches!(&**l, Expr::Const(ConstVal::Null))
6555 && side_int(r, vt)
6556 && !side_bool(r, vt)
6557 {
6558 **l = Expr::Const(ConstVal::Int(0));
6559 } else if matches!(&**r, Expr::Const(ConstVal::Null))
6560 && side_bool(l, vt)
6561 {
6562 let taken =
6563 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
6564 *x = if matches!(op, BinOp::Eq) {
6565 Expr::Un { op: UnOp::Not, e: taken }
6566 } else {
6567 *taken
6568 };
6569 } else if matches!(&**l, Expr::Const(ConstVal::Null))
6570 && side_bool(r, vt)
6571 {
6572 let taken =
6573 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
6574 *x = if matches!(op, BinOp::Eq) {
6575 Expr::Un { op: UnOp::Not, e: taken }
6576 } else {
6577 *taken
6578 };
6579 }
6580 }
6581 }
6582 if let Expr::ArrayIndex { index, .. } = x {
6586 if side_bool(index, vt) && !side_int(index, vt) {
6587 let taken =
6588 std::mem::replace(index, Box::new(Expr::Const(ConstVal::Null)));
6589 *index = wrap(taken);
6590 }
6591 }
6592 if let Expr::NewArray { dims, .. } = x {
6598 for d in dims.iter_mut() {
6599 if side_bool(d, vt) && !side_int(d, vt) {
6600 let taken =
6601 std::mem::replace(d, Expr::Const(ConstVal::Null));
6602 *d = *wrap(Box::new(taken));
6603 }
6604 }
6605 }
6606 if let Expr::Bin { op, l, r, .. } = x {
6607 let int_kind = matches!(
6608 op,
6609 BinOp::Or
6610 | BinOp::And
6611 | BinOp::Xor
6612 | BinOp::Add
6613 | BinOp::Sub
6614 | BinOp::Mul
6615 | BinOp::Div
6616 | BinOp::Rem
6617 | BinOp::Shl
6618 | BinOp::Shr
6619 | BinOp::Ushr
6620 | BinOp::Eq
6621 | BinOp::Ne
6622 | BinOp::Lt
6623 | BinOp::Ge
6624 | BinOp::Gt
6625 | BinOp::Le
6626 );
6627 if !int_kind {
6628 return;
6629 }
6630 if matches!(
6643 op,
6644 BinOp::Or
6645 | BinOp::And
6646 | BinOp::Xor
6647 | BinOp::Add
6648 | BinOp::Sub
6649 | BinOp::Mul
6650 | BinOp::Div
6651 | BinOp::Rem
6652 | BinOp::Shl
6653 | BinOp::Shr
6654 | BinOp::Ushr
6655 ) {
6656 if matches!(&**r, Expr::Const(ConstVal::Null))
6657 && (side_bool(l, vt) || side_int(l, vt))
6658 {
6659 **r = Expr::Const(ConstVal::Int(0));
6660 } else if matches!(&**l, Expr::Const(ConstVal::Null))
6661 && (side_bool(r, vt) || side_int(r, vt))
6662 {
6663 **l = Expr::Const(ConstVal::Int(0));
6664 }
6665 }
6666 let arith = matches!(
6676 op,
6677 BinOp::Add
6678 | BinOp::Sub
6679 | BinOp::Mul
6680 | BinOp::Div
6681 | BinOp::Rem
6682 | BinOp::Shl
6683 | BinOp::Shr
6684 | BinOp::Ushr
6685 );
6686 let ordered = matches!(op, BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le);
6687 if arith || ordered {
6688 let side_str = |e: &Expr| {
6689 matches!(
6690 match e {
6691 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
6692 other => other.type_ref().erased(),
6693 },
6694 JavaType::Object(ref s) if s.as_ref() == "java/lang/String"
6695 )
6696 };
6697 let concat = arith && (side_str(l) || side_str(r));
6698 if !concat {
6699 if side_bool(l, vt) {
6700 let taken =
6701 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
6702 *l = wrap(taken);
6703 }
6704 if side_bool(r, vt) {
6705 let taken =
6706 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
6707 *r = wrap(taken);
6708 }
6709 }
6710 return;
6711 }
6712 if side_bool(l, vt) && side_int(r, vt) && !side_bool(r, vt) {
6715 let taken = std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
6716 *l = wrap(taken);
6717 } else if side_bool(r, vt) && side_int(l, vt) && !side_bool(l, vt) {
6718 let taken = std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
6719 *r = wrap(taken);
6720 }
6721 }
6722 });
6723 });
6724}
6725
6726pub fn fold_value_diamonds(body: &mut Stmt, vt: &VarTable) {
6764 let Stmt::Block(stmts) = body else { return };
6765 fold_diamonds_in(stmts, vt);
6766}
6767
6768fn diamond_types_ok(a: &Expr, b: &Expr) -> bool {
6769 fn num(t: &JavaType) -> bool {
6770 matches!(
6771 t,
6772 JavaType::Byte
6773 | JavaType::Short
6774 | JavaType::Int
6775 | JavaType::Char
6776 | JavaType::Long
6777 | JavaType::Float
6778 | JavaType::Double
6779 )
6780 }
6781 if let Some(other) = if matches!(a, Expr::Const(ConstVal::Null)) {
6789 Some(b)
6790 } else if matches!(b, Expr::Const(ConstVal::Null)) {
6791 Some(a)
6792 } else {
6793 None
6794 } {
6795 return !num(&other.type_ref().erased());
6796 }
6797 let ta = a.type_ref().erased();
6798 let tb = b.type_ref().erased();
6799 ta == tb || (num(&ta) && num(&tb))
6800}
6801
6802fn single_local_assign(st: &Stmt) -> Option<(u32, Expr, Expr)> {
6810 let mut inner = st;
6811 while let Stmt::Block(v) = inner {
6812 if v.len() != 1 {
6813 return None;
6814 }
6815 inner = &v[0];
6816 }
6817 match inner {
6818 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain }) => {
6819 match &**target {
6820 Expr::Local { var, .. } => Some((*var, *target.clone(), *value.clone())),
6821 _ => None,
6822 }
6823 }
6824 _ => None,
6825 }
6826}
6827
6828fn arm_assign_inlined(st: &Stmt) -> Option<(u32, Expr, Expr)> {
6837 if let Some(r) = single_local_assign(st) {
6838 return Some(r);
6839 }
6840 let mut inner = st;
6841 while let Stmt::Block(v) = inner {
6842 if v.len() == 1 {
6843 inner = &v[0];
6844 } else {
6845 break;
6846 }
6847 }
6848 let Stmt::Block(v) = inner else { return None };
6849 if v.len() != 2 {
6850 return None;
6851 }
6852 let Stmt::LocalDef {
6853 var: x,
6854 init: Some(init),
6855 ..
6856 } = &v[0]
6857 else {
6858 return None;
6859 };
6860 let (var, tgt, val) = single_local_assign(&v[1])?;
6861 let mut count = 0usize;
6862 let mut c = val.clone();
6863 deep_rewrite(&mut c, &mut |e| {
6864 if let Expr::Local { var: lv, .. } = e {
6865 if lv == x {
6866 count += 1;
6867 }
6868 }
6869 });
6870 if count != 1 {
6871 return None;
6872 }
6873 let mut out = val.clone();
6874 deep_rewrite(&mut out, &mut |e| {
6875 if let Expr::Local { var: lv, .. } = e {
6876 if lv == x {
6877 *e = init.clone();
6878 }
6879 }
6880 });
6881 Some((var, tgt, out))
6882}
6883
6884fn mk_ternary_assign(tgt: Expr, c: Expr, a: Expr, b: Expr) -> Stmt {
6885 Stmt::ExprStmt(Expr::Assign {
6886 target: Box::new(tgt),
6887 op: AssignOp::Plain,
6888 value: Box::new(Expr::Cond {
6889 c: Box::new(c),
6890 t: Box::new(a),
6891 f: Box::new(b),
6892 }),
6893 })
6894}
6895
6896fn same_render_var(v1: u32, v2: u32, vt: &VarTable) -> bool {
6901 let (a, b) = (vt.vars.get(v1 as usize), vt.vars.get(v2 as usize));
6902 match (a, b) {
6903 (Some(x), Some(y)) => x.name == y.name && x.ty.erased() == y.ty.erased(),
6904 _ => false,
6905 }
6906}
6907
6908fn canon_stmts_eq(a: &[Stmt], b: &[Stmt], vt: &VarTable) -> bool {
6915 if a.len() != b.len() {
6916 return false;
6917 }
6918 let mut map: std::collections::HashMap<String, u32> = std::collections::HashMap::default();
6919 let canon = |s: &Stmt, map: &mut std::collections::HashMap<String, u32>| -> Stmt {
6920 let mut c = s.clone();
6921 walk_stmt_exprs(&mut c, &mut |e| {
6922 deep_rewrite(e, &mut |x| {
6923 if let Expr::Local { var, .. } = x {
6924 let key = match vt.vars.get(*var as usize) {
6925 Some(vi) => format!("{}#{:?}", vi.name, vi.ty.erased()),
6926 None => format!("v{}", var),
6927 };
6928 let id = *map.entry(key).or_insert(*var);
6929 *x = Expr::Local {
6930 var: id,
6931 ty: vt.var(id).ty.clone(),
6932 };
6933 }
6934 });
6935 });
6936 c
6937 };
6938 a.iter()
6939 .zip(b.iter())
6940 .all(|(x, y)| canon(x, &mut map) == canon(y, &mut map))
6941}
6942
6943fn fold_diamonds_in(stmts: &mut Vec<Stmt>, vt: &VarTable) {
6944 let mut i = 0usize;
6945 while i < stmts.len() {
6946 match &mut stmts[i] {
6947 Stmt::Block(v) => fold_diamonds_in(v, vt),
6948 Stmt::If { then_stmt, else_stmt, .. } => {
6949 if let Stmt::Block(v) = &mut **then_stmt {
6950 fold_diamonds_in(v, vt);
6951 }
6952 if let Some(e) = else_stmt {
6953 if let Stmt::Block(v) = &mut **e {
6954 fold_diamonds_in(v, vt);
6955 }
6956 }
6957 }
6958 _ => {}
6959 }
6960 let shape1 = if let Stmt::If { cond, then_stmt, else_stmt: Some(el), .. } = &stmts[i] {
6962 match (arm_assign_inlined(then_stmt), arm_assign_inlined(el)) {
6963 (Some((v1, tgt, a)), Some((v2, _, b)))
6964 if v1 == v2 && diamond_types_ok(&a, &b) =>
6965 {
6966 Some(mk_ternary_assign(tgt, cond.clone(), a, b))
6967 }
6968 _ => None,
6969 }
6970 } else {
6971 None
6972 };
6973 if let Some(f) = shape1 {
6974 stmts[i] = f;
6975 i += 1;
6976 continue;
6977 }
6978 let shape3 = if let Stmt::If {
6991 cond,
6992 then_stmt,
6993 else_stmt: Some(el),
6994 ..
6995 } = &stmts[i]
6996 {
6997 let arm = |st: &Stmt| -> Option<((u32, Expr, Expr), Vec<Stmt>)> {
6998 let Stmt::Block(v) = st else { return None };
6999 if v.len() < 2 {
7000 return None;
7001 }
7002 let first = single_local_assign(&v[0])?;
7003 Some((first, v[1..].to_vec()))
7004 };
7005 match (arm(then_stmt), arm(el)) {
7006 (Some(((v1, tgt1, a), mut rest_t)), Some(((v2, _t2, b), rest_e)))
7007 if !rest_t.is_empty()
7008 && is_bare_ctor_call(first_leaf_stmt(&rest_t[0]))
7009 && stmts_count_writes(&rest_t, v1) == 0
7010 && stmts_count_writes(&rest_t, v2) == 0
7011 && diamond_types_ok(&a, &b) =>
7012 {
7013 if v1 == v2 || same_render_var(v1, v2, vt) {
7014 if canon_stmts_eq(&rest_t, &rest_e, vt) {
7016 Some((mk_ternary_assign(tgt1, cond.clone(), a, b), rest_t))
7017 } else {
7018 None
7019 }
7020 } else if stmts_read_var(&rest_t, v1) == 1
7021 && stmts_read_var(&rest_e, v2) == 1
7022 && stmts_read_var(&rest_t, v2) == 0
7023 && stmts_read_var(&rest_e, v1) == 0
7024 {
7025 let mut unified = rest_e.clone();
7033 for st in unified.iter_mut() {
7034 let mut c2 = st.clone();
7035 walk_stmt_exprs(&mut c2, &mut |e| {
7036 deep_rewrite(e, &mut |x| {
7037 if let Expr::Local { var, .. } = x {
7038 if *var == v2 {
7039 *x = Expr::Local {
7040 var: v1,
7041 ty: vt.var(v1).ty.clone(),
7042 };
7043 }
7044 }
7045 });
7046 });
7047 *st = c2;
7048 }
7049 if unified != rest_t {
7050 None
7051 } else {
7052 let tern = Expr::Cond {
7053 c: Box::new(cond.clone()),
7054 t: Box::new(a.clone()),
7055 f: Box::new(b.clone()),
7056 };
7057 for st in rest_t.iter_mut() {
7058 let mut c2 = st.clone();
7059 walk_stmt_exprs(&mut c2, &mut |e| {
7060 deep_rewrite(e, &mut |x| {
7061 if let Expr::Local { var, .. } = x {
7062 if *var == v1 {
7063 *x = tern.clone();
7064 }
7065 }
7066 });
7067 });
7068 *st = c2;
7069 }
7070 let first = rest_t.remove(0);
7074 Some((first, rest_t))
7075 }
7076 } else {
7077 None
7078 }
7079 }
7080 _ => None,
7081 }
7082 } else {
7083 None
7084 };
7085 if let Some((folded, rest)) = shape3 {
7086 stmts[i] = folded;
7087 stmts.splice(i + 1..i + 1, rest);
7088 i += 1;
7089 continue;
7090 }
7091 if i + 1 < stmts.len() {
7101 let shape2 = {
7121 let s_i = single_local_assign(&stmts[i]);
7122 if let Stmt::If {
7123 cond,
7124 then_stmt,
7125 else_stmt,
7126 ..
7127 } = &stmts[i + 1]
7128 {
7129 let then_a = single_local_assign(then_stmt);
7130 let else_a = else_stmt.as_deref().and_then(single_local_assign);
7131 let then_empty =
7132 matches!(&**then_stmt, Stmt::Block(v) if v.is_empty());
7133 let else_empty = else_stmt
7134 .as_deref()
7135 .map(|e| matches!(e, Stmt::Block(v) if v.is_empty()))
7136 .unwrap_or(true);
7137 let try_fold = |over: Option<(u32, Expr, Expr)>,
7138 keep_over: bool|
7139 -> Option<Stmt> {
7140 let (v2, tgt2, a) = over?;
7141 let (v1, _t1, b) = s_i.as_ref()?;
7142 if !(*v1 == v2 || same_render_var(*v1, v2, vt)) {
7143 return None;
7144 }
7145 let keep = if *v1 == v2 {
7146 b.clone()
7147 } else {
7148 Expr::Local {
7149 var: *v1,
7150 ty: vt.var(*v1).ty.clone(),
7151 }
7152 };
7153 if !diamond_types_ok(&a, &keep) {
7154 return None;
7155 }
7156 if keep_over {
7157 Some(mk_ternary_assign(tgt2, cond.clone(), keep, a))
7158 } else {
7159 Some(mk_ternary_assign(tgt2, cond.clone(), a, keep))
7160 }
7161 };
7162 if then_a.is_some() && else_empty {
7163 try_fold(then_a, false)
7164 } else if else_a.is_some() && then_empty {
7165 try_fold(else_a, true)
7166 } else {
7167 None
7168 }
7169 } else {
7170 None
7171 }
7172 };
7173 let mut lead_fold: Option<(Stmt, Stmt)> = None;
7182 if shape2.is_none() && i + 1 < stmts.len() {
7183 let s_i2 = single_local_assign(&stmts[i]);
7184 let leading = |st: &Stmt| -> Option<((u32, Expr, Expr), Vec<Stmt>)> {
7185 let Stmt::Block(v) = st else { return None };
7186 if v.len() < 2 {
7187 return None;
7188 }
7189 let first = single_local_assign(&v[0])?;
7190 Some((first, v[1..].to_vec()))
7191 };
7192 if let (Some((v1, _t1, b)), Stmt::If { cond, then_stmt, else_stmt, .. }) =
7193 (&s_i2, &stmts[i + 1])
7194 {
7195 let else_absent = else_stmt.is_none()
7196 || else_stmt
7197 .as_deref()
7198 .is_some_and(|e| matches!(e, Stmt::Block(v) if v.is_empty()));
7199 let then_absent = matches!(&**then_stmt, Stmt::Block(v) if v.is_empty());
7200 let try_lead = |over: Option<((u32, Expr, Expr), Vec<Stmt>)>,
7201 keep_over: bool|
7202 -> Option<(Stmt, Stmt)> {
7203 let ((v2, tgt2, a), rest) = over?;
7204 if !(*v1 == v2 || same_render_var(*v1, v2, vt)) {
7205 return None;
7206 }
7207 if stmts_count_writes(&rest, *v1) != 0
7208 || stmts_count_writes(&rest, v2) != 0
7209 {
7210 return None;
7211 }
7212 let keep = if *v1 == v2 {
7213 b.clone()
7214 } else {
7215 Expr::Local {
7216 var: *v1,
7217 ty: vt.var(*v1).ty.clone(),
7218 }
7219 };
7220 if !diamond_types_ok(&a, &keep) {
7221 return None;
7222 }
7223 let t = if keep_over {
7224 mk_ternary_assign(tgt2, cond.clone(), keep, a)
7225 } else {
7226 mk_ternary_assign(tgt2, cond.clone(), a, keep)
7227 };
7228 let mut new_if = stmts[i + 1].clone();
7229 if let Stmt::If { then_stmt, else_stmt, .. } = &mut new_if {
7230 if keep_over {
7231 *else_stmt = Some(Box::new(Stmt::Block(rest)));
7232 } else {
7233 **then_stmt = Stmt::Block(rest);
7234 }
7235 }
7236 Some((t, new_if))
7237 };
7238 if else_absent {
7239 lead_fold = try_lead(leading(then_stmt), false);
7240 } else if then_absent {
7241 if let Some(e) = else_stmt {
7242 lead_fold = try_lead(leading(e), true);
7243 }
7244 }
7245 }
7246 }
7247 if let Some((folded, new_if)) = lead_fold {
7248 stmts[i] = folded;
7249 stmts[i + 1] = new_if;
7250 continue;
7251 }
7252 if let Some(f) = shape2 {
7253 stmts[i] = f;
7254 stmts.remove(i + 1);
7255 continue; }
7257 }
7258 i += 1;
7259 }
7260}
7261
7262pub fn fold_bool_value_diamonds(body: &mut Stmt, vt: &VarTable) {
7263 fn is_bool_var(v: u32, vt: &VarTable) -> bool {
7264 (v as usize) < vt.vars.len()
7265 && matches!(vt.vars[v as usize].ty.erased(), JavaType::Boolean)
7266 }
7267 fn bool_const(e: &Expr) -> Option<bool> {
7268 fn cint(e: &Expr) -> Option<i32> {
7269 match e {
7270 Expr::Const(ConstVal::Int(n)) => Some(*n),
7271 _ => None,
7272 }
7273 }
7274 match e {
7275 Expr::Const(ConstVal::Int(0)) => Some(false),
7276 Expr::Const(ConstVal::Int(1)) => Some(true),
7277 Expr::Bin { op: op @ (BinOp::Eq | BinOp::Ne), l, r, .. } => {
7281 let (a, b) = (cint(l)?, cint(r)?);
7282 let eq = a == b;
7283 Some(if matches!(op, BinOp::Eq) { eq } else { !eq })
7284 }
7285 _ => None,
7286 }
7287 }
7288 fn reads_var(e: &Expr, v: u32) -> bool {
7289 let mut hit = false;
7290 visit_exprs(e, &mut |x| {
7291 if let Expr::Local { var, .. } = x {
7292 if *var == v {
7293 hit = true;
7294 }
7295 }
7296 });
7297 hit
7298 }
7299 fn bool_valued(e: &Expr, vt: &VarTable) -> bool {
7303 fn side(e: &Expr, vt: &VarTable) -> bool {
7304 bool_const(e).is_some() || bool_valued(e, vt)
7305 }
7306 match e {
7307 Expr::Local { var, .. } => is_bool_var(*var, vt),
7308 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
7309 Expr::InstanceOf { .. } => true,
7310 Expr::Un { op: UnOp::Not, e: x, .. } => bool_valued(x, vt) || bool_const(x).is_some(),
7311 Expr::Bin { op, l, r, .. } => match op {
7312 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le => true,
7313 BinOp::LogAnd | BinOp::LogOr => {
7316 side(l, vt) && side(r, vt)
7317 }
7318 _ => false,
7319 },
7320 Expr::Cond { t, f, .. } => side(t, vt) && side(f, vt),
7321 _ => false,
7322 }
7323 }
7324 fn lone_assign(
7333 b: &Stmt,
7334 v: u32,
7335 vt: &VarTable,
7336 an: &VarAnalysis,
7337 consumed: &mut HashSet<u32>,
7338 ) -> Option<Expr> {
7339 let l = flat_list(b);
7340 if l.len() == 2 {
7341 let (u, e): (u32, &Expr) = match l[0] {
7342 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e),
7343 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
7344 match &**target {
7345 Expr::Local { var, .. } => (*var, &**value),
7346 _ => return None,
7347 }
7348 }
7349 _ => return None,
7350 };
7351 if u == v || consumed.contains(&u) {
7352 return None;
7353 }
7354 if an.assigns.get(u as usize).copied().unwrap_or(0) != 1
7361 || an.reads.get(u as usize).copied().unwrap_or(0) != 1
7362 {
7363 return None;
7364 }
7365 let (tv, rv) = match l[1] {
7366 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
7367 match (&**target, &**value) {
7368 (Expr::Local { var: t, .. }, Expr::Local { var: r, .. }) => (*t, *r),
7369 _ => return None,
7370 }
7371 }
7372 Stmt::LocalDef { var, init: Some(Expr::Local { var: r, .. }), .. } => (*var, *r),
7373 _ => return None,
7374 };
7375 if tv != v || rv != u {
7376 return None;
7377 }
7378 if reads_var(e, v) || !(bool_valued(e, vt) || bool_const(e).is_some()) {
7379 return None;
7380 }
7381 consumed.insert(u);
7382 return Some(e.clone());
7383 }
7384 if l.len() != 1 {
7385 return None;
7386 }
7387 let (target, value) = match l[0] {
7388 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => (target, value),
7389 Stmt::LocalDef { var, init: Some(value), .. } if *var == v => {
7390 if reads_var(value, v) || !(bool_valued(value, vt) || bool_const(value).is_some()) {
7391 return None;
7392 }
7393 return Some(value.clone());
7394 }
7395 _ => return None,
7396 };
7397 let Expr::Local { var: tv, .. } = &**target else { return None };
7398 if *tv != v || reads_var(value, v) {
7399 return None;
7400 }
7401 if !(bool_valued(value, vt) || bool_const(value).is_some()) {
7402 return None;
7403 }
7404 Some((**value).clone())
7405 }
7406 fn not(e: Expr) -> Expr {
7407 Expr::Un { op: UnOp::Not, e: Box::new(e) }
7408 }
7409 fn simplify_bool(e: Expr) -> Expr {
7414 match e {
7415 Expr::Un { op: UnOp::Not, e: inner, .. } => match *inner {
7416 Expr::Bin { op: BinOp::Ne, l, r, ty } => {
7421 simplify_bool(Expr::Bin { op: BinOp::Eq, l, r, ty })
7422 }
7423 Expr::Bin { op: BinOp::Eq, l, r, ty } => {
7424 simplify_bool(Expr::Bin { op: BinOp::Ne, l, r, ty })
7425 }
7426 other => Expr::Un { op: UnOp::Not, e: Box::new(simplify_bool(other)) },
7427 },
7428 Expr::Bin { op: op @ (BinOp::Eq | BinOp::Ne), l, r, ty } => {
7429 fn cond01(e: &Expr) -> Option<Expr> {
7431 if let Expr::Cond { c, t, f } = e {
7432 if matches!(&**t, Expr::Const(ConstVal::Int(1)))
7433 && matches!(&**f, Expr::Const(ConstVal::Int(0)))
7434 {
7435 return Some((**c).clone());
7436 }
7437 }
7438 None
7439 }
7440 let (lc, rc) = (cond01(&l), cond01(&r));
7441 match (lc, rc) {
7442 (Some(c), None) => match (&*r, op) {
7443 (Expr::Const(ConstVal::Int(1)), BinOp::Eq) => c,
7444 (Expr::Const(ConstVal::Int(1)), BinOp::Ne) => not(c),
7445 (Expr::Const(ConstVal::Int(0)), BinOp::Eq) => not(c),
7446 (Expr::Const(ConstVal::Int(0)), BinOp::Ne) => c,
7447 _ => Expr::Bin { op, l: Box::new(c), r, ty },
7448 },
7449 (None, Some(c)) => match (&*l, op) {
7450 (Expr::Const(ConstVal::Int(1)), BinOp::Eq) => c,
7451 (Expr::Const(ConstVal::Int(1)), BinOp::Ne) => not(c),
7452 (Expr::Const(ConstVal::Int(0)), BinOp::Eq) => not(c),
7453 (Expr::Const(ConstVal::Int(0)), BinOp::Ne) => c,
7454 _ => Expr::Bin { op, l, r: Box::new(c), ty },
7455 },
7456 _ => Expr::Bin { op, l, r, ty },
7457 }
7458 }
7459 other => other,
7460 }
7461 }
7462 fn bin(op: BinOp, l: Expr, r: Expr) -> Expr {
7463 Expr::Bin { op, l: Box::new(l), r: Box::new(r), ty: None }
7464 }
7465
7466 for _round in 0..4 {
7467 let mut changed = false;
7468 let mut analysis = VarAnalysis {
7469 assigns: Vec::new(),
7470 reads: Vec::new(),
7471 values: Vec::new(),
7472 };
7473 analyze_vars(body, &mut analysis);
7474 let mut consumed: HashSet<u32> = HashSet::default();
7475 walk_mut_deep(body, &mut |st| {
7476 let Stmt::Block(list) = st else { return };
7477 let mut i = 0usize;
7478 while i + 1 < list.len() {
7479 let folded = try_at(list, i, vt, &is_bool_var, &bool_const, &reads_var, &lone_assign, &analysis, &mut consumed);
7480 match folded {
7481 Some(expr) => {
7482 let is_def = matches!(&list[i], Stmt::LocalDef { .. });
7483 let var = match &list[i] {
7484 Stmt::LocalDef { var, .. } => *var,
7485 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
7486 match &**target { Expr::Local { var, .. } => *var, _ => unreachable!() }
7487 }
7488 _ => unreachable!(),
7489 };
7490 let (is_final, force_type) = match &list[i] {
7491 Stmt::LocalDef { is_final, force_type, .. } => (*is_final, *force_type),
7492 _ => (false, false),
7493 };
7494 list[i] = if is_def {
7495 Stmt::LocalDef { var, init: Some(expr), is_final, force_type }
7496 } else {
7497 Stmt::ExprStmt(Expr::Assign {
7498 target: Box::new(Expr::Local {
7499 var,
7500 ty: TypeRef::J(JavaType::Boolean),
7501 }),
7502 op: AssignOp::Plain,
7503 value: Box::new(expr),
7504 })
7505 };
7506 list.remove(i + 1);
7507 changed = true;
7508 }
7511 None => i += 1,
7512 }
7513 }
7514 });
7515 if !changed {
7516 break;
7517 }
7518 }
7519
7520 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
7521 fn try_at(
7522 list: &[Stmt],
7523 i: usize,
7524 vt: &VarTable,
7525 is_bool_var: &dyn Fn(u32, &VarTable) -> bool,
7526 bool_const: &dyn Fn(&Expr) -> Option<bool>,
7527 reads_var: &dyn Fn(&Expr, u32) -> bool,
7528 lone_assign: &dyn Fn(&Stmt, u32, &VarTable, &VarAnalysis, &mut HashSet<u32>) -> Option<Expr>,
7529 an: &VarAnalysis,
7530 consumed: &mut HashSet<u32>,
7531 ) -> Option<Expr> {
7532 let (v, base) = match &list[i] {
7534 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
7535 match &**target {
7536 Expr::Local { var, .. } => {
7537 let b = bool_const(value)?;
7540 if !is_bool_var(*var, vt) { return None; }
7541 (*var, b)
7542 }
7543 _ => return None,
7544 }
7545 }
7546 Stmt::LocalDef { var, init: Some(e), .. } => {
7547 let b = bool_const(e)?;
7548 if !is_bool_var(*var, vt) { return None; }
7549 (*var, b)
7550 }
7551 _ => return None,
7552 };
7553 let Stmt::If { cond, then_stmt, else_stmt } = &list[i + 1] else {
7554 return None;
7555 };
7556 if reads_var(cond, v) {
7557 return None;
7558 }
7559 let mut touches_v = false;
7560 visit_exprs(cond, &mut |x| {
7561 if let Expr::Assign { target, .. } = x {
7562 if let Expr::Local { var, .. } = &**target {
7563 if *var == v { touches_v = true; }
7564 }
7565 }
7566 });
7567 if touches_v {
7568 return None;
7569 }
7570 let (cond_n, then_arm, else_arm): (Expr, &Stmt, Option<&Stmt>) =
7573 if flat_list(then_stmt).is_empty() && else_stmt.is_some() {
7574 (not(cond.clone()), else_stmt.as_ref().unwrap(), None)
7575 } else {
7576 (cond.clone(), then_stmt.as_ref(), else_stmt.as_deref())
7577 };
7578 let e1 = lone_assign(then_arm, v, vt, an, consumed)?;
7579 let e2 = match else_arm {
7580 None => None,
7581 Some(b) => Some(lone_assign(b, v, vt, an, consumed)?),
7582 };
7583 let e1c = bool_const(&e1);
7584 let c = simplify_bool(cond_n);
7585 Some(match e2 {
7586 None => {
7587 if !base {
7588 match e1c {
7589 Some(true) => c,
7590 Some(false) => return None, None => bin(BinOp::LogAnd, c, e1),
7592 }
7593 } else {
7594 match e1c {
7595 Some(false) => not(c),
7596 Some(true) => return None,
7597 None => bin(BinOp::LogOr, not(c), e1),
7598 }
7599 }
7600 }
7601 Some(e2) => {
7602 if e1 == e2 {
7604 return None; }
7606 Expr::Cond { c: Box::new(c), t: Box::new(e1), f: Box::new(e2) }
7607 }
7608 })
7609 }
7610}
7611
7612pub fn idiom_compounds(body: &mut Stmt, vt: &VarTable) {
7613 walk_mut_deep(body, &mut |st| {
7614 let Stmt::ExprStmt(e) = st else { return };
7615 let Expr::Assign { target, op: AssignOp::Plain, value } = &mut *e else {
7616 return;
7617 };
7618 enum Tgt {
7621 Loc(u32),
7622 Fld,
7623 }
7624 let tgt = match &**target {
7625 Expr::Local { var, .. } => Tgt::Loc(*var),
7626 Expr::Field { owner, .. }
7627 if owner.is_none()
7628 || matches!(&**owner.as_ref().unwrap(), Expr::This) =>
7629 {
7630 Tgt::Fld
7631 }
7632 _ => return,
7633 };
7634 let Expr::Bin { op, l, r, .. } = &mut **value else {
7635 return;
7636 };
7637 match (&tgt, &**l) {
7639 (Tgt::Loc(v), Expr::Local { var, .. }) if *var == *v => {}
7640 (Tgt::Fld, Expr::Field { cls: c2, name: n2, .. }) => {
7641 let Expr::Field { cls: c1, name: n1, .. } = &**target else {
7642 return;
7643 };
7644 if c1 != c2 || n1 != n2 {
7645 return;
7646 }
7647 }
7648 _ => return,
7649 }
7650 let one = match &**r {
7652 Expr::Const(ConstVal::Int(1)) => Some(1i64),
7653 Expr::Const(ConstVal::Int(-1)) => Some(-1),
7654 Expr::Const(ConstVal::Long(1)) => Some(1),
7655 Expr::Const(ConstVal::Long(-1)) => Some(-1),
7656 _ => None,
7657 };
7658 if *op == BinOp::Add || *op == BinOp::Sub {
7659 if let Some(mut d) = one {
7660 if *op == BinOp::Sub {
7661 d = -d;
7662 }
7663 let tgt_ty = match &tgt {
7664 Tgt::Loc(v) => vt.var(*v).ty.erased(),
7665 Tgt::Fld => {
7666 let Expr::Field { ty, .. } = &**target else {
7667 return;
7668 };
7669 ty.erased()
7670 }
7671 };
7672 let numeric = matches!(
7673 tgt_ty,
7674 JavaType::Int
7675 | JavaType::Long
7676 | JavaType::Short
7677 | JavaType::Byte
7678 | JavaType::Char
7679 | JavaType::Float
7680 | JavaType::Double
7681 );
7682 if numeric {
7683 let wide = matches!(tgt_ty, JavaType::Long);
7684 let inner = match &tgt {
7685 Tgt::Loc(v) => Expr::Local {
7686 var: *v,
7687 ty: vt.var(*v).ty.clone(),
7688 },
7689 Tgt::Fld => (**target).clone(),
7690 };
7691 *e = Expr::PostIncDec {
7692 e: Box::new(inner),
7693 delta: d,
7694 wide,
7695 };
7696 return;
7697 }
7698 }
7699 }
7700 let cop = match op {
7701 BinOp::Add => AssignOp::Add,
7702 BinOp::Sub => AssignOp::Sub,
7703 BinOp::Mul => AssignOp::Mul,
7704 BinOp::Div => AssignOp::Div,
7705 BinOp::Rem => AssignOp::Rem,
7706 BinOp::Shl => AssignOp::Shl,
7707 BinOp::Shr => AssignOp::Shr,
7708 BinOp::Ushr => AssignOp::Ushr,
7709 BinOp::And => AssignOp::And,
7710 BinOp::Or => AssignOp::Or,
7711 BinOp::Xor => AssignOp::Xor,
7712 _ => return,
7713 };
7714 let rhs = std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
7715 let new_target = match &tgt {
7716 Tgt::Loc(v) => Box::new(Expr::Local {
7717 var: *v,
7718 ty: vt.var(*v).ty.clone(),
7719 }),
7720 Tgt::Fld => target.clone(),
7721 };
7722 *e = Expr::Assign {
7723 target: new_target,
7724 op: cop,
7725 value: rhs,
7726 };
7727 });
7728}
7729
7730pub fn booleanize(vt: &mut VarTable, body: &mut Stmt, ret_bool: bool) -> usize {
7736 let n0 = vt.vars.len();
7743 let mut derefed = vec![false; n0];
7744 mark_derefs(body, &mut derefed);
7745 let mut total = 0usize;
7746 for _ in 0..4 {
7747 let n = booleanize_round(vt, body, ret_bool, &derefed);
7748 if n == 0 {
7749 break;
7750 }
7751 total += n;
7752 }
7753 total
7754}
7755
7756fn mark_deref_expr(e: &Expr, derefed: &mut [bool]) {
7760 match e {
7761 Expr::Field { owner: Some(o), is_static: false, .. }
7762 | Expr::Method { owner: Some(o), .. } => {
7763 if let Expr::Local { var, .. } = &**o {
7764 if (*var as usize) < derefed.len() {
7765 derefed[*var as usize] = true;
7766 }
7767 }
7768 }
7769 Expr::ArrayIndex { array, .. } => {
7770 if let Expr::Local { var, .. } = &**array {
7771 if (*var as usize) < derefed.len() {
7772 derefed[*var as usize] = true;
7773 }
7774 }
7775 }
7776 _ => {}
7777 }
7778 for_each_child(e, &mut |c| mark_deref_expr(c, derefed));
7779}
7780
7781fn mark_derefs(body: &Stmt, derefed: &mut [bool]) {
7782 walk_all(body, &mut |st| {
7783 match st {
7784 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
7785 mark_deref_expr(e, derefed);
7786 }
7787 Stmt::Return(Some(e)) => mark_deref_expr(e, derefed),
7788 Stmt::LocalDef { init: Some(e), .. } => mark_deref_expr(e, derefed),
7789 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
7790 mark_deref_expr(cond, derefed);
7791 }
7792 Stmt::For { init, .. } => {
7793 for x in init {
7794 mark_derefs(x, derefed);
7795 }
7796 }
7797 Stmt::Switch { selector, .. } => mark_deref_expr(selector, derefed),
7798 Stmt::ForEach { iterable, .. } => mark_deref_expr(iterable, derefed),
7799 _ => {}
7800 }
7801 });
7802}
7803
7804fn booleanize_round(
7805 vt: &mut VarTable,
7806 body: &mut Stmt,
7807 ret_bool: bool,
7808 derefed: &[bool],
7809) -> usize {
7810 let n = vt.vars.len();
7811 let mut in_cond = vec![false; n];
7812
7813 walk_all(body, &mut |st| {
7816 let cond: Option<&Expr> = match st {
7817 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
7818 Some(cond)
7819 }
7820 _ => None,
7821 };
7822 if let Some(c) = cond {
7823 if let Expr::Bin {
7824 op: BinOp::Eq | BinOp::Ne,
7825 l,
7826 r,
7827 ..
7828 } = c
7829 {
7830 for (x, other) in [(l, r), (r, l)] {
7831 if let (Expr::Local { var, .. }, Expr::Const(ConstVal::Int(0))) =
7832 (&**x, &**other)
7833 {
7834 if (*var as usize) < n {
7835 in_cond[*var as usize] = true;
7836 }
7837 }
7838 }
7839 }
7840 }
7841 if ret_bool {
7846 if let Stmt::Return(Some(Expr::Local { var, .. })) = st {
7847 if (*var as usize) < n {
7848 in_cond[*var as usize] = true;
7849 }
7850 }
7851 }
7852 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = st {
7859 if let Expr::Field { ty, .. } = &**target {
7860 if ty.erased() == JavaType::Boolean {
7861 if let Expr::Local { var, .. } = &**value {
7862 if (*var as usize) < n {
7863 in_cond[*var as usize] = true;
7864 }
7865 }
7866 }
7867 }
7868 }
7869 let val: Option<&Expr> = match st {
7870 Stmt::ExprStmt(Expr::Assign { value, .. }) => Some(value),
7871 Stmt::ExprStmt(e) => Some(e),
7874 Stmt::LocalDef { init: Some(e), .. } => Some(e),
7875 Stmt::Return(Some(e)) => Some(e),
7876 _ => None,
7877 };
7878 if let Some(v) = val {
7879 visit_exprs(v, &mut |x| {
7880 if let Expr::Method { desc, args, .. } = x {
7886 for (i, a) in args.iter().enumerate() {
7887 if desc.args.get(i) == Some(&JavaType::Boolean) {
7888 if let Expr::Local { var, .. } = a {
7889 if (*var as usize) < n {
7890 in_cond[*var as usize] = true;
7891 }
7892 }
7893 }
7894 }
7895 }
7896 if let Expr::Bin { op, l, r, .. } = x {
7897 if matches!(
7898 op,
7899 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
7900 ) {
7901 for side in [l, r] {
7902 if let Expr::Local { var, .. } = &**side {
7903 if (*var as usize) < n {
7904 in_cond[*var as usize] = true;
7905 }
7906 }
7907 }
7908 }
7909 if matches!(op, BinOp::Or | BinOp::And) {
7915 let l_bool = l.type_ref().erased() == JavaType::Boolean;
7916 let r_bool = r.type_ref().erased() == JavaType::Boolean;
7917 if l_bool != r_bool {
7918 let side = if l_bool { r } else { l };
7919 if let Expr::Local { var, .. } = &**side {
7920 if (*var as usize) < n {
7921 in_cond[*var as usize] = true;
7922 }
7923 }
7924 }
7925 }
7926 }
7927 });
7928 }
7929 });
7930
7931 let mut assigned_any = vec![false; n];
7938 let mut all_bool = vec![true; n];
7939 let mut edges: Vec<(usize, usize)> = Vec::new();
7940 walk_all(body, &mut |st| {
7941 let (var, value) = match st {
7942 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
7943 Expr::Local { var, .. } => (var, &**value),
7944 _ => return,
7945 },
7946 Stmt::LocalDef {
7947 var, init: Some(e), ..
7948 } => (var, e),
7949 _ => return,
7950 };
7951 let i = *var as usize;
7952 if i < n {
7953 assigned_any[i] = true;
7954 if !is_boolean_valued(value, vt) {
7955 all_bool[i] = false;
7956 }
7957 if let Expr::Local { var: src, .. } = value {
7963 if (*src as usize) < n {
7964 edges.push((i, *src as usize));
7965 }
7966 }
7967 }
7968 });
7969 loop {
7974 let mut changed = false;
7975 for &(tgt, src) in &edges {
7976 if in_cond[tgt] && !in_cond[src] && all_bool[src] {
7977 in_cond[src] = true;
7978 changed = true;
7979 }
7980 if in_cond[src] && !in_cond[tgt] && all_bool[tgt] {
7986 in_cond[tgt] = true;
7987 changed = true;
7988 }
7989 }
7990 if !changed {
7991 break;
7992 }
7993 }
7994 let reads = count_locals_stmts(std::slice::from_ref(body));
7995 let mut boolean_vars: HashSet<u32> = HashSet::default();
7996 for i in 0..n {
7997 if vt.vars[i].is_param {
7998 continue;
7999 }
8000 if derefed[i] {
8001 continue;
8002 }
8003 if !in_cond[i] && !(reads.get(&(i as u32)).copied().unwrap_or(0) == 0) {
8004 continue;
8005 }
8006 if assigned_any[i] && all_bool[i] {
8007 boolean_vars.insert(i as u32);
8008 }
8009 }
8010 if boolean_vars.is_empty() {
8011 return 0;
8012 }
8013 for v in &boolean_vars {
8014 vt.vars[*v as usize].ty = TypeRef::J(JavaType::Boolean);
8015 }
8016 let types: Vec<&TypeRef> = vt.vars.iter().map(|v| &v.ty).collect();
8017 rewrite_exprs(body, &mut |e| {
8018 deep_rewrite(e, &mut |x| {
8019 if let Expr::Local { var, ty } = x {
8020 if let Some(want) = types.get(*var as usize) {
8021 if ty != *want {
8022 *ty = (*want).clone();
8023 }
8024 }
8025 }
8026 });
8027 });
8028
8029 fold_bool_conditions(body, &boolean_vars);
8031 boolean_vars.len()
8032}
8033
8034fn is_boolean_valued(e: &Expr, vt: &VarTable) -> bool {
8035 match e {
8036 Expr::Const(ConstVal::Int(0)) | Expr::Const(ConstVal::Int(1)) => true,
8037 Expr::Bin { op: BinOp::Or | BinOp::And, l, r, .. } => {
8044 is_boolean_valued(l, vt) || is_boolean_valued(r, vt)
8045 }
8046 Expr::Bin {
8055 op: BinOp::Xor,
8056 l,
8057 r,
8058 ..
8059 } => is_boolean_valued(l, vt) && is_boolean_valued(r, vt),
8060 Expr::Bin { op, .. } => matches!(
8061 op,
8062 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
8063 ),
8064 Expr::InstanceOf { .. } => true,
8065 Expr::Local { var, ty } => {
8071 let t = if (*var as usize) < vt.vars.len() {
8072 vt.var(*var).ty.erased()
8073 } else {
8074 ty.erased()
8075 };
8076 t == JavaType::Boolean
8077 }
8078 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
8079 Expr::Cond { t, f, .. } => is_boolean_valued(t, vt) && is_boolean_valued(f, vt),
8080 Expr::Un { op: UnOp::Not, .. } => true,
8081 Expr::Field { ty, .. } => ty.erased() == JavaType::Boolean,
8085 _ => false,
8086 }
8087}
8088
8089fn fold_bool_conditions(s: &mut Stmt, bv: &HashSet<u32>) {
8090 let fold = |e: &mut Expr| {
8091 deep_rewrite(e, &mut |x| {
8092 if let Expr::Bin { op, l, r, .. } = x {
8093 let (var_side, const_side) = match (&**l, &**r) {
8094 (Expr::Local { .. }, Expr::Const(ConstVal::Int(_))) => (l, 0),
8095 (Expr::Const(ConstVal::Int(_)), Expr::Local { .. }) => (r, 1),
8096 _ => return,
8097 };
8098 let var = match &**var_side {
8099 Expr::Local { var, .. } => *var,
8100 _ => return,
8101 };
8102 if !bv.contains(&var) {
8103 return;
8104 }
8105 let _ = const_side;
8106 match op {
8107 BinOp::Ne => {
8108 let inner = var_side.clone();
8109 *x = *inner;
8110 }
8111 BinOp::Eq => {
8112 let inner = var_side.clone();
8113 *x = Expr::Un {
8114 op: UnOp::Not,
8115 e: Box::new(*inner),
8116 };
8117 }
8118 _ => {}
8119 }
8120 }
8121 });
8122 };
8123 match s {
8124 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => fold(cond),
8125 _ => {}
8126 }
8127 walk_mut_deep(s, &mut |st| {
8128 let cond = match st {
8129 Stmt::If { cond, .. } => Some(cond),
8130 Stmt::While { cond, .. } => Some(cond),
8131 Stmt::DoWhile { cond, .. } => Some(cond),
8132 _ => None,
8133 };
8134 if let Some(c) = cond {
8135 fold(c);
8136 }
8137 });
8138}
8139
8140pub fn flip_const_compares(s: &mut Stmt) {
8142 rewrite_exprs(s, &mut |e| {
8143 deep_rewrite(e, &mut |x| {
8144 if let Expr::Bin { op, l, r, .. } = x {
8145 if matches!(
8146 op,
8147 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
8148 ) {
8149 let l_const = matches!(&**l, Expr::Const(_));
8150 let r_var = matches!(&**r, Expr::Local { .. });
8151 if l_const && r_var {
8152 std::mem::swap(l, r);
8153 }
8154 }
8155 }
8156 });
8157 });
8158}
8159
8160pub fn null_compares(vt: &VarTable, body: &mut Stmt) {
8162 let obj_vars: HashSet<u32> = vt
8163 .vars
8164 .iter()
8165 .filter(|v| v.ty.erased().is_reference())
8166 .map(|v| v.id)
8167 .collect();
8168 rewrite_exprs(body, &mut |e| {
8169 deep_rewrite(e, &mut |x| {
8170 null_side_rewrite(x, &|v| obj_vars.contains(&v));
8171 });
8172 });
8173}
8174
8175pub fn drop_dead_locals(body: &mut Stmt) {
8184 loop {
8185 let mut reads: Vec<usize> = Vec::new();
8186 count_reads(body, &mut reads);
8187 let mut dropped = 0usize;
8188 walk_mut_deep(body, &mut |st| match st {
8189 Stmt::Block(items) => prune_dead_items(items, &reads, &mut dropped),
8190 Stmt::Switch { cases, .. } => {
8200 for c in cases {
8201 prune_dead_items(&mut c.body, &reads, &mut dropped);
8202 }
8203 }
8204 Stmt::For { init, .. } => prune_dead_items(init, &reads, &mut dropped),
8205 _ => {}
8206 });
8207 if dropped == 0 {
8208 break;
8209 }
8210 }
8211 {
8220 let mut reads: Vec<usize> = Vec::new();
8221 count_reads(body, &mut reads);
8222 let dead_inplace = |st: &Stmt| -> bool {
8223 let Stmt::ExprStmt(Expr::Assign {
8224 target,
8225 value,
8226 op: jdc_core::ir::expr::AssignOp::Plain,
8227 ..
8228 }) = st
8229 else {
8230 return false;
8231 };
8232 let Expr::Local { var, .. } = &**target else {
8233 return false;
8234 };
8235 reads.get(*var as usize).copied().unwrap_or(0) == 0 && !has_side_effects(value)
8236 };
8237 walk_mut_deep(body, &mut |st| {
8238 if dead_inplace(st) {
8239 *st = Stmt::Block(vec![]);
8240 }
8241 });
8242 }
8243}
8244
8245fn prune_dead_items(items: &mut Vec<Stmt>, reads: &[usize], dropped: &mut usize) {
8250 let dead = |v: u32| reads.get(v as usize).copied().unwrap_or(0) == 0;
8251 items.retain_mut(|x| match x {
8252 Stmt::ExprStmt(Expr::Assign { target, value, op, .. }) => {
8253 let dead_target = matches!(&**target, Expr::Local { var, .. } if dead(*var));
8254 if dead_target && matches!(*op, AssignOp::Plain) {
8255 *dropped += 1;
8256 if has_side_effects(value) {
8257 let e = std::mem::replace(value, Box::new(Expr::This));
8258 *x = Stmt::ExprStmt(*e);
8259 true
8260 } else {
8261 false
8262 }
8263 } else {
8264 true
8265 }
8266 }
8267 Stmt::LocalDef { var, init, .. } if dead(*var) => {
8268 *dropped += 1;
8269 if let Some(e) = init.take() {
8270 if has_side_effects(&e) {
8271 *x = Stmt::ExprStmt(e);
8272 return true;
8273 }
8274 }
8275 false
8276 }
8277 _ => true,
8278 });
8279}
8280
8281
8282fn stmt_shallow_vars<F: FnMut(u32)>(st: &Stmt, out: &mut F) {
8287 fn ex<F: FnMut(u32)>(e: &Expr, out: &mut F) {
8288 visit_exprs(e, &mut |x| {
8289 if let Expr::Local { var, .. } = x {
8290 out(*var);
8291 }
8292 });
8293 }
8294 match st {
8295 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
8296 ex(e, out)
8297 }
8298 Stmt::TernaryValue { e } => ex(e, out),
8299 Stmt::Return(Some(e)) => ex(e, out),
8300 Stmt::LocalDef { var, init, .. } => {
8301 out(*var);
8302 if let Some(e) = init {
8303 ex(e, out);
8304 }
8305 }
8306 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
8307 ex(cond, out)
8308 }
8309 Stmt::For { cond, update, .. } => {
8310 if let Some(c) = cond {
8311 ex(c, out);
8312 }
8313 for u in update {
8314 ex(u, out);
8315 }
8316 }
8317 Stmt::ForEach { iterable, .. } => ex(iterable, out),
8318 Stmt::Switch { selector, cases, .. } => {
8319 ex(selector, out);
8320 for cg in cases {
8321 if let Some(g) = &cg.guard {
8322 ex(g, out);
8323 }
8324 }
8325 }
8326 Stmt::Assert { cond, msg } => {
8327 ex(cond, out);
8328 if let Some(m) = msg {
8329 ex(m, out);
8330 }
8331 }
8332 Stmt::Synchronized { lock, .. } => ex(lock, out),
8333 _ => {}
8334 }
8335}
8336
8337fn scope_pass_a(st: &Stmt, cur: u32, next: &mut u32, def_block: &mut [u32], sizes: &mut Vec<u32>) {
8349 if let Stmt::LocalDef { var, .. } = st {
8350 let i = *var as usize;
8351 if i < def_block.len() && def_block[i] == u32::MAX {
8352 def_block[i] = cur;
8353 }
8354 }
8355 fn scoped(c: &Stmt, next: &mut u32, def_block: &mut [u32], sizes: &mut Vec<u32>) {
8356 let id = *next;
8357 *next += 1;
8358 sizes.push(0);
8359 scope_pass_a(c, id, next, def_block, sizes);
8360 sizes[id as usize] = *next - id;
8361 }
8362 match st {
8363 Stmt::Block(v) => {
8364 for x in v {
8365 scope_pass_a(x, cur, next, def_block, sizes);
8366 }
8367 }
8368 Stmt::If {
8369 then_stmt,
8370 else_stmt,
8371 ..
8372 } => {
8373 scoped(then_stmt, next, def_block, sizes);
8374 if let Some(e) = else_stmt {
8375 scoped(e, next, def_block, sizes);
8376 }
8377 }
8378 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => {
8379 scoped(body, next, def_block, sizes)
8380 }
8381 Stmt::For { init, body, .. } => {
8382 let id = *next;
8385 *next += 1;
8386 sizes.push(0);
8387 for x in init {
8388 scope_pass_a(x, id, next, def_block, sizes);
8389 }
8390 scoped(body, next, def_block, sizes);
8391 sizes[id as usize] = *next - id;
8392 }
8393 Stmt::ForEach { body, .. } => scoped(body, next, def_block, sizes),
8394 Stmt::Switch { cases, default, .. } => {
8395 for c in cases {
8396 let id = *next;
8401 *next += 1;
8402 sizes.push(0);
8403 for x in &c.body {
8404 if matches!(x, Stmt::Block(_)) {
8405 scoped(x, next, def_block, sizes);
8406 } else {
8407 scope_pass_a(x, id, next, def_block, sizes);
8408 }
8409 }
8410 sizes[id as usize] = *next - id;
8411 }
8412 if let Some(d) = default {
8413 scoped(d, next, def_block, sizes);
8414 }
8415 }
8416 Stmt::Try {
8417 body,
8418 catches,
8419 finally,
8420 } => {
8421 scoped(body, next, def_block, sizes);
8422 for c in catches {
8423 scoped(&c.body, next, def_block, sizes);
8424 }
8425 if let Some(f) = finally {
8426 scoped(f, next, def_block, sizes);
8427 }
8428 }
8429 Stmt::TryWithResources {
8430 resources,
8431 body,
8432 catches,
8433 finally,
8434 } => {
8435 let id = *next;
8437 *next += 1;
8438 sizes.push(0);
8439 for r in resources {
8440 scope_pass_a(r, id, next, def_block, sizes);
8441 }
8442 scoped(body, next, def_block, sizes);
8443 for c in catches {
8444 scoped(&c.body, next, def_block, sizes);
8445 }
8446 if let Some(f) = finally {
8447 scoped(f, next, def_block, sizes);
8448 }
8449 sizes[id as usize] = *next - id;
8450 }
8451 Stmt::Synchronized { body, .. } => scoped(body, next, def_block, sizes),
8452 Stmt::Labeled { body, .. } => scope_pass_a(body, cur, next, def_block, sizes),
8453 _ => {}
8454 }
8455}
8456
8457fn scope_pass_b(
8462 st: &Stmt,
8463 cur: u32,
8464 next: &mut u32,
8465 def_block: &[u32],
8466 sizes: &[u32],
8467 within: &mut [u32],
8468 total: &mut [u32],
8469) {
8470 fn cnt(
8471 v: u32,
8472 at: u32,
8473 def_block: &[u32],
8474 sizes: &[u32],
8475 within: &mut [u32],
8476 total: &mut [u32],
8477 ) {
8478 let i = v as usize;
8479 if i < total.len() {
8480 total[i] += 1;
8481 let d = def_block[i];
8482 if d != u32::MAX && d <= at && at < d + sizes[d as usize] {
8483 within[i] += 1;
8484 }
8485 }
8486 }
8487 fn scoped(
8488 c: &Stmt,
8489 next: &mut u32,
8490 def_block: &[u32],
8491 sizes: &[u32],
8492 within: &mut [u32],
8493 total: &mut [u32],
8494 ) {
8495 let id = *next;
8496 *next += 1;
8497 scope_pass_b(c, id, next, def_block, sizes, within, total);
8498 }
8499 fn ex_at(
8500 e: &Expr,
8501 at: u32,
8502 def_block: &[u32],
8503 sizes: &[u32],
8504 within: &mut [u32],
8505 total: &mut [u32],
8506 ) {
8507 visit_exprs(e, &mut |x| {
8508 if let Expr::Local { var, .. } = x {
8509 cnt(*var, at, def_block, sizes, within, total);
8510 }
8511 });
8512 }
8513 match st {
8514 Stmt::For {
8515 init,
8516 cond,
8517 update,
8518 body,
8519 } => {
8520 let id = *next;
8521 *next += 1;
8522 for x in init {
8523 scope_pass_b(x, id, next, def_block, sizes, within, total);
8524 }
8525 if let Some(c) = cond {
8526 ex_at(c, id, def_block, sizes, within, total);
8527 }
8528 for u in update {
8529 ex_at(u, id, def_block, sizes, within, total);
8530 }
8531 scoped(body, next, def_block, sizes, within, total);
8532 }
8533 Stmt::TryWithResources {
8534 resources,
8535 body,
8536 catches,
8537 finally,
8538 } => {
8539 let id = *next;
8540 *next += 1;
8541 for r in resources {
8542 scope_pass_b(r, id, next, def_block, sizes, within, total);
8543 }
8544 scoped(body, next, def_block, sizes, within, total);
8545 for c in catches {
8546 scoped(&c.body, next, def_block, sizes, within, total);
8547 }
8548 if let Some(f) = finally {
8549 scoped(f, next, def_block, sizes, within, total);
8550 }
8551 }
8552 _ => {
8553 stmt_shallow_vars(st, &mut |v| cnt(v, cur, def_block, sizes, within, total));
8554 match st {
8555 Stmt::Block(v) => {
8556 for x in v {
8557 scope_pass_b(x, cur, next, def_block, sizes, within, total);
8558 }
8559 }
8560 Stmt::If {
8561 then_stmt,
8562 else_stmt,
8563 ..
8564 } => {
8565 scoped(then_stmt, next, def_block, sizes, within, total);
8566 if let Some(e) = else_stmt {
8567 scoped(e, next, def_block, sizes, within, total);
8568 }
8569 }
8570 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => {
8571 scoped(body, next, def_block, sizes, within, total)
8572 }
8573 Stmt::ForEach { body, .. } => {
8574 scoped(body, next, def_block, sizes, within, total)
8575 }
8576 Stmt::Switch { cases, default, .. } => {
8577 for c in cases {
8578 let id = *next;
8579 *next += 1;
8580 for x in &c.body {
8581 if matches!(x, Stmt::Block(_)) {
8582 scoped(x, next, def_block, sizes, within, total);
8583 } else {
8584 scope_pass_b(x, id, next, def_block, sizes, within, total);
8585 }
8586 }
8587 }
8588 if let Some(d) = default {
8589 scoped(d, next, def_block, sizes, within, total);
8590 }
8591 }
8592 Stmt::Try {
8593 body,
8594 catches,
8595 finally,
8596 } => {
8597 scoped(body, next, def_block, sizes, within, total);
8598 for c in catches {
8599 scoped(&c.body, next, def_block, sizes, within, total);
8600 }
8601 if let Some(f) = finally {
8602 scoped(f, next, def_block, sizes, within, total);
8603 }
8604 }
8605 Stmt::Synchronized { body, .. } => {
8606 scoped(body, next, def_block, sizes, within, total)
8607 }
8608 Stmt::Labeled { body, .. } => {
8609 scope_pass_b(body, cur, next, def_block, sizes, within, total)
8610 }
8611 _ => {}
8612 }
8613 }
8614 }
8615}
8616
8617pub fn strip_enum_const_inits(s: &mut Stmt, class: &crate::PoolClass) {
8626 let const_fields: jdc_core::FxHashSet<&str> = class
8627 .static_fields
8628 .iter()
8629 .filter(|f| f.access & crate::access::ACC_ENUM != 0)
8630 .map(|f| f.name.as_ref())
8631 .collect();
8632 if const_fields.is_empty() {
8633 return;
8634 }
8635 if let Stmt::Block(stmts) = s {
8636 stmts.retain(|st| {
8637 !matches!(
8638 st,
8639 Stmt::ExprStmt(Expr::Assign { target, value, .. })
8640 if matches!(&**target,
8641 Expr::Field { cls, name, is_static: true, .. }
8642 if cls.as_ref() == class.name
8643 && const_fields.contains(&**name))
8644 && matches!(&**value, Expr::New { cls: ncls, .. } if ncls.as_ref() == class.name)
8645 )
8646 });
8647 }
8648}
8649
8650fn contains_this_access(e: &Expr) -> bool {
8659 let mut hit = false;
8660 visit_exprs(e, &mut |x| {
8661 if hit {
8662 return;
8663 }
8664 match x {
8665 Expr::This => hit = true,
8666 Expr::Field { owner, is_static: false, .. } => {
8667 if owner.is_none() || matches!(&**owner.as_ref().unwrap(), Expr::This) {
8668 hit = true;
8669 }
8670 }
8671 Expr::Method { owner, is_static: false, .. } => {
8672 if owner.is_none() || matches!(&**owner.as_ref().unwrap(), Expr::This) {
8673 hit = true;
8674 }
8675 }
8676 _ => {}
8677 }
8678 });
8679 hit
8680}
8681
8682fn var_occurs_in(stmts: &[Stmt], var: u32) -> bool {
8697 stmts.iter().any(|st| {
8698 let mut hit = false;
8699 visit_stmt_exprs_ro(st, &mut |e| {
8700 if !hit {
8701 visit_exprs(e, &mut |x| {
8702 if let Expr::Local { var: v, .. } = x {
8703 if *v == var {
8704 hit = true;
8705 }
8706 }
8707 });
8708 }
8709 });
8710 hit
8711 })
8712}
8713
8714pub struct CtorHelper {
8724 pub name: String,
8725 pub ret: JavaType,
8726 pub params: Vec<(JavaType, String)>,
8727 pub body: Stmt,
8728 pub vt: VarTable,
8729}
8730
8731fn ctor_helpers() -> &'static std::sync::Mutex<std::collections::HashMap<String, Vec<CtorHelper>>> {
8732 static H: std::sync::OnceLock<std::sync::Mutex<std::collections::HashMap<String, Vec<CtorHelper>>>> =
8733 std::sync::OnceLock::new();
8734 H.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::default()))
8735}
8736
8737pub struct CtorRelay {
8742 pub formals: Vec<JavaType>,
8744 pub is_super: bool,
8745}
8746
8747type RelayMap = std::collections::HashMap<String, Vec<(String, CtorRelay)>>;
8748
8749fn ctor_relays() -> &'static std::sync::Mutex<RelayMap> {
8750 static H: std::sync::OnceLock<std::sync::Mutex<RelayMap>> = std::sync::OnceLock::new();
8751 H.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::default()))
8752}
8753
8754pub fn take_ctor_relays(class: &str) -> Vec<(String, CtorRelay)> {
8757 let mut m = ctor_relays().lock().unwrap_or_else(|e| e.into_inner());
8758 let mut v = m.remove(class).unwrap_or_default();
8759 v.sort_by(|a, b| a.0.cmp(&b.0));
8760 v
8761}
8762
8763fn array_init(args: Vec<Expr>) -> Expr {
8764 Expr::NewArray {
8765 elem: TypeRef::J(JavaType::Object(std::sync::Arc::from("java/lang/Object"))),
8766 dims: Vec::new(),
8767 trailing_dims: 0,
8768 init: Some(args),
8769 }
8770}
8771
8772fn helper_name(class: &str, desc: &str) -> String {
8775 let mut h: u64 = 0xcbf29ce484222325;
8776 for b in class.as_bytes().iter().chain(b"::").chain(desc.as_bytes()) {
8777 h ^= *b as u64;
8778 h = h.wrapping_mul(0x100000001b3);
8779 }
8780 format!("resolve${:x}", (h >> 16) & 0xffff_ffff)
8781}
8782
8783pub fn take_ctor_helpers(class: &str) -> Vec<CtorHelper> {
8786 let mut m = ctor_helpers().lock().unwrap_or_else(|e| e.into_inner());
8787 let mut v = m.remove(class).unwrap_or_default();
8788 v.sort_by(|a, b| a.name.cmp(&b.name));
8789 v
8790}
8791
8792fn strip_delegations_deep(stmts: &mut Vec<Stmt>) {
8793 stmts.retain(|s| !is_bare_ctor_call(s));
8794 for s in stmts.iter_mut() {
8795 strip_delegation_one(s);
8796 }
8797}
8798
8799fn strip_delegation_one(st: &mut Stmt) {
8800 if is_bare_ctor_call(st) {
8801 *st = Stmt::Block(Vec::new());
8802 return;
8803 }
8804 match st {
8805 Stmt::Block(v) => strip_delegations_deep(v),
8806 Stmt::If { then_stmt, else_stmt, .. } => {
8807 strip_delegation_one(then_stmt);
8808 if let Some(e) = else_stmt {
8809 strip_delegation_one(e);
8810 }
8811 }
8812 Stmt::While { body, .. }
8813 | Stmt::DoWhile { body, .. }
8814 | Stmt::ForEach { body, .. }
8815 | Stmt::Synchronized { body, .. }
8816 | Stmt::Labeled { body, .. } => strip_delegation_one(body),
8817 Stmt::For { init, body, .. } => {
8818 strip_delegations_deep(init);
8819 strip_delegation_one(body);
8820 }
8821 Stmt::Switch { cases, default, .. } => {
8822 for c in cases.iter_mut() {
8823 strip_delegations_deep(&mut c.body);
8824 }
8825 if let Some(d) = default {
8826 strip_delegation_one(d);
8827 }
8828 }
8829 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
8830 strip_delegation_one(body);
8831 for c in catches.iter_mut() {
8832 strip_delegation_one(&mut c.body);
8833 }
8834 if let Some(f) = finally {
8835 strip_delegation_one(f);
8836 }
8837 }
8838 _ => {}
8839 }
8840}
8841
8842#[allow(clippy::ptr_arg)]
8843fn rewrite_bare_returns(stmts: &mut Vec<Stmt>, val: &Expr) {
8844 for s in stmts.iter_mut() {
8845 rewrite_bare_returns_one(s, val);
8846 }
8847}
8848
8849fn rewrite_bare_returns_one(st: &mut Stmt, val: &Expr) {
8850 match st {
8851 Stmt::Return(None) => *st = Stmt::Return(Some(val.clone())),
8852 Stmt::Block(v) => rewrite_bare_returns(v, val),
8853 Stmt::If { then_stmt, else_stmt, .. } => {
8854 rewrite_bare_returns_one(then_stmt, val);
8855 if let Some(e) = else_stmt {
8856 rewrite_bare_returns_one(e, val);
8857 }
8858 }
8859 Stmt::While { body, .. }
8860 | Stmt::DoWhile { body, .. }
8861 | Stmt::ForEach { body, .. }
8862 | Stmt::Synchronized { body, .. }
8863 | Stmt::Labeled { body, .. } => rewrite_bare_returns_one(body, val),
8864 Stmt::For { init, body, .. } => {
8865 rewrite_bare_returns(init, val);
8866 rewrite_bare_returns_one(body, val);
8867 }
8868 Stmt::Switch { cases, default, .. } => {
8869 for c in cases.iter_mut() {
8870 rewrite_bare_returns(&mut c.body, val);
8871 }
8872 if let Some(d) = default {
8873 rewrite_bare_returns_one(d, val);
8874 }
8875 }
8876 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
8877 rewrite_bare_returns_one(body, val);
8878 for c in catches.iter_mut() {
8879 rewrite_bare_returns_one(&mut c.body, val);
8880 }
8881 if let Some(f) = finally {
8882 rewrite_bare_returns_one(f, val);
8883 }
8884 }
8885 _ => {}
8886 }
8887}
8888
8889fn collect_def_vars(stmts: &[Stmt], out: &mut jdc_core::FxHashSet<u32>) {
8890 for st in stmts {
8891 match st {
8892 Stmt::LocalDef { var, .. } => {
8893 out.insert(*var);
8894 }
8895 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
8896 if let Expr::Local { var, .. } = &**target {
8897 out.insert(*var);
8898 }
8899 }
8900 Stmt::Block(v) => collect_def_vars(v, out),
8901 Stmt::If { then_stmt, else_stmt, .. } => {
8902 collect_def_vars(std::slice::from_ref(then_stmt.as_ref()), out);
8903 if let Some(e) = else_stmt {
8904 collect_def_vars(std::slice::from_ref(e.as_ref()), out);
8905 }
8906 }
8907 Stmt::While { body, .. }
8908 | Stmt::DoWhile { body, .. }
8909 | Stmt::Synchronized { body, .. }
8910 | Stmt::Labeled { body, .. } => {
8911 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
8912 }
8913 Stmt::ForEach { var, body, .. } => {
8914 out.insert(*var);
8915 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
8916 }
8917 Stmt::For { init, body, .. } => {
8918 collect_def_vars(init, out);
8919 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
8920 }
8921 Stmt::Switch { cases, default, .. } => {
8922 for c in cases {
8923 collect_def_vars(&c.body, out);
8924 }
8925 if let Some(d) = default {
8926 collect_def_vars(std::slice::from_ref(d.as_ref()), out);
8927 }
8928 }
8929 Stmt::Try { body, catches, finally }
8930 | Stmt::TryWithResources { body, catches, finally, .. } => {
8931 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
8932 for c in catches {
8933 out.insert(c.var);
8934 collect_def_vars(std::slice::from_ref(c.body.as_ref()), out);
8935 }
8936 if let Some(f) = finally {
8937 collect_def_vars(std::slice::from_ref(f.as_ref()), out);
8938 }
8939 }
8940 _ => {}
8941 }
8942 }
8943}
8944
8945fn extraction_shape_ok(stmts: &[Stmt]) -> bool {
8948 stmts.iter().all(|st| match st {
8949 Stmt::Block(v) => extraction_shape_ok(v),
8950 Stmt::LocalDef { .. }
8951 | Stmt::ExprStmt(_)
8952 | Stmt::Return(_)
8953 | Stmt::Throw(_)
8954 | Stmt::Assert { .. }
8955 | Stmt::Break(_)
8956 | Stmt::Continue(_) => true,
8957 Stmt::If { then_stmt, else_stmt, .. } => {
8958 ok_one(then_stmt)
8959 && else_stmt.as_deref().map(ok_one).unwrap_or(true)
8960 }
8961 Stmt::While { body, .. }
8962 | Stmt::DoWhile { body, .. }
8963 | Stmt::ForEach { body, .. }
8964 | Stmt::Synchronized { body, .. } => ok_one(body),
8965 Stmt::Labeled { body, .. } => ok_one(body),
8966 Stmt::For { init, body, .. } => extraction_shape_ok(init) && ok_one(body),
8967 Stmt::Switch { cases, default, .. } => {
8968 cases.iter().all(|c| extraction_shape_ok(&c.body))
8969 && default.as_deref().map(ok_one).unwrap_or(true)
8970 }
8971 Stmt::Try { body, catches, finally }
8972 | Stmt::TryWithResources { body, catches, finally, .. } => {
8973 ok_one(body)
8974 && catches.iter().all(|c| ok_one(&c.body))
8975 && finally.as_deref().map(ok_one).unwrap_or(true)
8976 }
8977 _ => false,
8980 })
8981}
8982
8983fn ok_one(st: &Stmt) -> bool {
8984 extraction_shape_ok(std::slice::from_ref(st))
8985}
8986
8987fn static_safe(stmts: &[Stmt], this_id: Option<u32>) -> bool {
8988 let mut ok = true;
8989 for st in stmts {
8990 visit_stmt_exprs_ro(st, &mut |e| {
8991 visit_exprs(e, &mut |x| {
8992 match x {
8993 Expr::This => ok = false,
8994 Expr::Local { var, .. } => {
9000 if Some(*var) == this_id {
9001 ok = false;
9002 }
9003 }
9004 Expr::Field { owner: None, is_static: false, .. } => ok = false,
9006 Expr::Method { owner: None, is_static: false, .. } => ok = false,
9007 _ => {}
9008 }
9009 });
9010 });
9011 }
9012 ok
9013}
9014
9015pub fn strip_clinit_hotfix_guard(body: &mut Stmt) {
9026 if let Stmt::Block(vs) = body {
9030 flatten_top_blocks(vs);
9031 }
9032 let Stmt::Block(stmts) = body else { return };
9033 let mut gi = 0usize;
9036 while gi < stmts.len() && matches!(&stmts[gi], Stmt::LocalDef { init: None, .. }) {
9037 gi += 1;
9038 }
9039 let Some(first) = stmts.get(gi) else { return };
9040 let Stmt::If { then_stmt, else_stmt, .. } = first else {
9041 return;
9042 };
9043 if else_stmt.is_some() {
9044 return;
9045 }
9046 let mut has_ret = false;
9047 let mut has_proxy = false;
9048 let c = then_stmt.as_ref();
9049 walk_all(c, &mut |st| {
9050 if matches!(st, Stmt::Return(None)) {
9051 has_ret = true;
9052 }
9053 });
9054 let mut cc = c.clone();
9055 walk_stmt_exprs(&mut cc, &mut |e| {
9056 deep_rewrite(e, &mut |x| {
9057 if let Expr::Method { cls, .. } = x {
9058 if cls.contains("PatchProxy") {
9059 has_proxy = true;
9060 }
9061 }
9062 });
9063 });
9064 if has_ret && has_proxy {
9065 stmts.remove(gi);
9066 }
9067}
9068
9069pub(crate) fn first_leaf_stmt(s: &Stmt) -> &Stmt {
9090 match s {
9091 Stmt::Block(v) if !v.is_empty() => first_leaf_stmt(&v[0]),
9092 other => other,
9093 }
9094}
9095
9096pub fn strip_ctor_hotfix_guards(body: &mut Stmt) {
9115 fn marker_hit(e: &Expr) -> bool {
9116 let has = |n: &str| {
9117 let l = n.to_ascii_lowercase();
9118 l.contains("hotfix")
9119 || l.contains("instantrun")
9120 || l.contains("robust")
9121 || l.contains("titan")
9122 || l.contains("patchproxy")
9123 || l.contains("changequickredirect")
9124 || l.contains("constructorcode")
9125 };
9126 let mut hit = false;
9127 visit_exprs(e, &mut |x| {
9128 match x {
9129 Expr::Method { cls, .. } | Expr::New { cls, .. } | Expr::Field { cls, .. } => {
9130 if has(cls) {
9131 hit = true;
9132 }
9133 }
9134 _ => {}
9135 }
9136 if let Expr::Field { ty, .. } = x {
9137 if let TypeRef::J(JavaType::Object(n)) = ty {
9138 if has(n) {
9139 hit = true;
9140 }
9141 }
9142 }
9143 });
9144 hit
9145 }
9146 fn is_guard(st: &Stmt) -> bool {
9150 let mut cur = st;
9151 loop {
9152 match cur {
9153 Stmt::Block(v) if v.len() == 1 => cur = &v[0],
9154 Stmt::If { .. } => break,
9155 _ => return false,
9156 }
9157 }
9158 let mut has_del = false;
9159 walk_all(cur, &mut |s2| {
9160 if is_bare_ctor_call(s2) {
9161 has_del = true;
9162 }
9163 });
9164 if !has_del {
9165 return false;
9166 }
9167 let mut has_marker = false;
9168 visit_stmt_exprs_ro(cur, &mut |e| {
9169 if marker_hit(e) {
9170 has_marker = true;
9171 }
9172 });
9173 has_marker
9174 }
9175 fn strip_in(stmts: &mut Vec<Stmt>) {
9186 stmts.retain(|st| !is_guard(st));
9187 for st in stmts.iter_mut() {
9188 if let Stmt::Block(v) = st {
9189 strip_in(v);
9190 }
9191 }
9192 }
9193 let Stmt::Block(stmts) = body else { return };
9194 strip_in(stmts);
9195}
9196
9197pub fn hoist_branch_delegations(body: &mut Stmt, vt: &VarTable) {
9210 let Stmt::Block(stmts) = body else { return };
9211 if stmts.first().is_some_and(is_bare_ctor_call) {
9212 return; }
9214 let mut dels: Vec<Expr> = Vec::new();
9215 for st in stmts.iter() {
9216 visit_stmt_exprs_ro(st, &mut |e| {
9217 if is_delegation_expr(e) {
9218 dels.push(e.clone());
9219 }
9220 });
9221 }
9222 if dels.is_empty() {
9223 return;
9224 }
9225 if !dels.iter().all(|d| *d == dels[0]) {
9226 return;
9227 }
9228 let mut bad = false;
9229 visit_exprs(&dels[0], &mut |x| {
9230 if let Expr::Local { var, .. } = x {
9231 if (*var as usize) >= vt.vars.len() || !vt.vars[*var as usize].is_param {
9232 bad = true;
9233 }
9234 }
9235 });
9236 if bad {
9237 return;
9238 }
9239 let del = dels[0].clone();
9240 strip_delegations_deep(stmts);
9241 stmts.insert(0, Stmt::ExprStmt(del));
9242}
9243
9244pub fn extract_branched_delegation_helper(
9278 body: &mut Stmt,
9279 vt: &VarTable,
9280 pool: &DexPool,
9281 class: &crate::PoolClass,
9282 desc_str: &str,
9283) {
9284 if class.is_enum() {
9285 return;
9286 }
9287 if class.nesting.enclosing_class.is_some() && !crate::ctx::nested_is_static(pool, class) {
9292 return;
9293 }
9294 let Stmt::Block(stmts) = body else { return };
9295 let mut dels: Vec<Expr> = Vec::new();
9296 for st in stmts.iter() {
9297 visit_stmt_exprs_ro(st, &mut |e| {
9298 if is_delegation_expr(e) {
9299 dels.push(e.clone());
9300 }
9301 });
9302 }
9303 if dels.is_empty() {
9304 return;
9305 }
9306 if dels.len() == 1 && stmts.first().is_some_and(is_bare_ctor_call) {
9307 return; }
9309 let template = dels[0].clone();
9310 let Expr::Method { args: t_args, .. } = &template else {
9311 return;
9312 };
9313 enum Mode {
9317 Single(u32),
9318 Position(usize),
9319 Full,
9326 }
9327 let full_gate = (|| -> Option<()> {
9334 let Expr::Method {
9335 args: a0,
9336 desc: d0,
9337 is_super: s0,
9338 cls: c0,
9339 ..
9340 } = &dels[0]
9341 else {
9342 return None;
9343 };
9344 if a0.is_empty() {
9345 return None;
9346 }
9347 for d in &dels[1..] {
9348 match d {
9349 Expr::Method {
9350 args,
9351 desc,
9352 is_super,
9353 cls,
9354 ..
9355 } if args.len() == a0.len()
9356 && **desc == **d0
9357 && is_super == s0
9358 && cls == c0 => {}
9359 _ => return None,
9360 }
9361 }
9362 if class
9363 .all_methods()
9364 .any(|m| &*m.name == "<init>" && &*m.desc == "([Ljava/lang/Object;)V")
9365 {
9366 return None;
9367 }
9368 Some(())
9369 })()
9370 .is_some();
9371 let mode = if dels.iter().all(|d| *d == dels[0]) {
9372 let mut carrier: Option<u32> = None;
9373 let mut reads = 0usize;
9374 let mut multi = false;
9375 for a in t_args.iter() {
9376 let c = a.clone();
9377 visit_exprs(&c, &mut |x| {
9378 if let Expr::Local { var, .. } = x {
9379 if (*var as usize) < vt.vars.len() && !vt.vars[*var as usize].is_param {
9380 reads += 1;
9381 match carrier {
9382 Some(v) if v == *var => {}
9383 Some(_) => multi = true,
9384 None => carrier = Some(*var),
9385 }
9386 }
9387 }
9388 });
9389 }
9390 match carrier {
9391 Some(cv) if !multi && reads == 1 => Mode::Single(cv),
9392 _ if full_gate => Mode::Full,
9393 _ => return,
9394 }
9395 } else {
9396 let positioned: Option<Mode> = (|| {
9397 let arity = t_args.len();
9398 if dels
9402 .iter()
9403 .any(|d| !matches!(d, Expr::Method { args, .. } if args.len() == arity))
9404 {
9405 return None;
9406 }
9407 let mut diff: Vec<usize> = Vec::new();
9408 for j in 0..arity {
9409 if dels.iter().any(|d| match d {
9410 Expr::Method { args, .. } => args.get(j) != t_args.get(j),
9411 _ => true,
9412 }) {
9413 diff.push(j);
9414 }
9415 }
9416 if diff.len() != 1 {
9417 return None;
9418 }
9419 let k = diff[0];
9420 let mut base0 = dels[0].clone();
9424 if let Expr::Method { args, .. } = &mut base0 {
9425 args[k] = Expr::Const(ConstVal::Null);
9426 }
9427 for d in dels.iter() {
9428 let mut c = d.clone();
9429 if let Expr::Method { args, .. } = &mut c {
9430 args[k] = Expr::Const(ConstVal::Null);
9431 }
9432 if c != base0 {
9433 return None;
9434 }
9435 }
9436 let mut carrier_ty: Option<JavaType> = None;
9439 for d in dels.iter() {
9440 let Expr::Method { args, .. } = d else {
9441 return None;
9442 };
9443 let Expr::Local { var, .. } = &args[k] else {
9444 return None;
9445 };
9446 if (*var as usize) >= vt.vars.len() || vt.vars[*var as usize].is_param {
9447 return None;
9448 }
9449 let ty = vt.vars[*var as usize].ty.erased();
9450 match &carrier_ty {
9451 Some(t) if *t == ty => {}
9452 Some(_) => return None,
9453 None => carrier_ty = Some(ty),
9454 }
9455 for (j, a) in args.iter().enumerate() {
9461 if j == k {
9462 continue;
9463 }
9464 let mut bad = false;
9465 visit_exprs(a, &mut |x| {
9466 if let Expr::Local { var: v2, .. } = x {
9467 if (*v2 as usize) >= vt.vars.len() || !vt.vars[*v2 as usize].is_param {
9468 bad = true;
9469 }
9470 }
9471 });
9472 if bad {
9473 return None;
9474 }
9475 }
9476 }
9477 Some(Mode::Position(k))
9478 })();
9479 match positioned {
9480 Some(m) => m,
9481 None if full_gate => Mode::Full,
9482 None => return,
9483 }
9484 };
9485 let l = stmts.iter().rposition(is_bare_ctor_call);
9486 if l == Some(0) {
9487 return;
9488 }
9489 let (mut extracted, mut tail): (Vec<Stmt>, Vec<Stmt>) = match l {
9490 Some(l) => (stmts[..l].to_vec(), stmts[l + 1..].to_vec()),
9491 None => (stmts.clone(), Vec::new()),
9492 };
9493 if !extraction_shape_ok(&extracted) {
9494 return;
9495 }
9496 let mut site_tails: Vec<Vec<Stmt>> = Vec::new();
9503 {
9504 let mut probe = extracted.clone();
9505 site_tails_walk(&mut probe, &mut site_tails, false);
9506 }
9507 let hoist = !site_tails.is_empty() && {
9508 let n0 = normalize_tail(&site_tails[0]);
9509 site_tails.iter().all(|t| normalize_tail(t) == n0)
9510 && (l.is_none() || normalize_tail(&tail) == n0)
9511 };
9512 if hoist {
9513 site_tails_walk(&mut extracted, &mut Vec::new(), true);
9514 if l.is_none() {
9515 tail = normalize_tail(&site_tails[0]);
9516 }
9517 }
9518 let primary_del: Expr = match l {
9521 Some(l) => match &stmts[l] {
9522 Stmt::ExprStmt(e) => e.clone(),
9523 _ => return,
9524 },
9525 None => dels[0].clone(),
9526 };
9527 let ret = match mode {
9528 Mode::Single(cv) => vt.var(cv).ty.erased(),
9529 Mode::Position(k) => match &primary_del {
9530 Expr::Method { args, .. } => match args.get(k) {
9531 Some(Expr::Local { var, .. }) => vt.var(*var).ty.erased(),
9532 _ => return,
9533 },
9534 _ => return,
9535 },
9536 Mode::Full => JavaType::Array(Box::new(JavaType::Object(std::sync::Arc::from(
9537 "java/lang/Object",
9538 )))),
9539 };
9540 match mode {
9541 Mode::Single(cv) => {
9542 strip_delegations_deep(&mut extracted);
9543 let cv_local = Expr::Local {
9544 var: cv,
9545 ty: vt.var(cv).ty.clone(),
9546 };
9547 rewrite_bare_returns(&mut extracted, &cv_local);
9548 if !always_returns(&extracted) {
9549 extracted.push(Stmt::Return(Some(cv_local)));
9550 }
9551 }
9552 Mode::Position(k) => {
9553 fill_returns_by_position(&mut extracted, &|args: &[Expr]| args.get(k).cloned());
9557 let fallback = match &primary_del {
9558 Expr::Method { args, .. } => args[k].clone(),
9559 _ => return,
9560 };
9561 rewrite_bare_returns(&mut extracted, &fallback);
9562 if !always_returns(&extracted) {
9563 extracted.push(Stmt::Return(Some(fallback)));
9564 }
9565 }
9566 Mode::Full => {
9567 fill_returns_by_position(&mut extracted, &|args: &[Expr]| {
9574 Some(array_init(args.to_vec()))
9575 });
9576 if l.is_none() {
9577 if !always_returns(&extracted) {
9584 let cut = (1..=extracted.len())
9585 .find(|&i| always_returns(&extracted[..i]));
9586 match cut {
9587 Some(i) => extracted.truncate(i),
9588 None => return,
9589 }
9590 }
9591 }
9592 let fallback_args = match &primary_del {
9593 Expr::Method { args, .. } => args.clone(),
9594 _ => return,
9595 };
9596 let fallback = array_init(fallback_args);
9597 rewrite_bare_returns(&mut extracted, &fallback);
9598 if !always_returns(&extracted) {
9599 extracted.push(Stmt::Return(Some(fallback)));
9600 }
9601 }
9602 }
9603 let this_id = vt
9604 .vars
9605 .iter()
9606 .find(|v| v.is_param && v.name == "this")
9607 .map(|v| v.id);
9608 if !static_safe(&extracted, this_id) {
9609 return;
9610 }
9611 let mut defs: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
9612 collect_def_vars(&extracted, &mut defs);
9613 if defs.iter().any(|v| var_occurs_in(&tail, *v)) {
9614 return;
9615 }
9616 let params: Vec<(u32, JavaType, String)> = vt
9617 .vars
9618 .iter()
9619 .filter(|v| v.is_param && v.name != "this")
9620 .map(|v| (v.id, v.ty.erased(), v.name.clone()))
9621 .collect();
9622 let name = helper_name(&class.name, desc_str);
9623 let call = Expr::Method {
9624 owner: None,
9625 cls: std::sync::Arc::from(class.name.as_str()),
9626 name: std::sync::Arc::from(name.as_str()),
9627 desc: std::sync::Arc::new(jdc_core::types::MethodDescriptor {
9628 ret: ret.clone(),
9629 args: params.iter().map(|p| p.1.clone()).collect(),
9630 }),
9631 args: params
9632 .iter()
9633 .map(|(id, ty, _)| Expr::Local {
9634 var: *id,
9635 ty: TypeRef::J(ty.clone()),
9636 })
9637 .collect(),
9638 is_static: true,
9639 is_interface: false,
9640 is_special: false,
9641 is_super: false,
9642 is_dynamic: false,
9643 type_args: Vec::new(),
9644 };
9645 let mut new_del = primary_del.clone();
9646 match mode {
9647 Mode::Single(cv) => {
9648 deep_rewrite(&mut new_del, &mut |x| {
9649 if let Expr::Local { var, .. } = x {
9650 if *var == cv {
9651 *x = call.clone();
9652 }
9653 }
9654 });
9655 }
9656 Mode::Position(k) => {
9657 let Expr::Method { args, .. } = &mut new_del else {
9658 return;
9659 };
9660 if k >= args.len() {
9661 return;
9662 }
9663 args[k] = call.clone();
9664 }
9665 Mode::Full => {
9666 new_del = Expr::Method {
9670 owner: None,
9671 cls: std::sync::Arc::from(class.name.as_str()),
9672 name: "<init>".into(),
9673 desc: std::sync::Arc::new(jdc_core::types::MethodDescriptor {
9674 args: vec![JavaType::Array(Box::new(JavaType::Object(
9675 std::sync::Arc::from("java/lang/Object"),
9676 )))],
9677 ret: JavaType::Void,
9678 }),
9679 args: vec![call.clone()],
9680 is_static: false,
9681 is_interface: false,
9682 is_special: true,
9683 is_super: false,
9684 is_dynamic: false,
9685 type_args: Vec::new(),
9686 };
9687 }
9688 }
9689 {
9690 let mut m = ctor_helpers().lock().unwrap_or_else(|e| e.into_inner());
9691 let v = m.entry(class.name.clone()).or_default();
9692 if !v.iter().any(|h| h.name == name) {
9693 let mut hbody = Stmt::Block(extracted);
9697 invert_empty_thens(&mut hbody);
9698 v.push(CtorHelper {
9699 name,
9700 ret: ret.clone(),
9701 params: params.into_iter().map(|(_, ty, nm)| (ty, nm)).collect(),
9702 body: hbody,
9703 vt: vt.clone(),
9704 });
9705 if matches!(mode, Mode::Full) {
9706 let Expr::Method {
9707 desc: d0,
9708 is_super: s0,
9709 ..
9710 } = &primary_del
9711 else {
9712 return;
9713 };
9714 let mut rm = ctor_relays().lock().unwrap_or_else(|e| e.into_inner());
9715 let rv = rm.entry(class.name.clone()).or_default();
9716 if !rv.iter().any(|(d, _)| d == desc_str) {
9717 rv.push((
9718 desc_str.to_string(),
9719 CtorRelay {
9720 formals: d0.args.clone(),
9721 is_super: *s0,
9722 },
9723 ));
9724 }
9725 }
9726 } else {
9727 return; }
9729 }
9730 *stmts = std::iter::once(Stmt::ExprStmt(new_del)).chain(tail).collect();
9731}
9732
9733fn fill_returns_by_position(
9740 stmts: &mut Vec<Stmt>,
9741 pick: &dyn Fn(&[Expr]) -> Option<Expr>,
9742) {
9743 let mut i = 0;
9744 while i < stmts.len() {
9745 if is_bare_ctor_call(&stmts[i]) {
9746 let karg = match &stmts[i] {
9747 Stmt::ExprStmt(Expr::Method { args, .. }) => pick(args),
9748 _ => None,
9749 };
9750 if let Some(v) = karg {
9751 let mut j = i + 1;
9752 let mut found = None;
9753 while j < stmts.len() {
9754 if matches!(stmts[j], Stmt::Return(None)) {
9755 found = Some(j);
9756 break;
9757 }
9758 if is_bare_ctor_call(&stmts[j]) {
9759 break; }
9761 j += 1;
9762 }
9763 match found {
9764 Some(j) => {
9765 stmts[j] = Stmt::Return(Some(v));
9766 stmts.remove(i);
9767 continue;
9768 }
9769 None => {
9770 stmts[i] = Stmt::Return(Some(v));
9771 stmts.truncate(i + 1);
9772 break;
9773 }
9774 }
9775 }
9776 }
9777 fill_returns_one(&mut stmts[i], pick);
9778 i += 1;
9779 }
9780}
9781
9782fn fill_returns_one(st: &mut Stmt, pick: &dyn Fn(&[Expr]) -> Option<Expr>) {
9783 match st {
9784 Stmt::Block(v) => fill_returns_by_position(v, pick),
9785 Stmt::If { then_stmt, else_stmt, .. } => {
9786 fill_returns_stmt(then_stmt, pick);
9787 if let Some(e) = else_stmt {
9788 fill_returns_stmt(e, pick);
9789 }
9790 }
9791 Stmt::While { body, .. }
9792 | Stmt::DoWhile { body, .. }
9793 | Stmt::ForEach { body, .. }
9794 | Stmt::Synchronized { body, .. }
9795 | Stmt::Labeled { body, .. } => fill_returns_stmt(body, pick),
9796 Stmt::For { init, body, .. } => {
9797 fill_returns_by_position(init, pick);
9798 fill_returns_stmt(body, pick);
9799 }
9800 Stmt::Switch { cases, default, .. } => {
9801 for c in cases.iter_mut() {
9802 fill_returns_by_position(&mut c.body, pick);
9803 }
9804 if let Some(d) = default {
9805 fill_returns_stmt(d, pick);
9806 }
9807 }
9808 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
9809 fill_returns_stmt(body, pick);
9810 for c in catches.iter_mut() {
9811 fill_returns_stmt(&mut c.body, pick);
9812 }
9813 if let Some(f) = finally {
9814 fill_returns_stmt(f, pick);
9815 }
9816 }
9817 _ => {}
9818 }
9819}
9820
9821fn fill_returns_stmt(st: &mut Stmt, pick: &dyn Fn(&[Expr]) -> Option<Expr>) {
9822 if is_bare_ctor_call(st) {
9823 if let Stmt::ExprStmt(Expr::Method { args, .. }) = st {
9826 if let Some(v) = pick(args) {
9827 *st = Stmt::Return(Some(v));
9828 return;
9829 }
9830 }
9831 }
9832 fill_returns_one(st, pick);
9833}
9834
9835fn normalize_tail(t: &[Stmt]) -> Vec<Stmt> {
9836 let mut v = t.to_vec();
9837 while matches!(v.last(), Some(Stmt::Return(None))) {
9838 v.pop();
9839 }
9840 v
9841}
9842
9843fn site_tails_walk(stmts: &mut Vec<Stmt>, out: &mut Vec<Vec<Stmt>>, carve: bool) {
9848 let mut i = 0;
9849 while i < stmts.len() {
9850 if is_bare_ctor_call(&stmts[i]) {
9851 let mut j = i + 1;
9852 let mut end = stmts.len();
9853 while j < stmts.len() {
9854 if matches!(stmts[j], Stmt::Return(None)) {
9855 end = j;
9856 break;
9857 }
9858 if is_bare_ctor_call(&stmts[j]) {
9859 end = j;
9860 break;
9861 }
9862 j += 1;
9863 }
9864 out.push(stmts[i + 1..end].to_vec());
9865 if carve {
9866 stmts.splice(i + 1..end, []);
9867 }
9868 i += 1;
9869 continue;
9870 }
9871 site_tails_one(&mut stmts[i], out, carve);
9872 i += 1;
9873 }
9874}
9875
9876fn site_tails_one(st: &mut Stmt, out: &mut Vec<Vec<Stmt>>, carve: bool) {
9877 if is_bare_ctor_call(st) {
9878 out.push(Vec::new());
9879 return;
9880 }
9881 match st {
9882 Stmt::Block(v) => site_tails_walk(v, out, carve),
9883 Stmt::If { then_stmt, else_stmt, .. } => {
9884 site_tails_one(then_stmt, out, carve);
9885 if let Some(e) = else_stmt {
9886 site_tails_one(e, out, carve);
9887 }
9888 }
9889 Stmt::While { body, .. }
9890 | Stmt::DoWhile { body, .. }
9891 | Stmt::ForEach { body, .. }
9892 | Stmt::Synchronized { body, .. }
9893 | Stmt::Labeled { body, .. } => site_tails_one(body, out, carve),
9894 Stmt::For { init, body, .. } => {
9895 site_tails_walk(init, out, carve);
9896 site_tails_one(body, out, carve);
9897 }
9898 Stmt::Switch { cases, default, .. } => {
9899 for c in cases.iter_mut() {
9900 site_tails_walk(&mut c.body, out, carve);
9901 }
9902 if let Some(d) = default {
9903 site_tails_one(d, out, carve);
9904 }
9905 }
9906 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
9907 site_tails_one(body, out, carve);
9908 for c in catches.iter_mut() {
9909 site_tails_one(&mut c.body, out, carve);
9910 }
9911 if let Some(f) = finally {
9912 site_tails_one(f, out, carve);
9913 }
9914 }
9915 _ => {}
9916 }
9917}
9918
9919fn always_returns(stmts: &[Stmt]) -> bool {
9925 let Some(last) = stmts
9926 .iter()
9927 .rev()
9928 .find(|s| !matches!(s, Stmt::Block(v) if v.is_empty()))
9929 else {
9930 return false;
9931 };
9932 match last {
9933 Stmt::Return(Some(_)) | Stmt::Throw(_) => true,
9934 Stmt::Block(v) => always_returns(v),
9935 Stmt::If { then_stmt, else_stmt, .. } => match else_stmt {
9936 Some(e) => always_returns_one(then_stmt) && always_returns_one(e),
9937 None => false,
9938 },
9939 Stmt::Labeled { body, .. } | Stmt::Synchronized { body, .. } => {
9940 always_returns_one(body)
9941 }
9942 Stmt::Switch { cases, default, .. } => {
9943 default.as_ref().is_some_and(|d| always_returns_one(d))
9944 && cases.iter().all(|c| always_returns(&c.body))
9945 }
9946 Stmt::Try { body, catches, finally }
9947 | Stmt::TryWithResources { body, catches, finally, .. } => {
9948 finally.as_ref().is_some_and(|f| always_returns_one(f))
9949 || (always_returns_one(body)
9950 && catches.iter().all(|c| always_returns_one(&c.body)))
9951 }
9952 _ => false,
9953 }
9954}
9955
9956fn always_returns_one(st: &Stmt) -> bool {
9957 always_returns(std::slice::from_ref(st))
9958}
9959
9960fn inline_ctor_arg_defs(
9961 call: &mut Stmt,
9962 prefix: &[&Stmt],
9963 vt: &VarTable,
9964 consumed: &mut Vec<(u32, usize)>,
9965) -> bool {
9966 let Stmt::ExprStmt(cexpr) = call else {
9967 return false;
9968 };
9969 fn subst(e: &mut Expr, v: u32, val: &Option<Expr>) -> bool {
9972 let mut ok = true;
9973 deep_rewrite(e, &mut |x| {
9974 if let Expr::Local { var, .. } = x {
9975 if *var == v {
9976 match val {
9977 Some(e2) => *x = e2.clone(),
9978 None => ok = false,
9979 }
9980 }
9981 }
9982 });
9983 ok
9984 }
9985 let mut seen: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
9986 for _ in 0..32 {
9987 let mut need: Option<u32> = None;
9988 visit_exprs(cexpr, &mut |x| {
9989 if need.is_none() {
9990 if let Expr::Local { var, .. } = x {
9991 if (*var as usize) < vt.vars.len() && !vt.vars[*var as usize].is_param {
9992 need = Some(*var);
9993 }
9994 }
9995 }
9996 });
9997 let Some(v) = need else { break };
9998 if !seen.insert(v) {
9999 return false; }
10001 let mut val: Option<Expr> = None;
10013 let mut ok = true;
10014 for (si, st) in prefix.iter().enumerate() {
10015 match st {
10016 Stmt::LocalDef { var, init: None, .. } if *var == v => {}
10020 Stmt::LocalDef { var, init, .. } if *var == v => {
10021 let Some(e) = init else { ok = false; break };
10022 let mut e2 = (*e).clone();
10023 if !subst(&mut e2, v, &val) {
10024 ok = false;
10025 break;
10026 }
10027 val = Some(e2);
10028 consumed.push((v, si));
10029 }
10030 Stmt::ExprStmt(Expr::Assign { target, op, .. })
10031 if matches!(&**target, Expr::Local { var: tv, .. } if *tv == v) =>
10032 {
10033 if !matches!(op, AssignOp::Plain) {
10034 ok = false; break;
10036 }
10037 let Stmt::ExprStmt(Expr::Assign { value, .. }) = st else {
10038 unreachable!()
10039 };
10040 let mut e2 = value.as_ref().clone();
10041 if !subst(&mut e2, v, &val) {
10042 ok = false;
10043 break;
10044 }
10045 val = Some(e2);
10046 consumed.push((v, si));
10047 }
10048 _ => {
10049 let mut touched = false;
10050 visit_all_exprs(st, &mut |x| {
10051 if let Expr::Local { var, .. } = x {
10052 if *var == v {
10053 touched = true;
10054 }
10055 }
10056 });
10057 if touched {
10058 ok = false;
10059 break;
10060 }
10061 }
10062 }
10063 }
10064 if !ok {
10065 return false;
10066 }
10067 let Some(de) = val else { return false };
10068 if contains_this_access(&de) {
10069 return false;
10070 }
10071 let mut de_nodes = 0usize;
10073 visit_exprs(&de, &mut |_| de_nodes += 1);
10074 if de_nodes > 256 {
10075 return false;
10076 }
10077 deep_rewrite(cexpr, &mut |x| {
10078 if let Expr::Local { var, .. } = x {
10079 if *var == v {
10080 *x = de.clone();
10081 }
10082 }
10083 });
10084 let mut nodes = 0usize;
10089 visit_exprs(cexpr, &mut |_| nodes += 1);
10090 if nodes > 2_048 {
10091 return false;
10092 }
10093 }
10094 true
10095}
10096
10097fn fold_sb_chain(stmts: &mut Vec<Stmt>, pos: usize) -> usize {
10110 let mut sbv: Option<u32> = None;
10112 let mut def_i: Option<usize> = None;
10113 let mut def_init: Option<Expr> = None;
10114 for (i, st) in stmts[..pos].iter().enumerate() {
10115 let cand: Option<(u32, &Expr)> = match st {
10116 Stmt::LocalDef {
10117 var,
10118 init: Some(e),
10119 ..
10120 } => Some((*var, e)),
10121 Stmt::ExprStmt(Expr::Assign {
10122 target,
10123 value,
10124 op: AssignOp::Plain,
10125 ..
10126 }) => match &**target {
10127 Expr::Local { var, .. } => Some((*var, value.as_ref())),
10128 _ => None,
10129 },
10130 _ => None,
10131 };
10132 if let Some((v, init)) = cand {
10133 if let Expr::New { cls, .. } = init {
10134 if cls.as_ref() == "java/lang/StringBuilder" {
10135 sbv = Some(v);
10136 def_i = Some(i);
10137 def_init = Some(init.clone());
10138 break;
10139 }
10140 }
10141 }
10142 }
10143 let (Some(sbv), Some(def_i), Some(init)) = (sbv, def_i, def_init) else {
10144 return pos;
10145 };
10146 if contains_this_access(&init) {
10147 return pos;
10148 }
10149 let mut appends: Vec<usize> = Vec::new();
10151 for (i, st) in stmts[..pos].iter().enumerate() {
10152 if let Stmt::ExprStmt(Expr::Method {
10153 owner: Some(o),
10154 cls,
10155 name,
10156 ..
10157 }) = st
10158 {
10159 if matches!(&**o, Expr::Local { var, .. } if *var == sbv)
10160 && cls.as_ref() == "java/lang/StringBuilder"
10161 && &**name == "append"
10162 {
10163 appends.push(i);
10164 }
10165 }
10166 }
10167 let mut post_read = false;
10169 for st in stmts.iter().skip(pos + 1) {
10170 visit_all_exprs(st, &mut |e| {
10171 if let Expr::Local { var, .. } = e {
10172 if *var == sbv {
10173 post_read = true;
10174 }
10175 }
10176 });
10177 }
10178 if post_read {
10179 return pos;
10180 }
10181 for (i, st) in stmts[..pos].iter().enumerate() {
10184 if i == def_i || appends.contains(&i) {
10185 if let Stmt::ExprStmt(Expr::Method { args, .. }) = st {
10187 if args.iter().any(contains_this_access) {
10188 return pos;
10189 }
10190 }
10191 continue;
10192 }
10193 let mut leak = false;
10194 visit_all_exprs(st, &mut |e| {
10195 if let Expr::Local { var, .. } = e {
10196 if *var == sbv {
10197 leak = true;
10198 }
10199 }
10200 });
10201 if leak {
10202 return pos;
10203 }
10204 }
10205 let mut local_reads = 0usize;
10207 let mut tostring_reads = 0usize;
10208 visit_all_exprs(&stmts[pos], &mut |e| match e {
10209 Expr::Local { var, .. } if *var == sbv => local_reads += 1,
10210 Expr::Method {
10211 owner: Some(o),
10212 name,
10213 args,
10214 ..
10215 } if args.is_empty()
10216 && &**name == "toString"
10217 && matches!(&**o, Expr::Local { var, .. } if *var == sbv) =>
10218 {
10219 tostring_reads += 1;
10220 }
10221 _ => {}
10222 });
10223 if local_reads != 1 || tostring_reads != 1 {
10224 return pos;
10225 }
10226 let mut chain = init;
10228 for ai in &appends {
10229 if let Stmt::ExprStmt(Expr::Method {
10230 owner: _,
10231 cls,
10232 name,
10233 desc,
10234 args,
10235 is_static,
10236 is_interface,
10237 is_special,
10238 is_super,
10239 is_dynamic,
10240 type_args,
10241 ..
10242 }) = &stmts[*ai]
10243 {
10244 chain = Expr::Method {
10245 owner: Some(Box::new(chain)),
10246 cls: cls.clone(),
10247 name: name.clone(),
10248 desc: desc.clone(),
10249 args: args.clone(),
10250 is_static: *is_static,
10251 is_interface: *is_interface,
10252 is_special: *is_special,
10253 is_super: *is_super,
10254 is_dynamic: *is_dynamic,
10255 type_args: type_args.clone(),
10256 };
10257 }
10258 }
10259 let mut replaced = false;
10260 walk_stmt_exprs(&mut stmts[pos], &mut |e| {
10261 deep_rewrite(e, &mut |x| {
10262 if let Expr::Method {
10263 owner: Some(o),
10264 name,
10265 args,
10266 ..
10267 } = x
10268 {
10269 if args.is_empty()
10270 && &**name == "toString"
10271 && matches!(&**o, Expr::Local { var, .. } if *var == sbv)
10272 {
10273 **o = chain.clone();
10274 replaced = true;
10275 }
10276 }
10277 });
10278 });
10279 if !replaced {
10280 return pos; }
10282 let mut removed: Vec<usize> = appends.clone();
10284 removed.push(def_i);
10285 removed.sort_unstable();
10286 removed.dedup();
10287 for i in removed.iter().rev() {
10288 stmts.remove(*i);
10289 }
10290 pos - removed.len()
10291}
10292
10293pub fn fix_ctor_super_first(body: &mut Stmt, vt: &VarTable) {
10294 let Stmt::Block(stmts) = body else { return };
10295 if stmts.is_empty() || is_bare_ctor_call(stmts.first().unwrap()) {
10296 return;
10297 }
10298 if let Some(pos0) = stmts.iter().position(is_bare_ctor_call) {
10300 let pos = fold_sb_chain(stmts, pos0);
10301 let mut call = stmts[pos].clone();
10302 let prefix: Vec<&Stmt> = stmts[..pos].iter().collect();
10303 let mut consumed: Vec<(u32, usize)> = Vec::new();
10304 if !inline_ctor_arg_defs(&mut call, &prefix, vt, &mut consumed) {
10305 return;
10306 }
10307 stmts.remove(pos);
10308 let mut drop_idx: Vec<usize> = consumed
10315 .iter()
10316 .filter(|(v, _)| !var_occurs_in(&stmts[pos..], *v))
10317 .map(|(_, k)| *k)
10318 .collect();
10319 drop_idx.sort_unstable();
10320 drop_idx.dedup();
10321 for k in drop_idx.iter().rev() {
10322 stmts.remove(*k);
10323 }
10324 stmts.insert(0, call);
10325 return;
10326 }
10327 let mut found: Option<(usize, usize)> = None;
10337 for (i, st) in stmts.iter().enumerate() {
10338 if let Stmt::Block(inner) = st {
10339 if let Some(p) = inner.iter().position(is_bare_ctor_call) {
10340 if inner[..p]
10341 .iter()
10342 .all(|s| matches!(s, Stmt::LocalDef { .. } | Stmt::ExprStmt(_)))
10343 {
10344 found = Some((i, p));
10345 break;
10346 }
10347 }
10348 }
10349 }
10350 if let Some((i, p)) = found {
10351 let mut call = match &stmts[i] {
10352 Stmt::Block(inner) => inner[p].clone(),
10353 _ => unreachable!(),
10354 };
10355 let mut prefix: Vec<&Stmt> = stmts[..i].iter().collect();
10356 if let Stmt::Block(inner) = &stmts[i] {
10357 prefix.extend(inner[..p].iter());
10358 }
10359 let mut consumed: Vec<(u32, usize)> = Vec::new();
10360 if !inline_ctor_arg_defs(&mut call, &prefix, vt, &mut consumed) {
10361 return;
10362 }
10363 let mut ks: Vec<usize> = Vec::new();
10370 {
10371 let inner_tail: &[Stmt] = match &stmts[i] {
10372 Stmt::Block(inner) if p < inner.len() => &inner[p + 1..],
10373 _ => &[],
10374 };
10375 for (v, k) in &consumed {
10376 if !var_occurs_in(inner_tail, *v) && !var_occurs_in(&stmts[i + 1..], *v) {
10377 ks.push(*k);
10378 }
10379 }
10380 }
10381 ks.sort_unstable();
10382 ks.dedup();
10383 if let Stmt::Block(inner) = &mut stmts[i] {
10384 inner.remove(p);
10385 for k in ks.iter().rev().filter(|k| **k >= i) {
10386 inner.remove(k - i);
10387 }
10388 }
10389 for k in ks.iter().rev().filter(|k| **k < i) {
10390 stmts.remove(*k);
10391 }
10392 stmts.insert(0, call);
10393 }
10394}
10395
10396fn is_delegation_expr(e: &Expr) -> bool {
10405 matches!(
10406 e,
10407 Expr::Method { name, is_special: true, owner, .. }
10408 if &**name == "<init>"
10409 && (owner.is_none() || matches!(owner.as_deref(), Some(Expr::This)))
10410 )
10411}
10412
10413pub(crate) fn is_bare_ctor_call(s: &Stmt) -> bool {
10414 matches!(s, Stmt::ExprStmt(e) if is_delegation_expr(e))
10415}
10416
10417pub(crate) fn local_is_written(body: &Stmt, var: u32) -> bool {
10421 let mut hit = false;
10422 visit_stmt_exprs_ro(body, &mut |e| {
10423 if hit {
10424 return;
10425 }
10426 visit_exprs(e, &mut |x| {
10427 if hit {
10428 return;
10429 }
10430 match x {
10431 Expr::Assign { target, .. }
10432 if matches!(&**target, Expr::Local { var: v, .. } if *v == var) =>
10433 {
10434 hit = true;
10435 }
10436 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. }
10437 if matches!(&**e, Expr::Local { var: v, .. } if *v == var) =>
10438 {
10439 hit = true;
10440 }
10441 _ => {}
10442 }
10443 });
10444 });
10445 hit
10446}
10447
10448pub(crate) fn rewrite_inner_ctor_outer_param(
10458 body: &mut Stmt,
10459 param0: u32,
10460 outer_display: &str,
10461 outer_ty: &TypeRef,
10462 eligible: &[String],
10463) {
10464 walk_stmt_exprs(body, &mut |e| {
10468 if let Expr::Method { name, cls, args, .. } = e {
10469 if &**name == "<init>"
10470 && eligible.iter().any(|c| c.as_str() == cls.as_ref())
10471 && args.first().is_some_and(|a| matches!(a, Expr::Local { var: v, .. } if *v == param0))
10472 {
10473 args.remove(0);
10474 }
10475 }
10476 });
10477 let text = format!("{outer_display}.this");
10479 let ty = outer_ty.clone();
10480 walk_stmt_exprs(body, &mut |e| {
10481 deep_rewrite_reads(e, &mut |x| {
10482 if let Expr::Local { var: v, .. } = x {
10483 if *v == param0 {
10484 *x = Expr::RawT(text.clone(), ty.clone());
10485 }
10486 }
10487 });
10488 });
10489}
10490
10491fn stmts_count_writes(stmts: &[Stmt], var: u32) -> usize {
10539 let mut n = 0usize;
10540 for s in stmts {
10541 visit_stmt_exprs_ro(s, &mut |e| {
10542 visit_exprs(e, &mut |x| match x {
10543 Expr::Assign { target, .. }
10544 if matches!(&**target, Expr::Local { var: v, .. } if *v == var) =>
10545 {
10546 n += 1;
10547 }
10548 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. }
10549 if matches!(&**e, Expr::Local { var: v, .. } if *v == var) =>
10550 {
10551 n += 1;
10552 }
10553 _ => {}
10554 });
10555 });
10556 }
10557 n
10558}
10559
10560fn prefix_written_vars(stmts: &[Stmt]) -> jdc_core::FxHashSet<u32> {
10564 let mut set = jdc_core::FxHashSet::default();
10565 for s in stmts {
10566 let mut c = s.clone();
10567 walk_stmt_exprs(&mut c, &mut |e| {
10568 deep_rewrite(e, &mut |x| match x {
10569 Expr::Assign { target, .. } => {
10570 if let Expr::Local { var, .. } = &**target {
10571 set.insert(*var);
10572 }
10573 }
10574 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => {
10575 if let Expr::Local { var, .. } = &**e {
10576 set.insert(*var);
10577 }
10578 }
10579 _ => {}
10580 });
10581 });
10582 if let Stmt::LocalDef { var, .. } = s {
10583 set.insert(*var);
10584 }
10585 }
10586 set
10587}
10588
10589fn pre_this_safe(e: &Expr, prefix_vars: &jdc_core::FxHashSet<u32>) -> bool {
10593 match e {
10594 Expr::Local { var, .. } => !prefix_vars.contains(var),
10595 Expr::Const(_) => true,
10596 Expr::Cast { e, .. } => pre_this_safe(e, prefix_vars),
10597 Expr::Bin { l, r, .. } => {
10598 pre_this_safe(l, prefix_vars) && pre_this_safe(r, prefix_vars)
10599 }
10600 Expr::Cond { c, t, f } => {
10601 pre_this_safe(c, prefix_vars)
10602 && pre_this_safe(t, prefix_vars)
10603 && pre_this_safe(f, prefix_vars)
10604 }
10605 Expr::Field { owner, is_static, .. } => {
10606 *is_static
10607 && owner
10608 .as_ref()
10609 .is_none_or(|o| pre_this_safe(o, prefix_vars))
10610 }
10611 Expr::Method {
10612 owner,
10613 args,
10614 is_static,
10615 ..
10616 } => {
10617 args.iter().all(|a| pre_this_safe(a, prefix_vars))
10618 && match owner {
10619 Some(o) => pre_this_safe(o, prefix_vars),
10620 None => *is_static,
10621 }
10622 }
10623 Expr::New { args, .. } => args.iter().all(|a| pre_this_safe(a, prefix_vars)),
10631 Expr::NewArray { dims, init, .. } => {
10632 dims.iter().all(|d| pre_this_safe(d, prefix_vars))
10633 && init
10634 .as_ref()
10635 .map(|l| l.iter().all(|x| pre_this_safe(x, prefix_vars)))
10636 .unwrap_or(true)
10637 }
10638 Expr::Un { e, .. } | Expr::InstanceOf { e, .. } => pre_this_safe(e, prefix_vars),
10640 Expr::ArrayIndex { array, index } => {
10641 pre_this_safe(array, prefix_vars) && pre_this_safe(index, prefix_vars)
10642 }
10643 _ => false,
10644 }
10645}
10646
10647fn single_assign_to_v(s: &Stmt, v: u32) -> Option<Expr> {
10651 match s {
10652 Stmt::ExprStmt(Expr::Assign {
10653 target,
10654 op: AssignOp::Plain,
10655 value,
10656 }) if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v) => {
10657 Some((**value).clone())
10658 }
10659 Stmt::Block(xs) if xs.len() == 1 => single_assign_to_v(&xs[0], v),
10660 _ => None,
10661 }
10662}
10663
10664pub(crate) fn flatten_top_blocks(stmts: &mut Vec<Stmt>) {
10686 fn rec(s: Stmt, out: &mut Vec<Stmt>) {
10687 match s {
10688 Stmt::Block(inner) => {
10689 for x in inner {
10690 rec(x, out);
10691 }
10692 }
10693 other => out.push(other),
10694 }
10695 }
10696 if !stmts.iter().any(|s| matches!(s, Stmt::Block(_))) {
10697 return;
10698 }
10699 let mut out: Vec<Stmt> = Vec::with_capacity(stmts.len());
10700 for s in std::mem::take(stmts) {
10701 rec(s, &mut out);
10702 }
10703 *stmts = out;
10704}
10705
10706fn fold_default_arg_bridge(stmts: &mut Vec<Stmt>) -> bool {
10707 flatten_top_blocks(stmts);
10708 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
10709 return false;
10710 };
10711 if pos == 0 {
10712 return false;
10713 }
10714 let Stmt::ExprStmt(Expr::Method { args, .. }) = &stmts[pos] else {
10715 return false;
10716 };
10717 let mut arg_vars: Vec<u32> = Vec::new();
10718 let mut reads = 0usize;
10719 for a in args {
10720 let mut c = a.clone();
10721 deep_rewrite_reads(&mut c, &mut |x| {
10722 if let Expr::Local { var, .. } = x {
10723 reads += 1;
10724 if !arg_vars.contains(var) {
10725 arg_vars.push(*var);
10726 }
10727 }
10728 });
10729 }
10730 if arg_vars.is_empty() || reads != arg_vars.len() {
10731 return false; }
10733 for &v in &arg_vars {
10736 if stmts_read_var(&stmts[pos + 1..], v) != 0
10737 || stmts_count_writes(&stmts[pos + 1..], v) != 0
10738 {
10739 return false;
10740 }
10741 }
10742 let prefix_vars = prefix_written_vars(&stmts[..pos]);
10743 let mut repl: Vec<(u32, Expr)> = Vec::new();
10744 let mut drop: Vec<usize> = Vec::new();
10745 for &v in &arg_vars {
10746 let mut base: Option<(usize, Expr)> = None;
10748 let mut decl: Option<usize> = None;
10749 let mut condf: Option<(usize, Expr, Option<Expr>, Option<Expr>)> = None;
10750 let mut ok = true;
10751 for (i, s) in stmts[..pos].iter().enumerate() {
10752 match s {
10753 Stmt::LocalDef { var, init, .. } if *var == v => match init {
10754 None => {
10755 if decl.is_some() {
10756 ok = false;
10757 break;
10758 }
10759 decl = Some(i);
10760 }
10761 Some(e) => {
10762 if base.is_some() {
10763 ok = false;
10764 break;
10765 }
10766 base = Some((i, e.clone()));
10767 }
10768 },
10769 Stmt::ExprStmt(Expr::Assign {
10770 target,
10771 op: AssignOp::Plain,
10772 value,
10773 }) if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v) => {
10774 if base.is_some() {
10775 ok = false;
10776 break;
10777 }
10778 base = Some((i, (**value).clone()));
10779 }
10780 Stmt::If {
10781 cond,
10782 then_stmt,
10783 else_stmt,
10784 } => {
10785 let t = single_assign_to_v(then_stmt, v);
10786 let f = else_stmt.as_ref().and_then(|b| single_assign_to_v(b, v));
10787 let then_touches = t.is_some()
10788 || stmts_read_var(std::slice::from_ref(&**then_stmt), v) != 0
10789 || stmts_count_writes(std::slice::from_ref(&**then_stmt), v) != 0;
10790 let else_touches = else_stmt.as_ref().is_some_and(|b| {
10791 f.is_some()
10792 || stmts_read_var(std::slice::from_ref(b), v) != 0
10793 || stmts_count_writes(std::slice::from_ref(b), v) != 0
10794 });
10795 if !then_touches && !else_touches {
10796 continue; }
10798 if condf.is_some()
10803 || base.is_none()
10804 || (then_touches && t.is_none())
10805 || (else_touches && f.is_none())
10806 {
10807 ok = false;
10808 break;
10809 }
10810 let cond_stmt = Stmt::ExprStmt(cond.clone());
10811 if stmts_read_var(std::slice::from_ref(&cond_stmt), v) != 0 {
10812 ok = false;
10813 break;
10814 }
10815 condf = Some((i, cond.clone(), t, f));
10816 }
10817 _ => {}
10818 }
10819 }
10820 if !ok {
10821 continue;
10822 }
10823 let Some((bi, be)) = base else { continue };
10824 let want_writes = 1
10827 + condf
10828 .as_ref()
10829 .map(|(_, _, t, f)| t.is_some() as usize + f.is_some() as usize)
10830 .unwrap_or(0);
10831 if stmts_count_writes(&stmts[..pos], v) != want_writes
10832 || stmts_read_var(&stmts[..pos], v) != 0
10833 {
10834 continue;
10835 }
10836 let rep = match condf {
10837 Some((ci, c, t, f)) => {
10838 drop.push(ci);
10839 match (t, f) {
10840 (Some(t), Some(f)) => Expr::Cond {
10841 c: Box::new(c),
10842 t: Box::new(t),
10843 f: Box::new(f),
10844 },
10845 (Some(t), None) => Expr::Cond {
10846 c: Box::new(c),
10847 t: Box::new(t),
10848 f: Box::new(be.clone()),
10849 },
10850 (None, Some(f)) => Expr::Cond {
10851 c: Box::new(c),
10852 t: Box::new(be.clone()),
10853 f: Box::new(f),
10854 },
10855 (None, None) => unreachable!("condf requires a branch assign"),
10856 }
10857 }
10858 None => be.clone(),
10859 };
10860 if !pre_this_safe(&rep, &prefix_vars) {
10861 continue;
10862 }
10863 drop.push(bi);
10864 if let Some(d) = decl {
10865 drop.push(d);
10866 }
10867 repl.push((v, rep));
10868 }
10869 if repl.is_empty() {
10870 return false;
10871 }
10872 if let Stmt::ExprStmt(Expr::Method { args, .. }) = &mut stmts[pos] {
10873 for a in args.iter_mut() {
10874 deep_rewrite_reads(a, &mut |x| {
10875 if let Expr::Local { var, .. } = x {
10876 if let Some((_, e)) = repl.iter().find(|(vv, _)| vv == var) {
10877 *x = e.clone();
10878 }
10879 }
10880 });
10881 }
10882 }
10883 drop.sort_unstable();
10884 drop.dedup();
10885 for i in drop.into_iter().rev() {
10886 stmts.remove(i);
10887 }
10888 true
10889}
10890
10891pub fn rewrite_kotlin_facades(body: &mut Stmt, pool: &crate::DexPool) {
10899 let map = pool.kotlin_facade_map();
10900 if map.is_empty() {
10901 return;
10902 }
10903 walk_stmt_exprs(body, &mut |e| {
10904 match e {
10905 Expr::Method {
10906 cls,
10907 name,
10908 desc,
10909 is_static,
10910 ..
10911 } if *is_static => {
10912 if let Some((base, cover)) = map.get(cls.as_ref()) {
10913 let hit = match cover {
10914 None => true,
10915 Some(c) => c.0.contains(&(name.to_string(), desc.to_string())),
10916 };
10917 if hit {
10918 *cls = std::sync::Arc::from(base.as_str());
10919 }
10920 }
10921 }
10922 Expr::Field {
10923 cls,
10924 name,
10925 is_static,
10926 ..
10927 } if *is_static => {
10928 if let Some((base, cover)) = map.get(cls.as_ref()) {
10929 let hit = match cover {
10930 None => true,
10931 Some(c) => c.1.contains(name.as_ref()),
10932 };
10933 if hit {
10934 *cls = std::sync::Arc::from(base.as_str());
10935 }
10936 }
10937 }
10938 _ => {}
10939 }
10940 });
10941}
10942
10943pub fn fold_enum_default_arg_bridge(body: &mut Stmt) {
10951 let Stmt::Block(stmts) = body else { return };
10952 fold_default_arg_bridge(stmts);
10953}
10954
10955pub fn fix_ctor_delegation_arg_defs(body: &mut Stmt) {
10956 let Stmt::Block(stmts) = body else { return };
10957 fold_default_arg_bridge(stmts);
10960 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
10964 return;
10965 };
10966 if pos == 0 {
10967 return;
10968 }
10969 let mut arg_vars: Vec<u32> = Vec::new();
10972 let mut total_reads = 0usize;
10973 {
10974 let Stmt::ExprStmt(Expr::Method { args, .. }) = &stmts[pos] else {
10975 return;
10976 };
10977 for a in args {
10978 let mut c = a.clone();
10979 deep_rewrite_reads(&mut c, &mut |x| {
10980 if let Expr::Local { var: v, .. } = x {
10981 total_reads += 1;
10982 if !arg_vars.contains(v) {
10983 arg_vars.push(*v);
10984 }
10985 }
10986 });
10987 }
10988 }
10989 if arg_vars.is_empty() || total_reads != arg_vars.len() {
10990 return;
10991 }
10992 let prefix_defs: Vec<u32> = stmts[..pos]
10995 .iter()
10996 .filter_map(|s| match s {
10997 Stmt::LocalDef { var, init: Some(_), .. } => Some(*var),
10998 _ => None,
10999 })
11000 .collect();
11001 let mut plan: Vec<(u32, usize, Option<usize>)> = Vec::new();
11006 for v in arg_vars {
11007 let defs: Vec<usize> = stmts[..pos]
11008 .iter()
11009 .enumerate()
11010 .filter(|(i, _)| {
11011 matches!(&stmts[*i], Stmt::LocalDef { var, init: Some(_), .. } if *var == v)
11012 })
11013 .map(|(i, _)| i)
11014 .collect();
11015 if defs.len() > 1 {
11016 return; }
11018 let Some(&q) = defs.first() else {
11019 continue; };
11021 let init = match &stmts[q] {
11022 Stmt::LocalDef { init: Some(e), .. } => e.clone(),
11023 _ => return,
11024 };
11025 if stmts_read_var(&stmts[q + 1..pos], v) != 0 {
11026 return; }
11028 let mut init_vars: Vec<u32> = Vec::new();
11031 deep_rewrite_reads(&mut { init.clone() }, &mut |x| {
11032 if let Expr::Local { var: vv, .. } = x {
11033 if !init_vars.contains(vv) {
11034 init_vars.push(*vv);
11035 }
11036 }
11037 });
11038 if init_vars.iter().any(|iv| prefix_defs.contains(iv)) {
11039 return;
11040 }
11041 match stmts[pos + 1..]
11042 .iter()
11043 .position(|s| {
11044 matches!(s, Stmt::ExprStmt(Expr::Assign { target, .. })
11045 if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v))
11046 }) {
11047 Some(w) => {
11048 if stmts_read_var(&stmts[pos + 1..pos + 1 + w], v) != 0 {
11050 return;
11051 }
11052 plan.push((v, q, Some(pos + 1 + w)));
11053 }
11054 None => {
11055 if stmts_read_var(&stmts[pos + 1..], v) != 0 {
11057 return;
11058 }
11059 plan.push((v, q, None));
11060 }
11061 }
11062 }
11063 if plan.is_empty() {
11064 return;
11065 }
11066 let values: Vec<(u32, Expr)> = plan
11068 .iter()
11069 .map(|(_v, q, _)| match &stmts[*q] {
11070 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e.clone()),
11071 _ => unreachable!("plan entries carry a LocalDef with init"),
11072 })
11073 .collect();
11074 if let Stmt::ExprStmt(Expr::Method { args, .. }) = &mut stmts[pos] {
11075 for a in args {
11076 deep_rewrite_reads(a, &mut |x| {
11077 if let Expr::Local { var: v, .. } = x {
11078 if let Some((_, e)) = values.iter().find(|(vv, _)| vv == v) {
11079 *x = e.clone();
11080 }
11081 }
11082 });
11083 }
11084 }
11085 for (v, _, w) in &plan {
11089 if let Some(w) = w {
11090 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = &mut stmts[*w] {
11091 if matches!(&**target, Expr::Local { var: vv, .. } if vv == v) {
11092 let taken = std::mem::replace(&mut **value, Expr::Const(ConstVal::Null));
11093 stmts[*w] = Stmt::LocalDef {
11094 var: *v,
11095 init: Some(taken),
11096 is_final: false,
11097 force_type: false,
11098 };
11099 }
11100 }
11101 }
11102 }
11103 let mut order: Vec<usize> = plan.iter().map(|(_, q, _)| *q).collect();
11104 order.sort_unstable();
11105 order.reverse();
11106 for q in order {
11107 stmts.remove(q);
11108 }
11109}
11110
11111pub fn fix_bool_returns(vt: &VarTable, body: &mut Stmt) {
11122 walk_mut_deep(body, &mut |st| {
11123 if let Stmt::Return(Some(e)) = st {
11124 let numeric = match &*e {
11125 Expr::Local { var, ty } => {
11126 let t = if (*var as usize) < vt.vars.len() {
11127 vt.var(*var).ty.erased()
11128 } else {
11129 ty.erased()
11130 };
11131 matches!(
11132 t,
11133 JavaType::Int
11134 | JavaType::Long
11135 | JavaType::Short
11136 | JavaType::Byte
11137 | JavaType::Char
11138 )
11139 }
11140 other => matches!(
11141 other.type_ref().erased(),
11142 JavaType::Int | JavaType::Long | JavaType::Short | JavaType::Byte
11143 | JavaType::Char
11144 ),
11145 };
11146 if numeric {
11147 let taken = e.clone();
11148 *e = Expr::Bin {
11149 op: BinOp::Ne,
11150 l: Box::new(taken),
11151 r: Box::new(Expr::Const(ConstVal::Int(0))),
11152 ty: Some(TypeRef::J(JavaType::Boolean)),
11153 };
11154 }
11155 }
11156 });
11157}
11158
11159pub fn fix_int_returns(vt: &VarTable, body: &mut Stmt) {
11160 walk_mut_deep(body, &mut |st| {
11161 if let Stmt::Return(Some(e)) = st {
11162 let is_bool = match e {
11163 Expr::Local { var, .. } => {
11164 (*var as usize) < vt.vars.len()
11165 && vt.vars[*var as usize].ty.erased() == JavaType::Boolean
11166 }
11167 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
11168 _ => false,
11169 };
11170 if is_bool {
11171 *e = Expr::Cond {
11172 c: Box::new(e.clone()),
11173 t: Box::new(Expr::Const(ConstVal::Int(1))),
11174 f: Box::new(Expr::Const(ConstVal::Int(0))),
11175 };
11176 }
11177 }
11178 });
11179}
11180
11181static WIDEN_COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
11197
11198pub fn split_generations(vt: &mut VarTable, body: &mut Stmt, pool: &DexPool) {
11199 if std::env::var("DDC_DBG_PHI").is_ok() {
11200 eprintln!("[widen] cumulative={}", WIDEN_COUNT.load(std::sync::atomic::Ordering::Relaxed));
11201 }
11202 let mut counter: u32 = 0;
11203 let mut gen: std::collections::HashMap<u32, u32> = std::collections::HashMap::default();
11204 split_walk_stmt(body, vt, &mut gen, &mut counter, pool);
11205}
11206
11207fn split_needed(vt: &VarTable, target: u32, value: &Expr, pool: &DexPool) -> bool {
11209 let n = vt.vars.len();
11210 if target as usize >= n {
11211 return false;
11212 }
11213 let declared = vt.vars[target as usize].ty.erased();
11214 let vt_is_ref = |t: &JavaType| matches!(t, JavaType::Object(_) | JavaType::Array(_));
11215 fn side_bool(e: &Expr, vt: &VarTable) -> bool {
11224 match e {
11225 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
11226 matches!(vt.var(*var).ty.erased(), JavaType::Boolean)
11227 }
11228 Expr::Const(ConstVal::Int(0 | 1)) => true,
11229 Expr::Bin { op: BinOp::Or | BinOp::And | BinOp::Xor, l, r, .. } => {
11230 side_bool(l, vt) && side_bool(r, vt)
11231 }
11232 other => matches!(other.type_ref().erased(), JavaType::Boolean),
11233 }
11234 }
11235 let value_ty = match value {
11236 Expr::Local { var, ty } if (*var as usize) < vt.vars.len() => vt.var(*var).ty.erased(),
11237 Expr::Bin {
11238 op: BinOp::Or | BinOp::And | BinOp::Xor,
11239 l,
11240 r,
11241 ..
11242 } if side_bool(l, vt) && side_bool(r, vt) => JavaType::Boolean,
11243 other => other.type_ref().erased(),
11244 };
11245 if vt_is_ref(&declared) && vt_is_ref(&value_ty) {
11246 let obj = JavaType::Object("java/lang/Object".into());
11256 if declared == value_ty || declared == obj || value_ty == obj {
11257 return false;
11258 }
11259 if let (JavaType::Object(v), JavaType::Object(d)) = (&value_ty, &declared) {
11272 if matches!(value, Expr::New { .. })
11273 && (pool.get(v).is_none() || pool.get(d).is_none())
11274 {
11275 return false;
11276 }
11277 }
11278 true
11279 } else {
11280 if vt_is_ref(&declared) != vt_is_ref(&value_ty) {
11284 return true;
11285 }
11286 let d_bool = matches!(declared, JavaType::Boolean);
11294 let v_bool = matches!(value_ty, JavaType::Boolean);
11295 let zero_one = matches!(value, Expr::Const(ConstVal::Int(0 | 1)));
11296 d_bool != v_bool && !zero_one
11297 }
11298}
11299
11300fn rewrite_gen_reads(e: &mut Expr, gen: &std::collections::HashMap<u32, u32>, vt: &VarTable) {
11301 deep_rewrite(e, &mut |x| {
11302 if let Expr::Local { var, ty } = x {
11303 if let Some(&to) = gen.get(var) {
11304 if to != *var {
11305 *var = to;
11306 *ty = vt.var(to).ty.clone();
11307 }
11308 }
11309 }
11310 });
11311}
11312
11313fn do_split(
11314 vt: &mut VarTable,
11315 target: &mut Expr,
11316 old: u32,
11317 value_ty: &JavaType,
11318 gen: &mut std::collections::HashMap<u32, u32>,
11319 counter: &mut u32,
11320) {
11321 *counter += 1;
11322 let info = &vt.vars[old as usize];
11323 let id = vt.vars.len() as u32;
11324 let name = format!("{}_g{}", info.name, counter);
11325 let slot = info.slot;
11326 let is_param = false;
11334 vt.vars.push(jdc_core::var::VarInfo {
11335 id,
11336 slot,
11337 name,
11338 ty: TypeRef::J(value_ty.clone()),
11339 is_param,
11340 range_start: 0,
11341 range_end: u16::MAX,
11342 synthetic_name: true,
11343 });
11344 while vt.by_slot.len() <= slot as usize {
11345 vt.by_slot.push(Vec::new());
11346 }
11347 vt.by_slot[slot as usize].push((0, u16::MAX, id));
11348 gen.insert(old, id);
11349 if let Expr::Local { var, ty, .. } = target {
11350 *var = id;
11351 *ty = TypeRef::J(value_ty.clone());
11352 }
11353}
11354
11355fn rename_gen_back_batch(s: &mut Stmt, pairs: &[(u32, u32)], vt: &VarTable) {
11370 use jdc_core::FxHashMap;
11371 if pairs.is_empty() {
11372 return;
11373 }
11374 let map: FxHashMap<u32, (u32, JavaType)> = pairs
11375 .iter()
11376 .map(|&(from, to)| (from, (to, vt.vars[to as usize].ty.erased())))
11377 .collect();
11378 walk_mut_deep(s, &mut |st| {
11379 if let Stmt::LocalDef { var, .. } = st {
11380 if let Some(&(to, _)) = map.get(var) {
11381 *var = to;
11382 }
11383 }
11384 });
11385 rewrite_exprs(s, &mut |e| {
11386 deep_rewrite(e, &mut |x| {
11387 if let Expr::Local { var, ty } = x {
11388 if let Some(&(to, ref to_ty)) = map.get(var) {
11389 *var = to;
11390 *ty = TypeRef::J(to_ty.clone());
11391 }
11392 }
11393 });
11394 });
11395}
11396
11397fn drop_gen_slot(vt: &mut VarTable, dead: u32) {
11401 if (dead as usize) >= vt.vars.len() {
11402 return;
11403 }
11404 let slot = vt.vars[dead as usize].slot as usize;
11405 if let Some(seg) = vt.by_slot.get_mut(slot) {
11406 seg.retain(|&(_, _, id)| id != dead);
11407 }
11408}
11409
11410fn lub_pending() -> Option<String> {
11431 Some(String::new())
11432}
11433
11434enum Undo {
11436 Direct,
11438 Widen(String),
11440 Refuse,
11442}
11443
11444fn obj_name(t: &TypeRef) -> Option<&std::sync::Arc<str>> {
11445 match t {
11446 TypeRef::J(JavaType::Object(o)) => Some(o),
11447 _ => None,
11448 }
11449}
11450
11451fn pool_lub(a: &str, b: &str, pool: &DexPool) -> Option<String> {
11452 if a == b {
11453 return Some(a.to_string());
11454 }
11455 thread_local! {
11461 static MEMO: std::cell::RefCell<jdc_core::FxHashMap<(String, String), Option<String>>> =
11462 std::cell::RefCell::new(jdc_core::FxHashMap::default());
11463 }
11464 if let Some(hit) = MEMO.with(|m| m.borrow().get(&(a.to_string(), b.to_string())).cloned()) {
11465 return hit;
11466 }
11467 let r = pool_lub_uncached(a, b, pool);
11468 MEMO.with(|m| m.borrow_mut().insert((a.to_string(), b.to_string()), r.clone()));
11469 r
11470}
11471
11472fn pool_lub_uncached(a: &str, b: &str, pool: &DexPool) -> Option<String> {
11473 let mut anc: jdc_core::FxHashSet<String> = jdc_core::FxHashSet::default();
11474 let mut stack: Vec<String> = vec![a.to_string()];
11475 while let Some(c) = stack.pop() {
11476 if !anc.insert(c.clone()) {
11477 continue;
11478 }
11479 if anc.len() > 1024 {
11480 return None;
11481 }
11482 if let Some(cls) = pool.get(&c) {
11483 if let Some(s) = &cls.super_name {
11484 stack.push(s.clone());
11485 }
11486 for i in &cls.interfaces {
11487 stack.push(i.clone());
11488 }
11489 } else {
11490 if let Some(s) = crate::fwdb::super_of(&c) {
11491 stack.push(s.to_string());
11492 }
11493 crate::fwdb::for_each_interface(&c, |i| stack.push(i.to_string()));
11494 }
11495 }
11496 let mut seen: jdc_core::FxHashSet<String> = jdc_core::FxHashSet::default();
11497 let mut queue: std::collections::VecDeque<String> = std::collections::VecDeque::new();
11498 queue.push_back(b.to_string());
11499 while let Some(c) = queue.pop_front() {
11500 if !seen.insert(c.clone()) {
11501 continue;
11502 }
11503 if c != "java/lang/Object" && anc.contains(&c) {
11504 return Some(c);
11505 }
11506 if seen.len() > 1024 {
11507 return None;
11508 }
11509 if let Some(cls) = pool.get(&c) {
11510 for i in &cls.interfaces {
11511 queue.push_back(i.clone());
11512 }
11513 if let Some(s) = &cls.super_name {
11514 queue.push_back(s.clone());
11515 }
11516 } else {
11517 crate::fwdb::for_each_interface(&c, |i| queue.push_back(i.to_string()));
11518 if let Some(s) = crate::fwdb::super_of(&c) {
11519 queue.push_back(s.to_string());
11520 }
11521 }
11522 }
11523 None
11524}
11525
11526fn undo_type_safe(vt: &VarTable, k: u32, v: u32) -> bool {
11527 if (k as usize) >= vt.vars.len() || (v as usize) >= vt.vars.len() {
11528 return false;
11529 }
11530 let kt = vt.vars[k as usize].ty.erased();
11531 let vt_ty = vt.vars[v as usize].ty.erased();
11532 if kt == vt_ty {
11533 return true;
11534 }
11535 let is_prim = |t: &JavaType| {
11536 matches!(
11537 t,
11538 JavaType::Int
11539 | JavaType::Long
11540 | JavaType::Short
11541 | JavaType::Byte
11542 | JavaType::Char
11543 | JavaType::Float
11544 | JavaType::Double
11545 | JavaType::Boolean
11546 )
11547 };
11548 is_prim(&kt) && is_prim(&vt_ty)
11549}
11550
11551fn split_walk_stmt(
11552 st: &mut Stmt,
11553 vt: &mut VarTable,
11554 gen: &mut std::collections::HashMap<u32, u32>,
11555 counter: &mut u32,
11556 pool: &DexPool,
11557) {
11558 match st {
11559 Stmt::Block(v) => {
11560 for x in v.iter_mut() {
11561 split_walk_stmt(x, vt, gen, counter, pool);
11562 }
11563 }
11564 Stmt::ExprStmt(e) => {
11565 rewrite_gen_reads(e, gen, vt);
11566 if let Expr::Assign {
11567 target,
11568 value,
11569 op: AssignOp::Plain,
11570 ..
11571 } = e
11572 {
11573 if let Expr::Local { var, .. } = &**target {
11574 let old = *var;
11575 if split_needed(vt, old, value, pool) {
11576 let value_ty = value.type_ref().erased();
11577 do_split(vt, target, old, &value_ty, gen, counter);
11578 }
11579 }
11580 }
11581 }
11582 Stmt::LocalDef { var, init, .. } => {
11583 if let Some(e) = init {
11584 rewrite_gen_reads(e, gen, vt);
11585 if split_needed(vt, *var, e, pool) {
11586 let value_ty = e.type_ref().erased();
11590 let old = *var;
11591 let mut fake = Expr::Local {
11592 var: old,
11593 ty: vt.vars[old as usize].ty.clone(),
11594 };
11595 do_split(vt, &mut fake, old, &value_ty, gen, counter);
11596 if let Expr::Local { var: nv, .. } = fake {
11597 *var = nv;
11598 }
11599 }
11600 }
11601 }
11602 Stmt::If {
11603 cond,
11604 then_stmt,
11605 else_stmt,
11606 ..
11607 } => {
11608 rewrite_gen_reads(cond, gen, vt);
11609 let then_floor = vt.vars.len() as u32;
11610 let mut g_then = gen.clone();
11611 split_walk_stmt(then_stmt, vt, &mut g_then, counter, pool);
11612 let else_floor = vt.vars.len() as u32;
11613 let mut g_else = gen.clone();
11614 if let Some(e) = else_stmt {
11615 split_walk_stmt(e, vt, &mut g_else, counter, pool);
11616 }
11617 let mut undo_then: Vec<(u32, u32, Option<String>)> = 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 })
11661 .filter_map(|(&k, &v)| {
11662 if undo_type_safe(vt, k, v) {
11663 Some((v, k, None))
11664 } else if let (Some(_), Some(_)) =
11665 (obj_name(&vt.vars[k as usize].ty), obj_name(&vt.vars[v as usize].ty))
11666 {
11667 Some((v, k, lub_pending()))
11668 } else {
11669 None
11670 }
11671 })
11672 .collect();
11673 if else_floor > then_floor && !undo_then.is_empty() {
11674 let vars: Vec<u32> = undo_then.iter().map(|&(v, _, _)| v).collect();
11675 let mut hits = vec![false; vars.len()];
11676 mark_read_vars(std::slice::from_ref(&**then_stmt), &vars, &mut hits);
11677 let mut i = 0usize;
11678 undo_then.retain(|_| {
11679 let keep = !hits[i];
11680 i += 1;
11681 keep
11682 });
11683 }
11684 undo_then.sort_unstable_by_key(|&(v, _, _)| std::cmp::Reverse(v));
11685 let mut pairs: Vec<(u32, u32)> = Vec::with_capacity(undo_then.len());
11688 for (v, k, pending) in undo_then {
11689 let kind = match pending {
11695 None => Undo::Direct,
11696 Some(_) => {
11697 let (a, b) = (
11698 obj_name(&vt.vars[k as usize].ty),
11699 obj_name(&vt.vars[v as usize].ty),
11700 );
11701 match (a, b) {
11702 (Some(a), Some(b)) if pool.is_subtype(b, a) => Undo::Direct,
11703 (Some(a), Some(b)) => match pool_lub(a, b, pool) {
11704 Some(lub) => Undo::Widen(lub),
11705 None => Undo::Refuse,
11706 },
11707 _ => Undo::Refuse,
11708 }
11709 }
11710 };
11711 match kind {
11712 Undo::Refuse => continue,
11713 Undo::Direct => pairs.push((v, k)),
11714 Undo::Widen(lub) => {
11715 WIDEN_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
11716 vt.vars[k as usize].ty = TypeRef::J(JavaType::Object(lub.as_str().into()));
11717 pairs.push((v, k));
11718 }
11719 }
11720 }
11721 rename_gen_back_batch(then_stmt, &pairs, vt);
11722 for &(v, _) in &pairs {
11723 drop_gen_slot(vt, v);
11724 }
11725 if let Some(e) = else_stmt.as_deref_mut() {
11726 let mut undo_else: Vec<(u32, u32, Option<String>)> = g_else
11727 .iter()
11728 .filter(|(&k, &v)| {
11729 v >= else_floor && g_then.get(&k).copied() == gen.get(&k).copied()
11730 })
11731 .filter_map(|(&k, &v)| {
11732 if undo_type_safe(vt, k, v) {
11733 Some((v, k, None))
11734 } else if let (Some(_), Some(_)) =
11735 (obj_name(&vt.vars[k as usize].ty), obj_name(&vt.vars[v as usize].ty))
11736 {
11737 Some((v, k, lub_pending()))
11738 } else {
11739 None
11740 }
11741 })
11742 .collect();
11743 if (vt.vars.len() as u32) > else_floor && !undo_else.is_empty() {
11744 let vars: Vec<u32> = undo_else.iter().map(|&(v, _, _)| v).collect();
11745 let mut hits = vec![false; vars.len()];
11746 mark_read_vars(std::slice::from_ref(&*e), &vars, &mut hits);
11747 let mut i = 0usize;
11748 undo_else.retain(|_| {
11749 let keep = !hits[i];
11750 i += 1;
11751 keep
11752 });
11753 }
11754 undo_else.sort_unstable_by_key(|&(v, _, _)| std::cmp::Reverse(v));
11755 let mut pairs: Vec<(u32, u32)> = Vec::with_capacity(undo_else.len());
11756 for (v, k, pending) in undo_else {
11757 let kind = match pending {
11758 None => Undo::Direct,
11759 Some(_) => {
11760 let (a, b) = (
11761 obj_name(&vt.vars[k as usize].ty),
11762 obj_name(&vt.vars[v as usize].ty),
11763 );
11764 match (a, b) {
11765 (Some(a), Some(b)) if pool.is_subtype(b, a) => Undo::Direct,
11766 (Some(a), Some(b)) => match pool_lub(a, b, pool) {
11767 Some(lub) => Undo::Widen(lub),
11768 None => Undo::Refuse,
11769 },
11770 _ => Undo::Refuse,
11771 }
11772 }
11773 };
11774 match kind {
11775 Undo::Refuse => continue,
11776 Undo::Direct => pairs.push((v, k)),
11777 Undo::Widen(lub) => {
11778 WIDEN_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
11779 vt.vars[k as usize].ty = TypeRef::J(JavaType::Object(lub.as_str().into()));
11780 pairs.push((v, k));
11781 }
11782 }
11783 }
11784 rename_gen_back_batch(e, &pairs, vt);
11785 for &(v, _) in &pairs {
11786 drop_gen_slot(vt, v);
11787 }
11788 }
11789 gen.retain(|k, v| g_then.get(k) == Some(v) && g_else.get(k) == Some(v));
11792 }
11793 Stmt::While { cond, body, .. } | Stmt::DoWhile { cond, body, .. } => {
11794 let entry = gen.clone();
11798 for _ in 0..3 {
11799 rewrite_gen_reads(cond, gen, vt);
11800 split_walk_stmt(body, vt, gen, counter, pool);
11801 }
11802 *gen = entry;
11805 }
11806 Stmt::For { init, body, .. } => {
11807 for x in init.iter_mut() {
11808 split_walk_stmt(x, vt, gen, counter, pool);
11809 }
11810 let entry = gen.clone();
11811 for _ in 0..3 {
11812 split_walk_stmt(body, vt, gen, counter, pool);
11813 }
11814 *gen = entry;
11815 }
11816 Stmt::ForEach { body, .. } => {
11817 let entry = gen.clone();
11818 for _ in 0..3 {
11819 split_walk_stmt(body, vt, gen, counter, pool);
11820 }
11821 *gen = entry;
11822 }
11823 Stmt::Switch { cases, default, .. } => {
11824 let entry = gen.clone();
11825 for c in cases.iter_mut() {
11826 let mut g_case = entry.clone();
11827 for x in c.body.iter_mut() {
11828 split_walk_stmt(x, vt, &mut g_case, counter, pool);
11829 }
11830 gen.retain(|k, v| g_case.get(k) == Some(v));
11831 }
11832 if let Some(d) = default {
11833 let mut g_def = entry.clone();
11834 split_walk_stmt(d, vt, &mut g_def, counter, pool);
11835 gen.retain(|k, v| g_def.get(k) == Some(v));
11836 }
11837 }
11838 Stmt::Try {
11839 body,
11840 catches,
11841 finally,
11842 } => {
11843 let entry = gen.clone();
11844 let mut g_try = entry.clone();
11845 split_walk_stmt(body, vt, &mut g_try, counter, pool);
11846 let mut g_all = g_try.clone();
11847 for c in catches.iter_mut() {
11848 let mut g_c = entry.clone();
11851 split_walk_stmt(&mut c.body, vt, &mut g_c, counter, pool);
11852 g_all.retain(|k, v| g_c.get(k) == Some(v));
11853 }
11854 *gen = g_all;
11855 if let Some(f) = finally {
11856 split_walk_stmt(f, vt, gen, counter, pool);
11857 }
11858 }
11859 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => {
11860 split_walk_stmt(body, vt, gen, counter, pool);
11861 }
11862 Stmt::Return(Some(e)) | Stmt::Throw(e) => {
11863 rewrite_gen_reads(e, gen, vt);
11864 }
11865 _ => {}
11866 }
11867}
11868
11869fn fw_super(c: &str) -> Option<&'static str> {
11876 crate::fwdb::super_of(c)
11877}
11878
11879fn exc_subtype(sub: &str, sup: &str, pool: &DexPool) -> bool {
11885 if sub == sup {
11886 return true;
11887 }
11888 if pool.is_subtype(sub, sup) {
11889 return true;
11890 }
11891 let mut cur = sub;
11892 for _ in 0..24 {
11893 let Some(next) = fw_super(cur) else { break };
11894 if next == sup {
11895 return true;
11896 }
11897 cur = next;
11898 }
11899 false
11900}
11901
11902pub fn dedupe_multicatch(s: &mut Stmt, pool: &DexPool) {
11908 dedupe_multicatch_walk(s, pool);
11909}
11910
11911fn dedupe_multicatch_walk(s: &mut Stmt, pool: &DexPool) {
11912 match s {
11913 Stmt::Try {
11914 body,
11915 catches,
11916 finally,
11917 }
11918 | Stmt::TryWithResources {
11919 body,
11920 catches,
11921 finally,
11922 ..
11923 } => {
11924 dedupe_multicatch_walk(body, pool);
11925 for c in catches.iter_mut() {
11926 if c.exc.len() > 1 {
11927 let mut keep: Vec<std::sync::Arc<str>> = Vec::with_capacity(c.exc.len());
11928 for e in c.exc.iter() {
11929 let shadowed = c.exc.iter().any(|o| {
11930 o.as_ref() != e.as_ref() && exc_subtype(e, o, pool)
11931 });
11932 if !shadowed && !keep.iter().any(|k| k.as_ref() == e.as_ref()) {
11933 keep.push(e.clone());
11934 }
11935 }
11936 c.exc = keep;
11937 }
11938 dedupe_multicatch_walk(&mut c.body, pool);
11939 }
11940 if let Some(f) = finally {
11941 dedupe_multicatch_walk(f, pool);
11942 }
11943 }
11944 Stmt::Block(v) => {
11945 for x in v.iter_mut() {
11946 dedupe_multicatch_walk(x, pool);
11947 }
11948 }
11949 Stmt::If {
11950 then_stmt,
11951 else_stmt,
11952 ..
11953 } => {
11954 dedupe_multicatch_walk(then_stmt, pool);
11955 if let Some(e) = else_stmt {
11956 dedupe_multicatch_walk(e, pool);
11957 }
11958 }
11959 Stmt::While { body, .. }
11960 | Stmt::DoWhile { body, .. }
11961 | Stmt::For { body, .. }
11962 | Stmt::ForEach { body, .. }
11963 | Stmt::Labeled { body, .. }
11964 | Stmt::Synchronized { body, .. } => dedupe_multicatch_walk(body, pool),
11965 Stmt::Switch { cases, default, .. } => {
11966 for c in cases.iter_mut() {
11967 for x in c.body.iter_mut() {
11968 dedupe_multicatch_walk(x, pool);
11969 }
11970 }
11971 if let Some(d) = default {
11972 dedupe_multicatch_walk(d, pool);
11973 }
11974 }
11975 _ => {}
11976 }
11977}
11978
11979pub fn resolve_dangling_gotos(s: &mut Stmt) {
11991 let mut labels: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
11992 walk_all(s, &mut |st| {
11995 if let Stmt::Label(id) = st {
11996 labels.insert(*id);
11997 }
11998 });
11999 resolve_gotos_walk(s, &labels, 0);
12000}
12001
12002fn resolve_gotos_walk(s: &mut Stmt, labels: &jdc_core::FxHashSet<u32>, loop_depth: u32) {
12003 let depth = match s {
12004 Stmt::While { .. } | Stmt::DoWhile { .. } | Stmt::For { .. } | Stmt::ForEach { .. } => {
12005 loop_depth + 1
12006 }
12007 _ => loop_depth,
12008 };
12009 match s {
12010 Stmt::Goto(id) => {
12011 if labels.contains(id) {
12012 return; }
12014 if depth > 0 {
12015 *s = Stmt::Break(None);
12016 } else {
12017 *s = Stmt::Comment(format!(
12018 "$DDC: unresolved jump to block {} (irreducible control flow)",
12019 id
12020 ));
12021 }
12022 }
12023 Stmt::Block(v) => {
12024 let mut i = 0usize;
12025 while i < v.len() {
12026 let dangling_here =
12031 depth == 0 && matches!(&v[i], Stmt::Goto(id) if !labels.contains(id));
12032 resolve_gotos_walk(&mut v[i], labels, depth);
12033 if dangling_here {
12034 v.truncate(i + 1);
12035 break;
12036 }
12037 i += 1;
12038 }
12039 }
12040 Stmt::If { then_stmt, else_stmt, .. } => {
12041 resolve_gotos_walk(then_stmt, labels, depth);
12042 if let Some(e) = else_stmt {
12043 resolve_gotos_walk(e, labels, depth);
12044 }
12045 }
12046 Stmt::While { body, .. }
12047 | Stmt::DoWhile { body, .. }
12048 | Stmt::For { body, .. }
12049 | Stmt::ForEach { body, .. }
12050 | Stmt::Labeled { body, .. }
12051 | Stmt::Synchronized { body, .. } => resolve_gotos_walk(body, labels, depth),
12052 Stmt::Try {
12053 body,
12054 catches,
12055 finally,
12056 } => {
12057 resolve_gotos_walk(body, labels, depth);
12058 for c in catches.iter_mut() {
12059 resolve_gotos_walk(&mut c.body, labels, depth);
12060 }
12061 if let Some(f) = finally {
12062 resolve_gotos_walk(f, labels, depth);
12063 }
12064 }
12065 Stmt::Switch { cases, default, .. } => {
12066 for c in cases.iter_mut() {
12067 for x in c.body.iter_mut() {
12068 resolve_gotos_walk(x, labels, depth);
12069 }
12070 }
12071 if let Some(d) = default {
12072 resolve_gotos_walk(d, labels, depth);
12073 }
12074 }
12075 _ => {}
12076 }
12077}
12078
12079fn mark_read_vars(stmts: &[Stmt], vars: &[u32], hits: &mut [bool]) {
12091 let mut remaining = vars.len();
12092 for s in stmts {
12093 if remaining == 0 {
12094 break;
12095 }
12096 visit_stmt_exprs_ro(s, &mut |e| {
12097 if remaining == 0 {
12098 return;
12099 }
12100 visit_exprs_reads(e, &mut |x| {
12101 if remaining == 0 {
12102 return;
12103 }
12104 if let Expr::Local { var, .. } = x {
12105 for (i, v) in vars.iter().enumerate() {
12106 if *v == *var && !hits[i] {
12107 hits[i] = true;
12108 remaining -= 1;
12109 }
12110 }
12111 }
12112 });
12113 });
12114 }
12115}
12116
12117fn stmts_read_var(stmts: &[Stmt], v: u32) -> usize {
12118 let mut n = 0usize;
12119 for s in stmts {
12120 visit_stmt_exprs_ro(s, &mut |e| {
12121 visit_exprs_reads(e, &mut |x| {
12122 if let Expr::Local { var: vv, .. } = x {
12123 if *vv == v {
12124 n += 1;
12125 }
12126 }
12127 });
12128 });
12129 }
12130 n
12131}
12132
12133pub fn fix_ctor_conditional_super(body: &mut Stmt) {
12134 if !matches!(body, Stmt::Block(_)) {
12137 let inner = std::mem::replace(body, Stmt::Block(Vec::new()));
12138 *body = Stmt::Block(vec![inner]);
12139 }
12140 let Stmt::Block(stmts) = body else { return };
12141 while stmts.len() == 1 {
12145 let Stmt::Block(inner) = &stmts[0] else { break };
12146 *stmts = inner.clone();
12147 }
12148 if stmts.is_empty() {
12149 return;
12150 }
12151 if is_bare_ctor_call(stmts.first().unwrap()) {
12152 dedupe_ctor_delegations(body);
12153 return;
12154 }
12155 if try_linear_super_inline(stmts) {
12156 return;
12157 }
12158 try_merge_branched_super(stmts);
12159}
12160
12161pub fn strip_secondary_delegations(body: &mut Stmt) {
12185 let Stmt::Block(stmts) = body else { return };
12186 if !stmts.first().is_some_and(is_bare_ctor_call) {
12187 return;
12188 }
12189 fn strip_in(s: &mut Stmt) {
12190 if is_bare_ctor_call(s) {
12191 *s = Stmt::Block(Vec::new());
12192 return;
12193 }
12194 match s {
12195 Stmt::Block(v) => v.iter_mut().for_each(strip_in),
12196 Stmt::If {
12197 then_stmt,
12198 else_stmt,
12199 ..
12200 } => {
12201 strip_in(then_stmt);
12202 if let Some(e) = else_stmt {
12203 strip_in(e);
12204 }
12205 }
12206 Stmt::While { body, .. }
12207 | Stmt::DoWhile { body, .. }
12208 | Stmt::ForEach { body, .. }
12209 | Stmt::Synchronized { body, .. }
12210 | Stmt::Labeled { body, .. } => strip_in(body),
12211 Stmt::For { init, body, .. } => {
12212 init.iter_mut().for_each(strip_in);
12213 strip_in(body);
12214 }
12215 Stmt::Switch { cases, default, .. } => {
12216 for c in cases.iter_mut() {
12217 c.body.iter_mut().for_each(strip_in);
12218 }
12219 if let Some(d) = default {
12220 strip_in(d);
12221 }
12222 }
12223 Stmt::Try {
12224 body,
12225 catches,
12226 finally,
12227 }
12228 | Stmt::TryWithResources {
12229 body,
12230 catches,
12231 finally,
12232 ..
12233 } => {
12234 strip_in(body);
12235 for c in catches.iter_mut() {
12236 strip_in(&mut c.body);
12237 }
12238 if let Some(f) = finally {
12239 strip_in(f);
12240 }
12241 }
12242 _ => {}
12243 }
12244 }
12245 for st in stmts.iter_mut().skip(1) {
12246 strip_in(st);
12247 }
12248}
12249
12250pub fn dedupe_ctor_delegations(body: &mut Stmt) {
12251 let Stmt::Block(stmts) = body else { return };
12252 let Some(d0) = stmts.first().and_then(|s| match s {
12253 Stmt::ExprStmt(e) if is_delegation_expr(e) => Some(e.clone()),
12254 _ => None,
12255 }) else {
12256 return;
12257 };
12258 fn rec(s: &mut Stmt, d0: &Expr) {
12259 match s {
12260 Stmt::Block(v) => {
12261 v.retain(|x| {
12262 !matches!(x, Stmt::ExprStmt(e) if is_delegation_expr(e) && same_delegation(e, d0))
12263 });
12264 for x in v.iter_mut() {
12265 rec(x, d0);
12266 }
12267 }
12268 Stmt::If { then_stmt, else_stmt, .. } => {
12269 rec(then_stmt, d0);
12270 if let Some(e) = else_stmt {
12271 rec(e, d0);
12272 }
12273 }
12274 _ => {}
12275 }
12276 }
12277 {
12281 let d0r = &d0;
12282 let mut idx = 0usize;
12283 stmts.retain(|s| {
12284 idx += 1;
12285 idx == 1
12286 || !(matches!(s, Stmt::ExprStmt(e)
12287 if is_delegation_expr(e) && same_delegation(e, d0r)))
12288 });
12289 }
12290 for s in stmts.iter_mut().skip(1) {
12291 rec(s, &d0);
12292 }
12293}
12294
12295fn same_delegation(a: &Expr, b: &Expr) -> bool {
12299 match (a, b) {
12300 (
12301 Expr::Method { name: n1, cls: c1, is_super: s1, args: a1, .. },
12302 Expr::Method { name: n2, cls: c2, is_super: s2, args: a2, .. },
12303 ) => {
12304 &**n1 == "<init>"
12305 && &**n2 == "<init>"
12306 && c1 == c2
12307 && s1 == s2
12308 && (a1 == a2 || (a1.is_empty() && a2.is_empty()))
12309 }
12310 _ => false,
12311 }
12312}
12313
12314fn ctor_call_expr(s: &Stmt) -> Option<&Expr> {
12316 match s {
12317 Stmt::ExprStmt(e) if is_delegation_expr(e) => Some(e),
12318 _ => None,
12319 }
12320}
12321
12322fn same_ctor_kind(a: &Expr, b: &Expr) -> bool {
12324 match (a, b) {
12325 (
12326 Expr::Method { cls: c1, is_super: s1, owner: o1, .. },
12327 Expr::Method { cls: c2, is_super: s2, owner: o2, .. },
12328 ) => c1 == c2 && s1 == s2 && o1 == o2,
12329 _ => false,
12330 }
12331}
12332
12333fn flat_list(s: &Stmt) -> Vec<&Stmt> {
12338 fn rec<'a>(s: &'a Stmt, out: &mut Vec<&'a Stmt>) {
12339 match s {
12340 Stmt::Block(v) => {
12341 for x in v {
12342 rec(x, out);
12343 }
12344 }
12345 other => out.push(other),
12346 }
12347 }
12348 let mut out = Vec::new();
12349 rec(s, &mut out);
12350 out
12351}
12352
12353fn strip_lost_alloc_owners(e: &mut Expr) {
12359 deep_rewrite(e, &mut |x| {
12360 if let Expr::Method { name, is_special: true, owner: Some(o), .. } = x {
12361 if &**name == "<init>" && !matches!(**o, Expr::This) {
12362 **o = Expr::Const(jdc_core::ir::expr::ConstVal::Int(0));
12363 }
12364 }
12365 });
12366}
12367
12368fn count_locals_expr(e: &Expr) -> jdc_core::FxHashMap<u32, usize> {
12371 let mut m = jdc_core::FxHashMap::default();
12372 count_expr_ro(e, &mut m);
12373 m
12374}
12375
12376pub(crate) fn count_locals_stmts(ss: &[Stmt]) -> jdc_core::FxHashMap<u32, usize> {
12377 let mut m = jdc_core::FxHashMap::default();
12378 for s in ss {
12379 visit_stmt_exprs_ro(s, &mut |e| count_expr_ro(e, &mut m));
12380 }
12381 m
12382}
12383
12384fn cnt(m: &jdc_core::FxHashMap<u32, usize>, v: u32) -> usize {
12385 m.get(&v).copied().unwrap_or(0)
12386}
12387
12388fn inline_locals(
12392 e: &Expr,
12393 map: &std::collections::HashMap<u32, Expr>,
12394 depth: u32,
12395) -> Option<Expr> {
12396 if depth > 4 {
12397 return None;
12398 }
12399 let mut out = e.clone();
12400 let mut fail = false;
12401 deep_rewrite(&mut out, &mut |x| {
12402 if let Expr::Local { var, .. } = x {
12403 if let Some(def) = map.get(var) {
12404 match inline_locals(def, map, depth + 1) {
12405 Some(r) => *x = r,
12406 None => fail = true,
12407 }
12408 }
12409 }
12410 });
12411 if fail {
12412 None
12413 } else {
12414 Some(out)
12415 }
12416}
12417
12418fn def_of(s: &Stmt) -> Option<(u32, &Expr)> {
12420 match s {
12421 Stmt::LocalDef { var, init: Some(e), .. } => Some((*var, e)),
12422 Stmt::ExprStmt(Expr::Assign {
12423 target,
12424 value,
12425 op: jdc_core::ir::expr::AssignOp::Plain,
12426 ..
12427 }) => match &**target {
12428 Expr::Local { var, .. } => Some((*var, value)),
12429 _ => None,
12430 },
12431 _ => None,
12432 }
12433}
12434
12435fn try_linear_super_inline(stmts: &mut Vec<Stmt>) -> bool {
12437 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
12438 return false;
12439 };
12440 if pos == 0 {
12441 return false; }
12443 if !stmts[..pos]
12445 .iter()
12446 .all(|s| matches!(s, Stmt::LocalDef { .. } | Stmt::ExprStmt(_)))
12447 {
12448 return false;
12449 }
12450 let Some(call) = ctor_call_expr(&stmts[pos]) else {
12451 return false;
12452 };
12453 let Expr::Method { args, .. } = call else {
12454 return false;
12455 };
12456 let mut map: std::collections::HashMap<u32, Expr> =
12458 std::collections::HashMap::new();
12459 let mut def_idx: jdc_core::FxHashMap<u32, usize> =
12460 jdc_core::FxHashMap::default();
12461 for (i, s) in stmts[..pos].iter().enumerate() {
12462 if let Some((v, e)) = def_of(s) {
12463 map.insert(v, e.clone());
12464 def_idx.insert(v, i);
12465 }
12466 }
12467 let mut needed: Vec<u32> = Vec::new();
12469 for a in args {
12470 let mut probe = a.clone();
12471 strip_lost_alloc_owners(&mut probe);
12472 deep_rewrite(&mut probe, &mut |x| {
12473 if let Expr::Local { var, .. } = x {
12474 if map.contains_key(var) && !needed.contains(var) {
12475 needed.push(*var);
12476 }
12477 }
12478 });
12479 }
12480 if needed.is_empty() {
12481 return false; }
12483 let mut consumed: Vec<usize> = Vec::new();
12486 let use_counts = count_locals_stmts(stmts);
12487 for &v in &needed {
12488 let def = &map[&v];
12489 let uses = cnt(&use_counts, v);
12490 if jdc_core::ir::build::has_side_effects(def) && uses != 1 {
12491 return false;
12492 }
12493 if uses == 1 {
12494 consumed.push(def_idx[&v]);
12495 }
12496 }
12497 let mut new_args: Vec<Expr> = Vec::with_capacity(args.len());
12499 for a in args {
12500 match inline_locals(a, &map, 0) {
12501 Some(r) => new_args.push(r),
12502 None => return false,
12503 }
12504 }
12505 let mut call_stmt = stmts.remove(pos);
12508 if let Stmt::ExprStmt(Expr::Method { args: slot, .. }) = &mut call_stmt {
12509 *slot = new_args;
12510 }
12511 let mut rest: Vec<Stmt> = Vec::with_capacity(stmts.len() + 1);
12512 rest.push(call_stmt);
12513 for (i, s) in stmts.drain(..).enumerate() {
12514 let orig = if i >= pos { i + 1 } else { i };
12517 if !consumed.contains(&orig) {
12518 rest.push(s);
12519 }
12520 }
12521 *stmts = rest;
12522 true
12523}
12524
12525fn try_merge_branched_super(stmts: &mut Vec<Stmt>) -> bool {
12527 let if_positions: Vec<usize> = stmts
12531 .iter()
12532 .enumerate()
12533 .filter(|(_, s)| matches!(s, Stmt::If { .. }))
12534 .map(|(i, _)| i)
12535 .collect();
12536 for if_pos in if_positions {
12537 let gate = match &stmts[if_pos] {
12543 Stmt::If { then_stmt, else_stmt, .. } => {
12544 contains_delegation(then_stmt)
12545 || else_stmt.as_ref().is_some_and(|e| contains_delegation(e))
12546 }
12547 _ => false,
12548 };
12549 if gate && merge_at(stmts, if_pos) {
12550 return true;
12551 }
12552 }
12553 false
12554}
12555
12556fn contains_delegation(s: &Stmt) -> bool {
12559 match s {
12560 Stmt::ExprStmt(e) => is_delegation_expr(e),
12561 Stmt::Block(v) => v.iter().any(contains_delegation),
12562 Stmt::If { then_stmt, else_stmt, .. } => {
12563 contains_delegation(then_stmt)
12564 || else_stmt.as_ref().is_some_and(|e| contains_delegation(e))
12565 }
12566 _ => false,
12567 }
12568}
12569
12570
12571
12572fn merge_at(stmts: &mut Vec<Stmt>, if_pos: usize) -> bool {
12585 enum Br {
12590 Call(usize, Vec<(u32, Expr)>, Vec<usize>),
12592 Clean,
12593 Dirty,
12594 }
12595 fn split_branch(list: &[&Stmt]) -> Br {
12596 let call_pos = list.iter().position(|s| ctor_call_expr(s).is_some());
12597 let Some(c) = call_pos else {
12598 return if list.iter().any(|s| contains_delegation(s)) {
12601 Br::Dirty
12602 } else {
12603 Br::Clean
12604 };
12605 };
12606 if list.iter().skip(c + 1).any(|s| ctor_call_expr(s).is_some()) {
12607 return Br::Dirty; }
12609 let mut defs: Vec<(u32, Expr)> = Vec::new();
12610 let mut def_idxs: Vec<usize> = Vec::new();
12611 for (i, s) in list.iter().enumerate().take(c) {
12612 match def_of(s) {
12613 Some((v, e)) => {
12614 defs.push((v, e.clone()));
12615 def_idxs.push(i);
12616 }
12617 None => match s {
12618 Stmt::ExprStmt(_) | Stmt::LocalDef { .. } => {}
12621 _ => return Br::Dirty, },
12623 }
12624 }
12625 Br::Call(c, defs, def_idxs)
12626 }
12627 fn cond_def_of(s: &Stmt, scope: &[Stmt]) -> Option<(u32, Expr)> {
12629 let Stmt::If { cond, then_stmt, else_stmt: Some(e), .. } = s else {
12630 return None;
12631 };
12632 fn one<'x>(b: &'x Stmt, scope: &[Stmt]) -> Option<(u32, &'x Expr)> {
12633 let l = flat_list(b);
12634 if l.len() == 1 {
12635 return def_of(l[0]);
12636 }
12637 if l.len() == 2 {
12646 let (w, ex) = def_of(l[0])?;
12647 let Stmt::ExprStmt(Expr::Assign {
12648 target,
12649 op: AssignOp::Plain,
12650 value,
12651 }) = l[1]
12652 else {
12653 return None;
12654 };
12655 let Expr::Local { var: tv, .. } = &**target else {
12656 return None;
12657 };
12658 let Expr::Local { var: rv, .. } = &**value else {
12659 return None;
12660 };
12661 if *rv != w || *tv == w {
12662 return None;
12663 }
12664 let mut ldef_writes = 0usize;
12668 for st in scope {
12669 walk_all(st, &mut |x| {
12670 if let Stmt::LocalDef {
12671 var,
12672 init: Some(_),
12673 ..
12674 } = x
12675 {
12676 if *var == w {
12677 ldef_writes += 1;
12678 }
12679 }
12680 });
12681 }
12682 if stmts_read_var(scope, w) != 1
12683 || stmts_count_writes(scope, w) + ldef_writes != 1
12684 {
12685 return None;
12686 }
12687 return Some((*tv, ex));
12688 }
12689 None
12690 }
12691 let (v1, e1) = one(then_stmt, scope)?;
12692 let (v2, e2) = one(e, scope)?;
12693 if v1 != v2 {
12694 return None;
12695 }
12696 Some((
12697 v1,
12698 Expr::Cond {
12699 c: Box::new(cond.clone()),
12700 t: Box::new(e1.clone()),
12701 f: Box::new(e2.clone()),
12702 },
12703 ))
12704 }
12705
12706 let mut pmap: std::collections::HashMap<u32, Expr> =
12708 std::collections::HashMap::new();
12709 let mut pidx: jdc_core::FxHashMap<u32, usize> =
12710 jdc_core::FxHashMap::default();
12711 for (i, s) in stmts[..if_pos].iter().enumerate() {
12712 match s {
12713 Stmt::LocalDef { .. } | Stmt::ExprStmt(_) => {
12714 if let Some((v, e)) = def_of(s) {
12715 pmap.insert(v, e.clone());
12716 pidx.insert(v, i);
12717 }
12718 }
12719 Stmt::If { .. } => match cond_def_of(s, stmts) {
12720 Some((v, e)) => {
12721 pmap.insert(v, e);
12722 pidx.insert(v, i);
12723 }
12724 None => return false,
12725 },
12726 _ => return false,
12727 }
12728 }
12729 let (cond, then_v, else_v, had_else) = match &stmts[if_pos] {
12730 Stmt::If { cond, then_stmt, else_stmt, .. } => (
12731 cond.clone(),
12732 then_stmt.as_ref().clone(),
12733 else_stmt.as_ref().map_or_else(|| Stmt::Block(Vec::new()), |e| e.as_ref().clone()),
12734 else_stmt.is_some(),
12735 ),
12736 _ => return false,
12737 };
12738 fn normalize_branch(b: &mut Stmt) {
12744 let slot: &mut Stmt = match b {
12749 Stmt::Block(v) if v.len() == 1 => &mut v[0],
12750 other => other,
12751 };
12752 if !matches!(slot, Stmt::If { .. }) || !contains_delegation(slot) {
12753 return;
12754 }
12755 let taken = std::mem::replace(slot, Stmt::Block(Vec::new()));
12756 let mut tmp = vec![taken];
12757 if merge_at(&mut tmp, 0) {
12758 *slot = Stmt::Block(tmp);
12759 } else {
12760 *slot = tmp.pop().unwrap();
12761 }
12762 }
12763 let mut then_v = then_v;
12764 let mut else_v = else_v;
12765 normalize_branch(&mut then_v);
12766 normalize_branch(&mut else_v);
12767 let then_list = flat_list(&then_v);
12768 let else_list = flat_list(&else_v);
12769
12770 enum Side {
12773 Call {
12774 at: usize,
12775 defs: Vec<(u32, Expr)>,
12776 def_idxs: Vec<usize>,
12777 },
12778 Clean,
12779 }
12780 let (t_side, e_side, two_sided) = match (split_branch(&then_list), split_branch(&else_list)) {
12781 (Br::Call(tc, td, ti), Br::Call(ec, ed, ei)) => (
12782 Side::Call { at: tc, defs: td, def_idxs: ti },
12783 Side::Call { at: ec, defs: ed, def_idxs: ei },
12784 true,
12785 ),
12786 (Br::Call(tc, td, ti), Br::Clean) => (
12787 Side::Call { at: tc, defs: td, def_idxs: ti },
12788 Side::Clean,
12789 false,
12790 ),
12791 (Br::Clean, Br::Call(ec, ed, ei)) => (
12792 Side::Clean,
12793 Side::Call { at: ec, defs: ed, def_idxs: ei },
12794 false,
12795 ),
12796 _ => return false,
12797 };
12798 let (t_call, e_call) = match (&t_side, &e_side) {
12800 (Side::Call { at: ta, .. }, Side::Call { at: ea, .. }) => (
12801 ctor_call_expr(then_list[*ta]),
12802 ctor_call_expr(else_list[*ea]),
12803 ),
12804 (Side::Call { at: ta, .. }, Side::Clean) => {
12805 (ctor_call_expr(then_list[*ta]), None)
12806 }
12807 (Side::Clean, Side::Call { at: ea, .. }) => {
12808 (None, ctor_call_expr(else_list[*ea]))
12809 }
12810 _ => return false,
12811 };
12812 let (call_list, call_at) = match &t_side {
12814 Side::Call { at, .. } => (&then_list, *at),
12815 Side::Clean => match &e_side {
12816 Side::Call { at, .. } => (&else_list, *at),
12817 Side::Clean => return false,
12818 },
12819 };
12820 let (base_args, other_args) = if two_sided {
12821 let (Some(tc), Some(ec)) = (t_call, e_call) else {
12822 return false;
12823 };
12824 if !same_ctor_kind(tc, ec) {
12825 return false;
12826 }
12827 let (Expr::Method { args: a1, .. }, Expr::Method { args: a2, .. }) = (tc, ec) else {
12828 return false;
12829 };
12830 if a1.len() != a2.len() {
12831 return false;
12832 }
12833 (a1, Some(a2))
12834 } else {
12835 let c = t_call.or(e_call);
12836 let Some(c) = c else { return false };
12837 let Expr::Method { args, .. } = c else {
12838 return false;
12839 };
12840 (args, None)
12841 };
12842
12843 let mut tmap: std::collections::HashMap<u32, Expr> = pmap.clone();
12845 let mut emap: std::collections::HashMap<u32, Expr> = pmap.clone();
12846 if let Side::Call { defs, .. } = &t_side {
12847 for (v, e) in defs {
12848 tmap.insert(*v, e.clone());
12849 }
12850 }
12851 if let Side::Call { defs, .. } = &e_side {
12852 for (v, e) in defs {
12853 emap.insert(*v, e.clone());
12854 }
12855 }
12856 let Some(cond_i) = inline_locals(&cond, &pmap, 0) else {
12857 return false;
12858 };
12859 let mut then_args: Vec<Expr> = Vec::with_capacity(base_args.len());
12861 let mut else_args: Vec<Expr> = Vec::new();
12862 if two_sided {
12863 for a in base_args {
12864 match inline_locals(a, &tmap, 0) {
12865 Some(r) => then_args.push(r),
12866 None => return false,
12867 }
12868 }
12869 for a in other_args.unwrap() {
12870 match inline_locals(a, &emap, 0) {
12871 Some(r) => else_args.push(r),
12872 None => return false,
12873 }
12874 }
12875 } else {
12876 let from_then = matches!(&t_side, Side::Call { .. });
12879 for a in base_args {
12880 let r = if from_then {
12881 inline_locals(a, &tmap, 0)
12882 } else {
12883 inline_locals(a, &emap, 0)
12884 };
12885 match r {
12886 Some(r) => then_args.push(r),
12887 None => return false,
12888 }
12889 }
12890 }
12891 let primary_args = base_args;
12892 let mut merged: Vec<Expr> = Vec::with_capacity(then_args.len());
12894 let mut differ = 0usize;
12895 for (j, t) in then_args.iter().enumerate() {
12896 if two_sided {
12897 let e = &else_args[j];
12898 if t == e {
12899 merged.push(t.clone());
12900 } else {
12901 differ += 1;
12902 merged.push(Expr::Cond {
12903 c: Box::new(cond_i.clone()),
12904 t: Box::new(t.clone()),
12905 f: Box::new(e.clone()),
12906 });
12907 }
12908 } else {
12909 merged.push(t.clone());
12910 }
12911 }
12912
12913 let vars_in = |e: &Expr| -> Vec<u32> {
12915 let mut vs = Vec::new();
12916 let mut probe = e.clone();
12917 strip_lost_alloc_owners(&mut probe);
12918 deep_rewrite(&mut probe, &mut |x| {
12919 if let Expr::Local { var, .. } = x {
12920 if !vs.contains(var) {
12921 vs.push(*var);
12922 }
12923 }
12924 });
12925 vs
12926 };
12927 let branch_remainder = |list: &[&Stmt], side: &Side| -> Vec<Stmt> {
12935 match side {
12936 Side::Clean => list.iter().map(|s| (*s).clone()).collect(),
12937 Side::Call { at, def_idxs, .. } => {
12938 list.iter()
12939 .enumerate()
12940 .filter(|(i, _)| *i != *at && !def_idxs.contains(i))
12941 .map(|(_, s)| (*s).clone())
12942 .collect()
12943 }
12944 }
12945 };
12946 let tail_then_v = branch_remainder(&then_list, &t_side);
12947 let tail_else_v = branch_remainder(&else_list, &e_side);
12948 let post = &stmts[if_pos + 1..];
12949 let mut inlined: Vec<u32> = vars_in(&cond);
12950 for a in primary_args.iter().chain(else_args.iter()) {
12951 for v in vars_in(a) {
12952 if !inlined.contains(&v) {
12953 inlined.push(v);
12954 }
12955 }
12956 }
12957 inlined.retain(|v| pmap.contains_key(v) || tmap.contains_key(v) || emap.contains_key(v));
12958 for &v in &inlined {
12960 let in_branch = match (&t_side, &e_side) {
12961 (Side::Call { defs: td, .. }, Side::Call { defs: ed, .. }) => {
12962 td.iter().any(|(d, _)| *d == v) || ed.iter().any(|(d, _)| *d == v)
12963 }
12964 (Side::Call { defs, .. }, Side::Clean)
12965 | (Side::Clean, Side::Call { defs, .. }) => {
12966 defs.iter().any(|(d, _)| *d == v)
12967 }
12968 _ => false,
12969 };
12970 if pmap.contains_key(&v) && in_branch {
12971 return false;
12972 }
12973 }
12974 type Counts = jdc_core::FxHashMap<u32, usize>;
12975 type GuardCounts = (
12976 Counts,
12977 Vec<Counts>,
12978 Vec<Counts>,
12979 Counts,
12980 Counts,
12981 Counts,
12982 Vec<(u32, Counts)>,
12983 );
12984 let (cond_counts, a1_counts, a2_counts, tail1_counts, tail2_counts, post_counts, def_counts): GuardCounts =
12985 if inlined.is_empty() {
12986 Default::default()
12987 } else {
12988 let all_defs: Vec<(u32, &Expr)> = pmap
12989 .iter()
12990 .map(|(k, v)| (*k, v))
12991 .chain(match &t_side {
12992 Side::Call { defs, .. } => defs.iter().map(|(k, v)| (*k, v)).collect(),
12993 Side::Clean => Vec::new(),
12994 })
12995 .chain(match &e_side {
12996 Side::Call { defs, .. } => defs.iter().map(|(k, v)| (*k, v)).collect(),
12997 Side::Clean => Vec::new(),
12998 })
12999 .collect();
13000 (
13001 count_locals_expr(&cond),
13002 primary_args.iter().map(count_locals_expr).collect(),
13003 else_args.iter().map(count_locals_expr).collect(),
13004 count_locals_stmts(&tail_then_v),
13005 count_locals_stmts(&tail_else_v),
13006 count_locals_stmts(post),
13007 all_defs.into_iter().map(|(v, e)| (v, count_locals_expr(e))).collect(),
13008 )
13009 };
13010 for &v in &inlined {
13011 let in_t = matches!(&t_side, Side::Call { defs, .. } if defs.iter().any(|(d, _)| *d == v));
13012 let in_e = matches!(&e_side, Side::Call { defs, .. } if defs.iter().any(|(d, _)| *d == v));
13013 let effectful = [
13014 in_t.then(|| tmap.get(&v)).flatten(),
13015 in_e.then(|| emap.get(&v)).flatten(),
13016 pmap.get(&v),
13017 ]
13018 .into_iter()
13019 .flatten()
13020 .any(jdc_core::ir::build::has_side_effects);
13021 if !effectful {
13022 continue;
13023 }
13024 let u_cond = cnt(&cond_counts, v);
13025 let u_post = cnt(&post_counts, v);
13026 let u_t1 = cnt(&tail1_counts, v);
13027 let u_t2 = cnt(&tail2_counts, v);
13028 let mut u_odef = 0usize;
13029 for (dv, dc) in &def_counts {
13030 if *dv != v && inlined.contains(dv) {
13031 u_odef += cnt(dc, v);
13032 }
13033 }
13034 let mut emit = u_cond * (1 + differ);
13035 let mut uses_args = 0usize;
13036 for j in 0..primary_args.len() {
13037 let c1 = cnt(&a1_counts[j], v);
13038 let c2 = if two_sided && j < a2_counts.len() {
13039 cnt(&a2_counts[j], v)
13040 } else {
13041 0
13042 };
13043 uses_args += c1 + c2;
13044 emit += if two_sided && then_args[j] != else_args[j] {
13045 c1 + c2
13046 } else {
13047 c1.max(c2)
13048 };
13049 }
13050 let ok = if in_t || in_e {
13051 u_cond == 0
13052 && u_post == 0
13053 && u_t1 == 0
13054 && u_t2 == 0
13055 && u_odef == 0
13056 && a1_counts.iter().map(|c| cnt(c, v)).sum::<usize>() <= 1
13057 && a2_counts.iter().map(|c| cnt(c, v)).sum::<usize>() <= 1
13058 && emit <= 2
13059 && (in_t || a1_counts.iter().all(|c| cnt(c, v) == 0))
13060 && (in_e || a2_counts.iter().all(|c| cnt(c, v) == 0))
13061 } else {
13062 u_post == 0
13063 && u_t1 == 0
13064 && u_t2 == 0
13065 && u_odef == 0
13066 && ((u_cond == 1 && uses_args == 0 && emit <= 2)
13067 || (u_cond == 0 && emit <= 1))
13068 };
13069 if !ok {
13070 return false;
13071 }
13072 }
13073
13074 let mut call_stmt = call_list[call_at].clone();
13076 if let Stmt::ExprStmt(Expr::Method { args: slot, .. }) = &mut call_stmt {
13077 *slot = merged;
13078 }
13079 let residual = |v: u32| -> usize {
13080 cnt(&tail1_counts, v) + cnt(&tail2_counts, v) + cnt(&post_counts, v)
13081 };
13082 let mut kept_prelude: Vec<Stmt> = Vec::new();
13083 for (i, s) in stmts[..if_pos].iter().enumerate() {
13084 let consumed = match s {
13085 Stmt::LocalDef { var, .. } => Some(*var),
13086 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
13087 Expr::Local { var, .. } => Some(*var),
13088 _ => None,
13089 },
13090 Stmt::If { .. } => cond_def_of(s, stmts).map(|(v, _)| v),
13091 _ => None,
13092 };
13093 if let Some(v) = consumed {
13094 if inlined.contains(&v) && pidx.get(&v) == Some(&i) && residual(v) == 0 {
13095 continue;
13096 }
13097 }
13098 kept_prelude.push(s.clone());
13099 }
13100 let build_keep = |list: &[&Stmt], side: &Side| -> Vec<Stmt> {
13101 match side {
13102 Side::Clean => list.iter().map(|s| (*s).clone()).collect(),
13103 Side::Call { at, .. } => {
13104 let mut out = Vec::new();
13105 for (i, s) in list.iter().enumerate() {
13106 if i == *at {
13107 continue;
13108 }
13109 if i < *at {
13110 if let Some((v, _)) = def_of(s) {
13111 if inlined.contains(&v) && residual(v) == 0 {
13112 continue;
13113 }
13114 }
13115 }
13116 out.push((*s).clone());
13117 }
13118 out
13119 }
13120 }
13121 };
13122 let then_keep = build_keep(&then_list, &t_side);
13123 let else_keep = build_keep(&else_list, &e_side);
13124 let else_part = if else_keep.is_empty() && !had_else {
13125 None
13126 } else {
13127 Some(Box::new(Stmt::Block(else_keep)))
13128 };
13129 let new_if = Stmt::If {
13130 cond: cond_i,
13131 then_stmt: Box::new(Stmt::Block(then_keep)),
13132 else_stmt: else_part,
13133 };
13134 let if_empty = match &new_if {
13135 Stmt::If { then_stmt, else_stmt, .. } => {
13136 let be = |s: &Stmt| matches!(s, Stmt::Block(v) if v.is_empty());
13137 be(then_stmt) && else_stmt.as_ref().is_none_or(|e| be(e))
13138 }
13139 _ => false,
13140 };
13141 let mut out: Vec<Stmt> = Vec::with_capacity(stmts.len());
13142 out.push(call_stmt);
13143 out.extend(kept_prelude);
13144 if !if_empty {
13145 out.push(new_if);
13146 }
13147 out.extend(stmts[if_pos + 1..].to_vec());
13148 *stmts = out;
13149 true
13150}
13151
13152
13153pub fn strip_enum_ctor_super(body: &mut Stmt) {
13154 fn is_enum_super(s: &Stmt) -> bool {
13155 matches!(
13156 s,
13157 Stmt::ExprStmt(Expr::Method { name, is_super: true, .. }) if &**name == "<init>"
13158 )
13159 }
13160 fn strip_deep(stmts: &mut Vec<Stmt>) {
13170 stmts.retain(|s| !is_enum_super(s));
13171 for st in stmts.iter_mut() {
13172 strip_one(st);
13173 }
13174 }
13175 fn strip_one(st: &mut Stmt) {
13176 match st {
13177 Stmt::Block(v) => strip_deep(v),
13178 Stmt::If { then_stmt, else_stmt, .. } => {
13179 strip_one(then_stmt);
13180 if let Some(e) = else_stmt {
13181 strip_one(e);
13182 }
13183 }
13184 Stmt::While { body, .. }
13185 | Stmt::DoWhile { body, .. }
13186 | Stmt::ForEach { body, .. }
13187 | Stmt::Synchronized { body, .. }
13188 | Stmt::Labeled { body, .. } => strip_one(body),
13189 Stmt::For { init, body, .. } => {
13190 strip_deep(init);
13191 strip_one(body);
13192 }
13193 Stmt::Switch { cases, default, .. } => {
13194 for c in cases.iter_mut() {
13195 strip_deep(&mut c.body);
13196 }
13197 if let Some(d) = default {
13198 strip_one(d);
13199 }
13200 }
13201 Stmt::Try { body, catches, finally }
13202 | Stmt::TryWithResources { body, catches, finally, .. } => {
13203 strip_one(body);
13204 for c in catches.iter_mut() {
13205 strip_one(&mut c.body);
13206 }
13207 if let Some(f) = finally {
13208 strip_one(f);
13209 }
13210 }
13211 _ => {}
13212 }
13213 }
13214 let Stmt::Block(stmts) = body else {
13215 return;
13216 };
13217 strip_deep(stmts);
13218}
13219
13220pub fn ensure_declared(body: &mut Stmt, vt: &VarTable) {
13221 let n = vt.vars.len().max(1);
13225
13226 let mut seen: HashSet<u32> = HashSet::default();
13229 demote_dup_decls(body, &mut seen, vt);
13230
13231 let mut def_block: Vec<u32> = vec![u32::MAX; n];
13239 let mut sizes: Vec<u32> = vec![0];
13240 let mut next_id: u32 = 1;
13241 scope_pass_a(body, 0, &mut next_id, &mut def_block, &mut sizes);
13242 sizes[0] = next_id;
13243 let mut within: Vec<u32> = vec![0; n];
13244 let mut total: Vec<u32> = vec![0; n];
13245 let mut next_b: u32 = 1;
13246 scope_pass_b(body, 0, &mut next_b, &def_block, &sizes, &mut within, &mut total);
13247 let mut hoist: HashSet<u32> = HashSet::default();
13248 for v in 0..n {
13249 if def_block[v] != u32::MAX && def_block[v] != 0 && within[v] < total[v] {
13250 hoist.insert(v as u32);
13251 }
13252 }
13253 if !hoist.is_empty() {
13254 let mut seed: HashSet<u32> = hoist.iter().copied().collect();
13255 demote_dup_decls(body, &mut seed, vt);
13256 }
13257
13258 let mut declared: Vec<bool> = vec![false; n];
13259 let mut is_param: Vec<bool> = vec![false; n];
13260 for v in &vt.vars {
13261 if v.is_param && (v.id as usize) < n {
13262 is_param[v.id as usize] = true;
13263 }
13264 }
13265 let mut is_catch: Vec<bool> = vec![false; n];
13271 walk_all(body, &mut |st| {
13272 if let Stmt::Try { catches, .. } = st {
13273 for c in catches {
13274 if (c.var as usize) < n {
13275 is_catch[c.var as usize] = true;
13276 }
13277 }
13278 }
13279 });
13280 walk_all(body, &mut |st| {
13281 if let Stmt::LocalDef { var, .. } = st {
13282 if (*var as usize) < n {
13283 declared[*var as usize] = true;
13284 }
13285 }
13286 });
13287 let mut used: HashSet<u32> = HashSet::default();
13288 stmt_collect_vars(body, &mut used, true);
13292
13293 let mut needs_decl: Vec<u32> = used
13296 .into_iter()
13297 .filter(|v| {
13298 let i = *v as usize;
13299 i >= n
13300 || ((!declared[i] && !is_param[i] && !is_catch[i]) || hoist.contains(v))
13301 })
13302 .collect();
13303 needs_decl.sort_unstable();
13304 needs_decl.dedup();
13305
13306 if !needs_decl.is_empty() {
13307 let mut decls: Vec<Stmt> = needs_decl
13308 .into_iter()
13309 .map(|v| Stmt::LocalDef {
13310 var: v,
13311 init: None,
13312 is_final: false,
13313 force_type: true,
13314 })
13315 .collect();
13316 decls.reverse();
13317 match body {
13318 Stmt::Block(v) => {
13319 for d in decls {
13320 v.insert(0, d);
13321 }
13322 }
13323 other => {
13324 let inner = std::mem::replace(other, Stmt::Block(vec![]));
13325 *other = Stmt::Block({
13326 let mut vs = decls;
13327 vs.push(inner);
13328 vs
13329 });
13330 }
13331 }
13332 }
13333}
13334
13335fn demote_dup_decls(s: &mut Stmt, seen: &mut HashSet<u32>, vt: &VarTable) {
13336 match s {
13337 Stmt::Block(v) => {
13340 for x in v.iter_mut() {
13341 demote_dup_decls(x, seen, vt);
13342 }
13343 }
13344 Stmt::LocalDef { var, init, .. } => {
13345 if seen.contains(var) {
13346 let ty = vt.var(*var).ty.clone();
13347 if let Some(e) = init.take() {
13348 *s = Stmt::ExprStmt(Expr::Assign {
13349 target: Box::new(Expr::Local { var: *var, ty }),
13350 op: AssignOp::Plain,
13351 value: Box::new(e),
13352 });
13353 } else {
13354 *s = Stmt::Block(vec![]);
13355 }
13356 } else {
13357 seen.insert(*var);
13358 }
13359 }
13360 other => {
13361 walk_mut(other, &mut |x| demote_dup_decls(x, seen, vt));
13362 }
13363 }
13364}
13365
13366#[allow(dead_code)]
13367fn unused(_: &Vec<CaseGroup>, _: &Catch) {}
13368
13369pub fn apply_local_names(vt: &mut VarTable, body: &Stmt) {
13383 use std::collections::{HashMap, HashSet};
13384
13385 let mut by_call: HashMap<u32, Vec<String>> = HashMap::new();
13388 let mut by_intrinsics: HashMap<u32, String> = HashMap::new();
13389
13390 visit_all_exprs(body, &mut |e| {
13391 if let Expr::Method { cls, name, args, .. } = e {
13392 if (cls.as_ref() == "kotlin/jvm/internal/Intrinsics"
13393 || cls.as_ref() == "kotlin/jvm/internal/IntrinsicsKt")
13394 && matches!(name.as_ref(), "checkNotNullParameter" | "checkParameterIsNotNull")
13395 && args.len() == 2
13396 {
13397 if let (Expr::Local { var, .. }, Expr::Const(ConstVal::Str(s))) = (&args[0], &args[1])
13398 {
13399 by_intrinsics
13400 .entry(*var)
13401 .or_insert_with(|| sanitize_name(s).unwrap_or_default());
13402 }
13403 }
13404 }
13405 });
13406
13407 walk_all(body, &mut |st| {
13408 let (var, value) = match st {
13409 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e),
13410 Stmt::ExprStmt(Expr::Assign { target, value, .. })
13411 if matches!(&**target, Expr::Local { .. }) =>
13412 {
13413 let Expr::Local { var, .. } = &**target else { return };
13414 (*var, value.as_ref())
13415 }
13416 _ => return,
13417 };
13418 if let Some(n) = defining_call_name(value) {
13419 by_call.entry(var).or_default().push(n);
13420 }
13421 });
13422
13423 let sanit = |n: &str| crate::classdec::java_ident(n).into_owned();
13434 let mut taken: HashSet<String> = HashSet::default();
13435 for v in vt.vars.iter() {
13440 if v.synthetic_name {
13445 taken.insert(sanit(&v.name));
13446 }
13447 }
13448 for v in vt.vars.iter_mut() {
13449 if v.synthetic_name {
13450 continue;
13451 }
13452 if !taken.insert(sanit(&v.name)) {
13453 let base = sanit(&v.name);
13454 for i in 2.. {
13455 let cand = format!("{base}{i}");
13456 if taken.insert(cand.clone()) {
13457 v.name = cand;
13458 break;
13459 }
13460 }
13461 }
13462 }
13463 let claim = |taken: &mut HashSet<String>, want: &str| -> String {
13464 let w = crate::classdec::java_ident(want).into_owned();
13465 if taken.insert(w.clone()) {
13466 return w;
13467 }
13468 for i in 2.. {
13469 let cand = format!("{w}{i}");
13470 if taken.insert(cand.clone()) {
13471 return cand;
13472 }
13473 }
13474 w
13475 };
13476
13477 for info in vt.vars.iter_mut() {
13478 if !info.synthetic_name {
13479 continue;
13480 }
13481 if let Some(n) = by_intrinsics.get(&info.id) {
13484 if !n.is_empty() {
13485 info.name = claim(&mut taken, n);
13486 continue;
13487 }
13488 }
13489 if let Some(names) = by_call.get(&info.id) {
13491 let unique: HashSet<&String> = names.iter().collect();
13492 if unique.len() == 1 {
13493 let n = unique.into_iter().next().unwrap();
13494 if !is_java_keyword(n) {
13495 info.name = claim(&mut taken, n);
13496 continue;
13497 }
13498 }
13499 }
13500 let want = type_alias(&info.ty).map(str::to_string).or_else(|| simple_type_name(&info.ty));
13503 if let Some(n) = want {
13504 info.name = claim(&mut taken, &n);
13505 }
13506 }
13507
13508 let mut seen: HashSet<String> = HashSet::default();
13513 for v in vt.vars.iter_mut() {
13514 if !seen.insert(sanit(&v.name)) {
13515 let base = sanit(&v.name);
13516 for i in 2.. {
13517 let cand = format!("{base}{i}");
13518 if seen.insert(cand.clone()) {
13519 v.name = cand;
13520 break;
13521 }
13522 }
13523 }
13524 }
13525}
13526
13527fn simple_type_name(ty: &TypeRef) -> Option<String> {
13529 let TypeRef::J(JavaType::Object(n)) = ty else {
13530 return None;
13531 };
13532 let simple = n.rsplit('/').next().unwrap_or(n);
13533 let simple = simple.rsplit('$').next().unwrap_or(simple);
13534 let mut chars = simple.chars();
13537 let mut out = String::with_capacity(simple.len());
13538 let mut lowered = false;
13539 for c in chars.by_ref() {
13540 if c.is_ascii_alphabetic() && !lowered {
13541 out.push(c.to_ascii_lowercase());
13542 lowered = true;
13543 } else {
13544 out.push(c);
13545 }
13546 if lowered {
13547 break;
13548 }
13549 }
13550 out.extend(chars);
13551 sanitize_name(&out)
13552}
13553
13554fn defining_call_name(e: &Expr) -> Option<String> {
13558 match e {
13559 Expr::Method { name, .. } => {
13560 let base = name
13561 .strip_prefix("get")
13562 .or_else(|| name.strip_prefix("is"))
13563 .filter(|rest| rest.chars().next().is_some_and(|c| c.is_ascii_uppercase()))
13564 .unwrap_or(name);
13565 let first = base.chars().next()?;
13566 let mut out = String::with_capacity(base.len());
13567 out.push(first.to_ascii_lowercase());
13568 out.extend(base.chars().skip(1));
13569 sanitize_name(&out)
13570 }
13571 Expr::New { cls, .. } => {
13572 let simple = cls.rsplit('/').next().unwrap_or(cls);
13573 let simple = simple.rsplit('$').next().unwrap_or(simple);
13574 let first = simple.chars().next()?;
13575 let mut out = String::with_capacity(simple.len());
13576 out.push(first.to_ascii_lowercase());
13577 out.extend(simple.chars().skip(1));
13578 sanitize_name(&out)
13579 }
13580 _ => None,
13581 }
13582}
13583
13584fn sanitize_name(s: &str) -> Option<String> {
13586 let mut out = String::with_capacity(s.len());
13587 for c in s.chars() {
13588 if c.is_ascii_alphanumeric() || c == '_' || c == '$' {
13589 out.push(c);
13590 } else {
13591 out.push('_');
13592 }
13593 }
13594 if out.len() < 2
13595 || out.chars().next().is_some_and(|c| c.is_ascii_digit())
13596 || is_java_keyword(&out)
13597 {
13598 return None;
13599 }
13600 Some(out)
13601}
13602
13603fn type_alias(ty: &TypeRef) -> Option<&'static str> {
13606 let TypeRef::J(JavaType::Object(n)) = ty else {
13607 return None;
13608 };
13609 Some(match n.as_ref() {
13610 "java/lang/String" | "kotlin/String" => "str",
13611 "java/lang/Class" => "cls",
13612 "java/lang/Throwable" => "th",
13613 "java/lang/Object" | "kotlin/Any" => "obj",
13614 "java/util/Iterator" | "kotlin/collections/Iterator" => "it",
13615 "java/lang/Boolean" => "bool",
13616 "java/lang/Integer" => "num",
13617 "java/lang/Character" => "ch",
13618 "java/lang/Byte" => "b",
13619 "java/lang/Short" => "sh",
13620 "java/lang/Float" => "f",
13621 "java/lang/Double" => "d",
13622 "java/lang/Long" => "i",
13623 "java/lang/StringBuilder" => "sb",
13624 "java/util/ArrayList" => "list",
13625 "java/util/HashMap" => "map",
13626 "android/content/Context" => "context",
13627 "android/content/Intent" => "intent",
13628 "android/os/Bundle" => "bundle",
13629 "android/view/View" => "view",
13630 "android/graphics/Bitmap" => "bitmap",
13631 "android/view/ViewGroup" => "viewGroup",
13632 _ => return None,
13633 })
13634}
13635
13636pub(crate) fn visit_all_exprs<F: FnMut(&Expr)>(s: &Stmt, f: &mut F) {
13638 walk_all(s, &mut |st| {
13639 let exprs: Vec<&Expr> = match st {
13640 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
13641 let mut v: Vec<&Expr> = vec![value];
13642 if !matches!(&**target, Expr::Local { .. }) {
13643 v.push(target);
13644 }
13645 v
13646 }
13647 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
13648 vec![e]
13649 }
13650 Stmt::Return(Some(e)) => vec![e],
13651 Stmt::LocalDef { init: Some(e), .. } => vec![e],
13652 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
13653 vec![cond]
13654 }
13655 _ => Vec::new(),
13656 };
13657 for e in exprs {
13658 visit_exprs(e, f);
13659 }
13660 });
13661}
13662
13663pub fn platform_constants(body: &mut Stmt) {
13668 if !crate::platform::installed() {
13669 return;
13670 }
13671 rewrite_exprs(body, &mut |e| {
13672 if let Expr::Method { cls, name, desc, args, .. } = e {
13673 for a in args.iter_mut() {
13674 let (value, is_long) = match a {
13675 Expr::Const(ConstVal::Int(v)) => (*v as i64, false),
13676 Expr::Const(ConstVal::Long(v)) => (*v, true),
13677 _ => continue,
13678 };
13679 if let Some(named) =
13680 crate::platform::named_constant(cls, name, &desc_to_string(desc), 0, value)
13681 {
13682 let _ = is_long;
13683 *a = Expr::Raw(named);
13684 }
13685 }
13686 }
13687 });
13688}
13689
13690fn desc_to_string(d: &jdc_core::types::MethodDescriptor) -> String {
13692 let mut out = String::from("(");
13693 for a in &d.args {
13694 type_to_desc(a, &mut out);
13695 }
13696 out.push(')');
13697 type_to_desc(&d.ret, &mut out);
13698 out
13699}
13700
13701fn type_to_desc(t: &jdc_core::types::JavaType, out: &mut String) {
13702 match t {
13703 JavaType::Void => out.push('V'),
13704 JavaType::Boolean => out.push('Z'),
13705 JavaType::Byte => out.push('B'),
13706 JavaType::Char => out.push('C'),
13707 JavaType::Short => out.push('S'),
13708 JavaType::Int => out.push('I'),
13709 JavaType::Float => out.push('F'),
13710 JavaType::Long => out.push('J'),
13711 JavaType::Double => out.push('D'),
13712 JavaType::Object(n) => {
13713 out.push('L');
13714 out.push_str(n);
13715 out.push(';');
13716 }
13717 JavaType::Array(inner) => {
13718 out.push('[');
13719 type_to_desc(inner, out);
13720 }
13721 }
13722}
13723
13724pub fn remove_kotlin_checks(s: &mut Stmt) {
13730 walk_mut_deep(s, &mut |st| {
13731 let Stmt::Block(v) = st else { return };
13732 v.retain(|x| !is_kotlin_check(x));
13733 });
13734}
13735
13736fn is_kotlin_check(s: &Stmt) -> bool {
13737 let Stmt::ExprStmt(e) = s else { return false };
13738 let Expr::Method { cls, name, .. } = e else { return false };
13739 (cls.as_ref() == "kotlin/jvm/internal/Intrinsics" || cls.as_ref() == "kotlin/jvm/internal/IntrinsicsKt__Jdk7Kt")
13740 && matches!(
13741 name.as_ref(),
13742 "checkNotNullParameter"
13743 | "checkParameterIsNotNull"
13744 | "checkExpressionValueIsNotNull"
13745 | "checkNotNullExpressionValue"
13746 | "checkReturnedValueIsNotNull"
13747 | "checkFieldIsNotNull"
13748 | "checkNotNull"
13749 )
13750}
13751
13752fn inline_ref_ok(pool: &DexPool, host: &str, target: &str, member: Option<(&str, bool)>) -> bool {
13772 let hpkg = match host.rfind('/') {
13773 Some(i) => &host[..i],
13774 None => "",
13775 };
13776 let mut cur = target;
13777 loop {
13778 let tpkg = match cur.rfind('/') {
13779 Some(i) => &cur[..i],
13780 None => "",
13781 };
13782 if tpkg != hpkg {
13783 if let Some(pc) = pool.get(cur) {
13784 if pc.access & crate::access::ACC_PUBLIC == 0 {
13785 return false;
13786 }
13787 if cur == target {
13788 if let Some((mname, is_method)) = member {
13789 let ok = if is_method {
13790 pc.all_methods().any(|m| {
13791 &*m.name == mname
13792 && m.access & crate::access::ACC_PUBLIC != 0
13793 })
13794 } else {
13795 pc.static_fields
13796 .iter()
13797 .chain(pc.instance_fields.iter())
13798 .any(|f| {
13799 f.name.as_ref() == mname
13800 && f.access & crate::access::ACC_PUBLIC != 0
13801 })
13802 };
13803 if !ok
13808 && (if is_method {
13809 pc.all_methods().any(|m| &*m.name == mname)
13810 } else {
13811 pc.static_fields
13812 .iter()
13813 .chain(pc.instance_fields.iter())
13814 .any(|f| f.name.as_ref() == mname)
13815 })
13816 {
13817 return false;
13818 }
13819 }
13820 }
13821 }
13822 }
13823 match cur.rfind('$') {
13824 Some(i) if i > 0 => cur = &cur[..i],
13825 _ => break,
13826 }
13827 }
13828 true
13829}
13830
13831pub fn inline_accessors(s: &mut Stmt, pool: &DexPool, host: &str) {
13832 rewrite_exprs(s, &mut |e| {
13833 let Expr::Method { cls, name, desc, args, is_static, .. } = e else { return };
13834 if !*is_static || args.is_empty() {
13835 return;
13836 }
13837 let Some(target) = pool.get(cls) else { return };
13838 let desc_s = desc_to_string(desc);
13839 let Some(m) = target.find_method(name, &desc_s) else { return };
13840 if !(m.is_static() && m.access & access::ACC_SYNTHETIC != 0) {
13841 return;
13842 }
13843 let Some(dex) = pool.dex(m.dex_idx) else { return };
13844 let code = match pool.accessor_code(m.dex_idx, m.code_off) {
13849 Some(bytes) => ddc_dex::CodeItem::parse(&bytes, 0),
13850 None => dex.code_at(m.code_off),
13851 };
13852 let Some(code) = code else { return };
13853 let insns: Vec<&Insn> =
13854 code.insns.iter().filter(|i| !matches!(i.kind, InsnKind::Nop)).collect();
13855 let core: Vec<&Insn> = insns
13857 .iter()
13858 .copied()
13859 .filter(|i| !is_const_only_trace(&i.kind, &insns))
13860 .collect();
13861 match accessor_shape(&dex, &core, &code) {
13862 Some(Shape::Identity(arg_i)) => {
13863 if let Some(a) = args.get(arg_i) {
13864 *e = a.clone();
13865 }
13866 }
13867 Some(Shape::FieldRead { arg, cls: field_cls, field, field_ty }) => {
13868 if !inline_ref_ok(pool, host, &field_cls, Some((&field, false))) {
13869 return;
13870 }
13871 if let Some(owner) = args.get(arg).cloned().map(Box::new) {
13872 let ty = TypeRef::J(desc_type(&field_ty));
13873 let name = jdc_core::rename::field_display(
13879 &field_cls,
13880 &field,
13881 &field_ty,
13882 )
13883 .map(std::sync::Arc::from)
13884 .unwrap_or_else(|| field.into());
13885 *e = Expr::Field {
13886 owner: Some(owner),
13887 cls: field_cls.into(),
13888 name,
13889 ty,
13890 is_static: false,
13891 };
13892 }
13893 }
13894 Some(Shape::Forward { cls: tcls, name: tname, desc: tdesc, instance }) => {
13895 if !inline_ref_ok(pool, host, &tcls, Some((&tname, true))) {
13896 return;
13897 }
13898 let disp = jdc_core::rename::field_display(
13902 &tcls,
13903 &tname,
13904 &tdesc.to_string(),
13905 )
13906 .map(std::sync::Arc::from)
13907 .unwrap_or_else(|| tname.into());
13908 *cls = tcls.into();
13909 *name = disp;
13910 *desc = std::sync::Arc::new(tdesc);
13911 if instance && !args.is_empty() {
13912 let recv = args.remove(0);
13914 if let Expr::Method { owner, .. } = e {
13915 *owner = Some(Box::new(recv));
13916 }
13917 }
13918 }
13919 None => {}
13920 }
13921 });
13922}
13923
13924enum Shape {
13926 Identity(usize),
13928 FieldRead { arg: usize, cls: String, field: String, field_ty: String },
13930 Forward { cls: String, name: String, desc: MethodDescriptor, instance: bool },
13933}
13934
13935fn field_of(dex: &ddc_dex::DexFile, field_idx: u32) -> Option<(String, String, String)> {
13937 let f = dex.field(field_idx);
13938 Some((
13939 dex.class_name(f.class_idx),
13942 dex.string(f.name_idx).to_string(),
13943 dex.type_name(f.type_idx).to_string(),
13945 ))
13946}
13947
13948fn param_index(code: &ddc_dex::CodeItem, reg: u16) -> Option<usize> {
13951 let rs = code.registers_size as usize;
13952 let ins = code.ins_size as usize;
13953 let r = reg as usize;
13954 let first = rs.checked_sub(ins)?;
13955 (r >= first && r < rs).then(|| r - first)
13956}
13957
13958fn accessor_shape(
13959 dex: &ddc_dex::DexFile,
13960 core: &[&Insn],
13961 code: &ddc_dex::CodeItem,
13962) -> Option<Shape> {
13963 let reads = |rest: &[&Insn], r: u16| {
13973 rest.iter()
13974 .any(|i| crate::lift::src_regs(&i.kind).contains(&r))
13975 };
13976 let core: &[&Insn] = match core.split_first() {
13977 Some((first, rest))
13978 if matches!(first.kind, InsnKind::Const { dst, .. } if !reads(rest, dst)) =>
13979 {
13980 rest
13981 }
13982 _ => core,
13983 };
13984 let core: &[&Insn] = match core.split_last() {
13985 Some((last, rest))
13986 if matches!(last.kind, InsnKind::Const { dst, .. } if !reads(rest, dst)) =>
13987 {
13988 rest
13989 }
13990 _ => core,
13991 };
13992 match core {
13993 [Insn { kind: InsnKind::Return { src }, .. }] => {
13995 let idx = param_index(code, *src)?;
13996 Some(Shape::Identity(idx))
13997 }
13998 [
14000 Insn { kind: InsnKind::IGet { dst, obj, field_idx }, .. },
14001 Insn { kind: InsnKind::Return { src }, .. },
14002 ] if dst == src => {
14003 let arg = param_index(code, *obj)?;
14004 let (cls, field, field_ty) = field_of(dex, *field_idx)?;
14005 Some(Shape::FieldRead { arg, cls, field, field_ty })
14006 }
14007 [Insn { kind: InsnKind::Invoke { method_idx, regs, kind, .. }, .. }, rest @ ..] if rest.len() <= 2 => {
14009 if !regs.iter().all(|r| param_index(code, *r).is_some()) || regs.is_empty() {
14011 return None;
14012 }
14013 if rest.len() == 2 {
14016 let ok = matches!(rest[0].kind, InsnKind::MoveResult { .. })
14017 && matches!(rest[1].kind, InsnKind::Return { .. } | InsnKind::ReturnVoid);
14018 if !ok {
14019 return None;
14020 }
14021 }
14022 let mid = dex.method(*method_idx);
14023 let cls = dex.class_name(mid.class_idx);
14026 let name = dex.string(mid.name_idx).to_string();
14027 let mut s = String::from("(");
14028 for t in dex.proto_params(mid.proto_idx) {
14029 s.push_str(dex.type_name(*t));
14030 }
14031 s.push(')');
14032 s.push_str(dex.type_name(dex.proto(mid.proto_idx).return_type_idx));
14033 let desc = parse_method_descriptor(&s)?;
14034 Some(Shape::Forward { cls, name, desc, instance: !matches!(kind, InvokeKind::Static) })
14035 }
14036 _ => None,
14037 }
14038}
14039
14040
14041fn is_const_only_trace(kind: &InsnKind, insns: &[&Insn]) -> bool {
14047 let InsnKind::Invoke { kind, regs, .. } = kind else { return false };
14048 if !matches!(kind, InvokeKind::Static) || regs.is_empty() {
14049 return false;
14050 }
14051 regs.iter().all(|r| const_loaded(*r, insns, 0))
14052}
14053
14054fn const_loaded(r: u16, insns: &[&Insn], depth: u8) -> bool {
14057 if depth > 3 {
14058 return false;
14059 }
14060 insns.iter().any(|i| match &i.kind {
14061 InsnKind::Const { dst, .. } => *dst == r,
14062 InsnKind::Move { dst, src } => *dst == r && const_loaded(*src, insns, depth + 1),
14063 _ => false,
14064 })
14065}