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