1#![allow(clippy::collapsible_match)]
14
15use std::collections::HashSet;
16
17use jdc_core::ir::build::has_side_effects;
21use jdc_core::ir::expr::{AssignOp, BinOp, ConcatPart, ConstVal, Expr, TypeRef, UnOp};
22use jdc_core::ir::stmt::{CaseGroup, Catch, Stmt};
23use jdc_core::types::{JavaType, MethodDescriptor};
24use jdc_core::var::VarTable;
25
26use crate::lift::MethodEnv;
27
28pub fn rewrite_exprs<F: FnMut(&mut Expr)>(s: &mut Stmt, f: &mut F) {
34 walk_stmt_exprs(s, f);
35}
36
37fn walk_stmt_exprs<F: FnMut(&mut Expr)>(s: &mut Stmt, f: &mut F) {
38 match s {
39 Stmt::Block(v) => {
40 for x in v.iter_mut() {
41 walk_stmt_exprs(x, f);
42 }
43 }
44 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
45 f(e);
46 }
47 Stmt::Return(Some(e)) => f(e),
48 Stmt::LocalDef { init, .. } => {
49 if let Some(e) = init {
50 f(e);
51 }
52 }
53 Stmt::If {
54 cond,
55 then_stmt,
56 else_stmt,
57 } => {
58 f(cond);
59 walk_stmt_exprs(then_stmt, f);
60 if let Some(e) = else_stmt {
61 walk_stmt_exprs(e, f);
62 }
63 }
64 Stmt::While { cond, body } => {
65 f(cond);
66 walk_stmt_exprs(body, f);
67 }
68 Stmt::DoWhile { body, cond } => {
69 walk_stmt_exprs(body, f);
70 f(cond);
71 }
72 Stmt::For {
73 init,
74 cond,
75 update,
76 body,
77 } => {
78 for x in init.iter_mut() {
79 walk_stmt_exprs(x, f);
80 }
81 if let Some(c) = cond {
82 f(c);
83 }
84 for u in update.iter_mut() {
85 f(u);
86 }
87 walk_stmt_exprs(body, f);
88 }
89 Stmt::ForEach { iterable, body, .. } => {
90 f(iterable);
91 walk_stmt_exprs(body, f);
92 }
93 Stmt::Switch {
94 selector,
95 cases,
96 default,
97 ..
98 } => {
99 f(selector);
100 for c in cases {
101 for x in c.body.iter_mut() {
102 walk_stmt_exprs(x, f);
103 }
104 if let Some(g) = &mut c.guard {
105 f(g);
106 }
107 }
108 if let Some(d) = default {
109 walk_stmt_exprs(d, f);
110 }
111 }
112 Stmt::Try {
113 body,
114 catches,
115 finally,
116 } => {
117 walk_stmt_exprs(body, f);
118 for c in catches {
119 walk_stmt_exprs(&mut c.body, f);
120 }
121 if let Some(fl) = finally {
122 walk_stmt_exprs(fl, f);
123 }
124 }
125 Stmt::TryWithResources {
126 resources,
127 body,
128 catches,
129 finally,
130 } => {
131 for x in resources.iter_mut() {
132 walk_stmt_exprs(x, f);
133 }
134 walk_stmt_exprs(body, f);
135 for c in catches {
136 walk_stmt_exprs(&mut c.body, f);
137 }
138 if let Some(fl) = finally {
139 walk_stmt_exprs(fl, f);
140 }
141 }
142 Stmt::Assert { cond, msg } => {
143 f(cond);
144 if let Some(m) = msg {
145 f(m);
146 }
147 }
148 Stmt::Synchronized { lock, body } => {
149 f(lock);
150 walk_stmt_exprs(body, f);
151 }
152 Stmt::TernaryValue { e } => f(e),
153 Stmt::Labeled { body, .. } => walk_stmt_exprs(body, f),
154 Stmt::Goto(_) | Stmt::Label(_) | Stmt::Break(_) | Stmt::Continue(_) => {}
155 _ => {}
156 }
157}
158
159fn visit_exprs<F: FnMut(&Expr)>(e: &Expr, f: &mut F) {
161 f(e);
162 for_each_child(e, &mut |c| visit_exprs(c, f));
163}
164
165fn for_each_child<F: FnMut(&Expr)>(e: &Expr, f: &mut F) {
166 match e {
167 Expr::Un { e, .. } | Expr::Cast { e, .. } | Expr::InstanceOf { e, .. } => f(e),
168 Expr::Bin { l, r, .. } => {
169 f(l);
170 f(r);
171 }
172 Expr::Cond { c, t, f: fe } => {
173 f(c);
174 f(t);
175 f(fe);
176 }
177 Expr::Assign { target, value, .. } => {
178 f(target);
179 f(value);
180 }
181 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => f(e),
182 Expr::Field { owner, .. } => {
183 if let Some(o) = owner {
184 f(o);
185 }
186 }
187 Expr::Method { owner, args, .. } => {
188 if let Some(o) = owner {
189 f(o);
190 }
191 for a in args {
192 f(a);
193 }
194 }
195 Expr::ArrayIndex { array, index } => {
196 f(array);
197 f(index);
198 }
199 Expr::New { args, .. } => {
200 for a in args {
201 f(a);
202 }
203 }
204 Expr::NewArray { dims, init, .. } => {
205 for d in dims {
206 f(d);
207 }
208 if let Some(v) = init {
209 for x in v {
210 f(x);
211 }
212 }
213 }
214 Expr::NewMultiArray { dims, .. } => {
215 for d in dims {
216 f(d);
217 }
218 }
219 Expr::StringConcat(parts) => {
220 for p in parts {
221 if let ConcatPart::Str(x) = p {
222 f(x);
223 }
224 }
225 }
226 Expr::Invokedynamic { args, .. } => {
227 for a in args {
228 f(a);
229 }
230 }
231 Expr::AnonNew { args, .. } => {
232 for a in args {
233 f(a);
234 }
235 }
236 _ => {}
237 }
238}
239
240fn for_each_child_mut<F: FnMut(&mut Expr)>(e: &mut Expr, f: &mut F) {
242 match e {
243 Expr::Un { e, .. } | Expr::Cast { e, .. } | Expr::InstanceOf { e, .. } => f(e),
244 Expr::Bin { l, r, .. } => {
245 f(l);
246 f(r);
247 }
248 Expr::Cond { c, t, f: fe } => {
249 f(c);
250 f(t);
251 f(fe);
252 }
253 Expr::Assign { target, value, .. } => {
254 f(target);
255 f(value);
256 }
257 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => f(e),
258 Expr::Field { owner, .. } => {
259 if let Some(o) = owner {
260 f(o);
261 }
262 }
263 Expr::Method { owner, args, .. } => {
264 if let Some(o) = owner {
265 f(o);
266 }
267 for a in args.iter_mut() {
268 f(a);
269 }
270 }
271 Expr::ArrayIndex { array, index } => {
272 f(array);
273 f(index);
274 }
275 Expr::New { args, .. } => {
276 for a in args.iter_mut() {
277 f(a);
278 }
279 }
280 Expr::NewArray { dims, init, .. } => {
281 for d in dims.iter_mut() {
282 f(d);
283 }
284 if let Some(v) = init {
285 for x in v.iter_mut() {
286 f(x);
287 }
288 }
289 }
290 Expr::NewMultiArray { dims, .. } => {
291 for d in dims.iter_mut() {
292 f(d);
293 }
294 }
295 Expr::StringConcat(parts) => {
296 for p in parts {
297 if let ConcatPart::Str(x) = p {
298 f(x);
299 }
300 }
301 }
302 Expr::Invokedynamic { args, .. } => {
303 for a in args.iter_mut() {
304 f(a);
305 }
306 }
307 Expr::AnonNew { args, .. } => {
308 for a in args.iter_mut() {
309 f(a);
310 }
311 }
312 _ => {}
313 }
314}
315
316fn deep_rewrite<F: FnMut(&mut Expr)>(e: &mut Expr, f: &mut F) {
318 f(e);
319 for_each_child_mut(e, &mut |c| deep_rewrite(c, f));
320}
321
322fn collect_vars(e: &Expr, out: &mut HashSet<u32>) {
323 visit_exprs(e, &mut |x| {
324 if let Expr::Local { var, .. } = x {
325 out.insert(*var);
326 }
327 });
328}
329
330fn stmt_collect_vars(s: &Stmt, out: &mut HashSet<u32>, assignments: bool) {
335 match s {
336 Stmt::Block(v) => {
337 for x in v {
338 stmt_collect_vars(x, out, assignments);
339 }
340 }
341 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
342 if let Expr::Local { var, .. } = &**target {
343 if assignments {
344 out.insert(*var);
345 }
346 } else {
347 collect_vars(target, out);
348 }
349 collect_vars(value, out);
350 }
351 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
352 collect_vars(e, out)
353 }
354 Stmt::Return(Some(e)) => collect_vars(e, out),
355 Stmt::LocalDef { var, init, .. } => {
356 if assignments {
357 out.insert(*var);
358 }
359 if let Some(e) = init {
360 collect_vars(e, out);
361 }
362 }
363 Stmt::If {
364 cond,
365 then_stmt,
366 else_stmt,
367 } => {
368 collect_vars(cond, out);
369 stmt_collect_vars(then_stmt, out, assignments);
370 if let Some(e) = else_stmt {
371 stmt_collect_vars(e, out, assignments);
372 }
373 }
374 Stmt::While { cond, body } => {
375 collect_vars(cond, out);
376 stmt_collect_vars(body, out, assignments);
377 }
378 Stmt::DoWhile { body, cond } => {
379 stmt_collect_vars(body, out, assignments);
380 collect_vars(cond, out);
381 }
382 Stmt::For {
383 init,
384 cond,
385 update,
386 body,
387 } => {
388 for x in init {
389 stmt_collect_vars(x, out, assignments);
390 }
391 if let Some(c) = cond {
392 collect_vars(c, out);
393 }
394 for u in update {
395 collect_vars(u, out);
396 }
397 stmt_collect_vars(body, out, assignments);
398 }
399 Stmt::ForEach {
400 var,
401 iterable,
402 body,
403 ..
404 } => {
405 if assignments {
406 out.insert(*var);
407 }
408 collect_vars(iterable, out);
409 stmt_collect_vars(body, out, assignments);
410 }
411 Stmt::Switch {
412 selector,
413 cases,
414 default,
415 ..
416 } => {
417 collect_vars(selector, out);
418 for c in cases {
419 for x in &c.body {
420 stmt_collect_vars(x, out, assignments);
421 }
422 }
423 if let Some(d) = default {
424 stmt_collect_vars(d, out, assignments);
425 }
426 }
427 Stmt::Try {
428 body,
429 catches,
430 finally,
431 } => {
432 stmt_collect_vars(body, out, assignments);
433 for c in catches {
434 stmt_collect_vars(&c.body, out, assignments);
435 }
436 if let Some(f) = finally {
437 stmt_collect_vars(f, out, assignments);
438 }
439 }
440 Stmt::TryWithResources {
441 resources,
442 body,
443 catches,
444 finally,
445 } => {
446 for x in resources {
447 stmt_collect_vars(x, out, assignments);
448 }
449 stmt_collect_vars(body, out, assignments);
450 for c in catches {
451 stmt_collect_vars(&c.body, out, assignments);
452 }
453 if let Some(f) = finally {
454 stmt_collect_vars(f, out, assignments);
455 }
456 }
457 Stmt::Synchronized { lock, body } => {
458 collect_vars(lock, out);
459 stmt_collect_vars(body, out, assignments);
460 }
461 Stmt::Assert { cond, msg } => {
462 collect_vars(cond, out);
463 if let Some(m) = msg {
464 collect_vars(m, out);
465 }
466 }
467 Stmt::Labeled { body, .. } => stmt_collect_vars(body, out, assignments),
468 _ => {}
469 }
470}
471
472fn walk_all<F: FnMut(&Stmt)>(s: &Stmt, f: &mut F) {
473 f(s);
474 match s {
475 Stmt::Block(v) => {
476 for x in v {
477 walk_all(x, f);
478 }
479 }
480 Stmt::If {
481 then_stmt,
482 else_stmt,
483 ..
484 } => {
485 walk_all(then_stmt, f);
486 if let Some(e) = else_stmt {
487 walk_all(e, f);
488 }
489 }
490 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => walk_all(body, f),
491 Stmt::For { init, body, .. } => {
492 for x in init {
493 walk_all(x, f);
494 }
495 walk_all(body, f);
496 }
497 Stmt::ForEach { body, .. } => walk_all(body, f),
498 Stmt::Switch { cases, default, .. } => {
499 for c in cases {
500 for x in &c.body {
501 walk_all(x, f);
502 }
503 }
504 if let Some(d) = default {
505 walk_all(d, f);
506 }
507 }
508 Stmt::Try {
509 body,
510 catches,
511 finally,
512 } => {
513 walk_all(body, f);
514 for c in catches {
515 walk_all(&c.body, f);
516 }
517 if let Some(fl) = finally {
518 walk_all(fl, f);
519 }
520 }
521 Stmt::TryWithResources {
522 resources,
523 body,
524 catches,
525 finally,
526 } => {
527 for x in resources {
528 walk_all(x, f);
529 }
530 walk_all(body, f);
531 for c in catches {
532 walk_all(&c.body, f);
533 }
534 if let Some(fl) = finally {
535 walk_all(fl, f);
536 }
537 }
538 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => walk_all(body, f),
539 _ => {}
540 }
541}
542
543pub fn bind_catches(s: &mut Stmt, vt: &mut VarTable) {
550 match s {
551 Stmt::Try {
552 body,
553 catches,
554 finally,
555 } => {
556 bind_catches(body, vt);
557 for c in catches.iter_mut() {
558 if c.var == u32::MAX {
559 let stored = match c.body.as_ref() {
560 Stmt::Block(v) => match v.first() {
561 Some(Stmt::LocalDef { var, .. }) => Some(*var),
562 Some(Stmt::ExprStmt(Expr::Assign { target, .. })) => match &**target {
563 Expr::Local { var, .. } => Some(*var),
564 _ => None,
565 },
566 _ => None,
567 },
568 _ => None,
569 };
570 if let Some(v) = stored {
571 let exc = c
572 .exc
573 .first()
574 .cloned()
575 .unwrap_or_else(|| "java/lang/Throwable".into());
576 let slot = vt.var(v).slot;
577 let name = "e".to_string();
578 let new_var =
579 vt.add_catch_var(slot, name, TypeRef::J(JavaType::Object(exc)));
580 if let Stmt::Block(vs) = c.body.as_mut() {
581 vs.remove(0);
582 }
583 rewrite_local_refs(c.body.as_mut(), v, new_var);
584 c.var = new_var;
585 }
586 }
587 bind_catches(&mut c.body, vt);
588 }
589 if let Some(f) = finally {
590 bind_catches(f.as_mut(), vt);
591 }
592 }
593 Stmt::Block(v) => {
594 for x in v.iter_mut() {
595 bind_catches(x, vt);
596 }
597 }
598 Stmt::If {
599 then_stmt,
600 else_stmt,
601 ..
602 } => {
603 bind_catches(then_stmt, vt);
604 if let Some(e) = else_stmt {
605 bind_catches(e, vt);
606 }
607 }
608 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => bind_catches(body, vt),
609 Stmt::For { init, body, .. } => {
610 for x in init.iter_mut() {
611 bind_catches(x, vt);
612 }
613 bind_catches(body, vt);
614 }
615 Stmt::ForEach { body, .. }
616 | Stmt::Labeled { body, .. }
617 | Stmt::Synchronized { body, .. } => bind_catches(body, vt),
618 Stmt::Switch { cases, default, .. } => {
619 for c in cases.iter_mut() {
620 for x in c.body.iter_mut() {
621 bind_catches(x, vt);
622 }
623 }
624 if let Some(d) = default {
625 bind_catches(d, vt);
626 }
627 }
628 _ => {}
629 }
630}
631
632fn rewrite_local_refs(s: &mut Stmt, from: u32, to: u32) {
633 rewrite_exprs(s, &mut |e| {
634 deep_rewrite(e, &mut |x| {
635 if let Expr::Local { var, .. } = x {
636 if *var == from {
637 *var = to;
638 }
639 }
640 });
641 });
642}
643
644pub fn cleanup(s: &mut Stmt) {
646 jdc_core::ir::stmt::flatten(s);
647 strip_empty(s);
648 merge_singleton_stmts(s);
649}
650
651fn strip_empty(s: &mut Stmt) {
652 match s {
653 Stmt::Block(v) => {
654 v.retain(|x| !x.is_empty_block());
655 for x in v.iter_mut() {
656 strip_empty(x);
657 }
658 }
659 Stmt::If {
660 then_stmt,
661 else_stmt,
662 ..
663 } => {
664 strip_empty(then_stmt);
665 if let Some(e) = else_stmt {
666 strip_empty(e);
667 if e.is_empty_block() {
668 *else_stmt = None;
669 }
670 }
671 }
672 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => strip_empty(body),
673 Stmt::For { init, body, .. } => {
674 init.retain(|x| !x.is_empty_block());
675 strip_empty(body);
676 }
677 Stmt::ForEach { body, .. } => strip_empty(body),
678 Stmt::Switch { cases, default, .. } => {
679 for c in cases {
680 c.body.retain(|x| !x.is_empty_block());
681 }
682 if let Some(d) = default {
683 strip_empty(d);
684 }
685 }
686 Stmt::Try {
687 body,
688 catches,
689 finally,
690 } => {
691 strip_empty(body);
692 for c in catches {
693 strip_empty(&mut c.body);
694 }
695 if let Some(f) = finally {
696 strip_empty(f);
697 }
698 }
699 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => strip_empty(body),
700 _ => {}
701 }
702}
703
704fn merge_singleton_stmts(s: &mut Stmt) {
705 if let Stmt::Block(v) = s {
706 for x in v.iter_mut() {
707 merge_singleton_stmts(x);
708 }
709 }
710}
711
712pub fn prune_unreachable(s: &mut Stmt) {
714 if let Stmt::Block(v) = s {
715 let mut cut: Option<usize> = None;
716 for (i, x) in v.iter().enumerate() {
717 if matches!(x, Stmt::Return(_) | Stmt::Throw(_)) {
718 cut = Some(i + 1);
719 break;
720 }
721 }
722 if let Some(c) = cut {
723 if c < v.len() {
724 v.truncate(c);
725 }
726 }
727 for x in v.iter_mut() {
728 prune_unreachable(x);
729 }
730 } else {
731 walk_mut(s, &mut prune_unreachable);
732 }
733}
734
735fn walk_mut<F: FnMut(&mut Stmt)>(s: &mut Stmt, f: &mut F) {
736 match s {
737 Stmt::Block(v) => {
738 for x in v.iter_mut() {
739 f(x);
740 }
741 }
742 Stmt::If {
743 then_stmt,
744 else_stmt,
745 ..
746 } => {
747 f(then_stmt);
748 if let Some(e) = else_stmt {
749 f(e);
750 }
751 }
752 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => f(body),
753 Stmt::For { init, body, .. } => {
754 for x in init.iter_mut() {
755 f(x);
756 }
757 f(body);
758 }
759 Stmt::ForEach { body, .. } => f(body),
760 Stmt::Switch { cases, default, .. } => {
761 for c in cases {
762 for x in c.body.iter_mut() {
763 f(x);
764 }
765 }
766 if let Some(d) = default {
767 f(d);
768 }
769 }
770 Stmt::Try {
771 body,
772 catches,
773 finally,
774 } => {
775 f(body);
776 for c in catches {
777 f(&mut c.body);
778 }
779 if let Some(fl) = finally {
780 f(fl);
781 }
782 }
783 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => f(body),
784 _ => {}
785 }
786}
787
788pub fn count_localdefs(s: &Stmt, out: &mut usize) {
790 if matches!(s, Stmt::LocalDef { .. }) {
791 *out += 1;
792 }
793 walk_all(s, &mut |x| {
794 if matches!(x, Stmt::LocalDef { .. }) {
795 *out += 1;
796 }
797 });
798}
799
800pub fn strip_trailing_void_return(s: &mut Stmt) {
802 if let Stmt::Block(v) = s {
803 while let Some(Stmt::Return(None)) = v.last() {
804 v.pop();
805 }
806 }
807}
808
809pub fn prepend_comment(s: &mut Stmt, text: String) {
810 let stmt = Stmt::Comment(text);
811 match s {
812 Stmt::Block(v) => v.insert(0, stmt),
813 other => {
814 let inner = std::mem::replace(other, Stmt::Block(vec![]));
815 *other = Stmt::Block(vec![stmt, inner]);
816 }
817 }
818}
819
820pub fn fused_expr_rewrites(s: &mut Stmt, vt: &VarTable) {
824 let mut obj_vars: Vec<bool> = Vec::with_capacity(vt.vars.len());
826 let mut any_obj = false;
827 for v in &vt.vars {
828 let is_obj = v.ty.erased().is_reference();
829 if is_obj {
830 any_obj = true;
831 }
832 obj_vars.push(is_obj);
833 }
834 rewrite_exprs(s, &mut |e| {
835 deep_rewrite(e, &mut |x| {
836 if let Expr::Invokedynamic { name, args, .. } = x {
838 if name.starts_with('\0') && args.len() == 2 {
839 let (cls, ty) = match name.as_str() {
840 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
841 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
842 _ => ("java/lang/Double", JavaType::Double),
843 };
844 let desc = MethodDescriptor {
845 args: vec![ty.clone(), ty],
846 ret: JavaType::Int,
847 };
848 *x = Expr::Method {
849 owner: None,
850 cls: cls.into(),
851 name: "compare".into(),
852 desc,
853 args: args.clone(),
854 is_static: true,
855 is_interface: false,
856 is_special: false,
857 is_super: false,
858 is_dynamic: false,
859 type_args: vec![],
860 };
861 return;
862 }
863 }
864 if any_obj {
866 if let Expr::Bin { op, l, r, .. } = x {
867 if matches!(op, BinOp::Eq | BinOp::Ne) {
868 let rewrite = |side: &mut Expr, other: &Expr| {
869 if let Expr::Const(ConstVal::Int(0)) = other {
870 if let Expr::Local { var, .. } = &*side {
871 if obj_vars.get(*var as usize).copied().unwrap_or(false) {
872 *side = Expr::Const(ConstVal::Null);
873 }
874 }
875 }
876 };
877 let lo = l.clone();
878 let ro = r.clone();
879 rewrite(l, &ro);
880 rewrite(r, &lo);
881 }
882 }
883 }
884 if let Expr::Bin { op, l, r, .. } = x {
886 if matches!(
887 op,
888 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
889 ) {
890 let l_const = matches!(&**l, Expr::Const(_));
891 let r_var = matches!(&**r, Expr::Local { .. });
892 if l_const && r_var {
893 std::mem::swap(l, r);
894 }
895 }
896 }
897 });
898 });
899}
900
901#[allow(dead_code)]
904pub fn desugar_cmp_residuals(s: &mut Stmt) {
905 rewrite_exprs(s, &mut |e| {
906 deep_rewrite(e, &mut |x| {
907 if let Expr::Invokedynamic { name, args, .. } = x {
908 if name.starts_with('\0') && args.len() == 2 {
909 let (cls, ty) = match name.as_str() {
910 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
911 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
912 _ => ("java/lang/Double", JavaType::Double),
913 };
914 let desc = jdc_core::types::MethodDescriptor {
915 args: vec![ty.clone(), ty],
916 ret: JavaType::Int,
917 };
918 *x = Expr::Method {
919 owner: None,
920 cls: cls.into(),
921 name: "compare".into(),
922 desc,
923 args: args.clone(),
924 is_static: true,
925 is_interface: false,
926 is_special: false,
927 is_super: false,
928 is_dynamic: false,
929 type_args: vec![],
930 };
931 }
932 }
933 });
934 });
935}
936
937pub fn ternary_fold(s: &mut Stmt) {
939 let mut changed = true;
940 let mut guard = 0;
941 while changed && guard < 8 {
942 changed = false;
943 guard += 1;
944 ternary_fold_walk(s, &mut changed);
945 strip_empty(s);
946 }
947}
948
949fn ternary_fold_walk(s: &mut Stmt, changed: &mut bool) {
950 if let Stmt::Block(v) = s {
951 for x in v.iter_mut() {
952 ternary_fold_walk(x, changed);
953 }
954 return;
955 }
956 let Stmt::If {
957 cond,
958 then_stmt,
959 else_stmt,
960 } = s
961 else {
962 return;
963 };
964 ternary_fold_walk(then_stmt, changed);
965 if let Some(e) = else_stmt {
966 ternary_fold_walk(e, changed);
967 }
968 let Some(else_b) = else_stmt else { return };
969 let target_of = |st: &Stmt| -> Option<u32> {
970 let Stmt::ExprStmt(Expr::Assign { target, .. }) = st else {
971 return None;
972 };
973 if let Expr::Local { var, .. } = &**target {
974 Some(*var)
975 } else {
976 None
977 }
978 };
979 let (Stmt::Block(tv), Stmt::Block(ev)) = (then_stmt.as_mut(), else_b.as_mut()) else {
980 return;
981 };
982 if tv.len() != 1 || ev.len() != 1 {
983 return;
984 }
985 let (Some(vt_), Some(ve)) = (target_of(&tv[0]), target_of(&ev[0])) else {
986 return;
987 };
988 if vt_ != ve {
989 return;
990 }
991 let mut touched = HashSet::new();
995 let (Stmt::ExprStmt(a_then), Stmt::ExprStmt(a_else)) = (&tv[0], &ev[0]) else {
996 return;
997 };
998 if let Expr::Assign { value, .. } = a_then {
999 collect_vars(value, &mut touched);
1000 }
1001 if let Expr::Assign { value, .. } = a_else {
1002 collect_vars(value, &mut touched);
1003 }
1004 if touched.contains(&vt_) {
1005 return;
1006 }
1007 let value_then = match a_then {
1008 Expr::Assign { value, .. } => value.clone(),
1009 _ => return,
1010 };
1011 let value_else = match a_else {
1012 Expr::Assign { value, .. } => value.clone(),
1013 _ => return,
1014 };
1015 let cond_e = cond.clone();
1016 let folded = Stmt::ExprStmt(Expr::Assign {
1017 target: Box::new(Expr::Local {
1018 var: vt_,
1019 ty: vt_join_ty(&value_then, &value_else),
1020 }),
1021 op: AssignOp::Plain,
1022 value: Box::new(Expr::Cond {
1023 c: Box::new(cond_e),
1024 t: value_then,
1025 f: value_else,
1026 }),
1027 });
1028 *s = Stmt::Block(vec![folded]);
1029 *changed = true;
1030}
1031
1032fn vt_join_ty(a: &Expr, b: &Expr) -> TypeRef {
1033 let ta = a.type_ref();
1034 let tb = b.type_ref();
1035 if ta.erased() == tb.erased() {
1036 return ta;
1037 }
1038 TypeRef::J(JavaType::Object("java/lang/Object".into()))
1039}
1040
1041pub fn fold_string_builders(s: &mut Stmt, vt: &VarTable) {
1048 let n_vars = vt.vars.len().max(1);
1050 let mut counts = vec![0usize; n_vars];
1051 count_assignments(s, &mut counts);
1052 let mut values: Vec<Option<Expr>> = vec![None; n_vars];
1053 record_assignments(s, &counts, &mut values);
1054
1055 let mut appended_stmts: HashSet<u32> = HashSet::new();
1057 walk_all(s, &mut |st| {
1058 if let Stmt::ExprStmt(Expr::Method {
1059 cls,
1060 name,
1061 owner,
1062 args,
1063 ..
1064 }) = st
1065 {
1066 if name == "append" && is_string_builder(cls) && args.len() == 1 {
1067 if let Some(Expr::Local { var, .. }) = owner.as_deref() {
1068 appended_stmts.insert(*var);
1069 }
1070 }
1071 }
1072 });
1073
1074 rewrite_exprs(s, &mut |e| {
1075 fold_concat_in_expr(e, &values, &appended_stmts);
1076 });
1077
1078 for _ in 0..8 {
1082 let mut reads: HashSet<u32> = HashSet::new();
1083 stmt_collect_vars(s, &mut reads, false);
1084 if drop_unused_assigns(s, &reads) == 0 {
1085 break;
1086 }
1087 }
1088}
1089
1090pub fn count_stmts_deep(s: &Stmt, out: &mut usize) {
1092 *out += 1;
1093 walk_all(s, &mut |_| {
1094 *out += 1;
1095 });
1096}
1097
1098struct VarAnalysis {
1100 assigns: Vec<usize>,
1101 reads: Vec<usize>,
1102 values: Vec<Option<Expr>>,
1104}
1105
1106fn analyze_vars(s: &Stmt, a: &mut VarAnalysis) {
1107 walk_all(s, &mut |st| {
1108 let exprs: Vec<&Expr> = match st {
1109 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
1110 if let Expr::Local { var, .. } = &**target {
1111 grow_to(&mut a.assigns, *var);
1112 let idx = *var as usize;
1113 a.assigns[idx] += 1;
1114 if a.assigns[idx] == 1 {
1115 if idx >= a.values.len() {
1116 a.values.resize(idx + 1, None);
1117 }
1118 a.values[idx] = Some((**value).clone());
1119 } else if idx < a.values.len() {
1120 a.values[idx] = None;
1121 }
1122 }
1123 let mut v: Vec<&Expr> = vec![value];
1124 if !matches!(&**target, Expr::Local { .. }) {
1125 v.push(target);
1126 }
1127 v
1128 }
1129 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
1130 vec![e]
1131 }
1132 Stmt::Return(Some(e)) => vec![e],
1133 Stmt::LocalDef {
1134 var, init: Some(e), ..
1135 } => {
1136 grow_to(&mut a.assigns, *var);
1137 let idx = *var as usize;
1138 a.assigns[idx] += 1;
1139 if a.assigns[idx] == 1 {
1140 if idx >= a.values.len() {
1141 a.values.resize(idx + 1, None);
1142 }
1143 a.values[idx] = Some(e.clone());
1144 } else if idx < a.values.len() {
1145 a.values[idx] = None;
1146 }
1147 vec![e]
1148 }
1149 Stmt::If { cond, .. } => vec![cond],
1150 Stmt::While { cond, .. } => vec![cond],
1151 Stmt::DoWhile { cond, .. } => vec![cond],
1152 _ => Vec::new(),
1153 };
1154 for e in exprs {
1155 visit_exprs(e, &mut |x| {
1156 if let Expr::Local { var, .. } = x {
1157 grow_to(&mut a.reads, *var);
1158 a.reads[*var as usize] += 1;
1159 }
1160 });
1161 }
1162 });
1163 let n = a.assigns.len().max(a.reads.len()).max(a.values.len());
1165 a.assigns.resize(n, 0);
1166 a.reads.resize(n, 0);
1167 a.values.resize(n, None);
1168}
1169
1170fn grow_to(v: &mut Vec<usize>, var: u32) {
1171 if var as usize >= v.len() {
1172 v.resize(var as usize + 1, 0);
1173 }
1174}
1175
1176fn count_assignments(s: &Stmt, counts: &mut Vec<usize>) {
1177 walk_all(s, &mut |st| match st {
1178 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
1179 if let Expr::Local { var, .. } = &**target {
1180 grow_to(counts, *var);
1181 counts[*var as usize] += 1;
1182 }
1183 }
1184 Stmt::LocalDef {
1185 var, init: Some(_), ..
1186 } => {
1187 grow_to(counts, *var);
1188 counts[*var as usize] += 1;
1189 }
1190 _ => {}
1191 });
1192}
1193
1194fn record_assignments(s: &Stmt, counts: &[usize], out: &mut Vec<Option<Expr>>) {
1195 walk_all(s, &mut |st| match st {
1196 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
1197 if let Expr::Local { var, .. } = &**target {
1198 if counts.get(*var as usize).copied().unwrap_or(0) == 1 {
1199 if *var as usize >= out.len() {
1200 out.resize(*var as usize + 1, None);
1201 }
1202 out[*var as usize] = Some((**value).clone());
1203 }
1204 }
1205 }
1206 Stmt::LocalDef {
1207 var, init: Some(e), ..
1208 } if counts.get(*var as usize).copied().unwrap_or(0) == 1 => {
1209 if *var as usize >= out.len() {
1210 out.resize(*var as usize + 1, None);
1211 }
1212 out[*var as usize] = Some(e.clone());
1213 }
1214 _ => {}
1215 });
1216}
1217
1218fn fold_concat_in_expr(e: &mut Expr, values: &[Option<Expr>], appended: &HashSet<u32>) {
1219 deep_rewrite(e, &mut |x| {
1220 if let Expr::Method {
1221 cls,
1222 name,
1223 args,
1224 owner,
1225 ..
1226 } = x
1227 {
1228 if name == "toString" && args.is_empty() && is_string_builder(cls) {
1229 if let Some(o) = owner {
1230 let mut has_stmt_appends = false;
1233 visit_exprs(o, &mut |y| {
1234 if let Expr::Local { var, .. } = y {
1235 if appended.contains(var) {
1236 has_stmt_appends = true;
1237 }
1238 }
1239 });
1240 if has_stmt_appends {
1241 return;
1242 }
1243 let resolved = resolve_local(o, values);
1244 if let Some(parts) = collect_sb_parts(&resolved, values, 0) {
1245 *x = Expr::StringConcat(parts);
1246 }
1247 }
1248 }
1249 }
1250 });
1251}
1252
1253fn is_string_builder(cls: &str) -> bool {
1254 cls == "java/lang/StringBuilder" || cls == "java/lang/StringBuffer"
1255}
1256
1257fn resolve_local(e: &Expr, values: &[Option<Expr>]) -> Expr {
1258 match e {
1259 Expr::Local { var, ty } => values
1260 .get(*var as usize)
1261 .and_then(|o| o.clone())
1262 .unwrap_or_else(|| Expr::Local {
1263 var: *var,
1264 ty: ty.clone(),
1265 }),
1266 other => other.clone(),
1267 }
1268}
1269
1270fn collect_sb_parts(e: &Expr, values: &[Option<Expr>], depth: u32) -> Option<Vec<ConcatPart>> {
1272 if depth > 16 {
1273 return None;
1274 }
1275 match e {
1276 Expr::Method {
1277 cls,
1278 name,
1279 args,
1280 owner,
1281 ..
1282 } if name == "append" && is_string_builder(cls) && args.len() == 1 => {
1283 let mut parts =
1284 collect_sb_parts(&resolve_local(owner.as_deref()?, values), values, depth + 1)?;
1285 parts.push(ConcatPart::Str(args[0].clone()));
1286 Some(parts)
1287 }
1288 Expr::New {
1289 cls,
1290 args,
1291 raw: false,
1292 ..
1293 } if is_string_builder(cls) => {
1294 let mut parts = Vec::new();
1295 for a in args {
1296 if let Expr::Const(ConstVal::Str(sv)) = a {
1297 parts.push(ConcatPart::Const(sv.clone()));
1298 } else {
1299 parts.push(ConcatPart::Str(a.clone()));
1300 }
1301 }
1302 Some(parts)
1303 }
1304 _ => None,
1305 }
1306}
1307
1308fn drop_unused_assigns(s: &mut Stmt, reads: &HashSet<u32>) -> usize {
1309 let mut dropped = 0usize;
1310 walk_mut_deep(s, &mut |st| {
1311 if let Stmt::Block(v) = st {
1312 let before = v.len();
1313 v.retain(|x| match x {
1314 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
1317 Expr::Local { var, .. } => reads.contains(var) || !is_sbish(value),
1318 _ => true,
1319 },
1320 Stmt::LocalDef { var, init, .. } => {
1321 reads.contains(var) || !init.as_ref().map(is_sbish).unwrap_or(false)
1322 }
1323 _ => true,
1324 });
1325 dropped += before - v.len();
1326 }
1327 });
1328 dropped
1329}
1330
1331fn is_sbish(e: &Expr) -> bool {
1332 match e {
1333 Expr::New {
1334 cls, raw: false, ..
1335 } => is_string_builder(cls),
1336 Expr::Method { name, cls, .. } => name == "append" && is_string_builder(cls),
1337 _ => false,
1338 }
1339}
1340
1341fn walk_mut_deep<F: FnMut(&mut Stmt)>(s: &mut Stmt, f: &mut F) {
1342 f(s);
1343 walk_mut(s, &mut |x| walk_mut_deep(x, f));
1344}
1345
1346pub fn fold_synchronized(s: &mut Stmt) {
1350 fold_sync_walk(s);
1351}
1352
1353fn fold_sync_walk(s: &mut Stmt) {
1354 walk_mut_deep(s, &mut |st| {
1355 let Stmt::Block(v) = st else { return };
1356 let mut i = 0;
1357 while i < v.len() {
1358 if i + 1 < v.len() {
1359 if let Some(sync) = try_sync_at(v, i) {
1360 let _ = v.drain(i..i + 2);
1361 v.insert(i, sync);
1362 continue;
1363 }
1364 }
1365 i += 1;
1366 }
1367 });
1368}
1369
1370fn try_sync_at(v: &[Stmt], i: usize) -> Option<Stmt> {
1371 let Stmt::MonitorEnter(lock) = &v[i] else {
1372 return None;
1373 };
1374 let Stmt::Try {
1375 body,
1376 catches,
1377 finally,
1378 } = &v[i + 1]
1379 else {
1380 return None;
1381 };
1382 if finally.is_some() || catches.len() != 1 {
1383 return None;
1384 }
1385 let c = &catches[0];
1386 if !c.exc.is_empty() {
1387 return None;
1388 }
1389 let Stmt::Block(cv) = c.body.as_ref() else {
1391 return None;
1392 };
1393 let exit_matches = cv.len() >= 2
1394 && matches!(&cv[0], Stmt::MonitorExit(e) if expr_local_var(e) == expr_local_var(lock));
1395 let throws = cv.len() >= 2 && matches!(&cv[1], Stmt::Throw(_));
1396 if !(exit_matches && throws) {
1397 return None;
1398 }
1399 Some(Stmt::Synchronized {
1400 lock: lock.clone(),
1401 body: body.clone(),
1402 })
1403}
1404
1405fn expr_local_var(e: &Expr) -> Option<u32> {
1406 if let Expr::Local { var, .. } = e {
1407 Some(*var)
1408 } else {
1409 None
1410 }
1411}
1412
1413pub fn forward_single_use(s: &mut Stmt, _vt: &VarTable) {
1418 let mut analysis = VarAnalysis {
1422 assigns: Vec::new(),
1423 reads: Vec::new(),
1424 values: Vec::new(),
1425 };
1426 analyze_vars(s, &mut analysis);
1427 let mut assigns = analysis.assigns;
1428 let mut reads = analysis.reads;
1429 let n_vars = assigns.len().max(_vt.vars.len()).max(1);
1432 assigns.resize(n_vars, 0);
1433 reads.resize(n_vars, 0);
1434 let mut values = analysis.values;
1435 values.resize(n_vars, None);
1436
1437 let mut cand = vec![false; n_vars];
1442 let mut edges: Vec<Vec<u32>> = vec![Vec::new(); n_vars];
1443 for (v, cand_v) in cand.iter_mut().enumerate() {
1444 if assigns.get(v).copied().unwrap_or(0) != 1 || reads.get(v).copied().unwrap_or(0) != 1 {
1445 continue;
1446 }
1447 if let Some(val) = values.get(v).and_then(|o| o.as_ref()) {
1448 let mut refs = HashSet::new();
1449 collect_vars(val, &mut refs);
1450 if refs.iter().any(|r| assigns[*r as usize] > 1) {
1451 continue;
1452 }
1453 edges[v] = refs.into_iter().collect();
1457 }
1458 *cand_v = true;
1459 }
1460
1461 let mut color = vec![0u8; n_vars]; let mut bad = vec![false; n_vars];
1479 for root in 0..n_vars {
1480 if color[root] != 0 || edges[root].is_empty() {
1481 continue;
1482 }
1483 color[root] = 1;
1484 let mut stack: Vec<(usize, usize)> = vec![(root, 0)];
1485 while let Some(top) = stack.last_mut() {
1486 let v = top.0;
1487 let i = top.1;
1488 top.1 += 1;
1489 if i < edges[v].len() {
1490 let r = edges[v][i] as usize;
1491 if color[r] == 1 {
1492 for &(pn, _) in &stack {
1493 bad[pn] = true;
1494 }
1495 } else if color[r] == 0 {
1496 color[r] = 1;
1497 stack.push((r, 0));
1498 } else if bad[r] {
1499 for &(pn, _) in &stack {
1500 bad[pn] = true;
1501 }
1502 }
1503 } else {
1504 color[v] = 2;
1505 stack.pop();
1506 }
1507 }
1508 }
1509 let single: Vec<bool> = (0..n_vars).map(|v| cand[v] && !bad[v]).collect();
1510 if !single.iter().any(|&b| b) {
1511 return;
1512 }
1513 let pure: Vec<bool> = (0..n_vars)
1514 .map(|v| {
1515 single[v]
1516 && values
1517 .get(v)
1518 .and_then(|o| o.as_ref())
1519 .map(|e| !has_side_effects(e))
1520 .unwrap_or(false)
1521 })
1522 .collect();
1523 let impure: Vec<bool> = (0..n_vars).map(|v| single[v] && !pure[v]).collect();
1524
1525 if pure.iter().any(|&b| b) {
1527 let vals: Vec<Option<Expr>> = (0..n_vars)
1528 .map(|v| {
1529 if pure[v] {
1530 values.get(v).cloned().flatten()
1531 } else {
1532 None
1533 }
1534 })
1535 .collect();
1536 rewrite_exprs(s, &mut |e| {
1537 deep_rewrite(e, &mut |x| {
1538 if let Expr::Local { var, .. } = x {
1539 if let Some(Some(v)) = vals.get(*var as usize) {
1540 *x = v.clone();
1541 }
1542 }
1543 });
1544 });
1545 drop_defs(s, &pure);
1546 }
1547
1548 if impure.iter().any(|&b| b) {
1551 forward_adjacent_impure(s, &values, &impure);
1552 }
1553}
1554
1555fn count_reads(s: &Stmt, out: &mut Vec<usize>) {
1557 walk_all(s, &mut |st| {
1558 let exprs: Vec<&Expr> = match st {
1559 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
1562 let mut v: Vec<&Expr> = vec![value];
1563 if !matches!(&**target, Expr::Local { .. }) {
1564 v.push(target);
1565 }
1566 v
1567 }
1568 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
1569 vec![e]
1570 }
1571 Stmt::Return(Some(e)) => vec![e],
1572 Stmt::LocalDef { init: Some(e), .. } => vec![e],
1573 Stmt::If { cond, .. } => vec![cond],
1574 Stmt::While { cond, .. } => vec![cond],
1575 Stmt::DoWhile { cond, .. } => vec![cond],
1576 _ => Vec::new(),
1577 };
1578 for e in exprs {
1579 visit_exprs(e, &mut |x| {
1580 if let Expr::Local { var, .. } = x {
1581 grow_to(out, *var);
1582 out[*var as usize] += 1;
1583 }
1584 });
1585 }
1586 });
1587}
1588
1589fn drop_defs(s: &mut Stmt, vars: &[bool]) {
1590 walk_mut_deep(s, &mut |st| {
1591 if let Stmt::Block(v) = st {
1592 v.retain(|x| match x {
1593 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
1594 Expr::Local { var, .. } => !vars.get(*var as usize).copied().unwrap_or(false),
1595 _ => true,
1596 },
1597 Stmt::LocalDef { var, .. } => !vars.get(*var as usize).copied().unwrap_or(false),
1598 _ => true,
1599 });
1600 }
1601 });
1602}
1603
1604fn forward_adjacent_impure(s: &mut Stmt, values: &[Option<Expr>], impure: &[bool]) {
1606 if let Stmt::Block(v) = s {
1607 let mut i = 0;
1608 while i + 1 < v.len() {
1609 let def_var = match &v[i] {
1610 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
1611 Expr::Local { var, .. } => Some(*var),
1612 _ => None,
1613 },
1614 Stmt::LocalDef { var, .. } => Some(*var),
1615 _ => None,
1616 };
1617 if let Some(var) = def_var {
1618 if impure.get(var as usize).copied().unwrap_or(false) {
1619 let mut reads_here = vec![0usize; values.len().max(1)];
1621 count_reads(&v[i + 1], &mut reads_here);
1622 if reads_here.get(var as usize).copied().unwrap_or(0) == 1 {
1623 let val = match &v[i] {
1627 Stmt::ExprStmt(Expr::Assign { value, .. }) => (**value).clone(),
1628 Stmt::LocalDef { init: Some(e), .. } => (*e).clone(),
1629 _ => {
1630 i += 1;
1631 continue;
1632 }
1633 };
1634 rewrite_exprs(&mut v[i + 1], &mut |e| {
1635 deep_rewrite(e, &mut |x| {
1636 if let Expr::Local { var: v2, .. } = x {
1637 if *v2 == var {
1638 *x = val.clone();
1639 }
1640 }
1641 });
1642 });
1643 v.remove(i);
1644 i = i.saturating_sub(1);
1647 continue;
1648 }
1649 }
1650 }
1651 i += 1;
1652 }
1653 }
1654 walk_mut(s, &mut |x| forward_adjacent_impure(x, values, impure));
1655}
1656
1657pub fn infer_types(vt: &mut VarTable, body: &mut Stmt, ret: &JavaType, env: &MethodEnv) {
1663 let n_vars = vt.vars.len();
1664 let mut evidence: Vec<Vec<JavaType>> = vec![Vec::new(); n_vars];
1665 let ev = |evidence: &mut Vec<Vec<JavaType>>, var: u32, t: JavaType| {
1666 if (var as usize) < evidence.len() {
1667 evidence[var as usize].push(t);
1668 }
1669 };
1670
1671 walk_all(body, &mut |st| match st {
1672 Stmt::LocalDef { var, init, .. } => {
1673 if let Some(e) = init {
1674 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t));
1675 if let Expr::Local { var: src, .. } = e {
1676 let _ = src;
1677 }
1678 if let Expr::Local { var: src, .. } = e {
1679 if evidence.get(*var as usize).is_some() {
1680 let _ = src;
1681 }
1682 }
1683 }
1684 }
1685 Stmt::ExprStmt(e) => expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t)),
1686 Stmt::Throw(e) => {
1687 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t));
1688 if let Expr::Local { var, .. } = e {
1689 ev(
1690 &mut evidence,
1691 *var,
1692 JavaType::Object("java/lang/Throwable".into()),
1693 );
1694 }
1695 }
1696 Stmt::Return(Some(e)) => {
1697 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t));
1698 if let Expr::Local { var, .. } = e {
1699 ev(&mut evidence, *var, ret.clone());
1700 }
1701 }
1702 Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
1703 if let Expr::Local { var, .. } = e {
1704 ev(
1705 &mut evidence,
1706 *var,
1707 JavaType::Object("java/lang/Object".into()),
1708 );
1709 }
1710 }
1711 Stmt::ForEach {
1712 var,
1713 iterable,
1714 is_array,
1715 ..
1716 } => {
1717 if let Expr::Local { var: v0, .. } = iterable {
1718 if *is_array {
1719 ev(&mut evidence, *v0, JavaType::Array(Box::new(JavaType::Int)));
1720 } else {
1721 ev(
1722 &mut evidence,
1723 *v0,
1724 JavaType::Object("java/lang/Object".into()),
1725 );
1726 }
1727 }
1728 let _ = var;
1729 }
1730 _ => {}
1731 });
1732 let _ = env;
1733
1734 for (i, evs) in evidence.iter_mut().enumerate() {
1738 let info = &vt.vars[i];
1739 if info.is_param {
1740 continue;
1741 }
1742 if info.ty.erased().is_reference() {
1743 if let Some(t) = pick_object(evs) {
1745 vt.vars[i].ty = TypeRef::J(t);
1746 }
1747 } else if evs.iter().all(|t| t.is_numeric()) && !evs.is_empty() {
1748 if let Some(t) = pick_numeric(evs) {
1749 if !info.ty.erased().is_reference() {
1750 vt.vars[i].ty = TypeRef::J(t);
1751 }
1752 }
1753 } else if let Some(t) = pick_object(evs) {
1754 vt.vars[i].ty = TypeRef::J(t);
1755 }
1756 }
1757
1758 let types: Vec<TypeRef> = vt.vars.iter().map(|v| v.ty.clone()).collect();
1760 rewrite_exprs(body, &mut |e| {
1761 deep_rewrite(e, &mut |x| {
1762 if let Expr::Local { var, ty } = x {
1763 if (*var as usize) < types.len() {
1764 *ty = types[*var as usize].clone();
1765 }
1766 }
1767 });
1768 });
1769
1770 coerce_num_consts(body, &types);
1773}
1774
1775fn coerce_num_consts(body: &mut Stmt, types: &[TypeRef]) {
1776 walk_mut_deep(body, &mut |st| {
1777 let (var, val): (u32, &mut Expr) = match st {
1778 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &mut **target {
1779 Expr::Local { var, .. } => (*var, value),
1780 _ => return,
1781 },
1782 Stmt::LocalDef {
1783 var, init: Some(e), ..
1784 } => (*var, e),
1785 _ => return,
1786 };
1787 let want = match types.get(var as usize) {
1788 Some(TypeRef::J(t)) => t.clone(),
1789 _ => return,
1790 };
1791 let rewritten = match &*val {
1792 Expr::Const(ConstVal::Long(l)) => match &want {
1793 JavaType::Double => Some(Expr::Const(ConstVal::Double(*l as f64))),
1794 JavaType::Float => Some(Expr::Const(ConstVal::Float(*l as f32))),
1795 _ => None,
1796 },
1797 Expr::Const(ConstVal::Int(i)) => match &want {
1798 JavaType::Double => Some(Expr::Const(ConstVal::Double(*i as f64))),
1799 JavaType::Float => Some(Expr::Const(ConstVal::Float(*i as f32))),
1800 _ => None,
1801 },
1802 _ => None,
1803 };
1804 if let Some(e) = rewritten {
1805 *val = e;
1806 }
1807 });
1808}
1809
1810fn pick_object(evs: &[JavaType]) -> Option<JavaType> {
1811 evs.iter()
1812 .find(|t| t.is_reference() && !matches!(t, JavaType::Object(n) if n == "java/lang/Object"))
1813 .cloned()
1814}
1815
1816fn pick_numeric(evs: &[JavaType]) -> Option<JavaType> {
1817 if evs.is_empty() {
1818 return None;
1819 }
1820 let mut t = evs[0].clone();
1821 for x in &evs[1..] {
1822 t = join_numeric(&t, x);
1823 }
1824 Some(t)
1825}
1826
1827fn join_numeric(a: &JavaType, b: &JavaType) -> JavaType {
1828 if a == b {
1829 return a.clone();
1830 }
1831 match (a, b) {
1832 (JavaType::Double, _) | (_, JavaType::Double) => JavaType::Double,
1833 (JavaType::Float, _) | (_, JavaType::Float) => JavaType::Float,
1834 (JavaType::Long, _) | (_, JavaType::Long) => JavaType::Long,
1835 (JavaType::Int, x) if x.is_integral() => JavaType::Int,
1836 (x, JavaType::Int) if x.is_integral() => JavaType::Int,
1837 (x, y) if x.is_integral() && y.is_integral() => JavaType::Int,
1838 _ => JavaType::Int,
1839 }
1840}
1841
1842fn expr_evidence<F: FnMut(u32, JavaType)>(e: &Expr, f: &mut F) {
1844 visit_exprs(e, &mut |x| match x {
1845 Expr::Method {
1846 owner,
1847 args,
1848 desc,
1849 is_static,
1850 cls,
1851 ..
1852 } => {
1853 if !*is_static {
1854 if let Some(o) = owner {
1855 if let Expr::Local { var, .. } = &**o {
1856 f(*var, JavaType::Object(cls.clone()));
1857 }
1858 }
1859 }
1860 for (i, a) in args.iter().enumerate() {
1861 if let Expr::Local { var, .. } = a {
1862 if let Some(t) = desc.args.get(i) {
1863 f(*var, t.clone());
1864 }
1865 }
1866 }
1867 }
1868 Expr::Field {
1869 owner,
1870 ty,
1871 is_static,
1872 ..
1873 } => {
1874 if !*is_static {
1875 if let Some(o) = owner {
1876 if let Expr::Local { var, .. } = &**o {
1877 f(*var, JavaType::Object("java/lang/Object".into()));
1878 }
1879 }
1880 }
1881 let _ = ty;
1882 }
1883 Expr::ArrayIndex { array, .. } => {
1884 if let Expr::Local { var, ty, .. } = &**array {
1885 let t = ty.erased();
1890 if matches!(t, JavaType::Array(_)) {
1891 f(*var, t);
1892 } else {
1893 f(*var, JavaType::Array(Box::new(JavaType::Int)));
1894 }
1895 }
1896 }
1897 Expr::Assign { target, value, .. } => {
1898 if let Expr::Local { var, .. } = &**target {
1899 f(*var, value.type_ref().erased());
1900 }
1901 }
1902 Expr::Cast { ty, e: inner } => {
1903 if let Expr::Local { var, .. } = &**inner {
1904 f(*var, ty.erased());
1905 }
1906 }
1907 Expr::InstanceOf { e: inner, .. } => {
1908 let _ = inner;
1909 }
1910 _ => {}
1911 });
1912}
1913
1914pub fn booleanize(vt: &mut VarTable, body: &mut Stmt) {
1917 let n = vt.vars.len();
1918 let mut in_cond = vec![false; n];
1919
1920 walk_all(body, &mut |st| {
1923 let cond: Option<&Expr> = match st {
1924 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
1925 Some(cond)
1926 }
1927 _ => None,
1928 };
1929 if let Some(c) = cond {
1930 if let Expr::Bin {
1931 op: BinOp::Eq | BinOp::Ne,
1932 l,
1933 r,
1934 ..
1935 } = c
1936 {
1937 for (x, other) in [(l, r), (r, l)] {
1938 if let (Expr::Local { var, .. }, Expr::Const(ConstVal::Int(0))) =
1939 (&**x, &**other)
1940 {
1941 if (*var as usize) < n {
1942 in_cond[*var as usize] = true;
1943 }
1944 }
1945 }
1946 }
1947 }
1948 let val: Option<&Expr> = match st {
1949 Stmt::ExprStmt(Expr::Assign { value, .. }) => Some(value),
1950 Stmt::LocalDef { init: Some(e), .. } => Some(e),
1951 Stmt::Return(Some(e)) => Some(e),
1952 _ => None,
1953 };
1954 if let Some(v) = val {
1955 visit_exprs(v, &mut |x| {
1956 if let Expr::Bin { op, l, r, .. } = x {
1957 if matches!(
1958 op,
1959 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
1960 ) {
1961 for side in [l, r] {
1962 if let Expr::Local { var, .. } = &**side {
1963 if (*var as usize) < n {
1964 in_cond[*var as usize] = true;
1965 }
1966 }
1967 }
1968 }
1969 }
1970 });
1971 }
1972 });
1973
1974 let mut assigned_any = vec![false; n];
1981 let mut all_bool = vec![true; n];
1982 walk_all(body, &mut |st| {
1983 let (var, value) = match st {
1984 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
1985 Expr::Local { var, .. } => (var, &**value),
1986 _ => return,
1987 },
1988 Stmt::LocalDef {
1989 var, init: Some(e), ..
1990 } => (var, e),
1991 _ => return,
1992 };
1993 let i = *var as usize;
1994 if i < n {
1995 assigned_any[i] = true;
1996 if !is_boolean_valued(value) {
1997 all_bool[i] = false;
1998 }
1999 }
2000 });
2001 let mut boolean_vars: HashSet<u32> = HashSet::new();
2002 for i in 0..n {
2003 if vt.vars[i].is_param {
2004 continue;
2005 }
2006 if !in_cond[i] {
2007 continue;
2008 }
2009 if assigned_any[i] && all_bool[i] {
2010 boolean_vars.insert(i as u32);
2011 }
2012 }
2013 if boolean_vars.is_empty() {
2014 return;
2015 }
2016 for v in &boolean_vars {
2017 vt.vars[*v as usize].ty = TypeRef::J(JavaType::Boolean);
2018 }
2019 let types: Vec<TypeRef> = vt.vars.iter().map(|v| v.ty.clone()).collect();
2020 rewrite_exprs(body, &mut |e| {
2021 deep_rewrite(e, &mut |x| {
2022 if let Expr::Local { var, ty } = x {
2023 if (*var as usize) < types.len() {
2024 *ty = types[*var as usize].clone();
2025 }
2026 }
2027 });
2028 });
2029
2030 fold_bool_conditions(body, &boolean_vars);
2032}
2033
2034fn is_boolean_valued(e: &Expr) -> bool {
2035 match e {
2036 Expr::Const(ConstVal::Int(0)) | Expr::Const(ConstVal::Int(1)) => true,
2037 Expr::Bin { op, .. } => matches!(
2038 op,
2039 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
2040 ),
2041 Expr::InstanceOf { .. } => true,
2042 Expr::Local { ty, .. } => ty.erased() == JavaType::Boolean,
2043 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
2044 Expr::Cond { t, f, .. } => is_boolean_valued(t) && is_boolean_valued(f),
2045 Expr::Un { op: UnOp::Not, .. } => true,
2046 _ => false,
2047 }
2048}
2049
2050fn fold_bool_conditions(s: &mut Stmt, bv: &HashSet<u32>) {
2051 let fold = |e: &mut Expr| {
2052 deep_rewrite(e, &mut |x| {
2053 if let Expr::Bin { op, l, r, .. } = x {
2054 let (var_side, const_side) = match (&**l, &**r) {
2055 (Expr::Local { .. }, Expr::Const(ConstVal::Int(_))) => (l, 0),
2056 (Expr::Const(ConstVal::Int(_)), Expr::Local { .. }) => (r, 1),
2057 _ => return,
2058 };
2059 let var = match &**var_side {
2060 Expr::Local { var, .. } => *var,
2061 _ => return,
2062 };
2063 if !bv.contains(&var) {
2064 return;
2065 }
2066 let _ = const_side;
2067 match op {
2068 BinOp::Ne => {
2069 let inner = var_side.clone();
2070 *x = *inner;
2071 }
2072 BinOp::Eq => {
2073 let inner = var_side.clone();
2074 *x = Expr::Un {
2075 op: UnOp::Not,
2076 e: Box::new(*inner),
2077 };
2078 }
2079 _ => {}
2080 }
2081 }
2082 });
2083 };
2084 match s {
2085 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => fold(cond),
2086 _ => {}
2087 }
2088 walk_mut_deep(s, &mut |st| {
2089 let cond = match st {
2090 Stmt::If { cond, .. } => Some(cond),
2091 Stmt::While { cond, .. } => Some(cond),
2092 Stmt::DoWhile { cond, .. } => Some(cond),
2093 _ => None,
2094 };
2095 if let Some(c) = cond {
2096 fold(c);
2097 }
2098 });
2099}
2100
2101pub fn flip_const_compares(s: &mut Stmt) {
2103 rewrite_exprs(s, &mut |e| {
2104 deep_rewrite(e, &mut |x| {
2105 if let Expr::Bin { op, l, r, .. } = x {
2106 if matches!(
2107 op,
2108 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
2109 ) {
2110 let l_const = matches!(&**l, Expr::Const(_));
2111 let r_var = matches!(&**r, Expr::Local { .. });
2112 if l_const && r_var {
2113 std::mem::swap(l, r);
2114 }
2115 }
2116 }
2117 });
2118 });
2119}
2120
2121pub fn null_compares(vt: &VarTable, body: &mut Stmt) {
2123 let obj_vars: HashSet<u32> = vt
2124 .vars
2125 .iter()
2126 .filter(|v| v.ty.erased().is_reference())
2127 .map(|v| v.id)
2128 .collect();
2129 if obj_vars.is_empty() {
2130 return;
2131 }
2132 rewrite_exprs(body, &mut |e| {
2133 deep_rewrite(e, &mut |x| {
2134 if let Expr::Bin { op, l, r, .. } = x {
2135 if !matches!(op, BinOp::Eq | BinOp::Ne) {
2136 return;
2137 }
2138 let rewrite = |side: &mut Expr, other: &Expr| {
2139 if let Expr::Const(ConstVal::Int(0)) = other {
2140 if let Expr::Local { var, .. } = &*side {
2141 if obj_vars.contains(var) {
2142 *side = Expr::Const(ConstVal::Null);
2143 }
2144 }
2145 }
2146 };
2147 let lo = l.clone();
2148 let ro = r.clone();
2149 rewrite(l, &ro);
2150 rewrite(r, &lo);
2151 }
2152 });
2153 });
2154}
2155
2156pub fn ensure_declared(body: &mut Stmt, vt: &VarTable) {
2159 let n = vt.vars.len().max(1);
2163 let mut declared: Vec<bool> = vec![false; n];
2164 let mut is_param: Vec<bool> = vec![false; n];
2165 for v in &vt.vars {
2166 if v.is_param && (v.id as usize) < n {
2167 is_param[v.id as usize] = true;
2168 }
2169 }
2170 walk_all(body, &mut |st| {
2171 if let Stmt::LocalDef { var, .. } = st {
2172 if (*var as usize) < n {
2173 declared[*var as usize] = true;
2174 }
2175 }
2176 });
2177 let mut used: HashSet<u32> = HashSet::new();
2178 stmt_collect_vars(body, &mut used, false);
2179
2180 let mut needs_decl: Vec<u32> = used
2182 .into_iter()
2183 .filter(|v| {
2184 let i = *v as usize;
2185 i >= n || (!declared[i] && !is_param[i])
2186 })
2187 .collect();
2188 needs_decl.sort_unstable();
2189
2190 let mut seen: HashSet<u32> = HashSet::new();
2193 demote_dup_decls(body, &mut seen, vt);
2194
2195 if !needs_decl.is_empty() {
2196 let mut decls: Vec<Stmt> = needs_decl
2197 .into_iter()
2198 .map(|v| Stmt::LocalDef {
2199 var: v,
2200 init: None,
2201 is_final: false,
2202 force_type: true,
2203 })
2204 .collect();
2205 decls.reverse();
2206 match body {
2207 Stmt::Block(v) => {
2208 for d in decls {
2209 v.insert(0, d);
2210 }
2211 }
2212 other => {
2213 let inner = std::mem::replace(other, Stmt::Block(vec![]));
2214 *other = Stmt::Block({
2215 let mut vs = decls;
2216 vs.push(inner);
2217 vs
2218 });
2219 }
2220 }
2221 }
2222}
2223
2224fn demote_dup_decls(s: &mut Stmt, seen: &mut HashSet<u32>, vt: &VarTable) {
2225 match s {
2226 Stmt::Block(v) => {
2229 for x in v.iter_mut() {
2230 demote_dup_decls(x, seen, vt);
2231 }
2232 }
2233 Stmt::LocalDef { var, init, .. } => {
2234 if seen.contains(var) {
2235 let ty = vt.var(*var).ty.clone();
2236 if let Some(e) = init.take() {
2237 *s = Stmt::ExprStmt(Expr::Assign {
2238 target: Box::new(Expr::Local { var: *var, ty }),
2239 op: AssignOp::Plain,
2240 value: Box::new(e),
2241 });
2242 } else {
2243 *s = Stmt::Block(vec![]);
2244 }
2245 } else {
2246 seen.insert(*var);
2247 }
2248 }
2249 other => {
2250 walk_mut(other, &mut |x| demote_dup_decls(x, seen, vt));
2251 }
2252 }
2253}
2254
2255#[allow(dead_code)]
2256fn unused(_: &Vec<CaseGroup>, _: &Catch) {}