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
163fn visit_exprs<F: FnMut(&Expr)>(e: &Expr, f: &mut F) {
165 f(e);
166 for_each_child(e, &mut |c| visit_exprs(c, f));
167}
168
169fn for_each_child<F: FnMut(&Expr)>(e: &Expr, f: &mut F) {
170 match e {
171 Expr::Un { e, .. } | Expr::Cast { e, .. } | Expr::InstanceOf { e, .. } => f(e),
172 Expr::Bin { l, r, .. } => {
173 f(l);
174 f(r);
175 }
176 Expr::Cond { c, t, f: fe } => {
177 f(c);
178 f(t);
179 f(fe);
180 }
181 Expr::Assign { target, value, .. } => {
182 f(target);
183 f(value);
184 }
185 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => f(e),
186 Expr::Field { owner, .. } => {
187 if let Some(o) = owner {
188 f(o);
189 }
190 }
191 Expr::Method { owner, args, .. } => {
192 if let Some(o) = owner {
193 f(o);
194 }
195 for a in args {
196 f(a);
197 }
198 }
199 Expr::ArrayIndex { array, index } => {
200 f(array);
201 f(index);
202 }
203 Expr::New { args, .. } => {
204 for a in args {
205 f(a);
206 }
207 }
208 Expr::NewArray { dims, init, .. } => {
209 for d in dims {
210 f(d);
211 }
212 if let Some(v) = init {
213 for x in v {
214 f(x);
215 }
216 }
217 }
218 Expr::NewMultiArray { dims, .. } => {
219 for d in dims {
220 f(d);
221 }
222 }
223 Expr::StringConcat(parts) => {
224 for p in parts {
225 if let ConcatPart::Str(x) = p {
226 f(x);
227 }
228 }
229 }
230 Expr::Invokedynamic { args, .. } => {
231 for a in args {
232 f(a);
233 }
234 }
235 Expr::AnonNew { args, .. } => {
236 for a in args {
237 f(a);
238 }
239 }
240 _ => {}
241 }
242}
243
244fn for_each_child_mut<F: FnMut(&mut Expr)>(e: &mut Expr, f: &mut F) {
246 match e {
247 Expr::Un { e, .. } | Expr::Cast { e, .. } | Expr::InstanceOf { e, .. } => f(e),
248 Expr::Bin { l, r, .. } => {
249 f(l);
250 f(r);
251 }
252 Expr::Cond { c, t, f: fe } => {
253 f(c);
254 f(t);
255 f(fe);
256 }
257 Expr::Assign { target, value, .. } => {
258 f(target);
259 f(value);
260 }
261 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => f(e),
262 Expr::Field { owner, .. } => {
263 if let Some(o) = owner {
264 f(o);
265 }
266 }
267 Expr::Method { owner, args, .. } => {
268 if let Some(o) = owner {
269 f(o);
270 }
271 for a in args.iter_mut() {
272 f(a);
273 }
274 }
275 Expr::ArrayIndex { array, index } => {
276 f(array);
277 f(index);
278 }
279 Expr::New { args, .. } => {
280 for a in args.iter_mut() {
281 f(a);
282 }
283 }
284 Expr::NewArray { dims, init, .. } => {
285 for d in dims.iter_mut() {
286 f(d);
287 }
288 if let Some(v) = init {
289 for x in v.iter_mut() {
290 f(x);
291 }
292 }
293 }
294 Expr::NewMultiArray { dims, .. } => {
295 for d in dims.iter_mut() {
296 f(d);
297 }
298 }
299 Expr::StringConcat(parts) => {
300 for p in parts {
301 if let ConcatPart::Str(x) = p {
302 f(x);
303 }
304 }
305 }
306 Expr::Invokedynamic { args, .. } => {
307 for a in args.iter_mut() {
308 f(a);
309 }
310 }
311 Expr::AnonNew { args, .. } => {
312 for a in args.iter_mut() {
313 f(a);
314 }
315 }
316 _ => {}
317 }
318}
319
320pub(crate) fn deep_rewrite<F: FnMut(&mut Expr)>(e: &mut Expr, f: &mut F) {
322 f(e);
323 for_each_child_mut(e, &mut |c| deep_rewrite(c, f));
324}
325
326fn deep_rewrite_reads<F: FnMut(&mut Expr)>(e: &mut Expr, f: &mut F) {
335 f(e);
336 match e {
337 Expr::Assign { target, value, .. } => {
338 if !matches!(**target, Expr::Local { .. }) {
339 deep_rewrite_reads(target, f);
340 }
341 deep_rewrite_reads(value, f);
342 }
343 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
344 if !matches!(**inner, Expr::Local { .. }) {
345 deep_rewrite_reads(inner, f);
346 }
347 }
348 _ => for_each_child_mut(e, &mut |c| deep_rewrite_reads(c, f)),
349 }
350}
351
352fn collect_vars(e: &Expr, out: &mut HashSet<u32>) {
353 visit_exprs(e, &mut |x| {
354 if let Expr::Local { var, .. } = x {
355 out.insert(*var);
356 }
357 });
358}
359
360fn stmt_collect_vars(s: &Stmt, out: &mut HashSet<u32>, assignments: bool) {
365 match s {
366 Stmt::Block(v) => {
367 for x in v {
368 stmt_collect_vars(x, out, assignments);
369 }
370 }
371 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
372 if let Expr::Local { var, .. } = &**target {
373 if assignments {
374 out.insert(*var);
375 }
376 } else {
377 collect_vars(target, out);
378 }
379 collect_vars(value, out);
380 }
381 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
382 collect_vars(e, out)
383 }
384 Stmt::Return(Some(e)) => collect_vars(e, out),
385 Stmt::LocalDef { var, init, .. } => {
386 if assignments {
387 out.insert(*var);
388 }
389 if let Some(e) = init {
390 collect_vars(e, out);
391 }
392 }
393 Stmt::If {
394 cond,
395 then_stmt,
396 else_stmt,
397 } => {
398 collect_vars(cond, out);
399 stmt_collect_vars(then_stmt, out, assignments);
400 if let Some(e) = else_stmt {
401 stmt_collect_vars(e, out, assignments);
402 }
403 }
404 Stmt::While { cond, body } => {
405 collect_vars(cond, out);
406 stmt_collect_vars(body, out, assignments);
407 }
408 Stmt::DoWhile { body, cond } => {
409 stmt_collect_vars(body, out, assignments);
410 collect_vars(cond, out);
411 }
412 Stmt::For {
413 init,
414 cond,
415 update,
416 body,
417 } => {
418 for x in init {
419 stmt_collect_vars(x, out, assignments);
420 }
421 if let Some(c) = cond {
422 collect_vars(c, out);
423 }
424 for u in update {
425 collect_vars(u, out);
426 }
427 stmt_collect_vars(body, out, assignments);
428 }
429 Stmt::ForEach {
430 var,
431 iterable,
432 body,
433 ..
434 } => {
435 if assignments {
436 out.insert(*var);
437 }
438 collect_vars(iterable, out);
439 stmt_collect_vars(body, out, assignments);
440 }
441 Stmt::Switch {
442 selector,
443 cases,
444 default,
445 ..
446 } => {
447 collect_vars(selector, out);
448 for c in cases {
449 for x in &c.body {
450 stmt_collect_vars(x, out, assignments);
451 }
452 }
453 if let Some(d) = default {
454 stmt_collect_vars(d, out, assignments);
455 }
456 }
457 Stmt::Try {
458 body,
459 catches,
460 finally,
461 } => {
462 stmt_collect_vars(body, out, assignments);
463 for c in catches {
464 stmt_collect_vars(&c.body, out, assignments);
465 }
466 if let Some(f) = finally {
467 stmt_collect_vars(f, out, assignments);
468 }
469 }
470 Stmt::TryWithResources {
471 resources,
472 body,
473 catches,
474 finally,
475 } => {
476 for x in resources {
477 stmt_collect_vars(x, out, assignments);
478 }
479 stmt_collect_vars(body, out, assignments);
480 for c in catches {
481 stmt_collect_vars(&c.body, out, assignments);
482 }
483 if let Some(f) = finally {
484 stmt_collect_vars(f, out, assignments);
485 }
486 }
487 Stmt::Synchronized { lock, body } => {
488 collect_vars(lock, out);
489 stmt_collect_vars(body, out, assignments);
490 }
491 Stmt::Assert { cond, msg } => {
492 collect_vars(cond, out);
493 if let Some(m) = msg {
494 collect_vars(m, out);
495 }
496 }
497 Stmt::Labeled { body, .. } => stmt_collect_vars(body, out, assignments),
498 _ => {}
499 }
500}
501
502fn walk_all<F: FnMut(&Stmt)>(s: &Stmt, f: &mut F) {
503 f(s);
504 match s {
505 Stmt::Block(v) => {
506 for x in v {
507 walk_all(x, f);
508 }
509 }
510 Stmt::If {
511 then_stmt,
512 else_stmt,
513 ..
514 } => {
515 walk_all(then_stmt, f);
516 if let Some(e) = else_stmt {
517 walk_all(e, f);
518 }
519 }
520 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => walk_all(body, f),
521 Stmt::For { init, body, .. } => {
522 for x in init {
523 walk_all(x, f);
524 }
525 walk_all(body, f);
526 }
527 Stmt::ForEach { body, .. } => walk_all(body, f),
528 Stmt::Switch { cases, default, .. } => {
529 for c in cases {
530 for x in &c.body {
531 walk_all(x, f);
532 }
533 }
534 if let Some(d) = default {
535 walk_all(d, f);
536 }
537 }
538 Stmt::Try {
539 body,
540 catches,
541 finally,
542 } => {
543 walk_all(body, f);
544 for c in catches {
545 walk_all(&c.body, f);
546 }
547 if let Some(fl) = finally {
548 walk_all(fl, f);
549 }
550 }
551 Stmt::TryWithResources {
552 resources,
553 body,
554 catches,
555 finally,
556 } => {
557 for x in resources {
558 walk_all(x, f);
559 }
560 walk_all(body, f);
561 for c in catches {
562 walk_all(&c.body, f);
563 }
564 if let Some(fl) = finally {
565 walk_all(fl, f);
566 }
567 }
568 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => walk_all(body, f),
569 _ => {}
570 }
571}
572
573pub fn bind_catches(s: &mut Stmt, vt: &mut VarTable) {
580 match s {
581 Stmt::Try {
582 body,
583 catches,
584 finally,
585 } => {
586 bind_catches(body, vt);
587 for c in catches.iter_mut() {
588 if c.var == u32::MAX {
589 let stored = match c.body.as_ref() {
590 Stmt::Block(v) => match v.first() {
591 Some(Stmt::LocalDef { var, .. }) => Some(*var),
592 Some(Stmt::ExprStmt(Expr::Assign { target, .. })) => match &**target {
593 Expr::Local { var, .. } => Some(*var),
594 _ => None,
595 },
596 _ => None,
597 },
598 _ => None,
599 };
600 if let Some(v) = stored {
601 let exc = c
602 .exc
603 .first()
604 .cloned()
605 .unwrap_or_else(|| "java/lang/Throwable".into());
606 let slot = vt.var(v).slot;
607 let name = "e".to_string();
608 let new_var =
609 vt.add_catch_var(slot, name, TypeRef::J(JavaType::Object(exc)));
610 if let Stmt::Block(vs) = c.body.as_mut() {
611 vs.remove(0);
612 }
613 rewrite_local_refs(c.body.as_mut(), v, new_var);
614 c.var = new_var;
615 }
616 }
617 bind_catches(&mut c.body, vt);
618 }
619 if let Some(f) = finally {
620 bind_catches(f.as_mut(), vt);
621 }
622 }
623 Stmt::Block(v) => {
624 for x in v.iter_mut() {
625 bind_catches(x, vt);
626 }
627 }
628 Stmt::If {
629 then_stmt,
630 else_stmt,
631 ..
632 } => {
633 bind_catches(then_stmt, vt);
634 if let Some(e) = else_stmt {
635 bind_catches(e, vt);
636 }
637 }
638 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => bind_catches(body, vt),
639 Stmt::For { init, body, .. } => {
640 for x in init.iter_mut() {
641 bind_catches(x, vt);
642 }
643 bind_catches(body, vt);
644 }
645 Stmt::ForEach { body, .. }
646 | Stmt::Labeled { body, .. }
647 | Stmt::Synchronized { body, .. } => bind_catches(body, vt),
648 Stmt::Switch { cases, default, .. } => {
649 for c in cases.iter_mut() {
650 for x in c.body.iter_mut() {
651 bind_catches(x, vt);
652 }
653 }
654 if let Some(d) = default {
655 bind_catches(d, vt);
656 }
657 }
658 _ => {}
659 }
660}
661
662fn rewrite_local_refs(s: &mut Stmt, from: u32, to: u32) {
663 rewrite_exprs(s, &mut |e| {
664 deep_rewrite(e, &mut |x| {
665 if let Expr::Local { var, .. } = x {
666 if *var == from {
667 *var = to;
668 }
669 }
670 });
671 });
672}
673
674pub fn cleanup(s: &mut Stmt) {
676 jdc_core::ir::stmt::flatten(s);
677 strip_empty(s);
678 merge_singleton_stmts(s);
679}
680
681fn strip_empty(s: &mut Stmt) {
682 match s {
683 Stmt::Block(v) => {
684 v.retain(|x| !x.is_empty_block());
685 for x in v.iter_mut() {
686 strip_empty(x);
687 }
688 }
689 Stmt::If {
690 then_stmt,
691 else_stmt,
692 ..
693 } => {
694 strip_empty(then_stmt);
695 if let Some(e) = else_stmt {
696 strip_empty(e);
697 if e.is_empty_block() {
698 *else_stmt = None;
699 }
700 }
701 }
702 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => strip_empty(body),
703 Stmt::For { init, body, .. } => {
704 init.retain(|x| !x.is_empty_block());
705 strip_empty(body);
706 }
707 Stmt::ForEach { body, .. } => strip_empty(body),
708 Stmt::Switch { cases, default, .. } => {
709 for c in cases {
710 c.body.retain(|x| !x.is_empty_block());
711 }
712 if let Some(d) = default {
713 strip_empty(d);
714 }
715 }
716 Stmt::Try {
717 body,
718 catches,
719 finally,
720 } => {
721 strip_empty(body);
722 for c in catches {
723 strip_empty(&mut c.body);
724 }
725 if let Some(f) = finally {
726 strip_empty(f);
727 }
728 }
729 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => strip_empty(body),
730 _ => {}
731 }
732}
733
734fn merge_singleton_stmts(s: &mut Stmt) {
735 if let Stmt::Block(v) = s {
736 for x in v.iter_mut() {
737 merge_singleton_stmts(x);
738 }
739 }
740}
741
742pub fn prune_unreachable(s: &mut Stmt) {
744 if let Stmt::Block(v) = s {
745 let mut cut: Option<usize> = None;
746 for (i, x) in v.iter().enumerate() {
747 if matches!(x, Stmt::Return(_) | Stmt::Throw(_)) {
748 cut = Some(i + 1);
749 break;
750 }
751 }
752 if let Some(c) = cut {
753 if c < v.len() {
754 v.truncate(c);
755 }
756 }
757 for x in v.iter_mut() {
758 prune_unreachable(x);
759 }
760 } else {
761 walk_mut(s, &mut prune_unreachable);
762 }
763}
764
765fn walk_mut<F: FnMut(&mut Stmt)>(s: &mut Stmt, f: &mut F) {
766 match s {
767 Stmt::Block(v) => {
768 for x in v.iter_mut() {
769 f(x);
770 }
771 }
772 Stmt::If {
773 then_stmt,
774 else_stmt,
775 ..
776 } => {
777 f(then_stmt);
778 if let Some(e) = else_stmt {
779 f(e);
780 }
781 }
782 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => f(body),
783 Stmt::For { init, body, .. } => {
784 for x in init.iter_mut() {
785 f(x);
786 }
787 f(body);
788 }
789 Stmt::ForEach { body, .. } => f(body),
790 Stmt::Switch { cases, default, .. } => {
791 for c in cases {
792 for x in c.body.iter_mut() {
793 f(x);
794 }
795 }
796 if let Some(d) = default {
797 f(d);
798 }
799 }
800 Stmt::Try {
801 body,
802 catches,
803 finally,
804 } => {
805 f(body);
806 for c in catches {
807 f(&mut c.body);
808 }
809 if let Some(fl) = finally {
810 f(fl);
811 }
812 }
813 Stmt::TryWithResources {
814 resources,
815 body,
816 catches,
817 finally,
818 } => {
819 for r in resources.iter_mut() {
820 f(r);
821 }
822 f(body);
823 for c in catches {
824 f(&mut c.body);
825 }
826 if let Some(fl) = finally {
827 f(fl);
828 }
829 }
830 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => f(body),
831 _ => {}
832 }
833}
834
835pub fn count_localdefs(s: &Stmt, out: &mut usize) {
837 if matches!(s, Stmt::LocalDef { .. }) {
838 *out += 1;
839 }
840 walk_all(s, &mut |x| {
841 if matches!(x, Stmt::LocalDef { .. }) {
842 *out += 1;
843 }
844 });
845}
846
847pub fn strip_trailing_void_return(s: &mut Stmt) {
849 if let Stmt::Block(v) = s {
850 loop {
851 match v.last_mut() {
852 Some(Stmt::Return(None)) => {
853 v.pop();
854 }
855 Some(Stmt::Block(_)) => {
860 let last = v.last_mut().unwrap();
861 let before = match last {
862 Stmt::Block(b) => b.len(),
863 _ => 0,
864 };
865 strip_trailing_void_return(last);
866 let after = match last {
867 Stmt::Block(b) => b.len(),
868 _ => 0,
869 };
870 if after == before {
871 break; }
873 }
874 _ => break,
875 }
876 }
877 }
878}
879
880pub fn invert_empty_thens(s: &mut Stmt) {
885 walk_mut_deep(s, &mut |st| {
886 if let Stmt::If {
887 cond,
888 then_stmt,
889 else_stmt,
890 } = st
891 {
892 let then_empty = matches!(&**then_stmt, Stmt::Block(v) if v.is_empty());
893 if then_empty {
894 if let Some(els) = else_stmt.take() {
895 let c = std::mem::replace(cond, Expr::Const(ConstVal::Int(0)));
896 *cond = jdc_core::convert::negate(c);
897 *then_stmt = els;
898 }
899 }
900 }
901 });
902}
903
904pub fn fold_short_circuits(s: &mut Stmt) {
915 for _ in 0..8 {
916 let mut changed = false;
917 walk_mut_deep(s, &mut |st| {
918 if try_fold_diamond(st) {
919 changed = true;
920 }
921 });
922 if !changed {
923 break;
924 }
925 }
926}
927
928fn try_fold_diamond(st: &mut Stmt) -> bool {
929 enum Shape {
930 ThenAnd,
931 ThenAndNot,
932 ElseOr,
933 ElseOrNot,
934 BareAnd,
935 BareAndNot,
936 }
937 let shape = match &*st {
941 Stmt::If {
944 then_stmt,
945 else_stmt: None,
946 ..
947 } => match &**then_stmt {
948 Stmt::If {
949 then_stmt: t2,
950 else_stmt: None,
951 ..
952 } if !matches!(&**t2, Stmt::Block(v) if v.is_empty()) => Some(Shape::BareAnd),
953 Stmt::If {
954 then_stmt: t2,
955 else_stmt: Some(_),
956 ..
957 } if matches!(&**t2, Stmt::Block(v) if v.is_empty()) => Some(Shape::BareAndNot),
958 _ => None,
959 },
960 Stmt::If {
961 then_stmt,
962 else_stmt: Some(els),
963 ..
964 } => match (&**then_stmt, &**els) {
965 (Stmt::If { else_stmt: Some(f2), .. }, _) if f2.as_ref() == els.as_ref() => {
966 Some(Shape::ThenAnd)
967 }
968 (Stmt::If { then_stmt: t2, else_stmt: Some(_), .. }, _)
969 if t2.as_ref() == els.as_ref() =>
970 {
971 Some(Shape::ThenAndNot)
972 }
973 (_, Stmt::If { then_stmt: t2, else_stmt: Some(_), .. })
974 if t2.as_ref() == then_stmt.as_ref() =>
975 {
976 Some(Shape::ElseOr)
977 }
978 (_, Stmt::If { else_stmt: Some(f2), .. })
979 if f2.as_ref() == then_stmt.as_ref() =>
980 {
981 Some(Shape::ElseOrNot)
982 }
983 _ => None,
984 },
985 _ => None,
986 };
987 let Some(shape) = shape else {
988 return false;
989 };
990
991 let taken = std::mem::replace(st, Stmt::Block(vec![]));
992 if matches!(shape, Shape::BareAnd | Shape::BareAndNot) {
994 let Stmt::If {
995 cond: c1,
996 then_stmt,
997 else_stmt: None,
998 } = taken
999 else {
1000 unreachable!()
1001 };
1002 let Stmt::If {
1003 cond: c2,
1004 then_stmt: t2,
1005 else_stmt: f2,
1006 } = *then_stmt
1007 else {
1008 unreachable!()
1009 };
1010 let sc = |op: BinOp, l: Expr, r: Expr| Expr::Bin {
1011 op,
1012 l: Box::new(l),
1013 r: Box::new(r),
1014 ty: None,
1015 };
1016 match shape {
1017 Shape::BareAnd => {
1019 *st = Stmt::If {
1020 cond: sc(BinOp::LogAnd, c1, c2),
1021 then_stmt: t2,
1022 else_stmt: None,
1023 };
1024 }
1025 _ => {
1027 let body = f2.unwrap();
1028 *st = Stmt::If {
1029 cond: sc(
1030 BinOp::LogAnd,
1031 c1,
1032 Expr::Un {
1033 op: UnOp::Not,
1034 e: Box::new(c2),
1035 },
1036 ),
1037 then_stmt: body,
1038 else_stmt: None,
1039 };
1040 }
1041 }
1042 return true;
1043 }
1044 let Stmt::If {
1045 cond: c1,
1046 then_stmt,
1047 else_stmt: Some(els),
1048 } = taken
1049 else {
1050 unreachable!("shape probe guaranteed an If with else")
1051 };
1052 let sc = |op: BinOp, l: Expr, r: Expr| Expr::Bin {
1053 op,
1054 l: Box::new(l),
1055 r: Box::new(r),
1056 ty: None,
1057 };
1058 let not = |e: Expr| Expr::Un {
1059 op: UnOp::Not,
1060 e: Box::new(e),
1061 };
1062 match shape {
1063 Shape::BareAnd | Shape::BareAndNot => unreachable!("handled above"),
1064 Shape::ThenAnd | Shape::ThenAndNot => {
1065 let Stmt::If {
1066 cond: c2,
1067 then_stmt: t2,
1068 else_stmt: Some(f2),
1069 } = *then_stmt
1070 else {
1071 unreachable!()
1072 };
1073 let (nc, body, alt) = match shape {
1074 Shape::ThenAnd => (sc(BinOp::LogAnd, c1, c2), t2, f2),
1076 _ => (sc(BinOp::LogAnd, c1, not(c2)), f2, t2),
1078 };
1079 *st = Stmt::If {
1080 cond: nc,
1081 then_stmt: body,
1082 else_stmt: Some(alt),
1083 };
1084 }
1085 Shape::ElseOr | Shape::ElseOrNot => {
1086 let Stmt::If {
1087 cond: c2,
1088 then_stmt: t2,
1089 else_stmt: Some(f2),
1090 } = *els
1091 else {
1092 unreachable!()
1093 };
1094 let body = then_stmt;
1097 let (nc, alt) = match shape {
1098 Shape::ElseOr => (sc(BinOp::LogOr, c1, c2), f2),
1100 _ => (sc(BinOp::LogOr, c1, not(c2)), t2),
1102 };
1103 *st = Stmt::If {
1104 cond: nc,
1105 then_stmt: body,
1106 else_stmt: Some(alt),
1107 };
1108 }
1109 }
1110 true
1111}
1112
1113pub fn strip_clinit_returns(s: &mut Stmt) {
1119 walk_mut_deep(s, &mut |st| {
1120 if let Stmt::Block(v) = st {
1121 v.retain(|x| !matches!(x, Stmt::Return(None)));
1122 }
1123 });
1124}
1125
1126pub fn prepend_comment(s: &mut Stmt, text: String) {
1127 let stmt = Stmt::Comment(text);
1128 match s {
1129 Stmt::Block(v) => v.insert(0, stmt),
1130 other => {
1131 let inner = std::mem::replace(other, Stmt::Block(vec![]));
1132 *other = Stmt::Block(vec![stmt, inner]);
1133 }
1134 }
1135}
1136
1137fn is_obj_expr(e: &Expr, obj_var: &dyn Fn(u32) -> bool) -> bool {
1145 match e {
1146 Expr::Local { var, .. } => obj_var(*var),
1147 Expr::Const(_) => false,
1148 _ => e.type_ref().erased().is_reference(),
1149 }
1150}
1151
1152fn null_side_rewrite(x: &mut Expr, obj_var: &dyn Fn(u32) -> bool) {
1157 if let Expr::Bin { op, l, r, .. } = x {
1158 if !matches!(op, BinOp::Eq | BinOp::Ne) {
1159 return;
1160 }
1161 let l_zero = matches!(&**l, Expr::Const(ConstVal::Int(0)));
1162 let r_zero = matches!(&**r, Expr::Const(ConstVal::Int(0)));
1163 if l_zero && !r_zero && is_obj_expr(r, obj_var) {
1164 **l = Expr::Const(ConstVal::Null);
1165 } else if r_zero && !l_zero && is_obj_expr(l, obj_var) {
1166 **r = Expr::Const(ConstVal::Null);
1167 }
1168 }
1169}
1170
1171pub fn fused_expr_rewrites(s: &mut Stmt, vt: &VarTable) {
1172 let mut obj_vars: Vec<bool> = Vec::with_capacity(vt.vars.len());
1174 for v in &vt.vars {
1175 obj_vars.push(v.ty.erased().is_reference());
1176 }
1177 rewrite_exprs(s, &mut |e| {
1178 deep_rewrite(e, &mut |x| {
1179 if let Expr::Invokedynamic { name, args, .. } = x {
1181 if name.starts_with('\0') && args.len() == 2 {
1182 let (cls, ty) = match name.as_str() {
1183 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
1184 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
1185 _ => ("java/lang/Double", JavaType::Double),
1186 };
1187 let desc = MethodDescriptor {
1188 args: vec![ty.clone(), ty],
1189 ret: JavaType::Int,
1190 };
1191 *x = Expr::Method {
1192 owner: None,
1193 cls: cls.into(),
1194 name: "compare".into(),
1195 desc: std::sync::Arc::new(desc),
1196 args: args.clone(),
1197 is_static: true,
1198 is_interface: false,
1199 is_special: false,
1200 is_super: false,
1201 is_dynamic: false,
1202 type_args: vec![],
1203 };
1204 return;
1205 }
1206 }
1207 null_side_rewrite(x, &|v| obj_vars.get(v as usize).copied().unwrap_or(false));
1210 if let Expr::Bin { op, l, r, .. } = x {
1212 if matches!(
1213 op,
1214 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
1215 ) {
1216 let l_const = matches!(&**l, Expr::Const(_));
1217 let r_var = matches!(&**r, Expr::Local { .. });
1218 if l_const && r_var {
1219 std::mem::swap(l, r);
1220 }
1221 }
1222 }
1223 });
1224 });
1225 walk_mut_deep(s, &mut |st| {
1233 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = st {
1234 let tgt_is_obj = match &**target {
1235 Expr::Local { var, .. } => {
1236 obj_vars.get(*var as usize).copied().unwrap_or(false)
1237 }
1238 other => other.type_ref().erased().is_reference(),
1239 };
1240 if tgt_is_obj {
1241 if let Expr::Const(ConstVal::Int(0)) = &**value {
1242 **value = Expr::Const(ConstVal::Null);
1243 }
1244 }
1245 } else if let Stmt::LocalDef { var, init, .. } = st {
1246 if obj_vars.get(*var as usize).copied().unwrap_or(false) {
1247 if let Some(iv) = init {
1248 if let Expr::Const(ConstVal::Int(0)) = iv {
1249 *iv = Expr::Const(ConstVal::Null);
1250 }
1251 }
1252 }
1253 }
1254 });
1255}
1256
1257#[allow(dead_code)]
1260pub fn desugar_cmp_residuals(s: &mut Stmt) {
1261 rewrite_exprs(s, &mut |e| {
1262 deep_rewrite(e, &mut |x| {
1263 if let Expr::Invokedynamic { name, args, .. } = x {
1264 if name.starts_with('\0') && args.len() == 2 {
1265 let (cls, ty) = match name.as_str() {
1266 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
1267 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
1268 _ => ("java/lang/Double", JavaType::Double),
1269 };
1270 let desc = jdc_core::types::MethodDescriptor {
1271 args: vec![ty.clone(), ty],
1272 ret: JavaType::Int,
1273 };
1274 *x = Expr::Method {
1275 owner: None,
1276 cls: cls.into(),
1277 name: "compare".into(),
1278 desc: std::sync::Arc::new(desc),
1279 args: args.clone(),
1280 is_static: true,
1281 is_interface: false,
1282 is_special: false,
1283 is_super: false,
1284 is_dynamic: false,
1285 type_args: vec![],
1286 };
1287 }
1288 }
1289 });
1290 });
1291}
1292
1293pub fn ternary_fold(s: &mut Stmt) {
1295 let mut changed = true;
1296 let mut guard = 0;
1297 while changed && guard < 8 {
1298 changed = false;
1299 guard += 1;
1300 ternary_fold_walk(s, &mut changed);
1301 strip_empty(s);
1302 }
1303}
1304
1305fn ternary_fold_walk(s: &mut Stmt, changed: &mut bool) {
1306 if let Stmt::Block(v) = s {
1307 for x in v.iter_mut() {
1308 ternary_fold_walk(x, changed);
1309 }
1310 return;
1311 }
1312 let Stmt::If {
1313 cond,
1314 then_stmt,
1315 else_stmt,
1316 } = s
1317 else {
1318 return;
1319 };
1320 ternary_fold_walk(then_stmt, changed);
1321 if let Some(e) = else_stmt {
1322 ternary_fold_walk(e, changed);
1323 }
1324 let Some(else_b) = else_stmt else { return };
1325 let target_of = |st: &Stmt| -> Option<u32> {
1326 let Stmt::ExprStmt(Expr::Assign { target, .. }) = st else {
1327 return None;
1328 };
1329 if let Expr::Local { var, .. } = &**target {
1330 Some(*var)
1331 } else {
1332 None
1333 }
1334 };
1335 let (Stmt::Block(tv), Stmt::Block(ev)) = (then_stmt.as_mut(), else_b.as_mut()) else {
1336 return;
1337 };
1338 if tv.len() != 1 || ev.len() != 1 {
1339 return;
1340 }
1341 let (Some(vt_), Some(ve)) = (target_of(&tv[0]), target_of(&ev[0])) else {
1342 return;
1343 };
1344 if vt_ != ve {
1345 return;
1346 }
1347 let mut touched = HashSet::default();
1351 let (Stmt::ExprStmt(a_then), Stmt::ExprStmt(a_else)) = (&tv[0], &ev[0]) else {
1352 return;
1353 };
1354 if let Expr::Assign { value, .. } = a_then {
1355 collect_vars(value, &mut touched);
1356 }
1357 if let Expr::Assign { value, .. } = a_else {
1358 collect_vars(value, &mut touched);
1359 }
1360 if touched.contains(&vt_) {
1361 return;
1362 }
1363 let value_then = match a_then {
1364 Expr::Assign { value, .. } => value.clone(),
1365 _ => return,
1366 };
1367 let value_else = match a_else {
1368 Expr::Assign { value, .. } => value.clone(),
1369 _ => return,
1370 };
1371 let cond_e = cond.clone();
1372 let folded = Stmt::ExprStmt(Expr::Assign {
1373 target: Box::new(Expr::Local {
1374 var: vt_,
1375 ty: vt_join_ty(&value_then, &value_else),
1376 }),
1377 op: AssignOp::Plain,
1378 value: Box::new(Expr::Cond {
1379 c: Box::new(cond_e),
1380 t: value_then,
1381 f: value_else,
1382 }),
1383 });
1384 *s = Stmt::Block(vec![folded]);
1385 *changed = true;
1386}
1387
1388fn vt_join_ty(a: &Expr, b: &Expr) -> TypeRef {
1389 let ta = a.type_ref();
1390 let tb = b.type_ref();
1391 if ta.erased() == tb.erased() {
1392 return ta;
1393 }
1394 TypeRef::J(JavaType::Object("java/lang/Object".into()))
1395}
1396
1397pub fn fold_string_builders(s: &mut Stmt, vt: &VarTable) {
1404 let n_vars = vt.vars.len().max(1);
1406 let mut counts = vec![0usize; n_vars];
1407 count_assignments(s, &mut counts);
1408 let mut values: Vec<Option<Expr>> = vec![None; n_vars];
1409 record_assignments(s, &counts, &mut values);
1410
1411 let mut appended_stmts: HashSet<u32> = HashSet::default();
1413 walk_all(s, &mut |st| {
1414 if let Stmt::ExprStmt(Expr::Method {
1415 cls,
1416 name,
1417 owner,
1418 args,
1419 ..
1420 }) = st
1421 {
1422 if name.as_ref() == "append" && is_string_builder(cls) && args.len() == 1 {
1423 if let Some(Expr::Local { var, .. }) = owner.as_deref() {
1424 appended_stmts.insert(*var);
1425 }
1426 }
1427 }
1428 });
1429
1430 rewrite_exprs(s, &mut |e| {
1431 fold_concat_in_expr(e, &values, &appended_stmts);
1432 });
1433
1434 for _ in 0..8 {
1438 let mut reads: HashSet<u32> = HashSet::default();
1439 stmt_collect_vars(s, &mut reads, false);
1440 if drop_unused_assigns(s, &reads) == 0 {
1441 break;
1442 }
1443 }
1444}
1445
1446pub fn count_stmts_deep(s: &Stmt, out: &mut usize) {
1448 *out += 1;
1449 walk_all(s, &mut |_| {
1450 *out += 1;
1451 });
1452}
1453
1454struct VarAnalysis {
1456 assigns: Vec<usize>,
1457 reads: Vec<usize>,
1458 values: Vec<Option<Expr>>,
1460}
1461
1462fn analyze_vars(s: &Stmt, a: &mut VarAnalysis) {
1463 walk_all(s, &mut |st| {
1464 let exprs: Vec<&Expr> = match st {
1465 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
1466 if let Expr::Local { var, .. } = &**target {
1467 grow_to(&mut a.assigns, *var);
1468 let idx = *var as usize;
1469 a.assigns[idx] += 1;
1470 if a.assigns[idx] == 1 {
1471 if idx >= a.values.len() {
1472 a.values.resize(idx + 1, None);
1473 }
1474 a.values[idx] = Some((**value).clone());
1475 } else if idx < a.values.len() {
1476 a.values[idx] = None;
1477 }
1478 }
1479 let mut v: Vec<&Expr> = vec![value];
1480 if !matches!(&**target, Expr::Local { .. }) {
1481 v.push(target);
1482 }
1483 v
1484 }
1485 Stmt::ExprStmt(e @ (Expr::PreIncDec { .. } | Expr::PostIncDec { .. })) => {
1486 let inner = match e {
1491 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => e,
1492 _ => unreachable!(),
1493 };
1494 if let Expr::Local { var, .. } = &**inner {
1495 grow_to(&mut a.assigns, *var);
1496 let idx = *var as usize;
1497 a.assigns[idx] += 1;
1498 if idx < a.values.len() {
1499 a.values[idx] = None;
1500 }
1501 return;
1502 }
1503 vec![e]
1504 }
1505 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
1506 vec![e]
1507 }
1508 Stmt::Return(Some(e)) => vec![e],
1509 Stmt::LocalDef {
1510 var, init: Some(e), ..
1511 } => {
1512 grow_to(&mut a.assigns, *var);
1513 let idx = *var as usize;
1514 a.assigns[idx] += 1;
1515 if a.assigns[idx] == 1 {
1516 if idx >= a.values.len() {
1517 a.values.resize(idx + 1, None);
1518 }
1519 a.values[idx] = Some(e.clone());
1520 } else if idx < a.values.len() {
1521 a.values[idx] = None;
1522 }
1523 vec![e]
1524 }
1525 Stmt::If { cond, .. } => vec![cond],
1526 Stmt::While { cond, .. } => vec![cond],
1527 Stmt::DoWhile { cond, .. } => vec![cond],
1528 _ => Vec::new(),
1529 };
1530 for e in exprs {
1531 visit_exprs(e, &mut |x| {
1532 if let Expr::Local { var, .. } = x {
1533 grow_to(&mut a.reads, *var);
1534 a.reads[*var as usize] += 1;
1535 }
1536 });
1537 }
1538 });
1539 let n = a.assigns.len().max(a.reads.len()).max(a.values.len());
1541 a.assigns.resize(n, 0);
1542 a.reads.resize(n, 0);
1543 a.values.resize(n, None);
1544}
1545
1546fn grow_to(v: &mut Vec<usize>, var: u32) {
1547 if var as usize >= v.len() {
1548 v.resize(var as usize + 1, 0);
1549 }
1550}
1551
1552fn count_assignments(s: &Stmt, counts: &mut Vec<usize>) {
1553 walk_all(s, &mut |st| match st {
1554 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
1555 if let Expr::Local { var, .. } = &**target {
1556 grow_to(counts, *var);
1557 counts[*var as usize] += 1;
1558 }
1559 }
1560 Stmt::LocalDef {
1561 var, init: Some(_), ..
1562 } => {
1563 grow_to(counts, *var);
1564 counts[*var as usize] += 1;
1565 }
1566 _ => {}
1567 });
1568}
1569
1570fn record_assignments(s: &Stmt, counts: &[usize], out: &mut Vec<Option<Expr>>) {
1571 walk_all(s, &mut |st| match st {
1572 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
1573 if let Expr::Local { var, .. } = &**target {
1574 if counts.get(*var as usize).copied().unwrap_or(0) == 1 {
1575 if *var as usize >= out.len() {
1576 out.resize(*var as usize + 1, None);
1577 }
1578 out[*var as usize] = Some((**value).clone());
1579 }
1580 }
1581 }
1582 Stmt::LocalDef {
1583 var, init: Some(e), ..
1584 } if counts.get(*var as usize).copied().unwrap_or(0) == 1 => {
1585 if *var as usize >= out.len() {
1586 out.resize(*var as usize + 1, None);
1587 }
1588 out[*var as usize] = Some(e.clone());
1589 }
1590 _ => {}
1591 });
1592}
1593
1594fn fold_concat_in_expr(e: &mut Expr, values: &[Option<Expr>], appended: &HashSet<u32>) {
1595 deep_rewrite(e, &mut |x| {
1596 if let Expr::Method {
1597 cls,
1598 name,
1599 args,
1600 owner,
1601 ..
1602 } = x
1603 {
1604 if name.as_ref() == "toString" && args.is_empty() && is_string_builder(cls) {
1605 if let Some(o) = owner {
1606 let mut has_stmt_appends = false;
1609 visit_exprs(o, &mut |y| {
1610 if let Expr::Local { var, .. } = y {
1611 if appended.contains(var) {
1612 has_stmt_appends = true;
1613 }
1614 }
1615 });
1616 if has_stmt_appends {
1617 return;
1618 }
1619 let resolved = resolve_local(o, values);
1620 if let Some(parts) = collect_sb_parts(&resolved, values, 0) {
1621 if !parts.is_empty() {
1630 *x = Expr::StringConcat(parts);
1631 }
1632 }
1633 }
1634 }
1635 }
1636 });
1637}
1638
1639fn is_string_builder(cls: &str) -> bool {
1640 cls == "java/lang/StringBuilder" || cls == "java/lang/StringBuffer"
1641}
1642
1643fn resolve_local(e: &Expr, values: &[Option<Expr>]) -> Expr {
1644 match e {
1645 Expr::Local { var, ty } => values
1646 .get(*var as usize)
1647 .and_then(|o| o.clone())
1648 .unwrap_or_else(|| Expr::Local {
1649 var: *var,
1650 ty: ty.clone(),
1651 }),
1652 other => other.clone(),
1653 }
1654}
1655
1656fn collect_sb_parts(e: &Expr, values: &[Option<Expr>], depth: u32) -> Option<Vec<ConcatPart>> {
1658 if depth > 16 {
1659 return None;
1660 }
1661 match e {
1662 Expr::Method {
1663 cls,
1664 name,
1665 args,
1666 owner,
1667 ..
1668 } if name.as_ref() == "append" && is_string_builder(cls) && args.len() == 1 => {
1669 let mut parts =
1670 collect_sb_parts(&resolve_local(owner.as_deref()?, values), values, depth + 1)?;
1671 parts.push(ConcatPart::Str(args[0].clone()));
1672 Some(parts)
1673 }
1674 Expr::New {
1675 cls,
1676 args,
1677 raw: false,
1678 ..
1679 } if is_string_builder(cls) => {
1680 let mut parts = Vec::new();
1681 for a in args {
1682 if let Expr::Const(ConstVal::Str(sv)) = a {
1683 parts.push(ConcatPart::Const(sv.to_string()));
1684 } else {
1685 parts.push(ConcatPart::Str(a.clone()));
1686 }
1687 }
1688 Some(parts)
1689 }
1690 _ => None,
1691 }
1692}
1693
1694fn drop_unused_assigns(s: &mut Stmt, reads: &HashSet<u32>) -> usize {
1695 let mut dropped = 0usize;
1696 walk_mut_deep(s, &mut |st| {
1697 if let Stmt::Block(v) = st {
1698 let before = v.len();
1699 v.retain(|x| match x {
1700 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
1703 Expr::Local { var, .. } => reads.contains(var) || !is_sbish(value),
1704 _ => true,
1705 },
1706 Stmt::LocalDef { var, init, .. } => {
1707 reads.contains(var) || !init.as_ref().map(is_sbish).unwrap_or(false)
1708 }
1709 _ => true,
1710 });
1711 dropped += before - v.len();
1712 }
1713 });
1714 dropped
1715}
1716
1717fn is_sbish(e: &Expr) -> bool {
1718 match e {
1719 Expr::New {
1720 cls, raw: false, ..
1721 } => is_string_builder(cls),
1722 Expr::Method { name, cls, .. } => name.as_ref() == "append" && is_string_builder(cls),
1723 _ => false,
1724 }
1725}
1726
1727fn walk_mut_deep<F: FnMut(&mut Stmt)>(s: &mut Stmt, f: &mut F) {
1728 f(s);
1729 walk_mut(s, &mut |x| walk_mut_deep(x, f));
1730}
1731
1732pub fn fold_synchronized(s: &mut Stmt) {
1736 fold_sync_walk(s);
1737}
1738
1739fn fold_sync_walk(s: &mut Stmt) {
1740 walk_mut_deep(s, &mut |st| {
1741 let Stmt::Block(v) = st else { return };
1742 let mut i = 0;
1743 while i < v.len() {
1744 if i + 1 < v.len() {
1745 if let Some(sync) = try_sync_at(v, i) {
1746 let _ = v.drain(i..i + 2);
1747 v.insert(i, sync);
1748 continue;
1749 }
1750 }
1751 i += 1;
1752 }
1753 });
1754}
1755
1756fn try_sync_at(v: &[Stmt], i: usize) -> Option<Stmt> {
1757 let Stmt::MonitorEnter(lock) = &v[i] else {
1758 return None;
1759 };
1760 let Stmt::Try {
1761 body,
1762 catches,
1763 finally,
1764 } = &v[i + 1]
1765 else {
1766 return None;
1767 };
1768 if finally.is_some() || catches.len() != 1 {
1769 return None;
1770 }
1771 let c = &catches[0];
1772 if !c.exc.is_empty() {
1773 return None;
1774 }
1775 let Stmt::Block(cv) = c.body.as_ref() else {
1777 return None;
1778 };
1779 let exit_matches = cv.len() >= 2
1780 && matches!(&cv[0], Stmt::MonitorExit(e) if expr_local_var(e) == expr_local_var(lock));
1781 let throws = cv.len() >= 2 && matches!(&cv[1], Stmt::Throw(_));
1782 if !(exit_matches && throws) {
1783 return None;
1784 }
1785 Some(Stmt::Synchronized {
1786 lock: lock.clone(),
1787 body: body.clone(),
1788 })
1789}
1790
1791fn expr_local_var(e: &Expr) -> Option<u32> {
1792 if let Expr::Local { var, .. } = e {
1793 Some(*var)
1794 } else {
1795 None
1796 }
1797}
1798
1799pub fn forward_single_use(s: &mut Stmt, _vt: &VarTable) {
1804 let mut analysis = VarAnalysis {
1808 assigns: Vec::new(),
1809 reads: Vec::new(),
1810 values: Vec::new(),
1811 };
1812 analyze_vars(s, &mut analysis);
1813 let mut assigns = analysis.assigns;
1814 let mut reads = analysis.reads;
1815 let n_vars = assigns.len().max(_vt.vars.len()).max(1);
1818 assigns.resize(n_vars, 0);
1819 reads.resize(n_vars, 0);
1820 let mut values = analysis.values;
1821 values.resize(n_vars, None);
1822
1823 let mut cand = vec![false; n_vars];
1828 let mut edges: Vec<Vec<u32>> = vec![Vec::new(); n_vars];
1829 for (v, cand_v) in cand.iter_mut().enumerate() {
1830 if assigns.get(v).copied().unwrap_or(0) != 1 || reads.get(v).copied().unwrap_or(0) != 1 {
1831 continue;
1832 }
1833 if let Some(val) = values.get(v).and_then(|o| o.as_ref()) {
1834 let mut refs = HashSet::default();
1835 collect_vars(val, &mut refs);
1836 if refs.iter().any(|r| assigns[*r as usize] > 1) {
1837 continue;
1838 }
1839 edges[v] = refs.into_iter().collect();
1843 }
1844 *cand_v = true;
1845 }
1846
1847 let mut color = vec![0u8; n_vars]; let mut bad = vec![false; n_vars];
1865 for root in 0..n_vars {
1866 if color[root] != 0 || edges[root].is_empty() {
1867 continue;
1868 }
1869 color[root] = 1;
1870 let mut stack: Vec<(usize, usize)> = vec![(root, 0)];
1871 while let Some(top) = stack.last_mut() {
1872 let v = top.0;
1873 let i = top.1;
1874 top.1 += 1;
1875 if i < edges[v].len() {
1876 let r = edges[v][i] as usize;
1877 if color[r] == 1 {
1878 for &(pn, _) in &stack {
1879 bad[pn] = true;
1880 }
1881 } else if color[r] == 0 {
1882 color[r] = 1;
1883 stack.push((r, 0));
1884 } else if bad[r] {
1885 for &(pn, _) in &stack {
1886 bad[pn] = true;
1887 }
1888 }
1889 } else {
1890 color[v] = 2;
1891 stack.pop();
1892 }
1893 }
1894 }
1895 let single: Vec<bool> = (0..n_vars).map(|v| cand[v] && !bad[v]).collect();
1896 if !single.iter().any(|&b| b) {
1897 return;
1898 }
1899 let pure: Vec<bool> = (0..n_vars)
1900 .map(|v| {
1901 single[v]
1902 && values
1903 .get(v)
1904 .and_then(|o| o.as_ref())
1905 .map(|e| !has_side_effects(e))
1906 .unwrap_or(false)
1907 })
1908 .collect();
1909 let impure: Vec<bool> = (0..n_vars).map(|v| single[v] && !pure[v]).collect();
1910
1911 if pure.iter().any(|&b| b) {
1913 let vals: Vec<Option<Expr>> = (0..n_vars)
1914 .map(|v| {
1915 if pure[v] {
1916 values.get(v).cloned().flatten()
1917 } else {
1918 None
1919 }
1920 })
1921 .collect();
1922 rewrite_exprs(s, &mut |e| {
1923 deep_rewrite_reads(e, &mut |x| {
1924 if let Expr::Local { var, .. } = x {
1925 if let Some(Some(v)) = vals.get(*var as usize) {
1926 *x = v.clone();
1927 }
1928 }
1929 });
1930 });
1931 drop_defs(s, &pure);
1932 }
1933
1934 if impure.iter().any(|&b| b) {
1937 forward_adjacent_impure(s, &values, &impure);
1938 }
1939}
1940
1941fn count_reads(s: &Stmt, out: &mut Vec<usize>) {
1943 walk_all(s, &mut |st| {
1944 let exprs: Vec<&Expr> = match st {
1945 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
1948 let mut v: Vec<&Expr> = vec![value];
1949 if !matches!(&**target, Expr::Local { .. }) {
1950 v.push(target);
1951 }
1952 v
1953 }
1954 Stmt::ExprStmt(e @ (Expr::PreIncDec { .. } | Expr::PostIncDec { .. })) => {
1957 let inner = match e {
1958 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => e,
1959 _ => unreachable!(),
1960 };
1961 if matches!(**inner, Expr::Local { .. }) {
1962 Vec::new()
1963 } else {
1964 vec![e]
1965 }
1966 }
1967 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
1968 vec![e]
1969 }
1970 Stmt::Return(Some(e)) => vec![e],
1971 Stmt::LocalDef { init: Some(e), .. } => vec![e],
1972 Stmt::If { cond, .. } => vec![cond],
1973 Stmt::While { cond, .. } => vec![cond],
1974 Stmt::DoWhile { cond, .. } => vec![cond],
1975 Stmt::Switch { selector, .. } => vec![selector],
1981 Stmt::ForEach { iterable, .. } => vec![iterable],
1982 Stmt::Synchronized { lock, .. } => vec![lock],
1983 Stmt::For { cond, update, .. } => {
1984 let mut v: Vec<&Expr> = Vec::with_capacity(update.len() + 1);
1985 if let Some(c) = cond {
1986 v.push(c);
1987 }
1988 v.extend(update.iter());
1989 v
1990 }
1991 Stmt::Assert { cond, msg } => {
1992 let mut v: Vec<&Expr> = vec![cond];
1993 if let Some(m) = msg {
1994 v.push(m);
1995 }
1996 v
1997 }
1998 _ => Vec::new(),
1999 };
2000 for e in exprs {
2001 visit_exprs(e, &mut |x| {
2002 if let Expr::Local { var, .. } = x {
2003 grow_to(out, *var);
2004 out[*var as usize] += 1;
2005 }
2006 });
2007 }
2008 });
2009}
2010
2011fn drop_defs(s: &mut Stmt, vars: &[bool]) {
2012 walk_mut_deep(s, &mut |st| {
2013 if let Stmt::Block(v) = st {
2014 v.retain(|x| match x {
2015 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
2016 Expr::Local { var, .. } => !vars.get(*var as usize).copied().unwrap_or(false),
2017 _ => true,
2018 },
2019 Stmt::LocalDef { var, .. } => !vars.get(*var as usize).copied().unwrap_or(false),
2020 _ => true,
2021 });
2022 }
2023 });
2024}
2025
2026fn forward_adjacent_impure(s: &mut Stmt, values: &[Option<Expr>], impure: &[bool]) {
2028 if let Stmt::Block(v) = s {
2029 let mut i = 0;
2030 while i + 1 < v.len() {
2031 let def_var = match &v[i] {
2032 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
2033 Expr::Local { var, .. } => Some(*var),
2034 _ => None,
2035 },
2036 Stmt::LocalDef { var, .. } => Some(*var),
2037 _ => None,
2038 };
2039 if let Some(var) = def_var {
2040 if impure.get(var as usize).copied().unwrap_or(false) {
2041 let mut reads_here = vec![0usize; values.len().max(1)];
2043 count_reads(&v[i + 1], &mut reads_here);
2044 if reads_here.get(var as usize).copied().unwrap_or(0) == 1 {
2045 let val = match &v[i] {
2049 Stmt::ExprStmt(Expr::Assign { value, .. }) => (**value).clone(),
2050 Stmt::LocalDef { init: Some(e), .. } => (*e).clone(),
2051 _ => {
2052 i += 1;
2053 continue;
2054 }
2055 };
2056 rewrite_exprs(&mut v[i + 1], &mut |e| {
2057 deep_rewrite_reads(e, &mut |x| {
2058 if let Expr::Local { var: v2, .. } = x {
2059 if *v2 == var {
2060 *x = val.clone();
2061 }
2062 }
2063 });
2064 });
2065 v.remove(i);
2066 i = i.saturating_sub(1);
2069 continue;
2070 }
2071 }
2072 }
2073 i += 1;
2074 }
2075 }
2076 walk_mut(s, &mut |x| forward_adjacent_impure(x, values, impure));
2077}
2078
2079pub fn infer_types(vt: &mut VarTable, body: &mut Stmt, ret: &JavaType, env: &MethodEnv) {
2085 let n = vt.vars.len();
2086 let mut evidence: Vec<Vec<(JavaType, bool)>> = vec![Vec::new(); n];
2094 let ev = |evidence: &mut Vec<Vec<(JavaType, bool)>>, var: u32, t: JavaType, strong: bool| {
2095 if (var as usize) < evidence.len() {
2096 evidence[var as usize].push((t, strong));
2097 }
2098 };
2099
2100 walk_all(body, &mut |st| match st {
2101 Stmt::LocalDef { var, init, .. } => {
2102 if let Some(e) = init {
2103 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
2104 let strong = match e {
2112 Expr::Const(_) => false,
2113 Expr::Local { var: src, .. } => {
2114 let src_ty = vt.vars.get(*src as usize).map(|v| v.ty.erased());
2115 let tgt_ty = vt.vars.get(*var as usize).map(|v| v.ty.erased());
2116 match (src_ty, tgt_ty) {
2117 (Some(s), Some(g)) => {
2118 (s.is_numeric() && g.is_numeric())
2119 || (s.is_reference() && g.is_reference())
2120 }
2121 _ => false,
2122 }
2123 }
2124 _ => true,
2125 };
2126 ev(&mut evidence, *var, e.type_ref().erased(), strong);
2127 }
2128 }
2129 Stmt::ExprStmt(e) => {
2130 if let Expr::Assign { target, value, .. } = e {
2137 if let Expr::Local { var, .. } = &**target {
2138 let strong = match &**value {
2139 Expr::Const(_) => false,
2140 Expr::Local { var: src, .. } => {
2141 let src_ty = vt.vars.get(*src as usize).map(|v| v.ty.erased());
2148 let tgt_ty = vt.vars.get(*var as usize).map(|v| v.ty.erased());
2149 match (src_ty, tgt_ty) {
2150 (Some(s), Some(g)) => {
2151 (s.is_numeric() && g.is_numeric())
2152 || (s.is_reference() && g.is_reference())
2153 }
2154 _ => false,
2155 }
2156 }
2157 _ => true,
2158 };
2159 ev(&mut evidence, *var, value.type_ref().erased(), strong);
2160 }
2161 }
2162 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
2163 }
2164 Stmt::Throw(e) => {
2165 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
2166 if let Expr::Local { var, .. } = e {
2167 ev(
2168 &mut evidence,
2169 *var,
2170 JavaType::Object("java/lang/Throwable".into()),
2171 true,
2172 );
2173 }
2174 }
2175 Stmt::Return(Some(e)) => {
2176 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
2177 if let Expr::Local { var, .. } = e {
2178 ev(&mut evidence, *var, ret.clone(), true);
2179 }
2180 }
2181 Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
2182 if let Expr::Local { var, .. } = e {
2183 ev(
2184 &mut evidence,
2185 *var,
2186 JavaType::Object("java/lang/Object".into()),
2187 true,
2188 );
2189 }
2190 }
2191 Stmt::ForEach {
2192 var,
2193 iterable,
2194 is_array,
2195 ..
2196 } => {
2197 if let Expr::Local { var: v0, .. } = iterable {
2198 if *is_array {
2199 ev(&mut evidence, *v0, JavaType::Array(Box::new(JavaType::Int)), true);
2200 } else {
2201 ev(
2202 &mut evidence,
2203 *v0,
2204 JavaType::Object("java/lang/Object".into()),
2205 true,
2206 );
2207 }
2208 }
2209 let _ = var;
2210 }
2211 _ => {}
2212 });
2213 let _ = env;
2214
2215 for (i, evs) in evidence.iter().enumerate() {
2219 let info = &vt.vars[i];
2220 if info.is_param {
2221 continue;
2222 }
2223 let strong: Vec<JavaType> = evs
2224 .iter()
2225 .filter(|(_, s)| *s)
2226 .map(|(t, _)| t.clone())
2227 .collect();
2228 let all: Vec<JavaType> = evs.iter().map(|(t, _)| t.clone()).collect();
2229 if info.ty.erased().is_reference() {
2230 if strong.is_empty() {
2236 let refs: std::collections::HashSet<&str> = all
2237 .iter()
2238 .filter_map(|t| match t {
2239 JavaType::Object(n) if n.as_ref() != "java/lang/Object" => {
2240 Some(n.as_ref())
2241 }
2242 _ => None,
2243 })
2244 .collect();
2245 let mixed = all.iter().any(|t| t.is_numeric());
2246 if refs.len() >= 2 || (mixed && !refs.is_empty()) || (mixed && all.iter().any(|t| matches!(t, JavaType::Array(_)))) {
2247 vt.vars[i].ty =
2248 TypeRef::J(JavaType::Object("java/lang/Object".into()));
2249 continue;
2250 }
2251 }
2252 if let Some(t) = pick_object(&strong).or_else(|| pick_object(&all)) {
2254 vt.vars[i].ty = TypeRef::J(t);
2255 }
2256 } else if strong.contains(&JavaType::Boolean) {
2257 vt.vars[i].ty = TypeRef::J(JavaType::Boolean);
2261 } else if !all.is_empty() && all.iter().all(|t| t.is_numeric()) {
2262 if let Some(t) = pick_numeric(&all) {
2263 vt.vars[i].ty = TypeRef::J(t);
2264 }
2265 } else {
2266 let refs = all.iter().filter(|t| t.is_reference()).count();
2270 let nums = all.iter().filter(|t| t.is_numeric()).count();
2271 if refs > 0 && nums > 0 {
2272 vt.vars[i].ty =
2273 TypeRef::J(JavaType::Object("java/lang/Object".into()));
2274 } else if let Some(t) = pick_object(&strong).or_else(|| pick_object(&all)) {
2275 vt.vars[i].ty = TypeRef::J(t);
2276 }
2277 }
2278 }
2279
2280 let types: Vec<&TypeRef> = vt.vars.iter().map(|v| &v.ty).collect();
2285 rewrite_exprs(body, &mut |e| {
2286 deep_rewrite(e, &mut |x| {
2287 if let Expr::Local { var, ty } = x {
2288 if let Some(want) = types.get(*var as usize) {
2289 if ty != *want {
2290 *ty = (*want).clone();
2291 }
2292 }
2293 }
2294 });
2295 });
2296
2297 coerce_num_consts(body, &types);
2300}
2301
2302fn coerce_num_consts(body: &mut Stmt, types: &[&TypeRef]) {
2303 walk_mut_deep(body, &mut |st| {
2304 let (var, val): (u32, &mut Expr) = match st {
2305 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &mut **target {
2306 Expr::Local { var, .. } => (*var, value),
2307 _ => return,
2308 },
2309 Stmt::LocalDef {
2310 var, init: Some(e), ..
2311 } => (*var, e),
2312 _ => return,
2313 };
2314 let want = match types.get(var as usize) {
2315 Some(TypeRef::J(t)) => t.clone(),
2316 _ => return,
2317 };
2318 let rewritten = match &*val {
2319 Expr::Const(ConstVal::Long(l)) => match &want {
2320 JavaType::Double => Some(Expr::Const(ConstVal::Double(*l as f64))),
2321 JavaType::Float => Some(Expr::Const(ConstVal::Float(*l as f32))),
2322 _ => None,
2323 },
2324 Expr::Const(ConstVal::Int(i)) => match &want {
2325 JavaType::Double => Some(Expr::Const(ConstVal::Double(*i as f64))),
2326 JavaType::Float => Some(Expr::Const(ConstVal::Float(*i as f32))),
2327 _ => None,
2328 },
2329 _ => None,
2330 };
2331 if let Some(e) = rewritten {
2332 *val = e;
2333 }
2334 });
2335}
2336
2337fn pick_object(evs: &[JavaType]) -> Option<JavaType> {
2338 evs.iter()
2339 .find(|t| t.is_reference() && !matches!(t, JavaType::Object(n) if n.as_ref() == "java/lang/Object"))
2340 .cloned()
2341}
2342
2343fn pick_numeric(evs: &[JavaType]) -> Option<JavaType> {
2344 if evs.is_empty() {
2345 return None;
2346 }
2347 let mut t = evs[0].clone();
2348 for x in &evs[1..] {
2349 t = join_numeric(&t, x);
2350 }
2351 Some(t)
2352}
2353
2354fn join_numeric(a: &JavaType, b: &JavaType) -> JavaType {
2355 if a == b {
2356 return a.clone();
2357 }
2358 match (a, b) {
2359 (JavaType::Double, _) | (_, JavaType::Double) => JavaType::Double,
2360 (JavaType::Float, _) | (_, JavaType::Float) => JavaType::Float,
2361 (JavaType::Long, _) | (_, JavaType::Long) => JavaType::Long,
2362 (JavaType::Int, x) if x.is_integral() => JavaType::Int,
2363 (x, JavaType::Int) if x.is_integral() => JavaType::Int,
2364 (x, y) if x.is_integral() && y.is_integral() => JavaType::Int,
2365 _ => JavaType::Int,
2366 }
2367}
2368
2369fn expr_evidence<F: FnMut(u32, JavaType)>(e: &Expr, f: &mut F) {
2371 visit_exprs(e, &mut |x| match x {
2372 Expr::Method {
2373 owner,
2374 args,
2375 desc,
2376 is_static,
2377 cls,
2378 ..
2379 } => {
2380 if !*is_static {
2381 if let Some(o) = owner {
2382 if let Expr::Local { var, .. } = &**o {
2383 f(*var, JavaType::Object(cls.clone()));
2384 }
2385 }
2386 }
2387 for (i, a) in args.iter().enumerate() {
2388 if let Expr::Local { var, .. } = a {
2389 if let Some(t) = desc.args.get(i) {
2390 f(*var, t.clone());
2391 }
2392 }
2393 }
2394 }
2395 Expr::Field {
2396 owner,
2397 ty,
2398 is_static,
2399 ..
2400 } => {
2401 if !*is_static {
2402 if let Some(o) = owner {
2403 if let Expr::Local { var, .. } = &**o {
2404 f(*var, JavaType::Object("java/lang/Object".into()));
2405 }
2406 }
2407 }
2408 let _ = ty;
2409 }
2410 Expr::ArrayIndex { array, .. } => {
2411 if let Expr::Local { var, ty, .. } = &**array {
2412 let t = ty.erased();
2417 if matches!(t, JavaType::Array(_)) {
2418 f(*var, t);
2419 } else {
2420 f(*var, JavaType::Array(Box::new(JavaType::Int)));
2421 }
2422 }
2423 }
2424 Expr::Assign { .. } => {
2425 }
2428 Expr::Cast { e: inner, .. } => {
2429 let _ = inner;
2435 }
2436 Expr::InstanceOf { e: inner, .. } => {
2437 let _ = inner;
2438 }
2439 _ => {}
2440 });
2441}
2442
2443pub fn insert_object_narrowing_casts(vt: &VarTable, body: &mut Stmt) {
2455 let value_ty = |e: &Expr| -> JavaType {
2458 match e {
2459 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
2460 other => other.type_ref().erased(),
2461 }
2462 };
2463 let is_top_object = |e: &Expr| -> bool {
2464 value_ty(e) == JavaType::Object("java/lang/Object".into())
2465 };
2466 let specific_ref = |ty: &TypeRef| -> Option<TypeRef> {
2468 match ty.erased() {
2469 JavaType::Object(c) if c.as_ref() != "java/lang/Object" => Some(ty.clone()),
2470 _ => None,
2471 }
2472 };
2473 let castable = |e: &Expr| -> bool {
2474 is_top_object(e)
2475 && !matches!(e, Expr::Const(_) | Expr::Cast { .. } | Expr::InstanceOf { .. })
2476 };
2477 walk_mut_deep(body, &mut |st| match st {
2478 Stmt::ExprStmt(Expr::Assign {
2479 target,
2480 value,
2481 op: AssignOp::Plain,
2482 ..
2483 }) => {
2484 let tgt = match &**target {
2485 Expr::Local { var, .. } => vt.var(*var).ty.clone(),
2486 Expr::Field { ty, .. } => ty.clone(),
2487 _ => return,
2488 };
2489 if let Some(t) = specific_ref(&tgt) {
2490 if castable(value) {
2491 let v = std::mem::replace(value, Box::new(Expr::This));
2492 **value = Expr::Cast { ty: t, e: v };
2493 }
2494 }
2495 }
2496 Stmt::LocalDef { var, init: Some(value), .. } => {
2497 if let Some(t) = specific_ref(&vt.var(*var).ty) {
2498 if castable(value) {
2499 let v = std::mem::replace(value, Expr::This);
2500 *value = Expr::Cast {
2501 ty: t,
2502 e: Box::new(v),
2503 };
2504 }
2505 }
2506 }
2507 _ => {}
2508 });
2509}
2510
2511pub fn booleanize(vt: &mut VarTable, body: &mut Stmt, ret_bool: bool) {
2514 for _ in 0..4 {
2518 if booleanize_round(vt, body, ret_bool) == 0 {
2519 break;
2520 }
2521 }
2522}
2523
2524fn booleanize_round(vt: &mut VarTable, body: &mut Stmt, ret_bool: bool) -> usize {
2525 let n = vt.vars.len();
2526 let mut in_cond = vec![false; n];
2527
2528 walk_all(body, &mut |st| {
2531 let cond: Option<&Expr> = match st {
2532 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
2533 Some(cond)
2534 }
2535 _ => None,
2536 };
2537 if let Some(c) = cond {
2538 if let Expr::Bin {
2539 op: BinOp::Eq | BinOp::Ne,
2540 l,
2541 r,
2542 ..
2543 } = c
2544 {
2545 for (x, other) in [(l, r), (r, l)] {
2546 if let (Expr::Local { var, .. }, Expr::Const(ConstVal::Int(0))) =
2547 (&**x, &**other)
2548 {
2549 if (*var as usize) < n {
2550 in_cond[*var as usize] = true;
2551 }
2552 }
2553 }
2554 }
2555 }
2556 if ret_bool {
2561 if let Stmt::Return(Some(Expr::Local { var, .. })) = st {
2562 if (*var as usize) < n {
2563 in_cond[*var as usize] = true;
2564 }
2565 }
2566 }
2567 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = st {
2574 if let Expr::Field { ty, .. } = &**target {
2575 if ty.erased() == JavaType::Boolean {
2576 if let Expr::Local { var, .. } = &**value {
2577 if (*var as usize) < n {
2578 in_cond[*var as usize] = true;
2579 }
2580 }
2581 }
2582 }
2583 }
2584 let val: Option<&Expr> = match st {
2585 Stmt::ExprStmt(Expr::Assign { value, .. }) => Some(value),
2586 Stmt::ExprStmt(e) => Some(e),
2589 Stmt::LocalDef { init: Some(e), .. } => Some(e),
2590 Stmt::Return(Some(e)) => Some(e),
2591 _ => None,
2592 };
2593 if let Some(v) = val {
2594 visit_exprs(v, &mut |x| {
2595 if let Expr::Method { desc, args, .. } = x {
2601 for (i, a) in args.iter().enumerate() {
2602 if desc.args.get(i) == Some(&JavaType::Boolean) {
2603 if let Expr::Local { var, .. } = a {
2604 if (*var as usize) < n {
2605 in_cond[*var as usize] = true;
2606 }
2607 }
2608 }
2609 }
2610 }
2611 if let Expr::Bin { op, l, r, .. } = x {
2612 if matches!(
2613 op,
2614 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
2615 ) {
2616 for side in [l, r] {
2617 if let Expr::Local { var, .. } = &**side {
2618 if (*var as usize) < n {
2619 in_cond[*var as usize] = true;
2620 }
2621 }
2622 }
2623 }
2624 if matches!(op, BinOp::Or | BinOp::And) {
2630 let l_bool = l.type_ref().erased() == JavaType::Boolean;
2631 let r_bool = r.type_ref().erased() == JavaType::Boolean;
2632 if l_bool != r_bool {
2633 let side = if l_bool { r } else { l };
2634 if let Expr::Local { var, .. } = &**side {
2635 if (*var as usize) < n {
2636 in_cond[*var as usize] = true;
2637 }
2638 }
2639 }
2640 }
2641 }
2642 });
2643 }
2644 });
2645
2646 let mut assigned_any = vec![false; n];
2653 let mut all_bool = vec![true; n];
2654 let mut edges: Vec<(usize, usize)> = Vec::new();
2655 walk_all(body, &mut |st| {
2656 let (var, value) = match st {
2657 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
2658 Expr::Local { var, .. } => (var, &**value),
2659 _ => return,
2660 },
2661 Stmt::LocalDef {
2662 var, init: Some(e), ..
2663 } => (var, e),
2664 _ => return,
2665 };
2666 let i = *var as usize;
2667 if i < n {
2668 assigned_any[i] = true;
2669 if !is_boolean_valued(value) {
2670 all_bool[i] = false;
2671 }
2672 if let Expr::Local { var: src, .. } = value {
2678 if (*src as usize) < n {
2679 edges.push((i, *src as usize));
2680 }
2681 }
2682 }
2683 });
2684 loop {
2689 let mut changed = false;
2690 for &(tgt, src) in &edges {
2691 if in_cond[tgt] && !in_cond[src] && all_bool[src] {
2692 in_cond[src] = true;
2693 changed = true;
2694 }
2695 }
2696 if !changed {
2697 break;
2698 }
2699 }
2700 let mut boolean_vars: HashSet<u32> = HashSet::default();
2701 for i in 0..n {
2702 if vt.vars[i].is_param {
2703 continue;
2704 }
2705 if !in_cond[i] {
2706 continue;
2707 }
2708 if assigned_any[i] && all_bool[i] {
2709 boolean_vars.insert(i as u32);
2710 }
2711 }
2712 if boolean_vars.is_empty() {
2713 return 0;
2714 }
2715 for v in &boolean_vars {
2716 vt.vars[*v as usize].ty = TypeRef::J(JavaType::Boolean);
2717 }
2718 let types: Vec<&TypeRef> = vt.vars.iter().map(|v| &v.ty).collect();
2719 rewrite_exprs(body, &mut |e| {
2720 deep_rewrite(e, &mut |x| {
2721 if let Expr::Local { var, ty } = x {
2722 if let Some(want) = types.get(*var as usize) {
2723 if ty != *want {
2724 *ty = (*want).clone();
2725 }
2726 }
2727 }
2728 });
2729 });
2730
2731 fold_bool_conditions(body, &boolean_vars);
2733 boolean_vars.len()
2734}
2735
2736fn is_boolean_valued(e: &Expr) -> bool {
2737 match e {
2738 Expr::Const(ConstVal::Int(0)) | Expr::Const(ConstVal::Int(1)) => true,
2739 Expr::Bin { op, .. } => matches!(
2740 op,
2741 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
2742 ),
2743 Expr::InstanceOf { .. } => true,
2744 Expr::Local { ty, .. } => ty.erased() == JavaType::Boolean,
2745 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
2746 Expr::Cond { t, f, .. } => is_boolean_valued(t) && is_boolean_valued(f),
2747 Expr::Un { op: UnOp::Not, .. } => true,
2748 _ => false,
2749 }
2750}
2751
2752fn fold_bool_conditions(s: &mut Stmt, bv: &HashSet<u32>) {
2753 let fold = |e: &mut Expr| {
2754 deep_rewrite(e, &mut |x| {
2755 if let Expr::Bin { op, l, r, .. } = x {
2756 let (var_side, const_side) = match (&**l, &**r) {
2757 (Expr::Local { .. }, Expr::Const(ConstVal::Int(_))) => (l, 0),
2758 (Expr::Const(ConstVal::Int(_)), Expr::Local { .. }) => (r, 1),
2759 _ => return,
2760 };
2761 let var = match &**var_side {
2762 Expr::Local { var, .. } => *var,
2763 _ => return,
2764 };
2765 if !bv.contains(&var) {
2766 return;
2767 }
2768 let _ = const_side;
2769 match op {
2770 BinOp::Ne => {
2771 let inner = var_side.clone();
2772 *x = *inner;
2773 }
2774 BinOp::Eq => {
2775 let inner = var_side.clone();
2776 *x = Expr::Un {
2777 op: UnOp::Not,
2778 e: Box::new(*inner),
2779 };
2780 }
2781 _ => {}
2782 }
2783 }
2784 });
2785 };
2786 match s {
2787 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => fold(cond),
2788 _ => {}
2789 }
2790 walk_mut_deep(s, &mut |st| {
2791 let cond = match st {
2792 Stmt::If { cond, .. } => Some(cond),
2793 Stmt::While { cond, .. } => Some(cond),
2794 Stmt::DoWhile { cond, .. } => Some(cond),
2795 _ => None,
2796 };
2797 if let Some(c) = cond {
2798 fold(c);
2799 }
2800 });
2801}
2802
2803pub fn flip_const_compares(s: &mut Stmt) {
2805 rewrite_exprs(s, &mut |e| {
2806 deep_rewrite(e, &mut |x| {
2807 if let Expr::Bin { op, l, r, .. } = x {
2808 if matches!(
2809 op,
2810 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
2811 ) {
2812 let l_const = matches!(&**l, Expr::Const(_));
2813 let r_var = matches!(&**r, Expr::Local { .. });
2814 if l_const && r_var {
2815 std::mem::swap(l, r);
2816 }
2817 }
2818 }
2819 });
2820 });
2821}
2822
2823pub fn null_compares(vt: &VarTable, body: &mut Stmt) {
2825 let obj_vars: HashSet<u32> = vt
2826 .vars
2827 .iter()
2828 .filter(|v| v.ty.erased().is_reference())
2829 .map(|v| v.id)
2830 .collect();
2831 rewrite_exprs(body, &mut |e| {
2832 deep_rewrite(e, &mut |x| {
2833 null_side_rewrite(x, &|v| obj_vars.contains(&v));
2834 });
2835 });
2836}
2837
2838pub fn drop_dead_locals(body: &mut Stmt) {
2847 loop {
2848 let mut reads: Vec<usize> = Vec::new();
2849 count_reads(body, &mut reads);
2850 let mut dropped = 0usize;
2851 walk_mut_deep(body, &mut |st| match st {
2852 Stmt::Block(items) => prune_dead_items(items, &reads, &mut dropped),
2853 Stmt::Switch { cases, .. } => {
2863 for c in cases {
2864 prune_dead_items(&mut c.body, &reads, &mut dropped);
2865 }
2866 }
2867 Stmt::For { init, .. } => prune_dead_items(init, &reads, &mut dropped),
2868 _ => {}
2869 });
2870 if dropped == 0 {
2871 break;
2872 }
2873 }
2874}
2875
2876fn prune_dead_items(items: &mut Vec<Stmt>, reads: &[usize], dropped: &mut usize) {
2881 let dead = |v: u32| reads.get(v as usize).copied().unwrap_or(0) == 0;
2882 items.retain_mut(|x| match x {
2883 Stmt::ExprStmt(Expr::Assign { target, value, op, .. }) => {
2884 let dead_target = matches!(&**target, Expr::Local { var, .. } if dead(*var));
2885 if dead_target && matches!(*op, AssignOp::Plain) {
2886 *dropped += 1;
2887 if has_side_effects(value) {
2888 let e = std::mem::replace(value, Box::new(Expr::This));
2889 *x = Stmt::ExprStmt(*e);
2890 true
2891 } else {
2892 false
2893 }
2894 } else {
2895 true
2896 }
2897 }
2898 Stmt::LocalDef { var, init, .. } if dead(*var) => {
2899 *dropped += 1;
2900 if let Some(e) = init.take() {
2901 if has_side_effects(&e) {
2902 *x = Stmt::ExprStmt(e);
2903 return true;
2904 }
2905 }
2906 false
2907 }
2908 _ => true,
2909 });
2910}
2911
2912fn for_each_child_stmt<'a>(st: &'a Stmt, f: &mut impl FnMut(&'a Stmt)) {
2915 match st {
2916 Stmt::Block(v) => {
2917 for x in v {
2918 f(x);
2919 }
2920 }
2921 Stmt::If {
2922 then_stmt,
2923 else_stmt,
2924 ..
2925 } => {
2926 f(then_stmt);
2927 if let Some(e) = else_stmt {
2928 f(e);
2929 }
2930 }
2931 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => f(body),
2932 Stmt::For { init, body, .. } => {
2933 for x in init {
2934 f(x);
2935 }
2936 f(body);
2937 }
2938 Stmt::ForEach { body, .. } => f(body),
2939 Stmt::Switch { cases, default, .. } => {
2940 for c in cases {
2941 for x in &c.body {
2942 f(x);
2943 }
2944 }
2945 if let Some(d) = default {
2946 f(d);
2947 }
2948 }
2949 Stmt::Try {
2950 body,
2951 catches,
2952 finally,
2953 } => {
2954 f(body);
2955 for c in catches {
2956 f(&c.body);
2957 }
2958 if let Some(fl) = finally {
2959 f(fl);
2960 }
2961 }
2962 Stmt::TryWithResources {
2963 resources,
2964 body,
2965 catches,
2966 finally,
2967 } => {
2968 for r in resources {
2969 f(r);
2970 }
2971 f(body);
2972 for c in catches {
2973 f(&c.body);
2974 }
2975 if let Some(fl) = finally {
2976 f(fl);
2977 }
2978 }
2979 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => f(body),
2980 _ => {}
2981 }
2982}
2983
2984fn stmt_shallow_vars<F: FnMut(u32)>(st: &Stmt, out: &mut F) {
2989 fn ex<F: FnMut(u32)>(e: &Expr, out: &mut F) {
2990 visit_exprs(e, &mut |x| {
2991 if let Expr::Local { var, .. } = x {
2992 out(*var);
2993 }
2994 });
2995 }
2996 match st {
2997 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
2998 ex(e, out)
2999 }
3000 Stmt::TernaryValue { e } => ex(e, out),
3001 Stmt::Return(Some(e)) => ex(e, out),
3002 Stmt::LocalDef { var, init, .. } => {
3003 out(*var);
3004 if let Some(e) = init {
3005 ex(e, out);
3006 }
3007 }
3008 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
3009 ex(cond, out)
3010 }
3011 Stmt::For { cond, update, .. } => {
3012 if let Some(c) = cond {
3013 ex(c, out);
3014 }
3015 for u in update {
3016 ex(u, out);
3017 }
3018 }
3019 Stmt::ForEach { iterable, .. } => ex(iterable, out),
3020 Stmt::Switch { selector, cases, .. } => {
3021 ex(selector, out);
3022 for cg in cases {
3023 if let Some(g) = &cg.guard {
3024 ex(g, out);
3025 }
3026 }
3027 }
3028 Stmt::Assert { cond, msg } => {
3029 ex(cond, out);
3030 if let Some(m) = msg {
3031 ex(m, out);
3032 }
3033 }
3034 Stmt::Synchronized { lock, .. } => ex(lock, out),
3035 _ => {}
3036 }
3037}
3038
3039fn scope_pass_a(st: &Stmt, cur: u32, next: &mut u32, def_block: &mut [u32], sizes: &mut Vec<u32>) {
3042 if let Stmt::LocalDef { var, .. } = st {
3043 let i = *var as usize;
3044 if i < def_block.len() && def_block[i] == u32::MAX {
3045 def_block[i] = cur;
3046 }
3047 }
3048 for_each_child_stmt(st, &mut |c| {
3049 if matches!(c, Stmt::Block(_)) {
3050 let id = *next;
3051 *next += 1;
3052 sizes.push(0);
3053 scope_pass_a(c, id, next, def_block, sizes);
3054 sizes[id as usize] = *next - id;
3055 } else {
3056 scope_pass_a(c, cur, next, def_block, sizes);
3057 }
3058 });
3059}
3060
3061fn scope_pass_b(
3064 st: &Stmt,
3065 cur: u32,
3066 next: &mut u32,
3067 def_block: &[u32],
3068 sizes: &[u32],
3069 within: &mut [u32],
3070 total: &mut [u32],
3071) {
3072 stmt_shallow_vars(st, &mut |v| {
3073 let i = v as usize;
3074 if i < total.len() {
3075 total[i] += 1;
3076 let d = def_block[i];
3077 if d != u32::MAX && d <= cur && cur < d + sizes[d as usize] {
3078 within[i] += 1;
3079 }
3080 }
3081 });
3082 for_each_child_stmt(st, &mut |c| {
3083 if matches!(c, Stmt::Block(_)) {
3084 let id = *next;
3085 *next += 1;
3086 scope_pass_b(c, id, next, def_block, sizes, within, total);
3087 } else {
3088 scope_pass_b(c, cur, next, def_block, sizes, within, total);
3089 }
3090 });
3091}
3092
3093pub fn strip_enum_const_inits(s: &mut Stmt, class: &crate::PoolClass) {
3102 let const_fields: jdc_core::FxHashSet<&str> = class
3103 .static_fields
3104 .iter()
3105 .filter(|f| f.access & crate::access::ACC_ENUM != 0)
3106 .map(|f| f.name.as_str())
3107 .collect();
3108 if const_fields.is_empty() {
3109 return;
3110 }
3111 if let Stmt::Block(stmts) = s {
3112 stmts.retain(|st| {
3113 !matches!(
3114 st,
3115 Stmt::ExprStmt(Expr::Assign { target, value, .. })
3116 if matches!(&**target,
3117 Expr::Field { cls, name, is_static: true, .. }
3118 if cls.as_ref() == class.name
3119 && const_fields.contains(&**name))
3120 && matches!(&**value, Expr::New { cls: ncls, .. } if ncls.as_ref() == class.name)
3121 )
3122 });
3123 }
3124}
3125
3126pub fn fix_ctor_super_first(body: &mut Stmt) {
3132 let Stmt::Block(stmts) = body else { return };
3133 if stmts.is_empty() || is_bare_ctor_call(stmts.first().unwrap()) {
3134 return;
3135 }
3136 if let Some(pos) = stmts.iter().position(is_bare_ctor_call) {
3138 let call = stmts.remove(pos);
3139 stmts.insert(0, call);
3140 return;
3141 }
3142 let mut found: Option<(usize, usize)> = None;
3152 for (i, st) in stmts.iter().enumerate() {
3153 if let Stmt::Block(inner) = st {
3154 if let Some(p) = inner.iter().position(is_bare_ctor_call) {
3155 if inner[..p]
3156 .iter()
3157 .all(|s| matches!(s, Stmt::LocalDef { .. } | Stmt::ExprStmt(_)))
3158 {
3159 found = Some((i, p));
3160 break;
3161 }
3162 }
3163 }
3164 }
3165 if let Some((i, p)) = found {
3166 let call = match &mut stmts[i] {
3167 Stmt::Block(inner) => inner.remove(p),
3168 _ => unreachable!(),
3169 };
3170 stmts.insert(0, call);
3171 }
3172}
3173
3174fn is_delegation_expr(e: &Expr) -> bool {
3183 matches!(
3184 e,
3185 Expr::Method { name, is_special: true, owner, .. }
3186 if &**name == "<init>"
3187 && (owner.is_none() || matches!(owner.as_deref(), Some(Expr::This)))
3188 )
3189}
3190
3191fn is_bare_ctor_call(s: &Stmt) -> bool {
3192 matches!(s, Stmt::ExprStmt(e) if is_delegation_expr(e))
3193}
3194
3195pub fn fix_ctor_conditional_super(body: &mut Stmt) {
3226 if !matches!(body, Stmt::Block(_)) {
3229 let inner = std::mem::replace(body, Stmt::Block(Vec::new()));
3230 *body = Stmt::Block(vec![inner]);
3231 }
3232 let Stmt::Block(stmts) = body else { return };
3233 while stmts.len() == 1 {
3237 let Stmt::Block(inner) = &stmts[0] else { break };
3238 *stmts = inner.clone();
3239 }
3240 if stmts.is_empty() {
3241 return;
3242 }
3243 if is_bare_ctor_call(stmts.first().unwrap()) {
3244 dedupe_ctor_delegations(body);
3245 return;
3246 }
3247 if try_linear_super_inline(stmts) {
3248 return;
3249 }
3250 try_merge_branched_super(stmts);
3251}
3252
3253pub fn dedupe_ctor_delegations(body: &mut Stmt) {
3263 let Stmt::Block(stmts) = body else { return };
3264 let Some(d0) = stmts.first().and_then(|s| match s {
3265 Stmt::ExprStmt(e) if is_delegation_expr(e) => Some(e.clone()),
3266 _ => None,
3267 }) else {
3268 return;
3269 };
3270 fn rec(s: &mut Stmt, d0: &Expr) {
3271 match s {
3272 Stmt::Block(v) => {
3273 v.retain(|x| {
3274 !matches!(x, Stmt::ExprStmt(e) if is_delegation_expr(e) && same_delegation(e, d0))
3275 });
3276 for x in v.iter_mut() {
3277 rec(x, d0);
3278 }
3279 }
3280 Stmt::If { then_stmt, else_stmt, .. } => {
3281 rec(then_stmt, d0);
3282 if let Some(e) = else_stmt {
3283 rec(e, d0);
3284 }
3285 }
3286 _ => {}
3287 }
3288 }
3289 for s in stmts.iter_mut().skip(1) {
3290 rec(s, &d0);
3291 }
3292}
3293
3294fn same_delegation(a: &Expr, b: &Expr) -> bool {
3298 match (a, b) {
3299 (
3300 Expr::Method { name: n1, cls: c1, is_super: s1, args: a1, .. },
3301 Expr::Method { name: n2, cls: c2, is_super: s2, args: a2, .. },
3302 ) => {
3303 &**n1 == "<init>"
3304 && &**n2 == "<init>"
3305 && c1 == c2
3306 && s1 == s2
3307 && (a1 == a2 || (a1.is_empty() && a2.is_empty()))
3308 }
3309 _ => false,
3310 }
3311}
3312
3313fn ctor_call_expr(s: &Stmt) -> Option<&Expr> {
3315 match s {
3316 Stmt::ExprStmt(e) if is_delegation_expr(e) => Some(e),
3317 _ => None,
3318 }
3319}
3320
3321fn same_ctor_kind(a: &Expr, b: &Expr) -> bool {
3323 match (a, b) {
3324 (
3325 Expr::Method { cls: c1, is_super: s1, owner: o1, .. },
3326 Expr::Method { cls: c2, is_super: s2, owner: o2, .. },
3327 ) => c1 == c2 && s1 == s2 && o1 == o2,
3328 _ => false,
3329 }
3330}
3331
3332fn flat_list(s: &Stmt) -> Vec<&Stmt> {
3337 fn rec<'a>(s: &'a Stmt, out: &mut Vec<&'a Stmt>) {
3338 match s {
3339 Stmt::Block(v) => {
3340 for x in v {
3341 rec(x, out);
3342 }
3343 }
3344 other => out.push(other),
3345 }
3346 }
3347 let mut out = Vec::new();
3348 rec(s, &mut out);
3349 out
3350}
3351
3352fn strip_lost_alloc_owners(e: &mut Expr) {
3358 deep_rewrite(e, &mut |x| {
3359 if let Expr::Method { name, is_special: true, owner: Some(o), .. } = x {
3360 if &**name == "<init>" && !matches!(**o, Expr::This) {
3361 **o = Expr::Const(jdc_core::ir::expr::ConstVal::Int(0));
3362 }
3363 }
3364 });
3365}
3366
3367fn count_locals_expr(e: &Expr) -> std::collections::HashMap<u32, usize> {
3370 let mut m = std::collections::HashMap::new();
3371 let mut probe = e.clone();
3372 strip_lost_alloc_owners(&mut probe);
3373 deep_rewrite(&mut probe, &mut |x| {
3374 if let Expr::Local { var: v, .. } = x {
3375 *m.entry(*v).or_insert(0) += 1;
3376 }
3377 });
3378 m
3379}
3380
3381fn count_locals_stmts(ss: &[Stmt]) -> std::collections::HashMap<u32, usize> {
3382 let mut m = std::collections::HashMap::new();
3383 for s in ss {
3384 let mut c = s.clone();
3385 walk_stmt_exprs(&mut c, &mut |e| {
3386 strip_lost_alloc_owners(e);
3387 deep_rewrite(e, &mut |x| {
3388 if let Expr::Local { var: v, .. } = x {
3389 *m.entry(*v).or_insert(0) += 1;
3390 }
3391 });
3392 });
3393 }
3394 m
3395}
3396
3397fn cnt(m: &std::collections::HashMap<u32, usize>, v: u32) -> usize {
3398 m.get(&v).copied().unwrap_or(0)
3399}
3400
3401fn inline_locals(
3405 e: &Expr,
3406 map: &std::collections::HashMap<u32, Expr>,
3407 depth: u32,
3408) -> Option<Expr> {
3409 if depth > 4 {
3410 return None;
3411 }
3412 let mut out = e.clone();
3413 let mut fail = false;
3414 deep_rewrite(&mut out, &mut |x| {
3415 if let Expr::Local { var, .. } = x {
3416 if let Some(def) = map.get(var) {
3417 match inline_locals(def, map, depth + 1) {
3418 Some(r) => *x = r,
3419 None => fail = true,
3420 }
3421 }
3422 }
3423 });
3424 if fail {
3425 None
3426 } else {
3427 Some(out)
3428 }
3429}
3430
3431fn def_of(s: &Stmt) -> Option<(u32, &Expr)> {
3433 match s {
3434 Stmt::LocalDef { var, init: Some(e), .. } => Some((*var, e)),
3435 Stmt::ExprStmt(Expr::Assign {
3436 target,
3437 value,
3438 op: jdc_core::ir::expr::AssignOp::Plain,
3439 ..
3440 }) => match &**target {
3441 Expr::Local { var, .. } => Some((*var, value)),
3442 _ => None,
3443 },
3444 _ => None,
3445 }
3446}
3447
3448fn try_linear_super_inline(stmts: &mut Vec<Stmt>) -> bool {
3450 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
3451 return false;
3452 };
3453 if pos == 0 {
3454 return false; }
3456 if !stmts[..pos]
3458 .iter()
3459 .all(|s| matches!(s, Stmt::LocalDef { .. } | Stmt::ExprStmt(_)))
3460 {
3461 return false;
3462 }
3463 let Some(call) = ctor_call_expr(&stmts[pos]) else {
3464 return false;
3465 };
3466 let Expr::Method { args, .. } = call else {
3467 return false;
3468 };
3469 let mut map: std::collections::HashMap<u32, Expr> =
3471 std::collections::HashMap::new();
3472 let mut def_idx: std::collections::HashMap<u32, usize> =
3473 std::collections::HashMap::new();
3474 for (i, s) in stmts[..pos].iter().enumerate() {
3475 if let Some((v, e)) = def_of(s) {
3476 map.insert(v, e.clone());
3477 def_idx.insert(v, i);
3478 }
3479 }
3480 let mut needed: Vec<u32> = Vec::new();
3482 for a in args {
3483 let mut probe = a.clone();
3484 strip_lost_alloc_owners(&mut probe);
3485 deep_rewrite(&mut probe, &mut |x| {
3486 if let Expr::Local { var, .. } = x {
3487 if map.contains_key(var) && !needed.contains(var) {
3488 needed.push(*var);
3489 }
3490 }
3491 });
3492 }
3493 if needed.is_empty() {
3494 return false; }
3496 let mut consumed: Vec<usize> = Vec::new();
3499 let use_counts = count_locals_stmts(stmts);
3500 for &v in &needed {
3501 let def = &map[&v];
3502 let uses = cnt(&use_counts, v);
3503 if jdc_core::ir::build::has_side_effects(def) && uses != 1 {
3504 return false;
3505 }
3506 if uses == 1 {
3507 consumed.push(def_idx[&v]);
3508 }
3509 }
3510 let mut new_args: Vec<Expr> = Vec::with_capacity(args.len());
3512 for a in args {
3513 match inline_locals(a, &map, 0) {
3514 Some(r) => new_args.push(r),
3515 None => return false,
3516 }
3517 }
3518 let mut call_stmt = stmts.remove(pos);
3521 if let Stmt::ExprStmt(Expr::Method { args: slot, .. }) = &mut call_stmt {
3522 *slot = new_args;
3523 }
3524 let mut rest: Vec<Stmt> = Vec::with_capacity(stmts.len() + 1);
3525 rest.push(call_stmt);
3526 for (i, s) in stmts.drain(..).enumerate() {
3527 let orig = if i >= pos { i + 1 } else { i };
3530 if !consumed.contains(&orig) {
3531 rest.push(s);
3532 }
3533 }
3534 *stmts = rest;
3535 true
3536}
3537
3538fn try_merge_branched_super(stmts: &mut Vec<Stmt>) -> bool {
3540 let if_positions: Vec<usize> = stmts
3544 .iter()
3545 .enumerate()
3546 .filter(|(_, s)| matches!(s, Stmt::If { .. }))
3547 .map(|(i, _)| i)
3548 .collect();
3549 for if_pos in if_positions {
3550 let gate = match &stmts[if_pos] {
3556 Stmt::If { then_stmt, else_stmt, .. } => {
3557 contains_delegation(then_stmt)
3558 || else_stmt.as_ref().is_some_and(|e| contains_delegation(e))
3559 }
3560 _ => false,
3561 };
3562 if gate && merge_at(stmts, if_pos) {
3563 return true;
3564 }
3565 }
3566 false
3567}
3568
3569fn contains_delegation(s: &Stmt) -> bool {
3572 match s {
3573 Stmt::ExprStmt(e) => is_delegation_expr(e),
3574 Stmt::Block(v) => v.iter().any(contains_delegation),
3575 Stmt::If { then_stmt, else_stmt, .. } => {
3576 contains_delegation(then_stmt)
3577 || else_stmt.as_ref().is_some_and(|e| contains_delegation(e))
3578 }
3579 _ => false,
3580 }
3581}
3582
3583
3584
3585fn merge_at(stmts: &mut Vec<Stmt>, if_pos: usize) -> bool {
3598 enum Br {
3603 Call(usize, Vec<(u32, Expr)>),
3604 Clean,
3605 Dirty,
3606 }
3607 fn split_branch(list: &[&Stmt]) -> Br {
3608 let call_pos = list.iter().position(|s| ctor_call_expr(s).is_some());
3609 let Some(c) = call_pos else {
3610 return if list.iter().any(|s| contains_delegation(s)) {
3613 Br::Dirty
3614 } else {
3615 Br::Clean
3616 };
3617 };
3618 if list.iter().skip(c + 1).any(|s| ctor_call_expr(s).is_some()) {
3619 return Br::Dirty; }
3621 let mut defs: Vec<(u32, Expr)> = Vec::new();
3622 for s in list.iter().take(c) {
3623 match def_of(s) {
3624 Some((v, e)) => defs.push((v, e.clone())),
3625 None => match s {
3626 Stmt::ExprStmt(_) | Stmt::LocalDef { .. } => {}
3629 _ => return Br::Dirty, },
3631 }
3632 }
3633 Br::Call(c, defs)
3634 }
3635 fn cond_def_of(s: &Stmt) -> Option<(u32, Expr)> {
3637 let Stmt::If { cond, then_stmt, else_stmt: Some(e), .. } = s else {
3638 return None;
3639 };
3640 fn one(b: &Stmt) -> Option<(u32, &Expr)> {
3641 let l = flat_list(b);
3642 if l.len() == 1 {
3643 def_of(l[0])
3644 } else {
3645 None
3646 }
3647 }
3648 let (v1, e1) = one(then_stmt)?;
3649 let (v2, e2) = one(e)?;
3650 if v1 != v2 {
3651 return None;
3652 }
3653 Some((
3654 v1,
3655 Expr::Cond {
3656 c: Box::new(cond.clone()),
3657 t: Box::new(e1.clone()),
3658 f: Box::new(e2.clone()),
3659 },
3660 ))
3661 }
3662
3663 let mut pmap: std::collections::HashMap<u32, Expr> =
3665 std::collections::HashMap::new();
3666 let mut pidx: std::collections::HashMap<u32, usize> =
3667 std::collections::HashMap::new();
3668 for (i, s) in stmts[..if_pos].iter().enumerate() {
3669 match s {
3670 Stmt::LocalDef { .. } | Stmt::ExprStmt(_) => {
3671 if let Some((v, e)) = def_of(s) {
3672 pmap.insert(v, e.clone());
3673 pidx.insert(v, i);
3674 }
3675 }
3676 Stmt::If { .. } => match cond_def_of(s) {
3677 Some((v, e)) => {
3678 pmap.insert(v, e);
3679 pidx.insert(v, i);
3680 }
3681 None => return false,
3682 },
3683 _ => return false,
3684 }
3685 }
3686 let (cond, then_v, else_v, had_else) = match &stmts[if_pos] {
3687 Stmt::If { cond, then_stmt, else_stmt, .. } => (
3688 cond.clone(),
3689 then_stmt.as_ref().clone(),
3690 else_stmt.as_ref().map_or_else(|| Stmt::Block(Vec::new()), |e| e.as_ref().clone()),
3691 else_stmt.is_some(),
3692 ),
3693 _ => return false,
3694 };
3695 fn normalize_branch(b: &mut Stmt) {
3701 let slot: &mut Stmt = match b {
3706 Stmt::Block(v) if v.len() == 1 => &mut v[0],
3707 other => other,
3708 };
3709 if !matches!(slot, Stmt::If { .. }) || !contains_delegation(slot) {
3710 return;
3711 }
3712 let taken = std::mem::replace(slot, Stmt::Block(Vec::new()));
3713 let mut tmp = vec![taken];
3714 if merge_at(&mut tmp, 0) {
3715 *slot = Stmt::Block(tmp);
3716 } else {
3717 *slot = tmp.pop().unwrap();
3718 }
3719 }
3720 let mut then_v = then_v;
3721 let mut else_v = else_v;
3722 normalize_branch(&mut then_v);
3723 normalize_branch(&mut else_v);
3724 let then_list = flat_list(&then_v);
3725 let else_list = flat_list(&else_v);
3726
3727 enum Side {
3730 Call { at: usize, defs: Vec<(u32, Expr)> },
3731 Clean,
3732 }
3733 let (t_side, e_side, two_sided) = match (split_branch(&then_list), split_branch(&else_list)) {
3734 (Br::Call(tc, td), Br::Call(ec, ed)) => (
3735 Side::Call { at: tc, defs: td },
3736 Side::Call { at: ec, defs: ed },
3737 true,
3738 ),
3739 (Br::Call(tc, td), Br::Clean) => {
3740 (Side::Call { at: tc, defs: td }, Side::Clean, false)
3741 }
3742 (Br::Clean, Br::Call(ec, ed)) => {
3743 (Side::Clean, Side::Call { at: ec, defs: ed }, false)
3744 }
3745 _ => return false,
3746 };
3747 let (t_call, e_call) = match (&t_side, &e_side) {
3749 (Side::Call { at: ta, .. }, Side::Call { at: ea, .. }) => (
3750 ctor_call_expr(then_list[*ta]),
3751 ctor_call_expr(else_list[*ea]),
3752 ),
3753 (Side::Call { at: ta, .. }, Side::Clean) => {
3754 (ctor_call_expr(then_list[*ta]), None)
3755 }
3756 (Side::Clean, Side::Call { at: ea, .. }) => {
3757 (None, ctor_call_expr(else_list[*ea]))
3758 }
3759 _ => return false,
3760 };
3761 let (call_list, call_at) = match &t_side {
3763 Side::Call { at, .. } => (&then_list, *at),
3764 Side::Clean => match &e_side {
3765 Side::Call { at, .. } => (&else_list, *at),
3766 Side::Clean => return false,
3767 },
3768 };
3769 let (base_args, other_args) = if two_sided {
3770 let (Some(tc), Some(ec)) = (t_call, e_call) else {
3771 return false;
3772 };
3773 if !same_ctor_kind(tc, ec) {
3774 return false;
3775 }
3776 let (Expr::Method { args: a1, .. }, Expr::Method { args: a2, .. }) = (tc, ec) else {
3777 return false;
3778 };
3779 if a1.len() != a2.len() {
3780 return false;
3781 }
3782 (a1, Some(a2))
3783 } else {
3784 let c = t_call.or(e_call);
3785 let Some(c) = c else { return false };
3786 let Expr::Method { args, .. } = c else {
3787 return false;
3788 };
3789 (args, None)
3790 };
3791
3792 let mut tmap: std::collections::HashMap<u32, Expr> = pmap.clone();
3794 let mut emap: std::collections::HashMap<u32, Expr> = pmap.clone();
3795 if let Side::Call { defs, .. } = &t_side {
3796 for (v, e) in defs {
3797 tmap.insert(*v, e.clone());
3798 }
3799 }
3800 if let Side::Call { defs, .. } = &e_side {
3801 for (v, e) in defs {
3802 emap.insert(*v, e.clone());
3803 }
3804 }
3805 let Some(cond_i) = inline_locals(&cond, &pmap, 0) else {
3806 return false;
3807 };
3808 let mut then_args: Vec<Expr> = Vec::with_capacity(base_args.len());
3810 let mut else_args: Vec<Expr> = Vec::new();
3811 if two_sided {
3812 for a in base_args {
3813 match inline_locals(a, &tmap, 0) {
3814 Some(r) => then_args.push(r),
3815 None => return false,
3816 }
3817 }
3818 for a in other_args.unwrap() {
3819 match inline_locals(a, &emap, 0) {
3820 Some(r) => else_args.push(r),
3821 None => return false,
3822 }
3823 }
3824 } else {
3825 let from_then = matches!(&t_side, Side::Call { .. });
3828 for a in base_args {
3829 let r = if from_then {
3830 inline_locals(a, &tmap, 0)
3831 } else {
3832 inline_locals(a, &emap, 0)
3833 };
3834 match r {
3835 Some(r) => then_args.push(r),
3836 None => return false,
3837 }
3838 }
3839 }
3840 let primary_args = base_args;
3841 let mut merged: Vec<Expr> = Vec::with_capacity(then_args.len());
3843 let mut differ = 0usize;
3844 for (j, t) in then_args.iter().enumerate() {
3845 if two_sided {
3846 let e = &else_args[j];
3847 if t == e {
3848 merged.push(t.clone());
3849 } else {
3850 differ += 1;
3851 merged.push(Expr::Cond {
3852 c: Box::new(cond_i.clone()),
3853 t: Box::new(t.clone()),
3854 f: Box::new(e.clone()),
3855 });
3856 }
3857 } else {
3858 merged.push(t.clone());
3859 }
3860 }
3861
3862 let vars_in = |e: &Expr| -> Vec<u32> {
3864 let mut vs = Vec::new();
3865 let mut probe = e.clone();
3866 strip_lost_alloc_owners(&mut probe);
3867 deep_rewrite(&mut probe, &mut |x| {
3868 if let Expr::Local { var, .. } = x {
3869 if !vs.contains(var) {
3870 vs.push(*var);
3871 }
3872 }
3873 });
3874 vs
3875 };
3876 let branch_remainder = |list: &[&Stmt], side: &Side| -> Vec<Stmt> {
3879 match side {
3880 Side::Clean => list.iter().map(|s| (*s).clone()).collect(),
3881 Side::Call { at, .. } => {
3882 list.iter()
3883 .enumerate()
3884 .filter(|(i, _)| *i != *at)
3885 .map(|(_, s)| (*s).clone())
3886 .collect()
3887 }
3888 }
3889 };
3890 let tail_then_v = branch_remainder(&then_list, &t_side);
3891 let tail_else_v = branch_remainder(&else_list, &e_side);
3892 let post = &stmts[if_pos + 1..];
3893 let mut inlined: Vec<u32> = vars_in(&cond);
3894 for a in primary_args.iter().chain(else_args.iter()) {
3895 for v in vars_in(a) {
3896 if !inlined.contains(&v) {
3897 inlined.push(v);
3898 }
3899 }
3900 }
3901 inlined.retain(|v| pmap.contains_key(v) || tmap.contains_key(v) || emap.contains_key(v));
3902 for &v in &inlined {
3904 let in_branch = match (&t_side, &e_side) {
3905 (Side::Call { defs: td, .. }, Side::Call { defs: ed, .. }) => {
3906 td.iter().any(|(d, _)| *d == v) || ed.iter().any(|(d, _)| *d == v)
3907 }
3908 (Side::Call { defs, .. }, Side::Clean)
3909 | (Side::Clean, Side::Call { defs, .. }) => {
3910 defs.iter().any(|(d, _)| *d == v)
3911 }
3912 _ => false,
3913 };
3914 if pmap.contains_key(&v) && in_branch {
3915 return false;
3916 }
3917 }
3918 type Counts = std::collections::HashMap<u32, usize>;
3919 type GuardCounts = (
3920 Counts,
3921 Vec<Counts>,
3922 Vec<Counts>,
3923 Counts,
3924 Counts,
3925 Counts,
3926 Vec<(u32, Counts)>,
3927 );
3928 let (cond_counts, a1_counts, a2_counts, tail1_counts, tail2_counts, post_counts, def_counts): GuardCounts =
3929 if inlined.is_empty() {
3930 Default::default()
3931 } else {
3932 let all_defs: Vec<(u32, &Expr)> = pmap
3933 .iter()
3934 .map(|(k, v)| (*k, v))
3935 .chain(match &t_side {
3936 Side::Call { defs, .. } => defs.iter().map(|(k, v)| (*k, v)).collect(),
3937 Side::Clean => Vec::new(),
3938 })
3939 .chain(match &e_side {
3940 Side::Call { defs, .. } => defs.iter().map(|(k, v)| (*k, v)).collect(),
3941 Side::Clean => Vec::new(),
3942 })
3943 .collect();
3944 (
3945 count_locals_expr(&cond),
3946 primary_args.iter().map(count_locals_expr).collect(),
3947 else_args.iter().map(count_locals_expr).collect(),
3948 count_locals_stmts(&tail_then_v),
3949 count_locals_stmts(&tail_else_v),
3950 count_locals_stmts(post),
3951 all_defs.into_iter().map(|(v, e)| (v, count_locals_expr(e))).collect(),
3952 )
3953 };
3954 for &v in &inlined {
3955 let in_t = matches!(&t_side, Side::Call { defs, .. } if defs.iter().any(|(d, _)| *d == v));
3956 let in_e = matches!(&e_side, Side::Call { defs, .. } if defs.iter().any(|(d, _)| *d == v));
3957 let effectful = [
3958 in_t.then(|| tmap.get(&v)).flatten(),
3959 in_e.then(|| emap.get(&v)).flatten(),
3960 pmap.get(&v),
3961 ]
3962 .into_iter()
3963 .flatten()
3964 .any(jdc_core::ir::build::has_side_effects);
3965 if !effectful {
3966 continue;
3967 }
3968 let u_cond = cnt(&cond_counts, v);
3969 let u_post = cnt(&post_counts, v);
3970 let u_t1 = cnt(&tail1_counts, v);
3971 let u_t2 = cnt(&tail2_counts, v);
3972 let mut u_odef = 0usize;
3973 for (dv, dc) in &def_counts {
3974 if *dv != v && inlined.contains(dv) {
3975 u_odef += cnt(dc, v);
3976 }
3977 }
3978 let mut emit = u_cond * (1 + differ);
3979 let mut uses_args = 0usize;
3980 for j in 0..primary_args.len() {
3981 let c1 = cnt(&a1_counts[j], v);
3982 let c2 = if two_sided && j < a2_counts.len() {
3983 cnt(&a2_counts[j], v)
3984 } else {
3985 0
3986 };
3987 uses_args += c1 + c2;
3988 emit += if two_sided && then_args[j] != else_args[j] {
3989 c1 + c2
3990 } else {
3991 c1.max(c2)
3992 };
3993 }
3994 let ok = if in_t || in_e {
3995 u_cond == 0
3996 && u_post == 0
3997 && u_t1 == 0
3998 && u_t2 == 0
3999 && u_odef == 0
4000 && a1_counts.iter().map(|c| cnt(c, v)).sum::<usize>() <= 1
4001 && a2_counts.iter().map(|c| cnt(c, v)).sum::<usize>() <= 1
4002 && emit <= 2
4003 && (in_t || a1_counts.iter().all(|c| cnt(c, v) == 0))
4004 && (in_e || a2_counts.iter().all(|c| cnt(c, v) == 0))
4005 } else {
4006 u_post == 0
4007 && u_t1 == 0
4008 && u_t2 == 0
4009 && u_odef == 0
4010 && ((u_cond == 1 && uses_args == 0 && emit <= 2)
4011 || (u_cond == 0 && emit <= 1))
4012 };
4013 if !ok {
4014 return false;
4015 }
4016 }
4017
4018 let mut call_stmt = call_list[call_at].clone();
4020 if let Stmt::ExprStmt(Expr::Method { args: slot, .. }) = &mut call_stmt {
4021 *slot = merged;
4022 }
4023 let residual = |v: u32| -> usize {
4024 cnt(&tail1_counts, v) + cnt(&tail2_counts, v) + cnt(&post_counts, v)
4025 };
4026 let mut kept_prelude: Vec<Stmt> = Vec::new();
4027 for (i, s) in stmts[..if_pos].iter().enumerate() {
4028 let consumed = match s {
4029 Stmt::LocalDef { var, .. } => Some(*var),
4030 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
4031 Expr::Local { var, .. } => Some(*var),
4032 _ => None,
4033 },
4034 Stmt::If { .. } => cond_def_of(s).map(|(v, _)| v),
4035 _ => None,
4036 };
4037 if let Some(v) = consumed {
4038 if inlined.contains(&v) && pidx.get(&v) == Some(&i) && residual(v) == 0 {
4039 continue;
4040 }
4041 }
4042 kept_prelude.push(s.clone());
4043 }
4044 let build_keep = |list: &[&Stmt], side: &Side| -> Vec<Stmt> {
4045 match side {
4046 Side::Clean => list.iter().map(|s| (*s).clone()).collect(),
4047 Side::Call { at, .. } => {
4048 let mut out = Vec::new();
4049 for (i, s) in list.iter().enumerate() {
4050 if i == *at {
4051 continue;
4052 }
4053 if i < *at {
4054 if let Some((v, _)) = def_of(s) {
4055 if inlined.contains(&v) && residual(v) == 0 {
4056 continue;
4057 }
4058 }
4059 }
4060 out.push((*s).clone());
4061 }
4062 out
4063 }
4064 }
4065 };
4066 let then_keep = build_keep(&then_list, &t_side);
4067 let else_keep = build_keep(&else_list, &e_side);
4068 let else_part = if else_keep.is_empty() && !had_else {
4069 None
4070 } else {
4071 Some(Box::new(Stmt::Block(else_keep)))
4072 };
4073 let new_if = Stmt::If {
4074 cond: cond_i,
4075 then_stmt: Box::new(Stmt::Block(then_keep)),
4076 else_stmt: else_part,
4077 };
4078 let if_empty = match &new_if {
4079 Stmt::If { then_stmt, else_stmt, .. } => {
4080 let be = |s: &Stmt| matches!(s, Stmt::Block(v) if v.is_empty());
4081 be(then_stmt) && else_stmt.as_ref().is_none_or(|e| be(e))
4082 }
4083 _ => false,
4084 };
4085 let mut out: Vec<Stmt> = Vec::with_capacity(stmts.len());
4086 out.push(call_stmt);
4087 out.extend(kept_prelude);
4088 if !if_empty {
4089 out.push(new_if);
4090 }
4091 out.extend(stmts[if_pos + 1..].to_vec());
4092 *stmts = out;
4093 true
4094}
4095
4096
4097pub fn strip_enum_ctor_super(body: &mut Stmt) {
4098 let is_enum_super = |s: &Stmt| {
4099 matches!(
4100 s,
4101 Stmt::ExprStmt(Expr::Method { name, is_super: true, .. }) if &**name == "<init>"
4102 )
4103 };
4104 let Stmt::Block(stmts) = body else {
4105 return;
4106 };
4107 stmts.retain(|s| !is_enum_super(s));
4108 for st in stmts.iter_mut() {
4109 if let Stmt::Block(inner) = st {
4110 inner.retain(|s| !is_enum_super(s));
4111 }
4112 }
4113}
4114
4115pub fn ensure_declared(body: &mut Stmt, vt: &VarTable) {
4116 let n = vt.vars.len().max(1);
4120
4121 let mut seen: HashSet<u32> = HashSet::default();
4124 demote_dup_decls(body, &mut seen, vt);
4125
4126 let mut def_block: Vec<u32> = vec![u32::MAX; n];
4134 let mut sizes: Vec<u32> = vec![0];
4135 let mut next_id: u32 = 1;
4136 scope_pass_a(body, 0, &mut next_id, &mut def_block, &mut sizes);
4137 sizes[0] = next_id;
4138 let mut within: Vec<u32> = vec![0; n];
4139 let mut total: Vec<u32> = vec![0; n];
4140 let mut next_b: u32 = 1;
4141 scope_pass_b(body, 0, &mut next_b, &def_block, &sizes, &mut within, &mut total);
4142 let mut hoist: HashSet<u32> = HashSet::default();
4143 for v in 0..n {
4144 if def_block[v] != u32::MAX && def_block[v] != 0 && within[v] < total[v] {
4145 hoist.insert(v as u32);
4146 }
4147 }
4148 if !hoist.is_empty() {
4149 let mut seed: HashSet<u32> = hoist.iter().copied().collect();
4150 demote_dup_decls(body, &mut seed, vt);
4151 }
4152
4153 let mut declared: Vec<bool> = vec![false; n];
4154 let mut is_param: Vec<bool> = vec![false; n];
4155 for v in &vt.vars {
4156 if v.is_param && (v.id as usize) < n {
4157 is_param[v.id as usize] = true;
4158 }
4159 }
4160 let mut is_catch: Vec<bool> = vec![false; n];
4166 walk_all(body, &mut |st| {
4167 if let Stmt::Try { catches, .. } = st {
4168 for c in catches {
4169 if (c.var as usize) < n {
4170 is_catch[c.var as usize] = true;
4171 }
4172 }
4173 }
4174 });
4175 walk_all(body, &mut |st| {
4176 if let Stmt::LocalDef { var, .. } = st {
4177 if (*var as usize) < n {
4178 declared[*var as usize] = true;
4179 }
4180 }
4181 });
4182 let mut used: HashSet<u32> = HashSet::default();
4183 stmt_collect_vars(body, &mut used, true);
4187
4188 let mut needs_decl: Vec<u32> = used
4191 .into_iter()
4192 .filter(|v| {
4193 let i = *v as usize;
4194 i >= n
4195 || ((!declared[i] && !is_param[i] && !is_catch[i]) || hoist.contains(v))
4196 })
4197 .collect();
4198 needs_decl.sort_unstable();
4199 needs_decl.dedup();
4200
4201 if !needs_decl.is_empty() {
4202 let mut decls: Vec<Stmt> = needs_decl
4203 .into_iter()
4204 .map(|v| Stmt::LocalDef {
4205 var: v,
4206 init: None,
4207 is_final: false,
4208 force_type: true,
4209 })
4210 .collect();
4211 decls.reverse();
4212 match body {
4213 Stmt::Block(v) => {
4214 for d in decls {
4215 v.insert(0, d);
4216 }
4217 }
4218 other => {
4219 let inner = std::mem::replace(other, Stmt::Block(vec![]));
4220 *other = Stmt::Block({
4221 let mut vs = decls;
4222 vs.push(inner);
4223 vs
4224 });
4225 }
4226 }
4227 }
4228}
4229
4230fn demote_dup_decls(s: &mut Stmt, seen: &mut HashSet<u32>, vt: &VarTable) {
4231 match s {
4232 Stmt::Block(v) => {
4235 for x in v.iter_mut() {
4236 demote_dup_decls(x, seen, vt);
4237 }
4238 }
4239 Stmt::LocalDef { var, init, .. } => {
4240 if seen.contains(var) {
4241 let ty = vt.var(*var).ty.clone();
4242 if let Some(e) = init.take() {
4243 *s = Stmt::ExprStmt(Expr::Assign {
4244 target: Box::new(Expr::Local { var: *var, ty }),
4245 op: AssignOp::Plain,
4246 value: Box::new(e),
4247 });
4248 } else {
4249 *s = Stmt::Block(vec![]);
4250 }
4251 } else {
4252 seen.insert(*var);
4253 }
4254 }
4255 other => {
4256 walk_mut(other, &mut |x| demote_dup_decls(x, seen, vt));
4257 }
4258 }
4259}
4260
4261#[allow(dead_code)]
4262fn unused(_: &Vec<CaseGroup>, _: &Catch) {}
4263
4264pub fn apply_local_names(vt: &mut VarTable, body: &Stmt) {
4278 use std::collections::{HashMap, HashSet};
4279
4280 let mut by_call: HashMap<u32, Vec<String>> = HashMap::new();
4283 let mut by_intrinsics: HashMap<u32, String> = HashMap::new();
4284
4285 visit_all_exprs(body, &mut |e| {
4286 if let Expr::Method { cls, name, args, .. } = e {
4287 if (cls.as_ref() == "kotlin/jvm/internal/Intrinsics"
4288 || cls.as_ref() == "kotlin/jvm/internal/IntrinsicsKt")
4289 && matches!(name.as_ref(), "checkNotNullParameter" | "checkParameterIsNotNull")
4290 && args.len() == 2
4291 {
4292 if let (Expr::Local { var, .. }, Expr::Const(ConstVal::Str(s))) = (&args[0], &args[1])
4293 {
4294 by_intrinsics
4295 .entry(*var)
4296 .or_insert_with(|| sanitize_name(s).unwrap_or_default());
4297 }
4298 }
4299 }
4300 });
4301
4302 walk_all(body, &mut |st| {
4303 let (var, value) = match st {
4304 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e),
4305 Stmt::ExprStmt(Expr::Assign { target, value, .. })
4306 if matches!(&**target, Expr::Local { .. }) =>
4307 {
4308 let Expr::Local { var, .. } = &**target else { return };
4309 (*var, value.as_ref())
4310 }
4311 _ => return,
4312 };
4313 if let Some(n) = defining_call_name(value) {
4314 by_call.entry(var).or_default().push(n);
4315 }
4316 });
4317
4318 let sanit = |n: &str| crate::classdec::java_ident(n).into_owned();
4329 let mut taken: HashSet<String> = HashSet::default();
4330 for v in vt.vars.iter() {
4335 if v.synthetic_name {
4340 taken.insert(sanit(&v.name));
4341 }
4342 }
4343 for v in vt.vars.iter_mut() {
4344 if v.synthetic_name {
4345 continue;
4346 }
4347 if !taken.insert(sanit(&v.name)) {
4348 let base = sanit(&v.name);
4349 for i in 2.. {
4350 let cand = format!("{base}{i}");
4351 if taken.insert(cand.clone()) {
4352 v.name = cand;
4353 break;
4354 }
4355 }
4356 }
4357 }
4358 let claim = |taken: &mut HashSet<String>, want: &str| -> String {
4359 let w = crate::classdec::java_ident(want).into_owned();
4360 if taken.insert(w.clone()) {
4361 return w;
4362 }
4363 for i in 2.. {
4364 let cand = format!("{w}{i}");
4365 if taken.insert(cand.clone()) {
4366 return cand;
4367 }
4368 }
4369 w
4370 };
4371
4372 for info in vt.vars.iter_mut() {
4373 if !info.synthetic_name {
4374 continue;
4375 }
4376 if let Some(n) = by_intrinsics.get(&info.id) {
4379 if !n.is_empty() {
4380 info.name = claim(&mut taken, n);
4381 continue;
4382 }
4383 }
4384 if let Some(names) = by_call.get(&info.id) {
4386 let unique: HashSet<&String> = names.iter().collect();
4387 if unique.len() == 1 {
4388 let n = unique.into_iter().next().unwrap();
4389 if !is_java_keyword(n) {
4390 info.name = claim(&mut taken, n);
4391 continue;
4392 }
4393 }
4394 }
4395 let want = type_alias(&info.ty).map(str::to_string).or_else(|| simple_type_name(&info.ty));
4398 if let Some(n) = want {
4399 info.name = claim(&mut taken, &n);
4400 }
4401 }
4402
4403 let mut seen: HashSet<String> = HashSet::default();
4408 for v in vt.vars.iter_mut() {
4409 if !seen.insert(sanit(&v.name)) {
4410 let base = sanit(&v.name);
4411 for i in 2.. {
4412 let cand = format!("{base}{i}");
4413 if seen.insert(cand.clone()) {
4414 v.name = cand;
4415 break;
4416 }
4417 }
4418 }
4419 }
4420}
4421
4422fn simple_type_name(ty: &TypeRef) -> Option<String> {
4424 let TypeRef::J(JavaType::Object(n)) = ty else {
4425 return None;
4426 };
4427 let simple = n.rsplit('/').next().unwrap_or(n);
4428 let simple = simple.rsplit('$').next().unwrap_or(simple);
4429 let mut chars = simple.chars();
4432 let mut out = String::with_capacity(simple.len());
4433 let mut lowered = false;
4434 for c in chars.by_ref() {
4435 if c.is_ascii_alphabetic() && !lowered {
4436 out.push(c.to_ascii_lowercase());
4437 lowered = true;
4438 } else {
4439 out.push(c);
4440 }
4441 if lowered {
4442 break;
4443 }
4444 }
4445 out.extend(chars);
4446 sanitize_name(&out)
4447}
4448
4449fn defining_call_name(e: &Expr) -> Option<String> {
4453 match e {
4454 Expr::Method { name, .. } => {
4455 let base = name
4456 .strip_prefix("get")
4457 .or_else(|| name.strip_prefix("is"))
4458 .filter(|rest| rest.chars().next().is_some_and(|c| c.is_ascii_uppercase()))
4459 .unwrap_or(name);
4460 let first = base.chars().next()?;
4461 let mut out = String::with_capacity(base.len());
4462 out.push(first.to_ascii_lowercase());
4463 out.extend(base.chars().skip(1));
4464 sanitize_name(&out)
4465 }
4466 Expr::New { cls, .. } => {
4467 let simple = cls.rsplit('/').next().unwrap_or(cls);
4468 let simple = simple.rsplit('$').next().unwrap_or(simple);
4469 let first = simple.chars().next()?;
4470 let mut out = String::with_capacity(simple.len());
4471 out.push(first.to_ascii_lowercase());
4472 out.extend(simple.chars().skip(1));
4473 sanitize_name(&out)
4474 }
4475 _ => None,
4476 }
4477}
4478
4479fn sanitize_name(s: &str) -> Option<String> {
4481 let mut out = String::with_capacity(s.len());
4482 for c in s.chars() {
4483 if c.is_ascii_alphanumeric() || c == '_' || c == '$' {
4484 out.push(c);
4485 } else {
4486 out.push('_');
4487 }
4488 }
4489 if out.len() < 2
4490 || out.chars().next().is_some_and(|c| c.is_ascii_digit())
4491 || is_java_keyword(&out)
4492 {
4493 return None;
4494 }
4495 Some(out)
4496}
4497
4498fn type_alias(ty: &TypeRef) -> Option<&'static str> {
4501 let TypeRef::J(JavaType::Object(n)) = ty else {
4502 return None;
4503 };
4504 Some(match n.as_ref() {
4505 "java/lang/String" | "kotlin/String" => "str",
4506 "java/lang/Class" => "cls",
4507 "java/lang/Throwable" => "th",
4508 "java/lang/Object" | "kotlin/Any" => "obj",
4509 "java/util/Iterator" | "kotlin/collections/Iterator" => "it",
4510 "java/lang/Boolean" => "bool",
4511 "java/lang/Integer" => "num",
4512 "java/lang/Character" => "ch",
4513 "java/lang/Byte" => "b",
4514 "java/lang/Short" => "sh",
4515 "java/lang/Float" => "f",
4516 "java/lang/Double" => "d",
4517 "java/lang/Long" => "i",
4518 "java/lang/StringBuilder" => "sb",
4519 "java/util/ArrayList" => "list",
4520 "java/util/HashMap" => "map",
4521 "android/content/Context" => "context",
4522 "android/content/Intent" => "intent",
4523 "android/os/Bundle" => "bundle",
4524 "android/view/View" => "view",
4525 "android/graphics/Bitmap" => "bitmap",
4526 "android/view/ViewGroup" => "viewGroup",
4527 _ => return None,
4528 })
4529}
4530
4531fn visit_all_exprs<F: FnMut(&Expr)>(s: &Stmt, f: &mut F) {
4533 walk_all(s, &mut |st| {
4534 let exprs: Vec<&Expr> = match st {
4535 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
4536 let mut v: Vec<&Expr> = vec![value];
4537 if !matches!(&**target, Expr::Local { .. }) {
4538 v.push(target);
4539 }
4540 v
4541 }
4542 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
4543 vec![e]
4544 }
4545 Stmt::Return(Some(e)) => vec![e],
4546 Stmt::LocalDef { init: Some(e), .. } => vec![e],
4547 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
4548 vec![cond]
4549 }
4550 _ => Vec::new(),
4551 };
4552 for e in exprs {
4553 visit_exprs(e, f);
4554 }
4555 });
4556}
4557
4558pub fn platform_constants(body: &mut Stmt) {
4563 if !crate::platform::installed() {
4564 return;
4565 }
4566 rewrite_exprs(body, &mut |e| {
4567 if let Expr::Method { cls, name, desc, args, .. } = e {
4568 for a in args.iter_mut() {
4569 let (value, is_long) = match a {
4570 Expr::Const(ConstVal::Int(v)) => (*v as i64, false),
4571 Expr::Const(ConstVal::Long(v)) => (*v, true),
4572 _ => continue,
4573 };
4574 if let Some(named) =
4575 crate::platform::named_constant(cls, name, &desc_to_string(desc), 0, value)
4576 {
4577 let _ = is_long;
4578 *a = Expr::Raw(named);
4579 }
4580 }
4581 }
4582 });
4583}
4584
4585fn desc_to_string(d: &jdc_core::types::MethodDescriptor) -> String {
4587 let mut out = String::from("(");
4588 for a in &d.args {
4589 type_to_desc(a, &mut out);
4590 }
4591 out.push(')');
4592 type_to_desc(&d.ret, &mut out);
4593 out
4594}
4595
4596fn type_to_desc(t: &jdc_core::types::JavaType, out: &mut String) {
4597 match t {
4598 JavaType::Void => out.push('V'),
4599 JavaType::Boolean => out.push('Z'),
4600 JavaType::Byte => out.push('B'),
4601 JavaType::Char => out.push('C'),
4602 JavaType::Short => out.push('S'),
4603 JavaType::Int => out.push('I'),
4604 JavaType::Float => out.push('F'),
4605 JavaType::Long => out.push('J'),
4606 JavaType::Double => out.push('D'),
4607 JavaType::Object(n) => {
4608 out.push('L');
4609 out.push_str(n);
4610 out.push(';');
4611 }
4612 JavaType::Array(inner) => {
4613 out.push('[');
4614 type_to_desc(inner, out);
4615 }
4616 }
4617}
4618
4619pub fn remove_kotlin_checks(s: &mut Stmt) {
4625 walk_mut_deep(s, &mut |st| {
4626 let Stmt::Block(v) = st else { return };
4627 v.retain(|x| !is_kotlin_check(x));
4628 });
4629}
4630
4631fn is_kotlin_check(s: &Stmt) -> bool {
4632 let Stmt::ExprStmt(e) = s else { return false };
4633 let Expr::Method { cls, name, .. } = e else { return false };
4634 (cls.as_ref() == "kotlin/jvm/internal/Intrinsics" || cls.as_ref() == "kotlin/jvm/internal/IntrinsicsKt__Jdk7Kt")
4635 && matches!(
4636 name.as_ref(),
4637 "checkNotNullParameter"
4638 | "checkParameterIsNotNull"
4639 | "checkExpressionValueIsNotNull"
4640 | "checkNotNullExpressionValue"
4641 | "checkReturnedValueIsNotNull"
4642 | "checkFieldIsNotNull"
4643 | "checkNotNull"
4644 )
4645}
4646
4647pub fn inline_accessors(s: &mut Stmt, pool: &DexPool) {
4659 rewrite_exprs(s, &mut |e| {
4660 let Expr::Method { cls, name, desc, args, is_static, .. } = e else { return };
4661 if !*is_static || args.is_empty() {
4662 return;
4663 }
4664 let Some(target) = pool.get(cls) else { return };
4665 let desc_s = desc_to_string(desc);
4666 let Some(m) = target.find_method(name, &desc_s) else { return };
4667 if !(m.is_static() && m.access & access::ACC_SYNTHETIC != 0) {
4668 return;
4669 }
4670 let Some(dex) = pool.dex(m.dex_idx) else { return };
4671 let code = match pool.accessor_code(m.dex_idx, m.code_off) {
4676 Some(bytes) => ddc_dex::CodeItem::parse(&bytes, 0),
4677 None => dex.code_at(m.code_off),
4678 };
4679 let Some(code) = code else { return };
4680 let insns: Vec<&Insn> =
4681 code.insns.iter().filter(|i| !matches!(i.kind, InsnKind::Nop)).collect();
4682 let core: Vec<&Insn> = insns
4684 .iter()
4685 .copied()
4686 .filter(|i| !is_const_only_trace(&i.kind, &insns))
4687 .collect();
4688 match accessor_shape(&dex, &core, &code) {
4689 Some(Shape::Identity(arg_i)) => {
4690 if let Some(a) = args.get(arg_i) {
4691 *e = a.clone();
4692 }
4693 }
4694 Some(Shape::FieldRead { arg, cls: field_cls, field, field_ty }) => {
4695 if let Some(owner) = args.get(arg).cloned().map(Box::new) {
4696 let ty = TypeRef::J(desc_type(&field_ty));
4697 *e = Expr::Field {
4698 owner: Some(owner),
4699 cls: field_cls.into(),
4700 name: field.into(),
4701 ty,
4702 is_static: false,
4703 };
4704 }
4705 }
4706 Some(Shape::Forward { cls: tcls, name: tname, desc: tdesc, instance }) => {
4707 *cls = tcls.into();
4708 *name = tname.into();
4709 *desc = std::sync::Arc::new(tdesc);
4710 if instance && !args.is_empty() {
4711 let recv = args.remove(0);
4713 if let Expr::Method { owner, .. } = e {
4714 *owner = Some(Box::new(recv));
4715 }
4716 }
4717 }
4718 None => {}
4719 }
4720 });
4721}
4722
4723enum Shape {
4725 Identity(usize),
4727 FieldRead { arg: usize, cls: String, field: String, field_ty: String },
4729 Forward { cls: String, name: String, desc: MethodDescriptor, instance: bool },
4732}
4733
4734fn field_of(dex: &ddc_dex::DexFile, field_idx: u32) -> Option<(String, String, String)> {
4736 let f = dex.field(field_idx);
4737 Some((
4738 dex.class_name(f.class_idx),
4741 dex.string(f.name_idx).to_string(),
4742 dex.type_name(f.type_idx).to_string(),
4744 ))
4745}
4746
4747fn param_index(code: &ddc_dex::CodeItem, reg: u16) -> Option<usize> {
4750 let rs = code.registers_size as usize;
4751 let ins = code.ins_size as usize;
4752 let r = reg as usize;
4753 let first = rs.checked_sub(ins)?;
4754 (r >= first && r < rs).then(|| r - first)
4755}
4756
4757fn accessor_shape(
4758 dex: &ddc_dex::DexFile,
4759 core: &[&Insn],
4760 code: &ddc_dex::CodeItem,
4761) -> Option<Shape> {
4762 let core: &[&Insn] = match core.split_first() {
4765 Some((first, rest)) if matches!(first.kind, InsnKind::Const { .. }) => rest,
4766 _ => core,
4767 };
4768 let core: &[&Insn] = match core.split_last() {
4769 Some((last, rest)) if matches!(last.kind, InsnKind::Const { .. }) => rest,
4770 _ => core,
4771 };
4772 match core {
4773 [Insn { kind: InsnKind::Return { src }, .. }] => {
4775 let idx = param_index(code, *src)?;
4776 Some(Shape::Identity(idx))
4777 }
4778 [
4780 Insn { kind: InsnKind::IGet { dst, obj, field_idx }, .. },
4781 Insn { kind: InsnKind::Return { src }, .. },
4782 ] if dst == src => {
4783 let arg = param_index(code, *obj)?;
4784 let (cls, field, field_ty) = field_of(dex, *field_idx)?;
4785 Some(Shape::FieldRead { arg, cls, field, field_ty })
4786 }
4787 [Insn { kind: InsnKind::Invoke { method_idx, regs, kind, .. }, .. }, rest @ ..] if rest.len() <= 2 => {
4789 if !regs.iter().all(|r| param_index(code, *r).is_some()) || regs.is_empty() {
4791 return None;
4792 }
4793 if rest.len() == 2 {
4796 let ok = matches!(rest[0].kind, InsnKind::MoveResult { .. })
4797 && matches!(rest[1].kind, InsnKind::Return { .. } | InsnKind::ReturnVoid);
4798 if !ok {
4799 return None;
4800 }
4801 }
4802 let mid = dex.method(*method_idx);
4803 let cls = dex.class_name(mid.class_idx);
4806 let name = dex.string(mid.name_idx).to_string();
4807 let mut s = String::from("(");
4808 for t in dex.proto_params(mid.proto_idx) {
4809 s.push_str(dex.type_name(*t));
4810 }
4811 s.push(')');
4812 s.push_str(dex.type_name(dex.proto(mid.proto_idx).return_type_idx));
4813 let desc = parse_method_descriptor(&s)?;
4814 Some(Shape::Forward { cls, name, desc, instance: !matches!(kind, InvokeKind::Static) })
4815 }
4816 _ => None,
4817 }
4818}
4819
4820
4821fn is_const_only_trace(kind: &InsnKind, insns: &[&Insn]) -> bool {
4827 let InsnKind::Invoke { kind, regs, .. } = kind else { return false };
4828 if !matches!(kind, InvokeKind::Static) || regs.is_empty() {
4829 return false;
4830 }
4831 regs.iter().all(|r| const_loaded(*r, insns, 0))
4832}
4833
4834fn const_loaded(r: u16, insns: &[&Insn], depth: u8) -> bool {
4837 if depth > 3 {
4838 return false;
4839 }
4840 insns.iter().any(|i| match &i.kind {
4841 InsnKind::Const { dst, .. } => *dst == r,
4842 InsnKind::Move { dst, src } => *dst == r && const_loaded(*src, insns, depth + 1),
4843 _ => false,
4844 })
4845}