1#![allow(clippy::collapsible_match)]
14
15use jdc_core::FxHashSet as HashSet;
16
17use jdc_core::ir::build::has_side_effects;
21use jdc_core::emit::is_java_keyword;
22use jdc_core::ir::expr::{AssignOp, BinOp, ConcatPart, ConstVal, Expr, TypeRef, UnOp};
23use jdc_core::ir::stmt::{CaseGroup, Catch, Stmt};
24use jdc_core::types::{JavaType, MethodDescriptor};
25use jdc_core::var::VarTable;
26
27use crate::lift::MethodEnv;
28use crate::{access, desc_type, DexPool};
29use ddc_dex::insn::{Insn, InsnKind, InvokeKind};
30use jdc_core::types::parse_method_descriptor;
31
32pub fn rewrite_exprs<F: FnMut(&mut Expr)>(s: &mut Stmt, f: &mut F) {
38 walk_stmt_exprs(s, f);
39}
40
41pub(crate) fn walk_stmt_exprs<F: FnMut(&mut Expr)>(s: &mut Stmt, f: &mut F) {
42 match s {
43 Stmt::Block(v) => {
44 for x in v.iter_mut() {
45 walk_stmt_exprs(x, f);
46 }
47 }
48 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
49 f(e);
50 }
51 Stmt::Return(Some(e)) => f(e),
52 Stmt::LocalDef { init, .. } => {
53 if let Some(e) = init {
54 f(e);
55 }
56 }
57 Stmt::If {
58 cond,
59 then_stmt,
60 else_stmt,
61 } => {
62 f(cond);
63 walk_stmt_exprs(then_stmt, f);
64 if let Some(e) = else_stmt {
65 walk_stmt_exprs(e, f);
66 }
67 }
68 Stmt::While { cond, body } => {
69 f(cond);
70 walk_stmt_exprs(body, f);
71 }
72 Stmt::DoWhile { body, cond } => {
73 walk_stmt_exprs(body, f);
74 f(cond);
75 }
76 Stmt::For {
77 init,
78 cond,
79 update,
80 body,
81 } => {
82 for x in init.iter_mut() {
83 walk_stmt_exprs(x, f);
84 }
85 if let Some(c) = cond {
86 f(c);
87 }
88 for u in update.iter_mut() {
89 f(u);
90 }
91 walk_stmt_exprs(body, f);
92 }
93 Stmt::ForEach { iterable, body, .. } => {
94 f(iterable);
95 walk_stmt_exprs(body, f);
96 }
97 Stmt::Switch {
98 selector,
99 cases,
100 default,
101 ..
102 } => {
103 f(selector);
104 for c in cases {
105 for x in c.body.iter_mut() {
106 walk_stmt_exprs(x, f);
107 }
108 if let Some(g) = &mut c.guard {
109 f(g);
110 }
111 }
112 if let Some(d) = default {
113 walk_stmt_exprs(d, f);
114 }
115 }
116 Stmt::Try {
117 body,
118 catches,
119 finally,
120 } => {
121 walk_stmt_exprs(body, f);
122 for c in catches {
123 walk_stmt_exprs(&mut c.body, f);
124 }
125 if let Some(fl) = finally {
126 walk_stmt_exprs(fl, f);
127 }
128 }
129 Stmt::TryWithResources {
130 resources,
131 body,
132 catches,
133 finally,
134 } => {
135 for x in resources.iter_mut() {
136 walk_stmt_exprs(x, f);
137 }
138 walk_stmt_exprs(body, f);
139 for c in catches {
140 walk_stmt_exprs(&mut c.body, f);
141 }
142 if let Some(fl) = finally {
143 walk_stmt_exprs(fl, f);
144 }
145 }
146 Stmt::Assert { cond, msg } => {
147 f(cond);
148 if let Some(m) = msg {
149 f(m);
150 }
151 }
152 Stmt::Synchronized { lock, body } => {
153 f(lock);
154 walk_stmt_exprs(body, f);
155 }
156 Stmt::TernaryValue { e } => f(e),
157 Stmt::Labeled { body, .. } => walk_stmt_exprs(body, f),
158 Stmt::Goto(_) | Stmt::Label(_) | Stmt::Break(_) | Stmt::Continue(_) => {}
159 _ => {}
160 }
161}
162
163fn visit_exprs<F: FnMut(&Expr)>(e: &Expr, f: &mut F) {
165 f(e);
166 for_each_child(e, &mut |c| visit_exprs(c, f));
167}
168
169fn for_each_child<F: FnMut(&Expr)>(e: &Expr, f: &mut F) {
170 match e {
171 Expr::Un { e, .. } | Expr::Cast { e, .. } | Expr::InstanceOf { e, .. } => f(e),
172 Expr::Bin { l, r, .. } => {
173 f(l);
174 f(r);
175 }
176 Expr::Cond { c, t, f: fe } => {
177 f(c);
178 f(t);
179 f(fe);
180 }
181 Expr::Assign { target, value, .. } => {
182 f(target);
183 f(value);
184 }
185 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => f(e),
186 Expr::Field { owner, .. } => {
187 if let Some(o) = owner {
188 f(o);
189 }
190 }
191 Expr::Method { owner, args, .. } => {
192 if let Some(o) = owner {
193 f(o);
194 }
195 for a in args {
196 f(a);
197 }
198 }
199 Expr::ArrayIndex { array, index } => {
200 f(array);
201 f(index);
202 }
203 Expr::New { args, .. } => {
204 for a in args {
205 f(a);
206 }
207 }
208 Expr::NewArray { dims, init, .. } => {
209 for d in dims {
210 f(d);
211 }
212 if let Some(v) = init {
213 for x in v {
214 f(x);
215 }
216 }
217 }
218 Expr::NewMultiArray { dims, .. } => {
219 for d in dims {
220 f(d);
221 }
222 }
223 Expr::StringConcat(parts) => {
224 for p in parts {
225 if let ConcatPart::Str(x) = p {
226 f(x);
227 }
228 }
229 }
230 Expr::Invokedynamic { args, .. } => {
231 for a in args {
232 f(a);
233 }
234 }
235 Expr::AnonNew { args, .. } => {
236 for a in args {
237 f(a);
238 }
239 }
240 _ => {}
241 }
242}
243
244fn for_each_child_mut<F: FnMut(&mut Expr)>(e: &mut Expr, f: &mut F) {
246 match e {
247 Expr::Un { e, .. } | Expr::Cast { e, .. } | Expr::InstanceOf { e, .. } => f(e),
248 Expr::Bin { l, r, .. } => {
249 f(l);
250 f(r);
251 }
252 Expr::Cond { c, t, f: fe } => {
253 f(c);
254 f(t);
255 f(fe);
256 }
257 Expr::Assign { target, value, .. } => {
258 f(target);
259 f(value);
260 }
261 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => f(e),
262 Expr::Field { owner, .. } => {
263 if let Some(o) = owner {
264 f(o);
265 }
266 }
267 Expr::Method { owner, args, .. } => {
268 if let Some(o) = owner {
269 f(o);
270 }
271 for a in args.iter_mut() {
272 f(a);
273 }
274 }
275 Expr::ArrayIndex { array, index } => {
276 f(array);
277 f(index);
278 }
279 Expr::New { args, .. } => {
280 for a in args.iter_mut() {
281 f(a);
282 }
283 }
284 Expr::NewArray { dims, init, .. } => {
285 for d in dims.iter_mut() {
286 f(d);
287 }
288 if let Some(v) = init {
289 for x in v.iter_mut() {
290 f(x);
291 }
292 }
293 }
294 Expr::NewMultiArray { dims, .. } => {
295 for d in dims.iter_mut() {
296 f(d);
297 }
298 }
299 Expr::StringConcat(parts) => {
300 for p in parts {
301 if let ConcatPart::Str(x) = p {
302 f(x);
303 }
304 }
305 }
306 Expr::Invokedynamic { args, .. } => {
307 for a in args.iter_mut() {
308 f(a);
309 }
310 }
311 Expr::AnonNew { args, .. } => {
312 for a in args.iter_mut() {
313 f(a);
314 }
315 }
316 _ => {}
317 }
318}
319
320pub(crate) fn deep_rewrite<F: FnMut(&mut Expr)>(e: &mut Expr, f: &mut F) {
322 f(e);
323 for_each_child_mut(e, &mut |c| deep_rewrite(c, f));
324}
325
326fn deep_rewrite_reads<F: FnMut(&mut Expr)>(e: &mut Expr, f: &mut F) {
335 f(e);
336 match e {
337 Expr::Assign { target, value, .. } => {
338 if !matches!(**target, Expr::Local { .. }) {
339 deep_rewrite_reads(target, f);
340 }
341 deep_rewrite_reads(value, f);
342 }
343 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
344 if !matches!(**inner, Expr::Local { .. }) {
345 deep_rewrite_reads(inner, f);
346 }
347 }
348 _ => for_each_child_mut(e, &mut |c| deep_rewrite_reads(c, f)),
349 }
350}
351
352fn collect_vars(e: &Expr, out: &mut HashSet<u32>) {
353 visit_exprs(e, &mut |x| {
354 if let Expr::Local { var, .. } = x {
355 out.insert(*var);
356 }
357 });
358}
359
360fn stmt_collect_vars(s: &Stmt, out: &mut HashSet<u32>, assignments: bool) {
365 match s {
366 Stmt::Block(v) => {
367 for x in v {
368 stmt_collect_vars(x, out, assignments);
369 }
370 }
371 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
372 if let Expr::Local { var, .. } = &**target {
373 if assignments {
374 out.insert(*var);
375 }
376 } else {
377 collect_vars(target, out);
378 }
379 collect_vars(value, out);
380 }
381 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
382 collect_vars(e, out)
383 }
384 Stmt::Return(Some(e)) => collect_vars(e, out),
385 Stmt::LocalDef { var, init, .. } => {
386 if assignments {
387 out.insert(*var);
388 }
389 if let Some(e) = init {
390 collect_vars(e, out);
391 }
392 }
393 Stmt::If {
394 cond,
395 then_stmt,
396 else_stmt,
397 } => {
398 collect_vars(cond, out);
399 stmt_collect_vars(then_stmt, out, assignments);
400 if let Some(e) = else_stmt {
401 stmt_collect_vars(e, out, assignments);
402 }
403 }
404 Stmt::While { cond, body } => {
405 collect_vars(cond, out);
406 stmt_collect_vars(body, out, assignments);
407 }
408 Stmt::DoWhile { body, cond } => {
409 stmt_collect_vars(body, out, assignments);
410 collect_vars(cond, out);
411 }
412 Stmt::For {
413 init,
414 cond,
415 update,
416 body,
417 } => {
418 for x in init {
419 stmt_collect_vars(x, out, assignments);
420 }
421 if let Some(c) = cond {
422 collect_vars(c, out);
423 }
424 for u in update {
425 collect_vars(u, out);
426 }
427 stmt_collect_vars(body, out, assignments);
428 }
429 Stmt::ForEach {
430 var,
431 iterable,
432 body,
433 ..
434 } => {
435 if assignments {
436 out.insert(*var);
437 }
438 collect_vars(iterable, out);
439 stmt_collect_vars(body, out, assignments);
440 }
441 Stmt::Switch {
442 selector,
443 cases,
444 default,
445 ..
446 } => {
447 collect_vars(selector, out);
448 for c in cases {
449 for x in &c.body {
450 stmt_collect_vars(x, out, assignments);
451 }
452 }
453 if let Some(d) = default {
454 stmt_collect_vars(d, out, assignments);
455 }
456 }
457 Stmt::Try {
458 body,
459 catches,
460 finally,
461 } => {
462 stmt_collect_vars(body, out, assignments);
463 for c in catches {
464 stmt_collect_vars(&c.body, out, assignments);
465 }
466 if let Some(f) = finally {
467 stmt_collect_vars(f, out, assignments);
468 }
469 }
470 Stmt::TryWithResources {
471 resources,
472 body,
473 catches,
474 finally,
475 } => {
476 for x in resources {
477 stmt_collect_vars(x, out, assignments);
478 }
479 stmt_collect_vars(body, out, assignments);
480 for c in catches {
481 stmt_collect_vars(&c.body, out, assignments);
482 }
483 if let Some(f) = finally {
484 stmt_collect_vars(f, out, assignments);
485 }
486 }
487 Stmt::Synchronized { lock, body } => {
488 collect_vars(lock, out);
489 stmt_collect_vars(body, out, assignments);
490 }
491 Stmt::Assert { cond, msg } => {
492 collect_vars(cond, out);
493 if let Some(m) = msg {
494 collect_vars(m, out);
495 }
496 }
497 Stmt::Labeled { body, .. } => stmt_collect_vars(body, out, assignments),
498 _ => {}
499 }
500}
501
502fn walk_all<F: FnMut(&Stmt)>(s: &Stmt, f: &mut F) {
503 f(s);
504 match s {
505 Stmt::Block(v) => {
506 for x in v {
507 walk_all(x, f);
508 }
509 }
510 Stmt::If {
511 then_stmt,
512 else_stmt,
513 ..
514 } => {
515 walk_all(then_stmt, f);
516 if let Some(e) = else_stmt {
517 walk_all(e, f);
518 }
519 }
520 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => walk_all(body, f),
521 Stmt::For { init, body, .. } => {
522 for x in init {
523 walk_all(x, f);
524 }
525 walk_all(body, f);
526 }
527 Stmt::ForEach { body, .. } => walk_all(body, f),
528 Stmt::Switch { cases, default, .. } => {
529 for c in cases {
530 for x in &c.body {
531 walk_all(x, f);
532 }
533 }
534 if let Some(d) = default {
535 walk_all(d, f);
536 }
537 }
538 Stmt::Try {
539 body,
540 catches,
541 finally,
542 } => {
543 walk_all(body, f);
544 for c in catches {
545 walk_all(&c.body, f);
546 }
547 if let Some(fl) = finally {
548 walk_all(fl, f);
549 }
550 }
551 Stmt::TryWithResources {
552 resources,
553 body,
554 catches,
555 finally,
556 } => {
557 for x in resources {
558 walk_all(x, f);
559 }
560 walk_all(body, f);
561 for c in catches {
562 walk_all(&c.body, f);
563 }
564 if let Some(fl) = finally {
565 walk_all(fl, f);
566 }
567 }
568 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => walk_all(body, f),
569 _ => {}
570 }
571}
572
573pub fn bind_catches(s: &mut Stmt, vt: &mut VarTable) {
580 let mut defined: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
585 for v in &vt.vars {
586 if v.is_param {
587 defined.insert(v.id);
588 }
589 }
590 collect_defined_locals(s, &mut defined);
591 let reads_all = count_locals_stmts(std::slice::from_ref(s));
592 let mut assigned: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
596 collect_assigned_locals(s, &mut assigned);
597 let mut fallback_bound: Vec<u32> = Vec::new();
598 bind_catches_walk(s, vt, &defined, &reads_all, &assigned, &mut fallback_bound);
599 if !fallback_bound.is_empty() {
602 remove_bare_decls(s, &fallback_bound);
603 }
604}
605
606fn collect_defined_locals(s: &Stmt, out: &mut jdc_core::FxHashSet<u32>) {
607 crate::passes::walk_all(s, &mut |st| {
608 match st {
609 Stmt::LocalDef { var, .. } => {
610 out.insert(*var);
611 }
612 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
613 if let Expr::Local { var, .. } = &**target {
614 out.insert(*var);
615 }
616 }
617 _ => {}
618 }
619 });
620}
621
622
623fn collect_assigned_locals(s: &Stmt, out: &mut jdc_core::FxHashSet<u32>) {
628 crate::passes::walk_all(s, &mut |st| {
629 match st {
630 Stmt::LocalDef { var, init: Some(_), .. } => {
631 out.insert(*var);
632 }
633 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
634 if let Expr::Local { var, .. } = &**target {
635 out.insert(*var);
636 }
637 }
638 _ => {}
639 }
640 });
641}
642
643fn first_thrown_unassigned_local(
646 body: &Stmt,
647 vt: &VarTable,
648 assigned: &jdc_core::FxHashSet<u32>,
649) -> Option<u32> {
650 let mut found: Option<u32> = None;
651 let c = body.clone();
652 walk_all(&c, &mut |st| {
653 if let Stmt::Throw(th) = st {
654 if let Expr::Local { var, .. } = th {
655 let v = *var;
656 if !assigned.contains(&v) {
657 if let JavaType::Object(o) = vt.var(v).ty.erased() {
658 if o.as_ref() == "java/lang/Throwable" {
659 found = Some(v);
660 }
661 }
662 }
663 }
664 }
665 });
666 found
667}
668
669fn remove_bare_decls(s: &mut Stmt, vars: &[u32]) {
672 let vars: jdc_core::FxHashSet<u32> = vars.iter().copied().collect();
673 walk_mut_deep(s, &mut |st| {
674 if let Stmt::Block(v) = st {
675 v.retain(|x| {
676 !matches!(x, Stmt::LocalDef { var, init: None, .. } if vars.contains(var))
677 });
678 }
679 });
680}
681
682fn first_undefined_local_read(
683 body: &Stmt,
684 defined: &jdc_core::FxHashSet<u32>,
685) -> Option<u32> {
686 let mut found: Option<u32> = None;
687 let mut c = body.clone();
688 crate::passes::walk_stmt_exprs(&mut c, &mut |e| {
689 if found.is_none() {
690 deep_rewrite(e, &mut |x| {
691 if found.is_none() {
692 if let Expr::Local { var, .. } = x {
693 if !defined.contains(var) {
694 found = Some(*var);
695 }
696 }
697 }
698 });
699 }
700 });
701 found
702}
703
704#[allow(clippy::too_many_arguments)]
705fn bind_catches_walk(
706 s: &mut Stmt,
707 vt: &mut VarTable,
708 defined: &jdc_core::FxHashSet<u32>,
709 reads_all: &std::collections::HashMap<u32, usize>,
710 assigned: &jdc_core::FxHashSet<u32>,
711 fallback_bound: &mut Vec<u32>,
712) {
713 match s {
714 Stmt::Try {
715 body,
716 catches,
717 finally,
718 } => {
719 bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound);
720 for c in catches.iter_mut() {
721 if c.var == u32::MAX {
722 let stored = match c.body.as_ref() {
723 Stmt::Block(v) => match v.first() {
724 Some(Stmt::LocalDef { var, init: None, .. }) => Some(*var),
736 Some(Stmt::ExprStmt(Expr::Assign { target, value, .. })) => {
740 match (&**target, &**value) {
741 (Expr::Local { var, .. }, Expr::Local { var: src, .. })
742 if !defined.contains(src) =>
743 {
744 Some(*var)
745 }
746 _ => None,
747 }
748 }
749 _ => None,
750 },
751 _ => None,
752 };
753 if let Some(v) = stored {
754 let exc = c
755 .exc
756 .first()
757 .cloned()
758 .unwrap_or_else(|| "java/lang/Throwable".into());
759 let slot = vt.var(v).slot;
760 let name = "e".to_string();
761 let new_var =
762 vt.add_catch_var(slot, name, TypeRef::J(JavaType::Object(exc)));
763 if let Stmt::Block(vs) = c.body.as_mut() {
764 vs.remove(0);
765 }
766 rewrite_local_refs(c.body.as_mut(), v, new_var);
767 c.var = new_var;
768 } else if let Some(v) = first_undefined_local_read(c.body.as_ref(), defined)
769 .filter(|v| {
770 let in_catch = count_locals_stmts(std::slice::from_ref(c.body.as_ref()));
776 reads_all.get(v).copied().unwrap_or(0)
777 == in_catch.get(v).copied().unwrap_or(0)
778 })
779 .filter(|v| {
780 let ty = vt.var(*v).ty.erased();
788 let exc = c
789 .exc
790 .first()
791 .cloned()
792 .unwrap_or_else(|| "java/lang/Throwable".into());
793 matches!(&ty, JavaType::Object(o)
794 if o.as_ref() == exc.as_ref()
795 || o.as_ref() == "java/lang/Throwable")
796 })
797 {
798 c.var = v;
808 fallback_bound.push(v);
809 } else if let Some(v) =
810 first_thrown_unassigned_local(c.body.as_ref(), vt, assigned)
811 .filter(|v| {
812 let in_catch =
813 count_locals_stmts(std::slice::from_ref(c.body.as_ref()));
814 reads_all.get(v).copied().unwrap_or(0)
815 == in_catch.get(v).copied().unwrap_or(0)
816 })
817 .filter(|v| {
818 let ty = vt.var(*v).ty.erased();
822 let exc = c
823 .exc
824 .first()
825 .cloned()
826 .unwrap_or_else(|| "java/lang/Throwable".into());
827 matches!(&ty, JavaType::Object(o)
828 if o.as_ref() == exc.as_ref()
829 || o.as_ref() == "java/lang/Throwable")
830 })
831 {
832 c.var = v;
838 fallback_bound.push(v);
839 }
840 }
841 bind_catches_walk(&mut c.body, vt, defined, reads_all, assigned, fallback_bound);
842 }
843 if let Some(f) = finally {
844 bind_catches_walk(f.as_mut(), vt, defined, reads_all, assigned, fallback_bound);
845 }
846 }
847 Stmt::Block(v) => {
848 for x in v.iter_mut() {
849 bind_catches_walk(x, vt, defined, reads_all, assigned, fallback_bound);
850 }
851 }
852 Stmt::If {
853 then_stmt,
854 else_stmt,
855 ..
856 } => {
857 bind_catches_walk(then_stmt, vt, defined, reads_all, assigned, fallback_bound);
858 if let Some(e) = else_stmt {
859 bind_catches_walk(e, vt, defined, reads_all, assigned, fallback_bound);
860 }
861 }
862 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound),
863 Stmt::For { init, body, .. } => {
864 for x in init.iter_mut() {
865 bind_catches_walk(x, vt, defined, reads_all, assigned, fallback_bound);
866 }
867 bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound);
868 }
869 Stmt::ForEach { body, .. }
870 | Stmt::Labeled { body, .. }
871 | Stmt::Synchronized { body, .. } => bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound),
872 Stmt::Switch { cases, default, .. } => {
873 for c in cases.iter_mut() {
874 for x in c.body.iter_mut() {
875 bind_catches_walk(x, vt, defined, reads_all, assigned, fallback_bound);
876 }
877 }
878 if let Some(d) = default {
879 bind_catches_walk(d, vt, defined, reads_all, assigned, fallback_bound);
880 }
881 }
882 _ => {}
883 }
884}
885
886fn rewrite_local_refs(s: &mut Stmt, from: u32, to: u32) {
887 rewrite_exprs(s, &mut |e| {
888 deep_rewrite(e, &mut |x| {
889 if let Expr::Local { var, .. } = x {
890 if *var == from {
891 *var = to;
892 }
893 }
894 });
895 });
896}
897
898pub fn cleanup(s: &mut Stmt) {
900 jdc_core::ir::stmt::flatten(s);
901 strip_empty(s);
902 merge_singleton_stmts(s);
903}
904
905fn strip_empty(s: &mut Stmt) {
906 match s {
907 Stmt::Block(v) => {
908 v.retain(|x| !x.is_empty_block());
909 for x in v.iter_mut() {
910 strip_empty(x);
911 }
912 }
913 Stmt::If {
914 then_stmt,
915 else_stmt,
916 ..
917 } => {
918 strip_empty(then_stmt);
919 if let Some(e) = else_stmt {
920 strip_empty(e);
921 if e.is_empty_block() {
922 *else_stmt = None;
923 }
924 }
925 }
926 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => strip_empty(body),
927 Stmt::For { init, body, .. } => {
928 init.retain(|x| !x.is_empty_block());
929 strip_empty(body);
930 }
931 Stmt::ForEach { body, .. } => strip_empty(body),
932 Stmt::Switch { cases, default, .. } => {
933 for c in cases {
934 c.body.retain(|x| !x.is_empty_block());
935 }
936 if let Some(d) = default {
937 strip_empty(d);
938 }
939 }
940 Stmt::Try {
941 body,
942 catches,
943 finally,
944 } => {
945 strip_empty(body);
946 for c in catches {
947 strip_empty(&mut c.body);
948 }
949 if let Some(f) = finally {
950 strip_empty(f);
951 }
952 }
953 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => strip_empty(body),
954 _ => {}
955 }
956}
957
958fn merge_singleton_stmts(s: &mut Stmt) {
959 if let Stmt::Block(v) = s {
960 for x in v.iter_mut() {
961 merge_singleton_stmts(x);
962 }
963 }
964}
965
966pub fn prune_unreachable(s: &mut Stmt) {
968 if let Stmt::Block(v) = s {
969 let mut cut: Option<usize> = None;
970 for (i, x) in v.iter().enumerate() {
971 if matches!(x, Stmt::Return(_) | Stmt::Throw(_)) {
972 cut = Some(i + 1);
973 break;
974 }
975 }
976 if let Some(c) = cut {
977 if c < v.len() {
978 v.truncate(c);
979 }
980 }
981 for x in v.iter_mut() {
982 prune_unreachable(x);
983 }
984 } else {
985 walk_mut(s, &mut prune_unreachable);
986 }
987}
988
989fn walk_mut<F: FnMut(&mut Stmt)>(s: &mut Stmt, f: &mut F) {
990 match s {
991 Stmt::Block(v) => {
992 for x in v.iter_mut() {
993 f(x);
994 }
995 }
996 Stmt::If {
997 then_stmt,
998 else_stmt,
999 ..
1000 } => {
1001 f(then_stmt);
1002 if let Some(e) = else_stmt {
1003 f(e);
1004 }
1005 }
1006 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => f(body),
1007 Stmt::For { init, body, .. } => {
1008 for x in init.iter_mut() {
1009 f(x);
1010 }
1011 f(body);
1012 }
1013 Stmt::ForEach { body, .. } => f(body),
1014 Stmt::Switch { cases, default, .. } => {
1015 for c in cases {
1016 for x in c.body.iter_mut() {
1017 f(x);
1018 }
1019 }
1020 if let Some(d) = default {
1021 f(d);
1022 }
1023 }
1024 Stmt::Try {
1025 body,
1026 catches,
1027 finally,
1028 } => {
1029 f(body);
1030 for c in catches {
1031 f(&mut c.body);
1032 }
1033 if let Some(fl) = finally {
1034 f(fl);
1035 }
1036 }
1037 Stmt::TryWithResources {
1038 resources,
1039 body,
1040 catches,
1041 finally,
1042 } => {
1043 for r in resources.iter_mut() {
1044 f(r);
1045 }
1046 f(body);
1047 for c in catches {
1048 f(&mut c.body);
1049 }
1050 if let Some(fl) = finally {
1051 f(fl);
1052 }
1053 }
1054 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => f(body),
1055 _ => {}
1056 }
1057}
1058
1059pub fn count_localdefs(s: &Stmt, out: &mut usize) {
1061 if matches!(s, Stmt::LocalDef { .. }) {
1062 *out += 1;
1063 }
1064 walk_all(s, &mut |x| {
1065 if matches!(x, Stmt::LocalDef { .. }) {
1066 *out += 1;
1067 }
1068 });
1069}
1070
1071pub fn strip_trailing_void_return(s: &mut Stmt) {
1073 if let Stmt::Block(v) = s {
1074 loop {
1075 match v.last_mut() {
1076 Some(Stmt::Return(None)) => {
1077 v.pop();
1078 }
1079 Some(Stmt::Block(_)) => {
1084 let last = v.last_mut().unwrap();
1085 let before = match last {
1086 Stmt::Block(b) => b.len(),
1087 _ => 0,
1088 };
1089 strip_trailing_void_return(last);
1090 let after = match last {
1091 Stmt::Block(b) => b.len(),
1092 _ => 0,
1093 };
1094 if after == before {
1095 break; }
1097 }
1098 _ => break,
1099 }
1100 }
1101 }
1102}
1103
1104pub fn invert_empty_thens(s: &mut Stmt) {
1109 walk_mut_deep(s, &mut |st| {
1110 if let Stmt::If {
1111 cond,
1112 then_stmt,
1113 else_stmt,
1114 } = st
1115 {
1116 let then_empty = matches!(&**then_stmt, Stmt::Block(v) if v.is_empty());
1117 if then_empty {
1118 if let Some(els) = else_stmt.take() {
1119 let c = std::mem::replace(cond, Expr::Const(ConstVal::Int(0)));
1120 *cond = jdc_core::convert::negate(c);
1121 *then_stmt = els;
1122 }
1123 }
1124 }
1125 });
1126}
1127
1128pub fn fold_short_circuits(s: &mut Stmt) {
1139 for _ in 0..8 {
1140 let mut changed = false;
1141 walk_mut_deep(s, &mut |st| {
1142 if try_fold_diamond(st) {
1143 changed = true;
1144 }
1145 });
1146 if !changed {
1147 break;
1148 }
1149 }
1150}
1151
1152fn try_fold_diamond(st: &mut Stmt) -> bool {
1153 enum Shape {
1154 ThenAnd,
1155 ThenAndNot,
1156 ElseOr,
1157 ElseOrNot,
1158 BareAnd,
1159 BareAndNot,
1160 }
1161 let shape = match &*st {
1165 Stmt::If {
1168 then_stmt,
1169 else_stmt: None,
1170 ..
1171 } => match &**then_stmt {
1172 Stmt::If {
1173 then_stmt: t2,
1174 else_stmt: None,
1175 ..
1176 } if !matches!(&**t2, Stmt::Block(v) if v.is_empty()) => Some(Shape::BareAnd),
1177 Stmt::If {
1178 then_stmt: t2,
1179 else_stmt: Some(_),
1180 ..
1181 } if matches!(&**t2, Stmt::Block(v) if v.is_empty()) => Some(Shape::BareAndNot),
1182 _ => None,
1183 },
1184 Stmt::If {
1185 then_stmt,
1186 else_stmt: Some(els),
1187 ..
1188 } => match (&**then_stmt, &**els) {
1189 (Stmt::If { else_stmt: Some(f2), .. }, _) if f2.as_ref() == els.as_ref() => {
1190 Some(Shape::ThenAnd)
1191 }
1192 (Stmt::If { then_stmt: t2, else_stmt: Some(_), .. }, _)
1193 if t2.as_ref() == els.as_ref() =>
1194 {
1195 Some(Shape::ThenAndNot)
1196 }
1197 (_, Stmt::If { then_stmt: t2, else_stmt: Some(_), .. })
1198 if t2.as_ref() == then_stmt.as_ref() =>
1199 {
1200 Some(Shape::ElseOr)
1201 }
1202 (_, Stmt::If { else_stmt: Some(f2), .. })
1203 if f2.as_ref() == then_stmt.as_ref() =>
1204 {
1205 Some(Shape::ElseOrNot)
1206 }
1207 _ => None,
1208 },
1209 _ => None,
1210 };
1211 let Some(shape) = shape else {
1212 return false;
1213 };
1214
1215 let taken = std::mem::replace(st, Stmt::Block(vec![]));
1216 if matches!(shape, Shape::BareAnd | Shape::BareAndNot) {
1218 let Stmt::If {
1219 cond: c1,
1220 then_stmt,
1221 else_stmt: None,
1222 } = taken
1223 else {
1224 unreachable!()
1225 };
1226 let Stmt::If {
1227 cond: c2,
1228 then_stmt: t2,
1229 else_stmt: f2,
1230 } = *then_stmt
1231 else {
1232 unreachable!()
1233 };
1234 let sc = |op: BinOp, l: Expr, r: Expr| Expr::Bin {
1235 op,
1236 l: Box::new(l),
1237 r: Box::new(r),
1238 ty: None,
1239 };
1240 match shape {
1241 Shape::BareAnd => {
1243 *st = Stmt::If {
1244 cond: sc(BinOp::LogAnd, c1, c2),
1245 then_stmt: t2,
1246 else_stmt: None,
1247 };
1248 }
1249 _ => {
1251 let body = f2.unwrap();
1252 *st = Stmt::If {
1253 cond: sc(
1254 BinOp::LogAnd,
1255 c1,
1256 Expr::Un {
1257 op: UnOp::Not,
1258 e: Box::new(c2),
1259 },
1260 ),
1261 then_stmt: body,
1262 else_stmt: None,
1263 };
1264 }
1265 }
1266 return true;
1267 }
1268 let Stmt::If {
1269 cond: c1,
1270 then_stmt,
1271 else_stmt: Some(els),
1272 } = taken
1273 else {
1274 unreachable!("shape probe guaranteed an If with else")
1275 };
1276 let sc = |op: BinOp, l: Expr, r: Expr| Expr::Bin {
1277 op,
1278 l: Box::new(l),
1279 r: Box::new(r),
1280 ty: None,
1281 };
1282 let not = |e: Expr| Expr::Un {
1283 op: UnOp::Not,
1284 e: Box::new(e),
1285 };
1286 match shape {
1287 Shape::BareAnd | Shape::BareAndNot => unreachable!("handled above"),
1288 Shape::ThenAnd | Shape::ThenAndNot => {
1289 let Stmt::If {
1290 cond: c2,
1291 then_stmt: t2,
1292 else_stmt: Some(f2),
1293 } = *then_stmt
1294 else {
1295 unreachable!()
1296 };
1297 let (nc, body, alt) = match shape {
1298 Shape::ThenAnd => (sc(BinOp::LogAnd, c1, c2), t2, f2),
1300 _ => (sc(BinOp::LogAnd, c1, not(c2)), f2, t2),
1302 };
1303 *st = Stmt::If {
1304 cond: nc,
1305 then_stmt: body,
1306 else_stmt: Some(alt),
1307 };
1308 }
1309 Shape::ElseOr | Shape::ElseOrNot => {
1310 let Stmt::If {
1311 cond: c2,
1312 then_stmt: t2,
1313 else_stmt: Some(f2),
1314 } = *els
1315 else {
1316 unreachable!()
1317 };
1318 let body = then_stmt;
1321 let (nc, alt) = match shape {
1322 Shape::ElseOr => (sc(BinOp::LogOr, c1, c2), f2),
1324 _ => (sc(BinOp::LogOr, c1, not(c2)), t2),
1326 };
1327 *st = Stmt::If {
1328 cond: nc,
1329 then_stmt: body,
1330 else_stmt: Some(alt),
1331 };
1332 }
1333 }
1334 true
1335}
1336
1337pub fn strip_clinit_returns(s: &mut Stmt) {
1343 walk_mut_deep(s, &mut |st| {
1344 if let Stmt::Block(v) = st {
1345 v.retain(|x| !matches!(x, Stmt::Return(None)));
1346 }
1347 });
1348}
1349
1350pub fn prepend_comment(s: &mut Stmt, text: String) {
1351 let stmt = Stmt::Comment(text);
1352 match s {
1353 Stmt::Block(v) => v.insert(0, stmt),
1354 other => {
1355 let inner = std::mem::replace(other, Stmt::Block(vec![]));
1356 *other = Stmt::Block(vec![stmt, inner]);
1357 }
1358 }
1359}
1360
1361fn is_obj_expr(e: &Expr, obj_var: &dyn Fn(u32) -> bool) -> bool {
1369 match e {
1370 Expr::Local { var, .. } => obj_var(*var),
1371 Expr::Const(_) => false,
1372 _ => e.type_ref().erased().is_reference(),
1373 }
1374}
1375
1376fn null_side_rewrite(x: &mut Expr, obj_var: &dyn Fn(u32) -> bool) {
1381 if let Expr::Bin { op, l, r, .. } = x {
1382 if !matches!(op, BinOp::Eq | BinOp::Ne) {
1383 return;
1384 }
1385 let l_zero = matches!(&**l, Expr::Const(ConstVal::Int(0)));
1386 let r_zero = matches!(&**r, Expr::Const(ConstVal::Int(0)));
1387 if l_zero && !r_zero && is_obj_expr(r, obj_var) {
1388 **l = Expr::Const(ConstVal::Null);
1389 } else if r_zero && !l_zero && is_obj_expr(l, obj_var) {
1390 **r = Expr::Const(ConstVal::Null);
1391 }
1392 }
1393}
1394
1395pub fn fused_expr_rewrites(s: &mut Stmt, vt: &VarTable) {
1396 let mut obj_vars: Vec<bool> = Vec::with_capacity(vt.vars.len());
1398 for v in &vt.vars {
1399 obj_vars.push(v.ty.erased().is_reference());
1400 }
1401 rewrite_exprs(s, &mut |e| {
1402 deep_rewrite(e, &mut |x| {
1403 if let Expr::Invokedynamic { name, args, .. } = x {
1405 if name.starts_with('\0') && args.len() == 2 {
1406 let (cls, ty) = match name.as_str() {
1407 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
1408 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
1409 _ => ("java/lang/Double", JavaType::Double),
1410 };
1411 let desc = MethodDescriptor {
1412 args: vec![ty.clone(), ty],
1413 ret: JavaType::Int,
1414 };
1415 *x = Expr::Method {
1416 owner: None,
1417 cls: cls.into(),
1418 name: "compare".into(),
1419 desc: std::sync::Arc::new(desc),
1420 args: args.clone(),
1421 is_static: true,
1422 is_interface: false,
1423 is_special: false,
1424 is_super: false,
1425 is_dynamic: false,
1426 type_args: vec![],
1427 };
1428 return;
1429 }
1430 }
1431 null_side_rewrite(x, &|v| obj_vars.get(v as usize).copied().unwrap_or(false));
1434 if let Expr::Bin { op, l, r, .. } = x {
1436 if matches!(
1437 op,
1438 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
1439 ) {
1440 let l_const = matches!(&**l, Expr::Const(_));
1441 let r_var = matches!(&**r, Expr::Local { .. });
1442 if l_const && r_var {
1443 std::mem::swap(l, r);
1444 }
1445 }
1446 }
1447 });
1448 });
1449 walk_mut_deep(s, &mut |st| {
1457 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = st {
1458 let tgt_is_obj = match &**target {
1459 Expr::Local { var, .. } => {
1460 obj_vars.get(*var as usize).copied().unwrap_or(false)
1461 }
1462 other => other.type_ref().erased().is_reference(),
1463 };
1464 if tgt_is_obj {
1465 if let Expr::Const(ConstVal::Int(0)) = &**value {
1466 **value = Expr::Const(ConstVal::Null);
1467 }
1468 }
1469 } else if let Stmt::LocalDef { var, init, .. } = st {
1470 if obj_vars.get(*var as usize).copied().unwrap_or(false) {
1471 if let Some(iv) = init {
1472 if let Expr::Const(ConstVal::Int(0)) = iv {
1473 *iv = Expr::Const(ConstVal::Null);
1474 }
1475 }
1476 }
1477 }
1478 });
1479}
1480
1481#[allow(dead_code)]
1484pub fn desugar_cmp_residuals(s: &mut Stmt) {
1485 rewrite_exprs(s, &mut |e| {
1486 deep_rewrite(e, &mut |x| {
1487 if let Expr::Invokedynamic { name, args, .. } = x {
1488 if name.starts_with('\0') && args.len() == 2 {
1489 let (cls, ty) = match name.as_str() {
1490 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
1491 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
1492 _ => ("java/lang/Double", JavaType::Double),
1493 };
1494 let desc = jdc_core::types::MethodDescriptor {
1495 args: vec![ty.clone(), ty],
1496 ret: JavaType::Int,
1497 };
1498 *x = Expr::Method {
1499 owner: None,
1500 cls: cls.into(),
1501 name: "compare".into(),
1502 desc: std::sync::Arc::new(desc),
1503 args: args.clone(),
1504 is_static: true,
1505 is_interface: false,
1506 is_special: false,
1507 is_super: false,
1508 is_dynamic: false,
1509 type_args: vec![],
1510 };
1511 }
1512 }
1513 });
1514 });
1515}
1516
1517pub fn ternary_fold(s: &mut Stmt) {
1519 let mut changed = true;
1520 let mut guard = 0;
1521 while changed && guard < 8 {
1522 changed = false;
1523 guard += 1;
1524 ternary_fold_walk(s, &mut changed);
1525 strip_empty(s);
1526 }
1527}
1528
1529fn ternary_fold_walk(s: &mut Stmt, changed: &mut bool) {
1530 if let Stmt::Block(v) = s {
1531 for x in v.iter_mut() {
1532 ternary_fold_walk(x, changed);
1533 }
1534 return;
1535 }
1536 let Stmt::If {
1537 cond,
1538 then_stmt,
1539 else_stmt,
1540 } = s
1541 else {
1542 return;
1543 };
1544 ternary_fold_walk(then_stmt, changed);
1545 if let Some(e) = else_stmt {
1546 ternary_fold_walk(e, changed);
1547 }
1548 let Some(else_b) = else_stmt else { return };
1549 let target_of = |st: &Stmt| -> Option<u32> {
1550 let Stmt::ExprStmt(Expr::Assign { target, .. }) = st else {
1551 return None;
1552 };
1553 if let Expr::Local { var, .. } = &**target {
1554 Some(*var)
1555 } else {
1556 None
1557 }
1558 };
1559 let (Stmt::Block(tv), Stmt::Block(ev)) = (then_stmt.as_mut(), else_b.as_mut()) else {
1560 return;
1561 };
1562 if tv.len() != 1 || ev.len() != 1 {
1563 return;
1564 }
1565 let (Some(vt_), Some(ve)) = (target_of(&tv[0]), target_of(&ev[0])) else {
1566 return;
1567 };
1568 if vt_ != ve {
1569 return;
1570 }
1571 let mut touched = HashSet::default();
1575 let (Stmt::ExprStmt(a_then), Stmt::ExprStmt(a_else)) = (&tv[0], &ev[0]) else {
1576 return;
1577 };
1578 if let Expr::Assign { value, .. } = a_then {
1579 collect_vars(value, &mut touched);
1580 }
1581 if let Expr::Assign { value, .. } = a_else {
1582 collect_vars(value, &mut touched);
1583 }
1584 if touched.contains(&vt_) {
1585 return;
1586 }
1587 let value_then = match a_then {
1588 Expr::Assign { value, .. } => value.clone(),
1589 _ => return,
1590 };
1591 let value_else = match a_else {
1592 Expr::Assign { value, .. } => value.clone(),
1593 _ => return,
1594 };
1595 let cond_e = cond.clone();
1596 let folded = Stmt::ExprStmt(Expr::Assign {
1597 target: Box::new(Expr::Local {
1598 var: vt_,
1599 ty: vt_join_ty(&value_then, &value_else),
1600 }),
1601 op: AssignOp::Plain,
1602 value: Box::new(Expr::Cond {
1603 c: Box::new(cond_e),
1604 t: value_then,
1605 f: value_else,
1606 }),
1607 });
1608 *s = Stmt::Block(vec![folded]);
1609 *changed = true;
1610}
1611
1612fn vt_join_ty(a: &Expr, b: &Expr) -> TypeRef {
1613 let ta = a.type_ref();
1614 let tb = b.type_ref();
1615 if ta.erased() == tb.erased() {
1616 return ta;
1617 }
1618 TypeRef::J(JavaType::Object("java/lang/Object".into()))
1619}
1620
1621pub fn fold_string_builders(s: &mut Stmt, vt: &VarTable) {
1628 let n_vars = vt.vars.len().max(1);
1630 let mut counts = vec![0usize; n_vars];
1631 count_assignments(s, &mut counts);
1632 let mut values: Vec<Option<Expr>> = vec![None; n_vars];
1633 record_assignments(s, &counts, &mut values);
1634
1635 let mut appended_stmts: HashSet<u32> = HashSet::default();
1637 walk_all(s, &mut |st| {
1638 if let Stmt::ExprStmt(Expr::Method {
1639 cls,
1640 name,
1641 owner,
1642 args,
1643 ..
1644 }) = st
1645 {
1646 if name.as_ref() == "append" && is_string_builder(cls) && args.len() == 1 {
1647 if let Some(Expr::Local { var, .. }) = owner.as_deref() {
1648 appended_stmts.insert(*var);
1649 }
1650 }
1651 }
1652 });
1653
1654 rewrite_exprs(s, &mut |e| {
1655 fold_concat_in_expr(e, &values, &appended_stmts);
1656 });
1657
1658 for _ in 0..8 {
1662 let mut reads: HashSet<u32> = HashSet::default();
1663 stmt_collect_vars(s, &mut reads, false);
1664 if drop_unused_assigns(s, &reads) == 0 {
1665 break;
1666 }
1667 }
1668}
1669
1670pub fn count_stmts_deep(s: &Stmt, out: &mut usize) {
1672 *out += 1;
1673 walk_all(s, &mut |_| {
1674 *out += 1;
1675 });
1676}
1677
1678struct VarAnalysis {
1680 assigns: Vec<usize>,
1681 reads: Vec<usize>,
1682 values: Vec<Option<Expr>>,
1684}
1685
1686fn analyze_vars(s: &Stmt, a: &mut VarAnalysis) {
1687 walk_all(s, &mut |st| {
1688 let exprs: Vec<&Expr> = match st {
1689 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
1690 if let Expr::Local { var, .. } = &**target {
1691 grow_to(&mut a.assigns, *var);
1692 let idx = *var as usize;
1693 a.assigns[idx] += 1;
1694 if a.assigns[idx] == 1 {
1695 if idx >= a.values.len() {
1696 a.values.resize(idx + 1, None);
1697 }
1698 a.values[idx] = Some((**value).clone());
1699 } else if idx < a.values.len() {
1700 a.values[idx] = None;
1701 }
1702 }
1703 let mut v: Vec<&Expr> = vec![value];
1704 if !matches!(&**target, Expr::Local { .. }) {
1705 v.push(target);
1706 }
1707 v
1708 }
1709 Stmt::ExprStmt(e @ (Expr::PreIncDec { .. } | Expr::PostIncDec { .. })) => {
1710 let inner = match e {
1715 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => e,
1716 _ => unreachable!(),
1717 };
1718 if let Expr::Local { var, .. } = &**inner {
1719 grow_to(&mut a.assigns, *var);
1720 let idx = *var as usize;
1721 a.assigns[idx] += 1;
1722 if idx < a.values.len() {
1723 a.values[idx] = None;
1724 }
1725 return;
1726 }
1727 vec![e]
1728 }
1729 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
1730 vec![e]
1731 }
1732 Stmt::Return(Some(e)) => vec![e],
1733 Stmt::LocalDef {
1734 var, init: Some(e), ..
1735 } => {
1736 grow_to(&mut a.assigns, *var);
1737 let idx = *var as usize;
1738 a.assigns[idx] += 1;
1739 if a.assigns[idx] == 1 {
1740 if idx >= a.values.len() {
1741 a.values.resize(idx + 1, None);
1742 }
1743 a.values[idx] = Some(e.clone());
1744 } else if idx < a.values.len() {
1745 a.values[idx] = None;
1746 }
1747 vec![e]
1748 }
1749 Stmt::If { cond, .. } => vec![cond],
1750 Stmt::While { cond, .. } => vec![cond],
1751 Stmt::DoWhile { cond, .. } => vec![cond],
1752 _ => Vec::new(),
1753 };
1754 for e in exprs {
1755 visit_exprs(e, &mut |x| {
1756 if let Expr::Local { var, .. } = x {
1757 grow_to(&mut a.reads, *var);
1758 a.reads[*var as usize] += 1;
1759 }
1760 });
1761 }
1762 });
1763 let n = a.assigns.len().max(a.reads.len()).max(a.values.len());
1765 a.assigns.resize(n, 0);
1766 a.reads.resize(n, 0);
1767 a.values.resize(n, None);
1768}
1769
1770fn grow_to(v: &mut Vec<usize>, var: u32) {
1771 if var as usize >= v.len() {
1772 v.resize(var as usize + 1, 0);
1773 }
1774}
1775
1776fn count_assignments(s: &Stmt, counts: &mut Vec<usize>) {
1777 walk_all(s, &mut |st| match st {
1778 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
1779 if let Expr::Local { var, .. } = &**target {
1780 grow_to(counts, *var);
1781 counts[*var as usize] += 1;
1782 }
1783 }
1784 Stmt::LocalDef {
1785 var, init: Some(_), ..
1786 } => {
1787 grow_to(counts, *var);
1788 counts[*var as usize] += 1;
1789 }
1790 _ => {}
1791 });
1792}
1793
1794fn record_assignments(s: &Stmt, counts: &[usize], out: &mut Vec<Option<Expr>>) {
1795 walk_all(s, &mut |st| match st {
1796 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
1797 if let Expr::Local { var, .. } = &**target {
1798 if counts.get(*var as usize).copied().unwrap_or(0) == 1 {
1799 if *var as usize >= out.len() {
1800 out.resize(*var as usize + 1, None);
1801 }
1802 out[*var as usize] = Some((**value).clone());
1803 }
1804 }
1805 }
1806 Stmt::LocalDef {
1807 var, init: Some(e), ..
1808 } if counts.get(*var as usize).copied().unwrap_or(0) == 1 => {
1809 if *var as usize >= out.len() {
1810 out.resize(*var as usize + 1, None);
1811 }
1812 out[*var as usize] = Some(e.clone());
1813 }
1814 _ => {}
1815 });
1816}
1817
1818fn fold_concat_in_expr(e: &mut Expr, values: &[Option<Expr>], appended: &HashSet<u32>) {
1819 deep_rewrite(e, &mut |x| {
1820 if let Expr::Method {
1821 cls,
1822 name,
1823 args,
1824 owner,
1825 ..
1826 } = x
1827 {
1828 if name.as_ref() == "toString" && args.is_empty() && is_string_builder(cls) {
1829 if let Some(o) = owner {
1830 let mut has_stmt_appends = false;
1833 visit_exprs(o, &mut |y| {
1834 if let Expr::Local { var, .. } = y {
1835 if appended.contains(var) {
1836 has_stmt_appends = true;
1837 }
1838 }
1839 });
1840 if has_stmt_appends {
1841 return;
1842 }
1843 let resolved = resolve_local(o, values);
1844 if let Some(parts) = collect_sb_parts(&resolved, values, 0) {
1845 if !parts.is_empty() {
1854 *x = Expr::StringConcat(parts);
1855 }
1856 }
1857 }
1858 }
1859 }
1860 });
1861}
1862
1863fn is_string_builder(cls: &str) -> bool {
1864 cls == "java/lang/StringBuilder" || cls == "java/lang/StringBuffer"
1865}
1866
1867fn resolve_local(e: &Expr, values: &[Option<Expr>]) -> Expr {
1868 match e {
1869 Expr::Local { var, ty } => values
1870 .get(*var as usize)
1871 .and_then(|o| o.clone())
1872 .unwrap_or_else(|| Expr::Local {
1873 var: *var,
1874 ty: ty.clone(),
1875 }),
1876 other => other.clone(),
1877 }
1878}
1879
1880fn collect_sb_parts(e: &Expr, values: &[Option<Expr>], depth: u32) -> Option<Vec<ConcatPart>> {
1882 if depth > 16 {
1883 return None;
1884 }
1885 match e {
1886 Expr::Method {
1887 cls,
1888 name,
1889 args,
1890 owner,
1891 ..
1892 } if name.as_ref() == "append" && is_string_builder(cls) && args.len() == 1 => {
1893 let mut parts =
1894 collect_sb_parts(&resolve_local(owner.as_deref()?, values), values, depth + 1)?;
1895 parts.push(ConcatPart::Str(args[0].clone()));
1896 Some(parts)
1897 }
1898 Expr::New {
1899 cls,
1900 args,
1901 raw: false,
1902 ..
1903 } if is_string_builder(cls) => {
1904 let mut parts = Vec::new();
1905 for a in args {
1906 if let Expr::Const(ConstVal::Str(sv)) = a {
1907 parts.push(ConcatPart::Const(sv.to_string()));
1908 } else {
1909 parts.push(ConcatPart::Str(a.clone()));
1910 }
1911 }
1912 Some(parts)
1913 }
1914 _ => None,
1915 }
1916}
1917
1918fn drop_unused_assigns(s: &mut Stmt, reads: &HashSet<u32>) -> usize {
1919 let mut dropped = 0usize;
1920 walk_mut_deep(s, &mut |st| {
1921 if let Stmt::Block(v) = st {
1922 let before = v.len();
1923 v.retain(|x| match x {
1924 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
1927 Expr::Local { var, .. } => reads.contains(var) || !is_sbish(value),
1928 _ => true,
1929 },
1930 Stmt::LocalDef { var, init, .. } => {
1931 reads.contains(var) || !init.as_ref().map(is_sbish).unwrap_or(false)
1932 }
1933 _ => true,
1934 });
1935 dropped += before - v.len();
1936 }
1937 });
1938 dropped
1939}
1940
1941fn is_sbish(e: &Expr) -> bool {
1942 match e {
1943 Expr::New {
1944 cls, raw: false, ..
1945 } => is_string_builder(cls),
1946 Expr::Method { name, cls, .. } => name.as_ref() == "append" && is_string_builder(cls),
1947 _ => false,
1948 }
1949}
1950
1951fn walk_mut_deep<F: FnMut(&mut Stmt)>(s: &mut Stmt, f: &mut F) {
1952 f(s);
1953 walk_mut(s, &mut |x| walk_mut_deep(x, f));
1954}
1955
1956pub fn drop_dead_raw_news(body: &mut Stmt) {
1968 fn is_orphan(s: &Stmt) -> bool {
1969 matches!(s, Stmt::ExprStmt(Expr::New { raw: true, .. }))
1970 }
1971 walk_mut_deep(body, &mut |st| match st {
1972 Stmt::Block(v) => v.retain(|x| !is_orphan(x)),
1973 Stmt::Switch { cases, .. } => {
1974 for c in cases.iter_mut() {
1975 c.body.retain(|x| !is_orphan(x));
1976 }
1977 }
1978 Stmt::For { init, .. } => init.retain(|x| !is_orphan(x)),
1979 Stmt::TryWithResources { resources, .. } => resources.retain(|x| !is_orphan(x)),
1980 _ => {}
1981 });
1982}
1983
1984pub fn fold_synchronized(s: &mut Stmt) {
1988 fold_sync_walk(s);
1989}
1990
1991fn fold_sync_walk(s: &mut Stmt) {
1992 walk_mut_deep(s, &mut |st| {
1993 let Stmt::Block(v) = st else { return };
1994 let mut i = 0;
1995 while i < v.len() {
1996 if i + 1 < v.len() {
1997 if let Some(sync) = try_sync_at(v, i) {
1998 let _ = v.drain(i..i + 2);
1999 v.insert(i, sync);
2000 continue;
2001 }
2002 }
2003 i += 1;
2004 }
2005 });
2006}
2007
2008fn try_sync_at(v: &[Stmt], i: usize) -> Option<Stmt> {
2009 let Stmt::MonitorEnter(lock) = &v[i] else {
2010 return None;
2011 };
2012 let Stmt::Try {
2013 body,
2014 catches,
2015 finally,
2016 } = &v[i + 1]
2017 else {
2018 return None;
2019 };
2020 if finally.is_some() || catches.len() != 1 {
2021 return None;
2022 }
2023 let c = &catches[0];
2024 if !c.exc.is_empty() {
2025 return None;
2026 }
2027 let Stmt::Block(cv) = c.body.as_ref() else {
2029 return None;
2030 };
2031 let exit_matches = cv.len() >= 2
2032 && matches!(&cv[0], Stmt::MonitorExit(e) if expr_local_var(e) == expr_local_var(lock));
2033 let throws = cv.len() >= 2 && matches!(&cv[1], Stmt::Throw(_));
2034 if !(exit_matches && throws) {
2035 return None;
2036 }
2037 Some(Stmt::Synchronized {
2038 lock: lock.clone(),
2039 body: body.clone(),
2040 })
2041}
2042
2043fn expr_local_var(e: &Expr) -> Option<u32> {
2044 if let Expr::Local { var, .. } = e {
2045 Some(*var)
2046 } else {
2047 None
2048 }
2049}
2050
2051pub fn forward_single_use(s: &mut Stmt, _vt: &VarTable) {
2056 let mut analysis = VarAnalysis {
2060 assigns: Vec::new(),
2061 reads: Vec::new(),
2062 values: Vec::new(),
2063 };
2064 analyze_vars(s, &mut analysis);
2065 let mut assigns = analysis.assigns;
2066 let mut reads = analysis.reads;
2067 let n_vars = assigns.len().max(_vt.vars.len()).max(1);
2070 assigns.resize(n_vars, 0);
2071 reads.resize(n_vars, 0);
2072 let mut values = analysis.values;
2073 values.resize(n_vars, None);
2074
2075 let mut cand = vec![false; n_vars];
2080 let mut edges: Vec<Vec<u32>> = vec![Vec::new(); n_vars];
2081 for (v, cand_v) in cand.iter_mut().enumerate() {
2082 if assigns.get(v).copied().unwrap_or(0) != 1 || reads.get(v).copied().unwrap_or(0) != 1 {
2083 continue;
2084 }
2085 if let Some(val) = values.get(v).and_then(|o| o.as_ref()) {
2086 let mut refs = HashSet::default();
2087 collect_vars(val, &mut refs);
2088 if refs.iter().any(|r| assigns[*r as usize] > 1) {
2089 continue;
2090 }
2091 edges[v] = refs.into_iter().collect();
2095 }
2096 *cand_v = true;
2097 }
2098
2099 let mut color = vec![0u8; n_vars]; let mut bad = vec![false; n_vars];
2117 for root in 0..n_vars {
2118 if color[root] != 0 || edges[root].is_empty() {
2119 continue;
2120 }
2121 color[root] = 1;
2122 let mut stack: Vec<(usize, usize)> = vec![(root, 0)];
2123 while let Some(top) = stack.last_mut() {
2124 let v = top.0;
2125 let i = top.1;
2126 top.1 += 1;
2127 if i < edges[v].len() {
2128 let r = edges[v][i] as usize;
2129 if color[r] == 1 {
2130 for &(pn, _) in &stack {
2131 bad[pn] = true;
2132 }
2133 } else if color[r] == 0 {
2134 color[r] = 1;
2135 stack.push((r, 0));
2136 } else if bad[r] {
2137 for &(pn, _) in &stack {
2138 bad[pn] = true;
2139 }
2140 }
2141 } else {
2142 color[v] = 2;
2143 stack.pop();
2144 }
2145 }
2146 }
2147 let single: Vec<bool> = (0..n_vars).map(|v| cand[v] && !bad[v]).collect();
2148 if !single.iter().any(|&b| b) {
2149 return;
2150 }
2151 let pure: Vec<bool> = (0..n_vars)
2152 .map(|v| {
2153 single[v]
2154 && values
2155 .get(v)
2156 .and_then(|o| o.as_ref())
2157 .map(|e| !has_side_effects(e))
2158 .unwrap_or(false)
2159 })
2160 .collect();
2161 let impure: Vec<bool> = (0..n_vars).map(|v| single[v] && !pure[v]).collect();
2162
2163 if pure.iter().any(|&b| b) {
2165 let vals: Vec<Option<Expr>> = (0..n_vars)
2166 .map(|v| {
2167 if pure[v] {
2168 values.get(v).cloned().flatten()
2169 } else {
2170 None
2171 }
2172 })
2173 .collect();
2174 rewrite_exprs(s, &mut |e| {
2175 deep_rewrite_reads(e, &mut |x| {
2176 if let Expr::Local { var, .. } = x {
2177 if let Some(Some(v)) = vals.get(*var as usize) {
2178 *x = v.clone();
2179 }
2180 }
2181 });
2182 });
2183 drop_defs(s, &pure);
2184 }
2185
2186 if impure.iter().any(|&b| b) {
2189 forward_adjacent_impure(s, &values, &impure);
2190 }
2191}
2192
2193fn count_reads(s: &Stmt, out: &mut Vec<usize>) {
2195 walk_all(s, &mut |st| {
2196 let exprs: Vec<&Expr> = match st {
2197 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
2200 let mut v: Vec<&Expr> = vec![value];
2201 if !matches!(&**target, Expr::Local { .. }) {
2202 v.push(target);
2203 }
2204 v
2205 }
2206 Stmt::ExprStmt(e @ (Expr::PreIncDec { .. } | Expr::PostIncDec { .. })) => {
2209 let inner = match e {
2210 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => e,
2211 _ => unreachable!(),
2212 };
2213 if matches!(**inner, Expr::Local { .. }) {
2214 Vec::new()
2215 } else {
2216 vec![e]
2217 }
2218 }
2219 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
2220 vec![e]
2221 }
2222 Stmt::Return(Some(e)) => vec![e],
2223 Stmt::LocalDef { init: Some(e), .. } => vec![e],
2224 Stmt::If { cond, .. } => vec![cond],
2225 Stmt::While { cond, .. } => vec![cond],
2226 Stmt::DoWhile { cond, .. } => vec![cond],
2227 Stmt::Switch { selector, .. } => vec![selector],
2233 Stmt::ForEach { iterable, .. } => vec![iterable],
2234 Stmt::Synchronized { lock, .. } => vec![lock],
2235 Stmt::For { cond, update, .. } => {
2236 let mut v: Vec<&Expr> = Vec::with_capacity(update.len() + 1);
2237 if let Some(c) = cond {
2238 v.push(c);
2239 }
2240 v.extend(update.iter());
2241 v
2242 }
2243 Stmt::Assert { cond, msg } => {
2244 let mut v: Vec<&Expr> = vec![cond];
2245 if let Some(m) = msg {
2246 v.push(m);
2247 }
2248 v
2249 }
2250 _ => Vec::new(),
2251 };
2252 for e in exprs {
2253 visit_exprs(e, &mut |x| {
2254 if let Expr::Local { var, .. } = x {
2255 grow_to(out, *var);
2256 out[*var as usize] += 1;
2257 }
2258 });
2259 }
2260 });
2261}
2262
2263fn drop_defs(s: &mut Stmt, vars: &[bool]) {
2264 walk_mut_deep(s, &mut |st| {
2265 if let Stmt::Block(v) = st {
2266 v.retain(|x| match x {
2267 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
2268 Expr::Local { var, .. } => !vars.get(*var as usize).copied().unwrap_or(false),
2269 _ => true,
2270 },
2271 Stmt::LocalDef { var, .. } => !vars.get(*var as usize).copied().unwrap_or(false),
2272 _ => true,
2273 });
2274 }
2275 });
2276}
2277
2278fn forward_adjacent_impure(s: &mut Stmt, values: &[Option<Expr>], impure: &[bool]) {
2280 if let Stmt::Block(v) = s {
2281 let mut i = 0;
2282 while i + 1 < v.len() {
2283 let def_var = match &v[i] {
2284 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
2285 Expr::Local { var, .. } => Some(*var),
2286 _ => None,
2287 },
2288 Stmt::LocalDef { var, .. } => Some(*var),
2289 _ => None,
2290 };
2291 if let Some(var) = def_var {
2292 if impure.get(var as usize).copied().unwrap_or(false) {
2293 let mut reads_here = vec![0usize; values.len().max(1)];
2295 count_reads(&v[i + 1], &mut reads_here);
2296 if reads_here.get(var as usize).copied().unwrap_or(0) == 1 {
2297 let val = match &v[i] {
2301 Stmt::ExprStmt(Expr::Assign { value, .. }) => (**value).clone(),
2302 Stmt::LocalDef { init: Some(e), .. } => (*e).clone(),
2303 _ => {
2304 i += 1;
2305 continue;
2306 }
2307 };
2308 rewrite_exprs(&mut v[i + 1], &mut |e| {
2309 deep_rewrite_reads(e, &mut |x| {
2310 if let Expr::Local { var: v2, .. } = x {
2311 if *v2 == var {
2312 *x = val.clone();
2313 }
2314 }
2315 });
2316 });
2317 v.remove(i);
2318 i = i.saturating_sub(1);
2321 continue;
2322 }
2323 }
2324 }
2325 i += 1;
2326 }
2327 }
2328 walk_mut(s, &mut |x| forward_adjacent_impure(x, values, impure));
2329}
2330
2331pub fn infer_types(vt: &mut VarTable, body: &mut Stmt, ret: &JavaType, env: &MethodEnv) {
2337 let n = vt.vars.len();
2338 let mut evidence: Vec<Vec<(JavaType, bool)>> = vec![Vec::new(); n];
2346 let ev = |evidence: &mut Vec<Vec<(JavaType, bool)>>, var: u32, t: JavaType, strong: bool| {
2347 if (var as usize) < evidence.len() {
2348 evidence[var as usize].push((t, strong));
2349 }
2350 };
2351
2352 walk_all(body, &mut |st| match st {
2353 Stmt::LocalDef { var, init, .. } => {
2354 if let Some(e) = init {
2355 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
2356 let strong = match e {
2364 Expr::Const(_) => false,
2365 Expr::Local { var: src, .. } => {
2366 let src_ty = vt.vars.get(*src as usize).map(|v| v.ty.erased());
2367 let tgt_ty = vt.vars.get(*var as usize).map(|v| v.ty.erased());
2368 match (src_ty, tgt_ty) {
2369 (Some(s), Some(g)) => {
2370 (s.is_numeric() && g.is_numeric())
2371 || (s.is_reference() && g.is_reference())
2372 }
2373 _ => false,
2374 }
2375 }
2376 _ => true,
2377 };
2378 ev(&mut evidence, *var, e.type_ref().erased(), strong);
2379 }
2380 }
2381 Stmt::ExprStmt(e) => {
2382 if let Expr::Assign { target, value, .. } = e {
2389 if let Expr::Field { owner: Some(o), cls, is_static: false, .. } = &**target {
2396 if cls.is_empty() {
2397 } else if let Expr::Local { var, .. } = &**o {
2399 ev(
2400 &mut evidence,
2401 *var,
2402 JavaType::Object(cls.clone()),
2403 true,
2404 );
2405 }
2406 }
2407 if let Expr::Local { var, .. } = &**target {
2408 let strong = match &**value {
2409 Expr::Const(_) => false,
2410 Expr::Local { var: src, .. } => {
2411 let src_ty = vt.vars.get(*src as usize).map(|v| v.ty.erased());
2418 let tgt_ty = vt.vars.get(*var as usize).map(|v| v.ty.erased());
2419 match (src_ty, tgt_ty) {
2420 (Some(s), Some(g)) => {
2421 (s.is_numeric() && g.is_numeric())
2422 || (s.is_reference() && g.is_reference())
2423 }
2424 _ => false,
2425 }
2426 }
2427 _ => true,
2428 };
2429 ev(&mut evidence, *var, value.type_ref().erased(), strong);
2430 }
2431 }
2432 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
2433 }
2434 Stmt::Throw(e) => {
2435 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
2436 if let Expr::Local { var, .. } = e {
2437 ev(
2438 &mut evidence,
2439 *var,
2440 JavaType::Object("java/lang/Throwable".into()),
2441 true,
2442 );
2443 }
2444 }
2445 Stmt::Return(Some(e)) => {
2446 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
2447 if let Expr::Local { var, .. } = e {
2448 ev(&mut evidence, *var, ret.clone(), true);
2449 }
2450 }
2451 Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
2452 if let Expr::Local { var, .. } = e {
2453 ev(
2454 &mut evidence,
2455 *var,
2456 JavaType::Object("java/lang/Object".into()),
2457 true,
2458 );
2459 }
2460 }
2461 Stmt::ForEach {
2462 var,
2463 iterable,
2464 is_array,
2465 ..
2466 } => {
2467 if let Expr::Local { var: v0, .. } = iterable {
2468 if *is_array {
2469 ev(&mut evidence, *v0, JavaType::Array(Box::new(JavaType::Int)), true);
2470 } else {
2471 ev(
2472 &mut evidence,
2473 *v0,
2474 JavaType::Object("java/lang/Object".into()),
2475 true,
2476 );
2477 }
2478 }
2479 let _ = var;
2480 }
2481 _ => {}
2482 });
2483 let _ = env;
2484
2485 let mut derefed: Vec<bool> = vec![false; vt.vars.len()];
2495 mark_derefs(body, &mut derefed);
2496 let mut arr_derefed: Vec<bool> = vec![false; vt.vars.len()];
2501 visit_all_exprs(body, &mut |x| {
2502 if let Expr::ArrayIndex { array, .. } = x {
2503 if let Expr::Local { var, .. } = &**array {
2504 if (*var as usize) < arr_derefed.len() {
2505 arr_derefed[*var as usize] = true;
2506 }
2507 }
2508 }
2509 if let Expr::Bin { op, l, r, .. } = x {
2517 if matches!(op, BinOp::Eq | BinOp::Ne)
2518 && (matches!(&**r, Expr::Const(ConstVal::Null))
2519 || matches!(&**l, Expr::Const(ConstVal::Null)))
2520 {
2521 for side in [l, r] {
2522 if let Expr::Local { var, .. } = &**side {
2523 ev(
2524 &mut evidence,
2525 *var,
2526 JavaType::Object("java/lang/Object".into()),
2527 true,
2528 );
2529 }
2530 }
2531 }
2532 }
2533 });
2534 const FINAL_JDK: &[&str] = &[
2535 "java/lang/Boolean",
2536 "java/lang/String",
2537 "java/lang/Integer",
2538 "java/lang/Long",
2539 "java/lang/Short",
2540 "java/lang/Byte",
2541 "java/lang/Character",
2542 "java/lang/Float",
2543 "java/lang/Double",
2544 ];
2545 for (i, evs) in evidence.iter().enumerate() {
2546 let info = &vt.vars[i];
2547 if info.is_param {
2548 continue;
2549 }
2550 let strong: Vec<JavaType> = evs
2551 .iter()
2552 .filter(|(_, s)| *s)
2553 .map(|(t, _)| t.clone())
2554 .collect();
2555 let all: Vec<JavaType> = evs.iter().map(|(t, _)| t.clone()).collect();
2556 if info.ty.erased().is_reference() {
2557 if strong.is_empty() {
2563 let refs: std::collections::HashSet<&str> = all
2564 .iter()
2565 .filter_map(|t| match t {
2566 JavaType::Object(n) if n.as_ref() != "java/lang/Object" => {
2567 Some(n.as_ref())
2568 }
2569 _ => None,
2570 })
2571 .collect();
2572 let mixed = all.iter().any(|t| t.is_numeric());
2573 if refs.len() >= 2 || (mixed && !refs.is_empty()) || (mixed && all.iter().any(|t| matches!(t, JavaType::Array(_)))) {
2574 vt.vars[i].ty =
2575 TypeRef::J(JavaType::Object("java/lang/Object".into()));
2576 continue;
2577 }
2578 }
2579 if arr_derefed[i] {
2582 let arr = strong
2583 .iter()
2584 .chain(all.iter())
2585 .find(|t| matches!(t, JavaType::Array(_)))
2586 .cloned();
2587 if let Some(t) = arr {
2588 vt.vars[i].ty = TypeRef::J(t);
2589 continue;
2590 }
2591 }
2592 let deref_pick = || {
2596 if !derefed[i] {
2597 return None;
2598 }
2599 strong
2600 .iter()
2601 .find(|t| {
2602 matches!(t, JavaType::Object(n)
2603 if !n.is_empty()
2604 && n.as_ref() != "java/lang/Object"
2605 && !FINAL_JDK.contains(&n.as_ref()))
2606 })
2607 .cloned()
2608 };
2609 if let Some(t) = deref_pick()
2610 .or_else(|| pick_object(&strong))
2611 .or_else(|| pick_object(&all))
2612 {
2613 vt.vars[i].ty = TypeRef::J(t);
2614 }
2615 } else if let Some(t) = pick_object(&strong) {
2616 vt.vars[i].ty = TypeRef::J(t);
2620 } else if strong.contains(&JavaType::Boolean) {
2621 vt.vars[i].ty = TypeRef::J(JavaType::Boolean);
2625 } else if !all.is_empty() && all.iter().all(|t| t.is_numeric()) {
2626 if let Some(t) = pick_numeric(&all) {
2627 vt.vars[i].ty = TypeRef::J(t);
2628 }
2629 } else {
2630 let refs = all.iter().filter(|t| t.is_reference()).count();
2634 let nums = all.iter().filter(|t| t.is_numeric()).count();
2635 if refs > 0 && nums > 0 {
2636 vt.vars[i].ty =
2637 TypeRef::J(JavaType::Object("java/lang/Object".into()));
2638 } else if let Some(t) = pick_object(&strong).or_else(|| pick_object(&all)) {
2639 vt.vars[i].ty = TypeRef::J(t);
2640 }
2641 }
2642 }
2643
2644 let types: Vec<&TypeRef> = vt.vars.iter().map(|v| &v.ty).collect();
2649 rewrite_exprs(body, &mut |e| {
2650 deep_rewrite(e, &mut |x| {
2651 if let Expr::Local { var, ty } = x {
2652 if let Some(want) = types.get(*var as usize) {
2653 if ty != *want {
2654 *ty = (*want).clone();
2655 }
2656 }
2657 }
2658 });
2659 });
2660
2661 coerce_num_consts(body, &types);
2664}
2665
2666fn coerce_num_consts(body: &mut Stmt, types: &[&TypeRef]) {
2667 walk_mut_deep(body, &mut |st| {
2668 let (var, val): (u32, &mut Expr) = match st {
2669 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &mut **target {
2670 Expr::Local { var, .. } => (*var, value),
2671 _ => return,
2672 },
2673 Stmt::LocalDef {
2674 var, init: Some(e), ..
2675 } => (*var, e),
2676 _ => return,
2677 };
2678 let want = match types.get(var as usize) {
2679 Some(TypeRef::J(t)) => t.clone(),
2680 _ => return,
2681 };
2682 let rewritten = match &*val {
2683 Expr::Const(ConstVal::Long(l)) => match &want {
2684 JavaType::Double => Some(Expr::Const(ConstVal::Double(*l as f64))),
2685 JavaType::Float => Some(Expr::Const(ConstVal::Float(*l as f32))),
2686 _ => None,
2687 },
2688 Expr::Const(ConstVal::Int(i)) => match &want {
2689 JavaType::Double => Some(Expr::Const(ConstVal::Double(*i as f64))),
2690 JavaType::Float => Some(Expr::Const(ConstVal::Float(*i as f32))),
2691 _ => None,
2692 },
2693 _ => None,
2694 };
2695 if let Some(e) = rewritten {
2696 *val = e;
2697 }
2698 });
2699}
2700
2701fn pick_object(evs: &[JavaType]) -> Option<JavaType> {
2702 evs.iter()
2703 .find(|t| {
2704 t.is_reference()
2705 && !matches!(t, JavaType::Object(n) if n.is_empty() || n.as_ref() == "java/lang/Object")
2706 })
2707 .cloned()
2708}
2709
2710fn pick_numeric(evs: &[JavaType]) -> Option<JavaType> {
2711 if evs.is_empty() {
2712 return None;
2713 }
2714 let mut t = evs[0].clone();
2715 for x in &evs[1..] {
2716 t = join_numeric(&t, x);
2717 }
2718 Some(t)
2719}
2720
2721fn join_numeric(a: &JavaType, b: &JavaType) -> JavaType {
2722 if a == b {
2723 return a.clone();
2724 }
2725 match (a, b) {
2726 (JavaType::Double, _) | (_, JavaType::Double) => JavaType::Double,
2727 (JavaType::Float, _) | (_, JavaType::Float) => JavaType::Float,
2728 (JavaType::Long, _) | (_, JavaType::Long) => JavaType::Long,
2729 (JavaType::Int, x) if x.is_integral() => JavaType::Int,
2730 (x, JavaType::Int) if x.is_integral() => JavaType::Int,
2731 (x, y) if x.is_integral() && y.is_integral() => JavaType::Int,
2732 _ => JavaType::Int,
2733 }
2734}
2735
2736fn expr_evidence<F: FnMut(u32, JavaType)>(e: &Expr, f: &mut F) {
2738 visit_exprs(e, &mut |x| match x {
2739 Expr::Method {
2740 owner,
2741 args,
2742 desc,
2743 is_static,
2744 cls,
2745 ..
2746 } => {
2747 if !*is_static {
2748 if let Some(o) = owner {
2749 if let Expr::Local { var, .. } = &**o {
2750 f(*var, JavaType::Object(cls.clone()));
2751 }
2752 }
2753 }
2754 for (i, a) in args.iter().enumerate() {
2755 if let Expr::Local { var, .. } = a {
2756 if let Some(t) = desc.args.get(i) {
2757 f(*var, t.clone());
2758 }
2759 }
2760 }
2761 }
2762 Expr::Field {
2763 owner,
2764 cls,
2765 ty,
2766 is_static,
2767 ..
2768 } => {
2769 if !*is_static && !cls.is_empty() {
2770 if let Some(o) = owner {
2771 if let Expr::Local { var, .. } = &**o {
2772 f(*var, JavaType::Object(cls.clone()));
2782 }
2783 }
2784 }
2785 let _ = ty;
2786 }
2787 Expr::Bin { op, l, r, .. }
2792 if matches!(op, BinOp::Eq | BinOp::Ne)
2793 && (matches!(&**r, Expr::Const(ConstVal::Null))
2794 || matches!(&**l, Expr::Const(ConstVal::Null))) =>
2795 {
2796 for side in [l, r] {
2797 if let Expr::Local { var, .. } = &**side {
2798 f(*var, JavaType::Object("java/lang/Object".into()));
2799 }
2800 }
2801 }
2802 Expr::ArrayIndex { array, .. } => {
2803 if let Expr::Local { var, ty, .. } = &**array {
2804 let t = ty.erased();
2809 if matches!(t, JavaType::Array(_)) {
2810 f(*var, t);
2811 } else {
2812 f(*var, JavaType::Array(Box::new(JavaType::Int)));
2813 }
2814 }
2815 }
2816 Expr::Assign { .. } => {
2817 }
2820 Expr::Cast { e: inner, .. } => {
2821 let _ = inner;
2827 }
2828 Expr::InstanceOf { e: inner, .. } => {
2829 let _ = inner;
2830 }
2831 _ => {}
2832 });
2833}
2834
2835pub fn mark_field_owner_concrete(vt: &mut VarTable, body: &mut Stmt) {
2847 let mut owners: HashSet<u32> = HashSet::default();
2848 rewrite_exprs(body, &mut |e| {
2849 visit_exprs(e, &mut |x| {
2850 if let Expr::Field {
2851 owner: Some(o),
2852 is_static: false,
2853 ..
2854 } = x
2855 {
2856 if let Expr::Local { var, .. } = &**o {
2857 owners.insert(*var);
2858 }
2859 }
2860 });
2861 });
2862 for var in owners {
2863 let i = var as usize;
2864 if i >= vt.vars.len() {
2865 continue;
2866 }
2867 if let JavaType::Object(cls) = vt.vars[i].ty.erased() {
2868 if let Some(last) = cls.rsplit('$').next() {
2869 if !last.is_empty() && last.chars().all(|c| c.is_ascii_digit()) {
2870 vt.force_concrete_vars.insert(var);
2871 }
2872 }
2873 }
2874 }
2875 let concrete: Vec<u32> = vt
2884 .vars
2885 .iter()
2886 .filter(|v| {
2887 if let JavaType::Object(cls) = v.ty.erased() {
2888 cls.rsplit('$')
2889 .next()
2890 .is_some_and(|l| !l.is_empty() && l.chars().all(|c| c.is_ascii_digit()))
2891 } else {
2892 false
2893 }
2894 })
2895 .map(|v| v.id)
2896 .collect();
2897 for var in concrete {
2898 vt.force_concrete_vars.insert(var);
2899 }
2900}
2901
2902pub fn insert_object_narrowing_casts(
2914 vt: &VarTable,
2915 body: &mut Stmt,
2916 pool: &DexPool,
2917 ret: &JavaType,
2918) {
2919 let value_ty = |e: &Expr| -> JavaType {
2922 match e {
2923 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
2924 other => other.type_ref().erased(),
2925 }
2926 };
2927 let is_top_object = |e: &Expr| -> bool {
2928 value_ty(e) == JavaType::Object("java/lang/Object".into())
2929 };
2930 let specific_ref = |ty: &TypeRef| -> Option<TypeRef> {
2934 match ty.erased() {
2935 JavaType::Object(c) if c.as_ref() != "java/lang/Object" => Some(ty.clone()),
2936 JavaType::Array(_) => Some(ty.clone()),
2937 _ => None,
2938 }
2939 };
2940 let castable = |e: &Expr| -> bool {
2941 is_top_object(e)
2942 && !matches!(e, Expr::Const(_) | Expr::Cast { .. } | Expr::InstanceOf { .. })
2943 };
2944 let needs_downcast = |e: &Expr, tt: &JavaType| -> bool {
2954 if matches!(e, Expr::Const(_) | Expr::Cast { .. } | Expr::InstanceOf { .. }) {
2955 return false;
2956 }
2957 let v = value_ty(e);
2958 let (JavaType::Object(vn), JavaType::Object(tn)) = (&v, tt) else {
2959 return false;
2960 };
2961 if vn.as_ref() == "java/lang/Object" || tn.as_ref() == "java/lang/Object" {
2962 return false;
2963 }
2964 vn != tn && !pool.is_subtype(vn, tn)
2965 };
2966 walk_mut_deep(body, &mut |st| match st {
2967 Stmt::ExprStmt(Expr::Assign {
2968 target,
2969 value,
2970 op: AssignOp::Plain,
2971 ..
2972 }) => {
2973 let tgt = match &**target {
2974 Expr::Local { var, .. } => vt.var(*var).ty.clone(),
2975 Expr::Field { ty, .. } => ty.clone(),
2976 Expr::ArrayIndex { array, .. } => {
2983 let aty = match &**array {
2984 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
2985 other => other.type_ref().erased(),
2986 };
2987 match aty {
2988 JavaType::Array(inner) => TypeRef::J(*inner),
2989 _ => return,
2990 }
2991 }
2992 _ => return,
2993 };
2994 if let Some(t) = specific_ref(&tgt) {
2995 if castable(value) || needs_downcast(value, &tgt.erased()) {
2996 let v = std::mem::replace(value, Box::new(Expr::This));
2997 **value = Expr::Cast { ty: t, e: v };
2998 }
2999 }
3000 }
3001 Stmt::LocalDef { var, init: Some(value), .. } => {
3002 if let Some(t) = specific_ref(&vt.var(*var).ty) {
3003 let tt = vt.var(*var).ty.erased();
3004 if castable(value) || needs_downcast(value, &tt) {
3005 let v = std::mem::replace(value, Expr::This);
3006 *value = Expr::Cast {
3007 ty: t,
3008 e: Box::new(v),
3009 };
3010 }
3011 }
3012 }
3013 Stmt::Return(Some(value)) => {
3014 if let Some(t) = specific_ref(&TypeRef::J(ret.clone())) {
3015 if castable(value) || needs_downcast(value, ret) {
3016 let v = std::mem::replace(value, Expr::This);
3017 *value = Expr::Cast { ty: t, e: Box::new(v) };
3018 }
3019 }
3020 }
3021 _ => {}
3022 });
3023 walk_stmt_exprs(body, &mut |e| {
3030 deep_rewrite(e, &mut |x| {
3031 let (args, arg_tys): (&mut Vec<Expr>, &[JavaType]) = match x {
3032 Expr::Method { desc, args, is_dynamic: false, .. } => {
3033 (args, desc.args.as_slice())
3034 }
3035 Expr::New { arg_tys, args, .. } => (args, arg_tys.as_slice()),
3039 _ => return,
3040 };
3041 for (i, a) in args.iter_mut().enumerate() {
3042 let Some(formal) = arg_tys.get(i) else {
3043 continue;
3044 };
3045 let Some(t) = specific_ref(&TypeRef::J(formal.clone())) else {
3046 continue;
3047 };
3048 if !(castable(a) || needs_downcast(a, formal)) {
3049 continue;
3050 }
3051 let v = std::mem::replace(a, Expr::Const(ConstVal::Null));
3052 *a = Expr::Cast { ty: t, e: Box::new(v) };
3053 }
3054 });
3055 });
3056}
3057
3058pub fn fix_primitive_assign_casts(vt: &VarTable, body: &mut Stmt, ret_ty: &JavaType) {
3067 fn numlike(t: &JavaType) -> bool {
3068 matches!(
3069 t,
3070 JavaType::Byte
3071 | JavaType::Short
3072 | JavaType::Int
3073 | JavaType::Char
3074 | JavaType::Long
3075 | JavaType::Float
3076 | JavaType::Double
3077 )
3078 }
3079 fn val_ty(e: &Expr, vt: &VarTable) -> JavaType {
3080 match e {
3081 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
3082 vt.var(*var).ty.erased()
3083 }
3084 other => other.type_ref().erased(),
3085 }
3086 }
3087 fn wrap(ty: &JavaType, value: &mut Expr) {
3088 if matches!(value, Expr::Cast { .. }) {
3089 return;
3090 }
3091 if val_ty_static(value) == *ty {
3092 return;
3093 }
3094 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
3095 *value = Expr::Cast {
3096 ty: TypeRef::J(ty.clone()),
3097 e: Box::new(taken),
3098 };
3099 }
3100 fn val_ty_static(e: &Expr) -> JavaType {
3101 e.type_ref().erased()
3102 }
3103 fn coerce(tt: &JavaType, value: &mut Expr, vt: &VarTable) {
3111 match (&*value, tt) {
3116 (Expr::Const(ConstVal::Int(b)), JavaType::Float) => {
3117 *value = Expr::Const(ConstVal::Float(f32::from_bits(*b as u32)));
3118 return;
3119 }
3120 (Expr::Const(ConstVal::Long(b)), JavaType::Double) => {
3121 *value = Expr::Const(ConstVal::Double(f64::from_bits(*b as u64)));
3122 return;
3123 }
3124 (Expr::Const(ConstVal::Float(f)), JavaType::Int) => {
3125 *value = Expr::Const(ConstVal::Int(f.to_bits() as i32));
3126 return;
3127 }
3128 (Expr::Const(ConstVal::Double(d)), JavaType::Long) => {
3129 *value = Expr::Const(ConstVal::Long(d.to_bits() as i64));
3130 return;
3131 }
3132 _ => {}
3133 }
3134 let vt_val = val_ty(value, vt);
3135 let tt_bool = matches!(tt, JavaType::Boolean);
3136 let val_bool = matches!(vt_val, JavaType::Boolean);
3137 if tt_bool && !val_bool && numlike(&vt_val) {
3138 if matches!(
3139 value,
3140 Expr::Const(_)
3141 | Expr::Bin { op: BinOp::Eq | BinOp::Ne, .. }
3142 | Expr::Un { op: UnOp::Not, .. }
3143 ) {
3144 return;
3149 }
3150 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
3151 *value = Expr::Bin {
3152 op: BinOp::Ne,
3153 l: Box::new(taken),
3154 r: Box::new(Expr::Const(ConstVal::Int(0))),
3155 ty: Some(TypeRef::J(JavaType::Boolean)),
3156 };
3157 } else if !tt_bool && val_bool && numlike(tt) {
3158 if matches!(value, Expr::Cast { .. }) {
3159 return;
3160 }
3161 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
3162 *value = Expr::Cast {
3163 ty: TypeRef::J(tt.clone()),
3164 e: Box::new(Expr::Cond {
3165 c: Box::new(taken),
3166 t: Box::new(Expr::Const(ConstVal::Int(1))),
3167 f: Box::new(Expr::Const(ConstVal::Int(0))),
3168 }),
3169 };
3170 } else if numlike(tt) && numlike(&vt_val) {
3171 wrap(tt, value);
3172 }
3173 }
3174 walk_mut_deep(body, &mut |st| match st {
3175 Stmt::ExprStmt(Expr::Assign {
3176 target,
3177 op: AssignOp::Plain,
3178 value,
3179 }) => {
3180 let tt = match &**target {
3181 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
3182 vt.var(*var).ty.erased()
3183 }
3184 Expr::Field { ty, .. } => ty.erased(),
3185 Expr::ArrayIndex { array, .. } => match array.type_ref().erased() {
3189 JavaType::Array(inner) => *inner,
3190 _ => return,
3191 },
3192 _ => return,
3193 };
3194 coerce(&tt, value, vt);
3195 }
3196 Stmt::LocalDef {
3197 var,
3198 init: Some(value),
3199 ..
3200 } => {
3201 if (*var as usize) >= vt.vars.len() {
3202 return;
3203 }
3204 let tt = vt.var(*var).ty.erased();
3205 coerce(&tt, value, vt);
3206 }
3207 Stmt::Return(Some(e)) => {
3208 if !numlike(ret_ty) || !numlike(&val_ty(e, vt)) {
3209 return;
3210 }
3211 wrap(ret_ty, e);
3212 }
3213 _ => {}
3214 });
3215}
3216
3217pub fn fix_field_owner_downcasts(body: &mut Stmt, vt: &VarTable, pool: &DexPool) {
3236 fn owner_internal(o: &Expr, vt: &VarTable) -> Option<std::sync::Arc<str>> {
3237 let t = match o {
3238 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
3239 other => other.type_ref().erased(),
3240 };
3241 match t {
3242 JavaType::Object(n) => Some(n),
3243 _ => None,
3244 }
3245 }
3246 fn declares_up(pool: &DexPool, class: &str, name: &str) -> bool {
3247 let mut cur = Some(class.to_string());
3248 let mut hops = 0;
3249 while let Some(c) = cur {
3250 hops += 1;
3251 if hops > 64 {
3252 return false;
3253 }
3254 let Some(pc) = pool.get(&c) else { return false };
3255 if pc
3256 .instance_fields
3257 .iter()
3258 .chain(pc.static_fields.iter())
3259 .any(|f| f.name.as_str() == name)
3260 {
3261 return true;
3262 }
3263 cur = pc.super_name.clone();
3264 }
3265 false
3266 }
3267 walk_stmt_exprs(body, &mut |e| {
3268 deep_rewrite(e, &mut |x| {
3269 if let Expr::Field {
3270 owner: Some(o),
3271 cls,
3272 name,
3273 is_static: false,
3274 ..
3275 } = x
3276 {
3277 let Some(ot) = owner_internal(o, vt) else {
3278 return;
3279 };
3280 if ot.as_ref() == cls.as_ref() || !pool.is_subtype(cls, &ot) {
3281 return;
3282 }
3283 if declares_up(pool, &ot, name) || !declares_up(pool, cls, name) {
3284 return;
3285 }
3286 let ty = TypeRef::J(JavaType::Object(cls.clone()));
3287 if let Expr::Cast { ty: ct, .. } = &mut **o {
3288 *ct = ty;
3289 } else {
3290 let taken = std::mem::replace(&mut **o, Expr::This);
3291 **o = Expr::Cast { ty, e: Box::new(taken) };
3292 }
3293 }
3294 let (tgt_ty, value) = match x {
3296 Expr::Assign { target, op: AssignOp::Plain, value } => {
3297 let t = match &**target {
3298 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
3299 Expr::Field { ty, .. } => ty.erased(),
3300 _ => return,
3301 };
3302 (t, value)
3303 }
3304 _ => return,
3305 };
3306 let JavaType::Object(sn) = tgt_ty else { return };
3307 if let Expr::Cast { ty, .. } = &mut **value {
3308 if let TypeRef::J(JavaType::Object(tn)) = ty {
3309 if tn.as_ref() != sn.as_ref() && pool.is_subtype(&sn, tn) {
3310 *ty = TypeRef::J(JavaType::Object(sn.clone()));
3311 }
3312 }
3313 }
3314 });
3315 });
3316 walk_mut_deep(body, &mut |st| {
3318 if let Stmt::LocalDef { var, init: Some(value), .. } = st {
3319 let tt = if (*var as usize) < vt.vars.len() {
3320 vt.vars[*var as usize].ty.erased()
3321 } else {
3322 return;
3323 };
3324 let JavaType::Object(sn) = tt else { return };
3325 if let Expr::Cast { ty, .. } = value {
3326 if let TypeRef::J(JavaType::Object(tn)) = ty {
3327 if tn.as_ref() != sn.as_ref() && pool.is_subtype(&sn, tn) {
3328 *ty = TypeRef::J(JavaType::Object(sn.clone()));
3329 }
3330 }
3331 }
3332 }
3333 });
3334}
3335
3336pub fn fix_incomparable_equality(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
3343 fn side_ty(e: &Expr, vt: &VarTable) -> JavaType {
3344 match e {
3345 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
3346 vt.var(*var).ty.erased()
3347 }
3348 other => other.type_ref().erased(),
3349 }
3350 }
3351 let incomparable = |a: &JavaType, b: &JavaType| -> bool {
3352 use JavaType::{Array, Object};
3353 match (a, b) {
3354 (Object(x), Object(y)) => {
3355 if x == y || x.as_ref() == "java/lang/Object" || y.as_ref() == "java/lang/Object"
3356 {
3357 return false;
3358 }
3359 let xi = pool.get(x).map(|c| c.is_interface()).unwrap_or(true);
3360 let yi = pool.get(y).map(|c| c.is_interface()).unwrap_or(true);
3361 if xi || yi {
3365 return false;
3366 }
3367 !pool.is_subtype(x, y) && !pool.is_subtype(y, x)
3368 }
3369 (Array(_), Object(y)) | (Object(y), Array(_)) => {
3370 y.as_ref() != "java/lang/Object"
3371 && y.as_ref() != "java/lang/Cloneable"
3372 && y.as_ref() != "java/io/Serializable"
3373 && !pool.get(y).map(|c| c.is_interface()).unwrap_or(false)
3374 }
3375 _ => false,
3376 }
3377 };
3378 let is_ref = |t: &JavaType| matches!(t, JavaType::Object(_) | JavaType::Array(_));
3379 walk_stmt_exprs(body, &mut |e| {
3380 deep_rewrite(e, &mut |x| {
3381 if let Expr::Bin {
3382 op: BinOp::Eq | BinOp::Ne,
3383 l,
3384 r,
3385 ..
3386 } = x
3387 {
3388 let lt = side_ty(l, vt);
3389 let rt = side_ty(r, vt);
3390 if is_ref(<) && matches!(&**r, Expr::Const(ConstVal::Int(0))) {
3396 **r = Expr::Const(ConstVal::Null);
3397 return;
3398 }
3399 if is_ref(&rt) && matches!(&**l, Expr::Const(ConstVal::Int(0))) {
3400 **l = Expr::Const(ConstVal::Null);
3401 return;
3402 }
3403 if incomparable(<, &rt) {
3404 let taken = std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
3405 **l = Expr::Cast {
3406 ty: TypeRef::J(JavaType::Object("java/lang/Object".into())),
3407 e: taken,
3408 };
3409 }
3410 }
3411 });
3412 });
3413}
3414
3415pub fn fix_primitive_arg_bridges(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
3429 fn val_ty(e: &Expr, vt: &VarTable) -> JavaType {
3430 match e {
3431 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
3432 other => other.type_ref().erased(),
3433 }
3434 }
3435 #[allow(clippy::type_complexity)]
3436 let is_bool = |t: &JavaType| matches!(t, JavaType::Boolean);
3437 let is_num = |t: &JavaType| {
3438 matches!(
3439 t,
3440 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
3441 | JavaType::Long | JavaType::Float | JavaType::Double
3442 )
3443 };
3444 fn bridge(actual: &JavaType, formal: &JavaType, arg: &Expr) -> Option<Expr> {
3449 match (arg, formal) {
3459 (Expr::Const(ConstVal::Int(b)), JavaType::Float) => {
3460 return Some(Expr::Const(ConstVal::Float(f32::from_bits(*b as u32))));
3461 }
3462 (Expr::Const(ConstVal::Long(b)), JavaType::Double) => {
3463 return Some(Expr::Const(ConstVal::Double(f64::from_bits(*b as u64))));
3464 }
3465 (Expr::Const(ConstVal::Float(f)), JavaType::Int) => {
3466 return Some(Expr::Const(ConstVal::Int(f.to_bits() as i32)));
3467 }
3468 (Expr::Const(ConstVal::Double(d)), JavaType::Long) => {
3469 return Some(Expr::Const(ConstVal::Long(d.to_bits() as i64)));
3470 }
3471 _ => {}
3472 }
3473 if matches!(actual, JavaType::Boolean)
3474 && matches!(
3475 formal,
3476 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
3477 | JavaType::Long | JavaType::Float | JavaType::Double
3478 )
3479 {
3480 Some(Expr::Cast {
3481 ty: TypeRef::J(formal.clone()),
3482 e: Box::new(Expr::Cond {
3483 c: Box::new(arg.clone()),
3484 t: Box::new(Expr::Const(ConstVal::Int(1))),
3485 f: Box::new(Expr::Const(ConstVal::Int(0))),
3486 }),
3487 })
3488 } else if matches!(formal, JavaType::Boolean)
3489 && matches!(
3490 actual,
3491 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
3492 | JavaType::Long | JavaType::Float | JavaType::Double
3493 )
3494 {
3495 Some(Expr::Bin {
3496 op: BinOp::Ne,
3497 l: Box::new(arg.clone()),
3498 r: Box::new(Expr::Const(ConstVal::Int(0))),
3499 ty: Some(TypeRef::J(JavaType::Boolean)),
3500 })
3501 } else if matches!(
3502 actual,
3503 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
3504 | JavaType::Long | JavaType::Float | JavaType::Double
3505 ) && matches!(
3506 formal,
3507 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
3508 | JavaType::Long | JavaType::Float | JavaType::Double
3509 ) && !implicit_widening(actual, formal)
3510 {
3511 Some(Expr::Cast {
3520 ty: TypeRef::J(formal.clone()),
3521 e: Box::new(arg.clone()),
3522 })
3523 } else {
3524 None
3525 }
3526 }
3527 fn fw_resolvable(n: &str) -> bool {
3543 crate::fwdb::exists(n)
3544 }
3545 fn null_arg_cast(a: &mut Expr, f: &JavaType, pool: &DexPool) -> bool {
3546 if !matches!(&*a, Expr::Const(ConstVal::Null)) {
3547 return false;
3548 }
3549 if let JavaType::Object(n) = f {
3550 let resolvable = pool.get(n).is_some() || fw_resolvable(n);
3556 if n.as_ref() != "java/lang/Object" && resolvable {
3557 *a = Expr::Cast {
3558 ty: TypeRef::J(f.clone()),
3559 e: Box::new(Expr::Const(ConstVal::Null)),
3560 };
3561 return true;
3562 }
3563 } else if let JavaType::Array(_) = f {
3564 let mut e: &JavaType = f;
3571 while let JavaType::Array(inner) = e {
3572 e = &**inner;
3573 }
3574 let resolvable = match e {
3575 JavaType::Object(n) => pool.get(n).is_some() || fw_resolvable(n),
3576 _ => true, };
3578 if resolvable {
3579 *a = Expr::Cast {
3580 ty: TypeRef::J(f.clone()),
3581 e: Box::new(Expr::Const(ConstVal::Null)),
3582 };
3583 return true;
3584 }
3585 }
3586 false
3589 }
3590 fn implicit_widening(a: &JavaType, f: &JavaType) -> bool {
3593 use JavaType::*;
3594 match (a, f) {
3595 (Byte, Short) | (Byte, Int) | (Byte, Long) | (Byte, Float) | (Byte, Double) => true,
3596 (Short, Int) | (Short, Long) | (Short, Float) | (Short, Double) => true,
3597 (Char, Int) | (Char, Long) | (Char, Float) | (Char, Double) => true,
3598 (Int, Long) | (Int, Float) | (Int, Double) => true,
3599 (Long, Float) | (Long, Double) => true,
3600 (Float, Double) => true,
3601 (x, y) => x == y,
3602 }
3603 }
3604 let overload_competes = |cls: &str, name: &str, args: &[JavaType]| -> bool {
3613 if let Some(c) = pool.get(cls) {
3614 return c.all_methods().any(|m| {
3615 &*m.name == name
3616 && m.access & (crate::access::ACC_BRIDGE | crate::access::ACC_SYNTHETIC) == 0
3622 && m.parsed_desc()
3623 .is_some_and(|d| d.args.len() == args.len() && d.args != args)
3624 });
3625 }
3626 let _ = args;
3635 false
3636 };
3637 let ctor_formals = |cls: &str, n: usize| -> Option<Vec<JavaType>> {
3640 let c = pool.get(cls)?;
3641 let mut out: Option<Vec<JavaType>> = None;
3642 for m in c.all_methods() {
3643 if &*m.name != "<init>" {
3644 continue;
3645 }
3646 let Some(d) = m.parsed_desc() else { continue };
3647 if d.args.len() != n {
3648 continue;
3649 }
3650 match &out {
3651 None => out = Some(d.args.clone()),
3652 Some(prev) if *prev != d.args => return None,
3653 Some(_) => {}
3654 }
3655 }
3656 out
3657 };
3658 walk_stmt_exprs(body, &mut |e| {
3659 deep_rewrite(e, &mut |x| match x {
3660 Expr::Method {
3661 cls,
3662 name,
3663 desc,
3664 args,
3665 is_static,
3666 ..
3667 } => {
3668 if args.len() != desc.args.len() {
3669 return;
3670 }
3671 let _ = is_static;
3672 let mut competes: Option<bool> = None;
3673 for (a, f) in args.iter_mut().zip(desc.args.iter()) {
3674 if null_arg_cast(a, f, pool) {
3675 continue;
3676 }
3677 if matches!(a, Expr::Cast { .. }) {
3678 continue;
3679 }
3680 let subtype_arg = match (&val_ty(a, vt), f) {
3691 (JavaType::Object(an), JavaType::Object(fn_)) => {
3692 (an != fn_ && pool.is_subtype(an, fn_))
3697 || (fn_.as_ref() == "java/lang/Object"
3698 && an.as_ref() != "java/lang/Object")
3699 }
3700 (JavaType::Array(_), JavaType::Object(fn_))
3701 if fn_.as_ref() == "java/lang/Object" =>
3702 {
3703 true
3704 }
3705 _ => false,
3706 };
3707 let null_obj =
3721 matches!(a, Expr::Const(ConstVal::Null)) && matches!(f, JavaType::Object(_));
3722 let framework_owner = pool.get(cls.as_ref()).is_none();
3723 let fw_null_ambiguous = framework_owner
3724 && matches!(
3725 (cls.as_ref(), name.as_ref()),
3726 ("java/lang/StringBuilder", "append")
3727 | ("java/lang/StringBuffer", "append")
3728 | ("java/io/PrintStream", "println")
3729 | ("java/io/PrintStream", "print")
3730 | ("java/io/PrintWriter", "println")
3731 | ("java/io/PrintWriter", "print")
3732 | ("java/lang/String", "valueOf")
3733 );
3734 let fw_subtype_pinnable = framework_owner
3745 && subtype_arg
3746 && !matches!(f, JavaType::Object(fn_) if fn_.as_ref() == "java/lang/Object")
3747 && matches!(
3748 (cls.as_ref(), name.as_ref()),
3749 ("android/content/Intent", "putExtra")
3750 | ("java/util/concurrent/ScheduledExecutorService", "schedule")
3751 | ("java/util/concurrent/ExecutorService", "submit")
3752 | ("java/util/concurrent/ExecutorService", "invokeAll")
3753 );
3754 if fw_subtype_pinnable
3755 || (subtype_arg
3756 && *competes.get_or_insert_with(|| {
3757 overload_competes(cls, name, &desc.args)
3758 }))
3759 || (null_obj
3760 && (fw_null_ambiguous
3761 || *competes.get_or_insert_with(|| {
3762 overload_competes(cls, name, &desc.args)
3763 })))
3764 {
3765 let old =
3766 std::mem::replace(a, Expr::Const(ConstVal::Null));
3767 *a = Expr::Cast {
3768 ty: TypeRef::J(f.clone()),
3769 e: Box::new(old),
3770 };
3771 continue;
3772 }
3773 if !is_bool(&val_ty(a, vt)) && !is_num(&val_ty(a, vt)) {
3774 continue;
3775 }
3776 if !is_bool(f) && !is_num(f) {
3777 continue;
3778 }
3779 if let Some(b) = bridge(&val_ty(a, vt), f, a) {
3780 *a = b;
3781 }
3782 }
3783 }
3784 Expr::New { cls, args, arg_tys, .. } => {
3785 let formals_owned = if arg_tys.len() == args.len() && !arg_tys.is_empty() {
3786 Some(arg_tys.clone())
3787 } else {
3788 ctor_formals(cls, args.len())
3789 };
3790 if let Some(formals) = formals_owned {
3791 for (a, f) in args.iter_mut().zip(formals.iter()) {
3792 if null_arg_cast(a, f, pool) {
3793 continue;
3794 }
3795 if matches!(a, Expr::Cast { .. }) {
3796 continue;
3797 }
3798 if !is_bool(f) && !is_num(f) {
3799 continue;
3800 }
3801 if let Some(b) = bridge(&val_ty(a, vt), f, a) {
3802 *a = b;
3803 }
3804 }
3805 }
3806 }
3807 _ => {}
3808 });
3809 });
3810}
3811
3812pub fn deshadow_locals(vt: &mut VarTable, pool: &crate::DexPool, class_name: &str, body: &Stmt) {
3824 let segs = pool.root_pkg_segs();
3825 let obscured = crate::classdec::obscured_simples_snapshot();
3829 let self_pkg = class_name.rfind('/').map(|i| &class_name[..i]).unwrap_or("");
3836 let mut same_pkg_simples: jdc_core::FxHashSet<String> = jdc_core::FxHashSet::default();
3837 visit_all_exprs(body, &mut |x| {
3838 let cls = match x {
3839 Expr::Field { cls, is_static: true, .. } => Some(cls.as_ref()),
3840 Expr::Method { cls, is_static: true, .. } => Some(cls.as_ref()),
3841 _ => None,
3842 };
3843 if let Some(c) = cls {
3844 let pkg = c.rfind('/').map(|i| &c[..i]).unwrap_or("");
3845 if pkg == self_pkg {
3846 let simple = &c[c.rfind('/').map(|i| i + 1).unwrap_or(0)..];
3847 let head = simple.split('$').next().unwrap_or(simple);
3848 if !head.is_empty() {
3849 same_pkg_simples.insert(head.to_string());
3850 }
3851 }
3852 }
3853 });
3854 let hit = |name: &str| {
3855 segs.contains(name) || obscured.contains(name) || same_pkg_simples.contains(name)
3856 };
3857 if (segs.is_empty() && obscured.is_empty() && same_pkg_simples.is_empty())
3858 || !vt.vars.iter().any(|v| hit(&v.name))
3859 {
3860 return;
3861 }
3862 let mut taken: jdc_core::FxHashSet<String> =
3863 vt.vars.iter().map(|v| v.name.clone()).collect();
3864 for v in vt.vars.iter_mut() {
3865 if !hit(&v.name) {
3866 continue;
3867 }
3868 let mut k = 0u32;
3869 let cand = loop {
3870 k += 1;
3871 let c = if k == 1 {
3872 format!("{}x", v.name)
3873 } else {
3874 format!("{}x{}", v.name, k)
3875 };
3876 if !taken.contains(&c) {
3877 break c;
3878 }
3879 };
3880 taken.insert(cand.clone());
3881 v.name = cand;
3882 }
3883}
3884
3885pub fn fix_ctor_this_aliases(body: &mut Stmt, vt: &VarTable) {
3911 let Some(this_var) = vt
3912 .vars
3913 .iter()
3914 .find(|v| v.is_param && v.name == "this")
3915 .map(|v| v.id)
3916 else {
3917 return;
3918 };
3919 let mut analysis = VarAnalysis {
3920 assigns: Vec::new(),
3921 reads: Vec::new(),
3922 values: Vec::new(),
3923 };
3924 analyze_vars(body, &mut analysis);
3925 let n = vt.vars.len().max(analysis.assigns.len());
3926 let mut alias = vec![false; n];
3927 for _ in 0..4 {
3928 let mut changed = false;
3929 for v in 0..n as u32 {
3930 if alias[v as usize] {
3931 continue;
3932 }
3933 if analysis.assigns.get(v as usize).copied().unwrap_or(0) != 1 {
3934 continue;
3935 }
3936 let Some(Expr::Local { var: src, .. }) = analysis.values.get(v as usize).and_then(|o| o.as_ref()) else {
3937 continue;
3938 };
3939 if *src == this_var
3940 || (*src < n as u32 && alias[*src as usize])
3941 {
3942 alias[v as usize] = true;
3943 changed = true;
3944 }
3945 }
3946 if !changed {
3947 break;
3948 }
3949 }
3950 if !alias.iter().any(|&b| b) {
3951 return;
3952 }
3953 walk_stmt_exprs(body, &mut |e| {
3954 deep_rewrite_reads(e, &mut |x| {
3955 if let Expr::Local { var, .. } = x {
3956 if (*var as usize) < n && alias[*var as usize] {
3957 *x = Expr::This;
3958 }
3959 }
3960 });
3961 });
3962}
3963
3964pub fn fix_this0_owners(body: &mut Stmt, class_name: &str) {
3965 walk_stmt_exprs(body, &mut |e| {
3966 deep_rewrite(e, &mut |x| {
3967 if let Expr::Field { owner: Some(_), cls, name, .. } = x {
3968 if name.as_ref() == "this$0" && cls.as_ref() == class_name {
3969 if let Expr::Field { owner, .. } = x {
3970 *owner = None;
3971 }
3972 }
3973 }
3974 });
3975 });
3976}
3977
3978pub fn rescue_primitive_receivers(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
3979 fn prim(t: &JavaType) -> bool {
3980 match t {
3981 JavaType::Int
3982 | JavaType::Long
3983 | JavaType::Short
3984 | JavaType::Byte
3985 | JavaType::Char
3986 | JavaType::Float
3987 | JavaType::Double
3988 | JavaType::Boolean => true,
3989 JavaType::Object(n) => matches!(
3993 n.as_ref(),
3994 "java/lang/Boolean"
3995 | "java/lang/String"
3996 | "java/lang/Integer"
3997 | "java/lang/Long"
3998 | "java/lang/Short"
3999 | "java/lang/Byte"
4000 | "java/lang/Character"
4001 | "java/lang/Float"
4002 | "java/lang/Double"
4003 ),
4004 _ => false,
4005 }
4006 }
4007 walk_stmt_exprs(body, &mut |e| {
4008 deep_rewrite(e, &mut |x| {
4009 let (recv_var, recv_ty) = {
4011 let o: &Expr = match &*x {
4012 Expr::Method { owner: Some(o), .. } => o,
4013 Expr::Field { owner: Some(o), is_static: false, .. } => o,
4014 _ => return,
4015 };
4016 let Expr::Local { var, ty } = o else {
4017 return;
4018 };
4019 let vt_ty = if (*var as usize) < vt.vars.len() {
4020 vt.var(*var).ty.erased()
4021 } else {
4022 ty.erased()
4023 };
4024 if !prim(&vt_ty) {
4025 return;
4026 }
4027 (*var, vt_ty)
4028 };
4029 let cls: std::sync::Arc<str> = match &*x {
4030 Expr::Method { cls, .. } | Expr::Field { cls, .. } => cls.clone(),
4031 _ => return,
4032 };
4033 if let JavaType::Object(n) = &recv_ty {
4042 if n.as_ref() == cls.as_ref() || pool.is_subtype(n.as_ref(), cls.as_ref()) {
4043 return;
4044 }
4045 }
4046 let mut cands: Vec<u32> = Vec::new();
4048 for v in &vt.vars {
4049 if let JavaType::Object(n) = v.ty.erased() {
4050 if n.as_ref() == cls.as_ref()
4051 || pool.is_subtype(n.as_ref(), cls.as_ref())
4052 {
4053 cands.push(v.id);
4054 }
4055 }
4056 }
4057 let id = if cands.len() == 1 {
4058 cands[0]
4059 } else if cands.len() > 1 {
4060 let slot = if (recv_var as usize) < vt.vars.len() {
4065 Some(vt.var(recv_var).slot)
4066 } else {
4067 None
4068 };
4069 let mut hits: Vec<u32> = match slot {
4070 Some(sl) => cands
4071 .iter()
4072 .copied()
4073 .filter(|&c| {
4074 c != recv_var
4075 && (c as usize) < vt.vars.len()
4076 && vt.var(c).slot == sl
4077 })
4078 .collect(),
4079 None => Vec::new(),
4080 };
4081 if hits.is_empty() {
4082 hits = cands
4085 .iter()
4086 .copied()
4087 .filter(|&c| c != recv_var)
4088 .collect();
4089 }
4090 hits.sort_unstable();
4091 hits.dedup();
4092 if hits.is_empty() {
4093 return;
4094 }
4095 *hits.last().unwrap()
4096 } else {
4097 if cands.is_empty() {
4104 return;
4105 }
4106 let slot = if (recv_var as usize) < vt.vars.len() {
4107 vt.var(recv_var).slot
4108 } else {
4109 return;
4110 };
4111 let mut hits: Vec<u32> = cands
4112 .into_iter()
4113 .filter(|&c| {
4114 c != recv_var
4115 && (c as usize) < vt.vars.len()
4116 && vt.var(c).slot == slot
4117 })
4118 .collect();
4119 hits.sort_unstable();
4120 hits.dedup();
4121 if hits.len() != 1 {
4122 return;
4123 }
4124 hits[0]
4125 };
4126 let o: &mut Box<Expr> = match x {
4128 Expr::Method { owner: Some(o), .. } => o,
4129 Expr::Field { owner: Some(o), is_static: false, .. } => o,
4130 _ => return,
4131 };
4132 if let Expr::Local { var, ty } = &mut **o {
4133 if *var == recv_var {
4134 *var = id;
4135 *ty = vt.vars[id as usize].ty.clone();
4136 }
4137 }
4138 });
4139 });
4140
4141}
4142
4143pub fn rescue_arg_return_swaps(
4149 body: &mut Stmt,
4150 vt: &VarTable,
4151 pool: &crate::DexPool,
4152 ret_ty: &JavaType,
4153) {
4154 let mut top_ids: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
4162 for v in &vt.vars {
4163 if v.is_param {
4164 top_ids.insert(v.id);
4165 }
4166 }
4167 if let Stmt::Block(stmts) = body {
4168 for st in stmts.iter() {
4169 if let Stmt::LocalDef { var, .. } = st {
4170 top_ids.insert(*var);
4171 }
4172 }
4173 }
4174 let ret_wants_ref = match ret_ty {
4175 JavaType::Object(n) => n.as_ref() != "java/lang/Object",
4176 JavaType::Array(_) => true,
4177 _ => false,
4178 };
4179 let try_swap = |a: &mut Expr, want: &JavaType| {
4180 let Expr::Local { var, ty } = &mut *a else {
4181 return;
4182 };
4183 if (*var as usize) >= vt.vars.len() {
4184 return;
4185 }
4186 let avt = vt.var(*var).ty.erased();
4187 let avt_ref = matches!(avt, JavaType::Object(_) | JavaType::Array(_));
4188 let want_arr = matches!(want, JavaType::Array(_));
4189 if avt_ref && !(avt == JavaType::Object("java/lang/Object".into())) {
4192 return;
4193 }
4194 if avt_ref && !matches!(want, JavaType::Object(_)) {
4195 return;
4196 }
4197 let slot = vt.var(*var).slot;
4198 let mut hits: Vec<u32> = Vec::new();
4199 for v in &vt.vars {
4200 if v.slot != slot || !top_ids.contains(&v.id) || v.id == *var {
4201 continue;
4202 }
4203 let t = v.ty.erased();
4204 let ok = match (&t, want) {
4205 (JavaType::Object(n), JavaType::Object(w)) => {
4206 n.as_ref() == w.as_ref()
4207 || pool.is_subtype(n.as_ref(), w.as_ref())
4208 }
4209 (JavaType::Array(_), JavaType::Array(_)) => want_arr,
4210 _ => false,
4211 };
4212 if ok {
4213 hits.push(v.id);
4214 }
4215 }
4216 hits.sort_unstable();
4217 hits.dedup();
4218 if hits.is_empty() {
4219 return;
4220 }
4221 let id = *hits.last().unwrap();
4227 *var = id;
4228 *ty = vt.vars[id as usize].ty.clone();
4229 };
4230 walk_stmt_exprs(body, &mut |e| {
4231 deep_rewrite(e, &mut |x| {
4232 match x {
4233 Expr::Method { desc, args, is_dynamic: false, .. } => {
4234 for (i, a) in args.iter_mut().enumerate() {
4235 if let Some(f) = desc.args.get(i) {
4236 if matches!(f, JavaType::Object(n) if n.as_ref() != "java/lang/Object")
4237 || matches!(f, JavaType::Array(_))
4238 {
4239 try_swap(a, f);
4240 }
4241 }
4242 }
4243 }
4244 Expr::New { arg_tys, args, .. } => {
4245 for (i, a) in args.iter_mut().enumerate() {
4246 if let Some(f) = arg_tys.get(i) {
4247 if matches!(f, JavaType::Object(n) if n.as_ref() != "java/lang/Object")
4248 || matches!(f, JavaType::Array(_))
4249 {
4250 try_swap(a, f);
4251 }
4252 }
4253 }
4254 }
4255 _ => {}
4256 }
4257 });
4258 });
4259 if ret_wants_ref {
4260 walk_mut_deep(body, &mut |st| {
4261 if let Stmt::Return(Some(e)) = st {
4262 try_swap(e, ret_ty);
4263 }
4264 });
4265 }
4266}
4267
4268pub fn fix_ref_null_assigns(vt: &VarTable, body: &mut Stmt) {
4283 let is_ref_var = |var: u32| -> bool {
4284 (var as usize) < vt.vars.len()
4285 && matches!(
4286 vt.vars[var as usize].ty.erased(),
4287 JavaType::Object(_) | JavaType::Array(_)
4288 )
4289 };
4290 walk_mut_deep(body, &mut |st| match st {
4291 Stmt::ExprStmt(Expr::Assign {
4292 target,
4293 value,
4294 op: AssignOp::Plain,
4295 ..
4296 }) => {
4297 if let Expr::Local { var, .. } = &**target {
4298 if is_ref_var(*var) && matches!(&**value, Expr::Const(ConstVal::Int(0))) {
4299 **value = Expr::Const(ConstVal::Null);
4300 }
4301 }
4302 }
4303 Stmt::LocalDef {
4304 var,
4305 init: Some(e),
4306 ..
4307 } => {
4308 if is_ref_var(*var) && matches!(e, Expr::Const(ConstVal::Int(0))) {
4309 *e = Expr::Const(ConstVal::Null);
4310 }
4311 }
4312 _ => {}
4313 });
4314}
4315
4316pub fn fix_ref_array_null_consts(body: &mut Stmt) {
4317 walk_stmt_exprs(body, &mut |e| {
4318 deep_rewrite(e, &mut |x| {
4319 if let Expr::NewArray { elem, init: Some(list), .. } = x {
4320 if matches!(elem.erased(), JavaType::Object(_) | JavaType::Array(_)) {
4321 for slot in list.iter_mut() {
4322 if matches!(&*slot, Expr::Const(ConstVal::Int(0))) {
4323 *slot = Expr::Const(ConstVal::Null);
4324 }
4325 }
4326 }
4327 }
4328 });
4329 });
4330}
4331
4332pub fn fix_null_sentinels(body: &mut Stmt, vt: &VarTable, ret: &JavaType) {
4371 #[derive(Clone, Copy, PartialEq)]
4372 enum WK {
4373 Zero,
4374 Copy(u32),
4375 Other,
4376 }
4377 fn is_ref_jt(t: &JavaType) -> bool {
4378 matches!(t, JavaType::Object(_) | JavaType::Array(_))
4379 }
4380 fn cls_write(e: &Expr) -> WK {
4381 match e {
4382 Expr::Const(ConstVal::Int(0)) => WK::Zero,
4383 Expr::Local { var, .. } => WK::Copy(*var),
4384 _ => WK::Other,
4385 }
4386 }
4387 fn expr_writes(e: &Expr, writes: &mut std::collections::HashMap<u32, Vec<WK>>) {
4388 visit_exprs(e, &mut |x| match x {
4389 Expr::Assign { target, op, value } => {
4390 if let Expr::Local { var, .. } = &**target {
4391 let k = if matches!(op, AssignOp::Plain) {
4392 cls_write(value)
4393 } else {
4394 WK::Other
4395 };
4396 writes.entry(*var).or_default().push(k);
4397 }
4398 }
4399 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
4400 if let Expr::Local { var, .. } = &**inner {
4401 writes.entry(*var).or_default().push(WK::Other);
4402 }
4403 }
4404 _ => {}
4405 });
4406 }
4407 fn collect_writes(
4408 s: &Stmt,
4409 writes: &mut std::collections::HashMap<u32, Vec<WK>>,
4410 has_zero: &mut bool,
4411 ) {
4412 fn note(e: &Expr, has_zero: &mut bool, writes: &mut std::collections::HashMap<u32, Vec<WK>>) {
4413 visit_exprs(e, &mut |x| {
4414 if let Expr::Const(ConstVal::Int(0)) = x {
4415 *has_zero = true;
4416 }
4417 });
4418 expr_writes(e, writes);
4419 }
4420 match s {
4421 Stmt::Block(v) => {
4422 for x in v {
4423 collect_writes(x, writes, has_zero);
4424 }
4425 }
4426 Stmt::LocalDef { var, init, .. } => {
4427 if let Some(e) = init {
4428 if matches!(e, Expr::Const(ConstVal::Int(0))) {
4429 *has_zero = true;
4430 }
4431 writes.entry(*var).or_default().push(cls_write(e));
4432 }
4433 }
4434 Stmt::ExprStmt(e) => note(e, has_zero, writes),
4435 Stmt::Return(e) => {
4436 if let Some(x) = e {
4437 note(x, has_zero, writes);
4438 }
4439 }
4440 Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
4441 note(e, has_zero, writes);
4442 }
4443 Stmt::TernaryValue { e } => note(e, has_zero, writes),
4444 Stmt::If { cond, then_stmt, else_stmt } => {
4445 note(cond, has_zero, writes);
4446 collect_writes(then_stmt, writes, has_zero);
4447 if let Some(b) = else_stmt {
4448 collect_writes(b, writes, has_zero);
4449 }
4450 }
4451 Stmt::While { cond, body } | Stmt::DoWhile { body, cond } => {
4452 note(cond, has_zero, writes);
4453 collect_writes(body, writes, has_zero);
4454 }
4455 Stmt::For { init, cond, update, body } => {
4456 for x in init {
4457 collect_writes(x, writes, has_zero);
4458 }
4459 if let Some(c) = cond {
4460 note(c, has_zero, writes);
4461 }
4462 for u in update {
4463 note(u, has_zero, writes);
4464 }
4465 collect_writes(body, writes, has_zero);
4466 }
4467 Stmt::ForEach { var, iterable, body, .. } => {
4468 writes.entry(*var).or_default().push(WK::Other);
4469 note(iterable, has_zero, writes);
4470 collect_writes(body, writes, has_zero);
4471 }
4472 Stmt::Switch { selector, cases, default, .. } => {
4473 note(selector, has_zero, writes);
4474 for c in cases {
4475 if let Some(g) = &c.guard {
4476 note(g, has_zero, writes);
4477 }
4478 for x in &c.body {
4479 collect_writes(x, writes, has_zero);
4480 }
4481 }
4482 if let Some(d) = default {
4483 collect_writes(d, writes, has_zero);
4484 }
4485 }
4486 Stmt::Try { body, catches, finally } => {
4487 collect_writes(body, writes, has_zero);
4488 for c in catches {
4489 writes.entry(c.var).or_default().push(WK::Other);
4490 collect_writes(&c.body, writes, has_zero);
4491 }
4492 if let Some(f) = finally {
4493 collect_writes(f, writes, has_zero);
4494 }
4495 }
4496 Stmt::TryWithResources { resources, body, catches, finally, .. } => {
4497 for r in resources {
4498 collect_writes(r, writes, has_zero);
4499 }
4500 collect_writes(body, writes, has_zero);
4501 for c in catches {
4502 writes.entry(c.var).or_default().push(WK::Other);
4503 collect_writes(&c.body, writes, has_zero);
4504 }
4505 if let Some(f) = finally {
4506 collect_writes(f, writes, has_zero);
4507 }
4508 }
4509 Stmt::Assert { cond, msg } => {
4510 note(cond, has_zero, writes);
4511 if let Some(m) = msg {
4512 note(m, has_zero, writes);
4513 }
4514 }
4515 Stmt::Synchronized { lock, body } => {
4516 note(lock, has_zero, writes);
4517 collect_writes(body, writes, has_zero);
4518 }
4519 Stmt::Labeled { body, .. } => collect_writes(body, writes, has_zero),
4520 _ => {}
4521 }
4522 }
4523
4524 let mut writes: std::collections::HashMap<u32, Vec<WK>> =
4525 std::collections::HashMap::new();
4526 let mut has_zero = false;
4527 collect_writes(body, &mut writes, &mut has_zero);
4528
4529 let mut eligible: HashSet<u32> = HashSet::default();
4531 let int_typed = |v: u32| {
4532 (v as usize) < vt.vars.len() && vt.vars[v as usize].ty.erased() == JavaType::Int
4533 };
4534 loop {
4535 let mut changed = false;
4536 for (&v, ws) in writes.iter() {
4537 if eligible.contains(&v) || ws.is_empty() || !int_typed(v) {
4538 continue;
4539 }
4540 if ws.iter().all(|k| match k {
4541 WK::Zero => true,
4542 WK::Copy(u) => *u != v && eligible.contains(u),
4543 WK::Other => false,
4544 }) {
4545 eligible.insert(v);
4546 changed = true;
4547 }
4548 }
4549 if !changed {
4550 break;
4551 }
4552 }
4553 if eligible.is_empty() && !has_zero {
4554 return; }
4556
4557 #[derive(Clone, Copy, PartialEq)]
4558 enum Mode {
4559 Scan,
4560 Rewrite,
4561 }
4562 struct St<'a> {
4563 mode: Mode,
4564 vt: &'a VarTable,
4565 eligible: &'a HashSet<u32>,
4566 ref_read: HashSet<u32>,
4567 dead: HashSet<u32>,
4568 sentinels: HashSet<u32>,
4569 n_lit: usize,
4570 n_var: usize,
4571 }
4572 fn arg_ctx(t: &JavaType) -> Option<JavaType> {
4576 match t {
4577 JavaType::Object(n) if n.as_ref() == "java/lang/Object" => None,
4578 t if is_ref_jt(t) => Some(t.clone()),
4579 _ => None,
4580 }
4581 }
4582 fn walk_expr(e: &mut Expr, ctx: Option<JavaType>, st: &mut St) {
4583 walk_expr_c(e, ctx, false, st);
4584 }
4585 fn walk_expr_c(e: &mut Expr, ctx: Option<JavaType>, cast_arg: bool, st: &mut St) {
4591 let slot_ref = ctx.as_ref().is_some_and(is_ref_jt);
4592 fn mk_null(ctx: &Option<JavaType>, cast_arg: bool) -> Expr {
4593 match (cast_arg, ctx) {
4594 (true, Some(t)) => Expr::Cast {
4595 ty: TypeRef::J(t.clone()),
4596 e: Box::new(Expr::Const(ConstVal::Null)),
4597 },
4598 _ => Expr::Const(ConstVal::Null),
4599 }
4600 }
4601 match e {
4602 Expr::Const(ConstVal::Int(0)) if slot_ref => {
4603 if st.mode == Mode::Rewrite {
4604 *e = mk_null(&ctx, cast_arg);
4605 st.n_lit += 1;
4606 }
4607 return;
4608 }
4609 Expr::Local { var, .. } => {
4610 if st.eligible.contains(var) {
4611 if slot_ref {
4612 st.ref_read.insert(*var);
4613 } else {
4614 st.dead.insert(*var);
4615 }
4616 }
4617 if st.mode == Mode::Rewrite && slot_ref && st.sentinels.contains(var) {
4618 *e = mk_null(&ctx, cast_arg);
4619 st.n_var += 1;
4620 }
4621 return;
4622 }
4623 _ => {}
4624 }
4625 match e {
4626 Expr::Method { owner, desc, args, .. } => {
4627 if let Some(o) = owner {
4628 walk_expr(o, None, st);
4629 }
4630 for (i, a) in args.iter_mut().enumerate() {
4631 let c = desc.args.get(i).and_then(arg_ctx);
4632 walk_expr_c(a, c, true, st);
4633 }
4634 }
4635 Expr::Invokedynamic { desc, args, .. } => {
4636 for (i, a) in args.iter_mut().enumerate() {
4637 let c = desc.args.get(i).and_then(arg_ctx);
4638 walk_expr_c(a, c, true, st);
4639 }
4640 }
4641 Expr::New { args, arg_tys, .. } => {
4642 for (i, a) in args.iter_mut().enumerate() {
4643 let c = arg_tys.get(i).and_then(arg_ctx);
4644 walk_expr_c(a, c, true, st);
4645 }
4646 }
4647 Expr::AnonNew { args, .. } => {
4648 for a in args.iter_mut() {
4649 walk_expr(a, None, st);
4650 }
4651 }
4652 Expr::Assign { target, value, .. } => {
4653 if let Expr::Local { var: u, .. } = &**target {
4657 if st.eligible.contains(u) {
4658 return;
4659 }
4660 }
4661 let vctx = match &**target {
4662 Expr::Field { ty, .. } => {
4663 let t = ty.erased();
4664 is_ref_jt(&t).then_some(t)
4665 }
4666 Expr::ArrayIndex { array, .. } => match array.type_ref().erased() {
4667 JavaType::Array(inner) if is_ref_jt(&inner) => Some(*inner),
4668 _ => None,
4669 },
4670 _ => None,
4671 };
4672 walk_expr(target, None, st);
4673 walk_expr(value, vctx, st);
4674 }
4675 Expr::Cast { ty, e: inner } => {
4676 let t = ty.erased();
4677 let c = is_ref_jt(&t).then_some(t);
4678 walk_expr(inner, c, st);
4679 }
4680 Expr::InstanceOf { e: inner, .. } => {
4681 walk_expr(inner, Some(JavaType::Object("java/lang/Object".into())), st);
4682 }
4683 Expr::Cond { c, t, f } => {
4684 walk_expr(c, None, st);
4685 let tc = ctx.clone();
4686 walk_expr(t, tc, st);
4687 walk_expr(f, ctx, st);
4688 }
4689 Expr::NewArray { elem, dims, init, .. } => {
4690 for d in dims.iter_mut() {
4691 walk_expr(d, None, st);
4692 }
4693 if let Some(list) = init {
4694 let et = elem.erased();
4695 let c = is_ref_jt(&et).then_some(et);
4696 for slot in list.iter_mut() {
4697 walk_expr(slot, c.clone(), st);
4698 }
4699 }
4700 }
4701 Expr::NewMultiArray { dims, .. } => {
4702 for d in dims.iter_mut() {
4703 walk_expr(d, None, st);
4704 }
4705 }
4706 Expr::ArrayIndex { array, index } => {
4707 walk_expr(array, None, st);
4708 walk_expr(index, None, st);
4709 }
4710 Expr::Field { owner, .. } => {
4711 if let Some(o) = owner {
4712 walk_expr(o, None, st);
4713 }
4714 }
4715 Expr::Bin { l, r, .. } => {
4716 walk_expr(l, None, st);
4717 walk_expr(r, None, st);
4718 }
4719 Expr::Un { e: inner, .. } => walk_expr(inner, None, st),
4720 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
4721 walk_expr(inner, None, st);
4722 }
4723 Expr::StringConcat(parts) => {
4724 for p in parts.iter_mut() {
4725 if let ConcatPart::Str(x) = p {
4726 walk_expr(x, None, st);
4727 }
4728 }
4729 }
4730 Expr::Lambda(l) => {
4731 for c in l.captures.iter_mut() {
4732 walk_expr(c, None, st);
4733 }
4734 }
4735 _ => {}
4736 }
4737 }
4738 fn walk_stmt(s: &mut Stmt, st: &mut St, ret: &JavaType) {
4739 match s {
4740 Stmt::Block(v) => {
4741 for x in v.iter_mut() {
4742 walk_stmt(x, st, ret);
4743 }
4744 }
4745 Stmt::ExprStmt(e) => walk_expr(e, None, st),
4746 Stmt::LocalDef { var, init, .. } => {
4747 if let Some(e) = init {
4748 let ctx = if (*var as usize) < st.vt.vars.len() {
4749 let t = st.vt.vars[*var as usize].ty.erased();
4750 is_ref_jt(&t).then_some(t)
4751 } else {
4752 None
4753 };
4754 walk_expr(e, ctx, st);
4755 }
4756 }
4757 Stmt::Return(e) => {
4758 if let Some(x) = e {
4759 let c = is_ref_jt(ret).then(|| ret.clone());
4760 walk_expr(x, c, st);
4761 }
4762 }
4763 Stmt::Throw(e) => {
4764 walk_expr(e, Some(JavaType::Object("java/lang/Throwable".into())), st);
4765 }
4766 Stmt::If { cond, then_stmt, else_stmt } => {
4767 walk_expr(cond, None, st);
4768 walk_stmt(then_stmt, st, ret);
4769 if let Some(b) = else_stmt {
4770 walk_stmt(b, st, ret);
4771 }
4772 }
4773 Stmt::While { cond, body } => {
4774 walk_expr(cond, None, st);
4775 walk_stmt(body, st, ret);
4776 }
4777 Stmt::DoWhile { body, cond } => {
4778 walk_stmt(body, st, ret);
4779 walk_expr(cond, None, st);
4780 }
4781 Stmt::For { init, cond, update, body } => {
4782 for x in init.iter_mut() {
4783 walk_stmt(x, st, ret);
4784 }
4785 if let Some(c) = cond {
4786 walk_expr(c, None, st);
4787 }
4788 for u in update.iter_mut() {
4789 walk_expr(u, None, st);
4790 }
4791 walk_stmt(body, st, ret);
4792 }
4793 Stmt::ForEach { iterable, body, .. } => {
4794 walk_expr(iterable, None, st);
4795 walk_stmt(body, st, ret);
4796 }
4797 Stmt::Switch { selector, cases, default, .. } => {
4798 walk_expr(selector, None, st);
4799 for c in cases.iter_mut() {
4800 if let Some(g) = &mut c.guard {
4801 walk_expr(g, None, st);
4802 }
4803 for x in c.body.iter_mut() {
4804 walk_stmt(x, st, ret);
4805 }
4806 }
4807 if let Some(d) = default {
4808 walk_stmt(d, st, ret);
4809 }
4810 }
4811 Stmt::Try { body, catches, finally } => {
4812 walk_stmt(body, st, ret);
4813 for c in catches.iter_mut() {
4814 walk_stmt(&mut c.body, st, ret);
4815 }
4816 if let Some(f) = finally {
4817 walk_stmt(f, st, ret);
4818 }
4819 }
4820 Stmt::TryWithResources { resources, body, catches, finally, .. } => {
4821 for r in resources.iter_mut() {
4822 walk_stmt(r, st, ret);
4823 }
4824 walk_stmt(body, st, ret);
4825 for c in catches.iter_mut() {
4826 walk_stmt(&mut c.body, st, ret);
4827 }
4828 if let Some(f) = finally {
4829 walk_stmt(f, st, ret);
4830 }
4831 }
4832 Stmt::Assert { cond, msg } => {
4833 walk_expr(cond, None, st);
4834 if let Some(m) = msg {
4835 walk_expr(m, None, st);
4836 }
4837 }
4838 Stmt::Synchronized { lock, body } => {
4839 walk_expr(lock, Some(JavaType::Object("java/lang/Object".into())), st);
4840 walk_stmt(body, st, ret);
4841 }
4842 Stmt::TernaryValue { e } => walk_expr(e, None, st),
4843 Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
4844 walk_expr(e, Some(JavaType::Object("java/lang/Object".into())), st);
4845 }
4846 Stmt::Labeled { body, .. } => walk_stmt(body, st, ret),
4847 _ => {}
4848 }
4849 }
4850
4851 let mut st = St {
4852 mode: Mode::Scan,
4853 vt,
4854 eligible: &eligible,
4855 ref_read: HashSet::default(),
4856 dead: HashSet::default(),
4857 sentinels: HashSet::default(),
4858 n_lit: 0,
4859 n_var: 0,
4860 };
4861 if !eligible.is_empty() {
4862 walk_stmt(body, &mut st, ret);
4863 st.sentinels = eligible
4864 .iter()
4865 .copied()
4866 .filter(|v| st.ref_read.contains(v) && !st.dead.contains(v))
4867 .collect();
4868 }
4869 if st.sentinels.is_empty() && !has_zero {
4870 return;
4871 }
4872 st.mode = Mode::Rewrite;
4873 walk_stmt(body, &mut st, ret);
4874 if std::env::var_os("DDC_SENT").is_some() {
4875 eprintln!(
4876 "[sent] elig={} confirmed={} lit={} var={}",
4877 eligible.len(),
4878 st.sentinels.len(),
4879 st.n_lit,
4880 st.n_var
4881 );
4882 }
4883}
4884
4885pub fn fix_int_operand_bridges(body: &mut Stmt, vt: &VarTable) {
4893 fn side_bool(e: &Expr, vt: &VarTable) -> bool {
4894 matches!(
4895 match e {
4896 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4897 other => other.type_ref().erased(),
4898 },
4899 JavaType::Boolean
4900 )
4901 }
4902 fn side_int(e: &Expr, vt: &VarTable) -> bool {
4903 matches!(
4904 match e {
4905 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4906 other => other.type_ref().erased(),
4907 },
4908 JavaType::Int | JavaType::Short | JavaType::Byte | JavaType::Char | JavaType::Long
4909 )
4910 }
4911 fn wrap(e: Box<Expr>) -> Box<Expr> {
4912 Box::new(Expr::Cond {
4913 c: e,
4914 t: Box::new(Expr::Const(ConstVal::Int(1))),
4915 f: Box::new(Expr::Const(ConstVal::Int(0))),
4916 })
4917 }
4918 walk_stmt_exprs(body, &mut |e| {
4919 deep_rewrite(e, &mut |x| {
4920 if let Expr::Assign { target, value, op: AssignOp::Plain } = x {
4927 let tgt_ty: Option<JavaType> = match &**target {
4928 Expr::Field { ty, .. } => Some(ty.erased()),
4929 Expr::ArrayIndex { array, .. } => {
4930 match array.type_ref().erased() {
4931 JavaType::Array(el) => Some(el.as_ref().clone()),
4932 _ => None,
4933 }
4934 }
4935 Expr::Local { var, .. } => {
4940 if (*var as usize) < vt.vars.len() {
4941 Some(vt.var(*var).ty.erased())
4942 } else {
4943 None
4944 }
4945 }
4946 _ => None,
4947 };
4948 if let Some(t) = tgt_ty {
4949 let tgt_int = matches!(
4950 t,
4951 JavaType::Int
4952 | JavaType::Short
4953 | JavaType::Byte
4954 | JavaType::Char
4955 | JavaType::Long
4956 );
4957 if tgt_int && side_bool(value, vt) && !side_int(value, vt) {
4958 let taken = std::mem::replace(
4959 value,
4960 Box::new(Expr::Const(ConstVal::Null)),
4961 );
4962 let mut wrapped = wrap(taken);
4963 if !matches!(t, JavaType::Int | JavaType::Long) {
4964 wrapped = Box::new(Expr::Cast {
4965 ty: TypeRef::J(t.clone()),
4966 e: wrapped,
4967 });
4968 }
4969 *value = wrapped;
4970 } else if matches!(t, JavaType::Boolean)
4971 && side_int(value, vt)
4972 && !side_bool(value, vt)
4973 {
4974 let taken = std::mem::replace(
4975 value,
4976 Box::new(Expr::Const(ConstVal::Null)),
4977 );
4978 **value = Expr::Bin {
4979 op: BinOp::Ne,
4980 l: taken,
4981 r: Box::new(Expr::Const(ConstVal::Int(0))),
4982 ty: Some(TypeRef::J(JavaType::Boolean)),
4983 };
4984 }
4985 }
4986 }
4987 if let Expr::Cast { ty, e } = x {
4993 let t = ty.erased();
4994 let t_num = matches!(
4995 t,
4996 JavaType::Int
4997 | JavaType::Short
4998 | JavaType::Byte
4999 | JavaType::Char
5000 | JavaType::Long
5001 | JavaType::Float
5002 | JavaType::Double
5003 );
5004 if t_num && side_bool(e, vt) && !side_int(e, vt) {
5005 let taken = std::mem::replace(e, Box::new(Expr::Const(ConstVal::Null)));
5006 **e = Expr::Cond {
5007 c: taken,
5008 t: Box::new(Expr::Const(ConstVal::Int(1))),
5009 f: Box::new(Expr::Const(ConstVal::Int(0))),
5010 };
5011 } else if matches!(t, JavaType::Boolean)
5012 && side_int(e, vt)
5013 && !side_bool(e, vt)
5014 {
5015 let taken = std::mem::replace(e, Box::new(Expr::Const(ConstVal::Null)));
5016 *x = Expr::Bin {
5017 op: BinOp::Ne,
5018 l: taken,
5019 r: Box::new(Expr::Const(ConstVal::Int(0))),
5020 ty: Some(TypeRef::J(JavaType::Boolean)),
5021 };
5022 }
5023 }
5024 if let Expr::Bin { op, l, r, .. } = x {
5030 if matches!(op, BinOp::Eq | BinOp::Ne) {
5031 if matches!(&**r, Expr::Const(ConstVal::Null))
5032 && side_int(l, vt)
5033 && !side_bool(l, vt)
5034 {
5035 **r = Expr::Const(ConstVal::Int(0));
5036 } else if matches!(&**l, Expr::Const(ConstVal::Null))
5037 && side_int(r, vt)
5038 && !side_bool(r, vt)
5039 {
5040 **l = Expr::Const(ConstVal::Int(0));
5041 }
5042 }
5043 }
5044 if let Expr::ArrayIndex { index, .. } = x {
5048 if side_bool(index, vt) && !side_int(index, vt) {
5049 let taken =
5050 std::mem::replace(index, Box::new(Expr::Const(ConstVal::Null)));
5051 *index = wrap(taken);
5052 }
5053 }
5054 if let Expr::NewArray { dims, .. } = x {
5060 for d in dims.iter_mut() {
5061 if side_bool(d, vt) && !side_int(d, vt) {
5062 let taken =
5063 std::mem::replace(d, Expr::Const(ConstVal::Null));
5064 *d = *wrap(Box::new(taken));
5065 }
5066 }
5067 }
5068 if let Expr::Bin { op, l, r, .. } = x {
5069 let int_kind = matches!(
5070 op,
5071 BinOp::Or
5072 | BinOp::And
5073 | BinOp::Xor
5074 | BinOp::Add
5075 | BinOp::Sub
5076 | BinOp::Mul
5077 | BinOp::Div
5078 | BinOp::Rem
5079 | BinOp::Shl
5080 | BinOp::Shr
5081 | BinOp::Ushr
5082 | BinOp::Eq
5083 | BinOp::Ne
5084 | BinOp::Lt
5085 | BinOp::Ge
5086 | BinOp::Gt
5087 | BinOp::Le
5088 );
5089 if !int_kind {
5090 return;
5091 }
5092 if side_bool(l, vt) && side_int(r, vt) && !side_bool(r, vt) {
5095 let taken = std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
5096 *l = wrap(taken);
5097 } else if side_bool(r, vt) && side_int(l, vt) && !side_bool(l, vt) {
5098 let taken = std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
5099 *r = wrap(taken);
5100 }
5101 }
5102 });
5103 });
5104}
5105
5106pub fn fold_value_diamonds(body: &mut Stmt, vt: &VarTable) {
5144 let Stmt::Block(stmts) = body else { return };
5145 fold_diamonds_in(stmts, vt);
5146}
5147
5148fn diamond_types_ok(a: &Expr, b: &Expr) -> bool {
5149 fn num(t: &JavaType) -> bool {
5150 matches!(
5151 t,
5152 JavaType::Byte
5153 | JavaType::Short
5154 | JavaType::Int
5155 | JavaType::Char
5156 | JavaType::Long
5157 | JavaType::Float
5158 | JavaType::Double
5159 )
5160 }
5161 if let Some(other) = if matches!(a, Expr::Const(ConstVal::Null)) {
5169 Some(b)
5170 } else if matches!(b, Expr::Const(ConstVal::Null)) {
5171 Some(a)
5172 } else {
5173 None
5174 } {
5175 return !num(&other.type_ref().erased());
5176 }
5177 let ta = a.type_ref().erased();
5178 let tb = b.type_ref().erased();
5179 ta == tb || (num(&ta) && num(&tb))
5180}
5181
5182fn single_local_assign(st: &Stmt) -> Option<(u32, Expr, Expr)> {
5190 let mut inner = st;
5191 while let Stmt::Block(v) = inner {
5192 if v.len() != 1 {
5193 return None;
5194 }
5195 inner = &v[0];
5196 }
5197 match inner {
5198 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain }) => {
5199 match &**target {
5200 Expr::Local { var, .. } => Some((*var, *target.clone(), *value.clone())),
5201 _ => None,
5202 }
5203 }
5204 _ => None,
5205 }
5206}
5207
5208fn mk_ternary_assign(tgt: Expr, c: Expr, a: Expr, b: Expr) -> Stmt {
5209 Stmt::ExprStmt(Expr::Assign {
5210 target: Box::new(tgt),
5211 op: AssignOp::Plain,
5212 value: Box::new(Expr::Cond {
5213 c: Box::new(c),
5214 t: Box::new(a),
5215 f: Box::new(b),
5216 }),
5217 })
5218}
5219
5220fn same_render_var(v1: u32, v2: u32, vt: &VarTable) -> bool {
5225 let (a, b) = (vt.vars.get(v1 as usize), vt.vars.get(v2 as usize));
5226 match (a, b) {
5227 (Some(x), Some(y)) => x.name == y.name && x.ty.erased() == y.ty.erased(),
5228 _ => false,
5229 }
5230}
5231
5232fn fold_diamonds_in(stmts: &mut Vec<Stmt>, vt: &VarTable) {
5233 let mut i = 0usize;
5234 while i < stmts.len() {
5235 match &mut stmts[i] {
5236 Stmt::Block(v) => fold_diamonds_in(v, vt),
5237 Stmt::If { then_stmt, else_stmt, .. } => {
5238 if let Stmt::Block(v) = &mut **then_stmt {
5239 fold_diamonds_in(v, vt);
5240 }
5241 if let Some(e) = else_stmt {
5242 if let Stmt::Block(v) = &mut **e {
5243 fold_diamonds_in(v, vt);
5244 }
5245 }
5246 }
5247 _ => {}
5248 }
5249 let shape1 = if let Stmt::If { cond, then_stmt, else_stmt: Some(el), .. } = &stmts[i] {
5251 match (single_local_assign(then_stmt), single_local_assign(el)) {
5252 (Some((v1, tgt, a)), Some((v2, _, b)))
5253 if v1 == v2 && diamond_types_ok(&a, &b) =>
5254 {
5255 Some(mk_ternary_assign(tgt, cond.clone(), a, b))
5256 }
5257 _ => None,
5258 }
5259 } else {
5260 None
5261 };
5262 if let Some(f) = shape1 {
5263 stmts[i] = f;
5264 i += 1;
5265 continue;
5266 }
5267 if i + 1 < stmts.len() {
5277 let shape2 = {
5297 let s_i = single_local_assign(&stmts[i]);
5298 if let Stmt::If {
5299 cond,
5300 then_stmt,
5301 else_stmt,
5302 ..
5303 } = &stmts[i + 1]
5304 {
5305 let then_a = single_local_assign(then_stmt);
5306 let else_a = else_stmt.as_deref().and_then(single_local_assign);
5307 let then_empty =
5308 matches!(&**then_stmt, Stmt::Block(v) if v.is_empty());
5309 let else_empty = else_stmt
5310 .as_deref()
5311 .map(|e| matches!(e, Stmt::Block(v) if v.is_empty()))
5312 .unwrap_or(true);
5313 let try_fold = |over: Option<(u32, Expr, Expr)>,
5314 keep_over: bool|
5315 -> Option<Stmt> {
5316 let (v2, tgt2, a) = over?;
5317 let (v1, _t1, b) = s_i.as_ref()?;
5318 if !(*v1 == v2 || same_render_var(*v1, v2, vt)) {
5319 return None;
5320 }
5321 let keep = if *v1 == v2 {
5322 b.clone()
5323 } else {
5324 Expr::Local {
5325 var: *v1,
5326 ty: vt.var(*v1).ty.clone(),
5327 }
5328 };
5329 if !diamond_types_ok(&a, &keep) {
5330 return None;
5331 }
5332 if keep_over {
5333 Some(mk_ternary_assign(tgt2, cond.clone(), keep, a))
5334 } else {
5335 Some(mk_ternary_assign(tgt2, cond.clone(), a, keep))
5336 }
5337 };
5338 if then_a.is_some() && else_empty {
5339 try_fold(then_a, false)
5340 } else if else_a.is_some() && then_empty {
5341 try_fold(else_a, true)
5342 } else {
5343 None
5344 }
5345 } else {
5346 None
5347 }
5348 };
5349 if let Some(f) = shape2 {
5350 stmts[i] = f;
5351 stmts.remove(i + 1);
5352 continue; }
5354 }
5355 i += 1;
5356 }
5357}
5358
5359pub fn fold_bool_value_diamonds(body: &mut Stmt, vt: &VarTable) {
5360 fn is_bool_var(v: u32, vt: &VarTable) -> bool {
5361 (v as usize) < vt.vars.len()
5362 && matches!(vt.vars[v as usize].ty.erased(), JavaType::Boolean)
5363 }
5364 fn bool_const(e: &Expr) -> Option<bool> {
5365 fn cint(e: &Expr) -> Option<i32> {
5366 match e {
5367 Expr::Const(ConstVal::Int(n)) => Some(*n),
5368 _ => None,
5369 }
5370 }
5371 match e {
5372 Expr::Const(ConstVal::Int(0)) => Some(false),
5373 Expr::Const(ConstVal::Int(1)) => Some(true),
5374 Expr::Bin { op: op @ (BinOp::Eq | BinOp::Ne), l, r, .. } => {
5378 let (a, b) = (cint(l)?, cint(r)?);
5379 let eq = a == b;
5380 Some(if matches!(op, BinOp::Eq) { eq } else { !eq })
5381 }
5382 _ => None,
5383 }
5384 }
5385 fn reads_var(e: &Expr, v: u32) -> bool {
5386 let mut hit = false;
5387 visit_exprs(e, &mut |x| {
5388 if let Expr::Local { var, .. } = x {
5389 if *var == v {
5390 hit = true;
5391 }
5392 }
5393 });
5394 hit
5395 }
5396 fn bool_valued(e: &Expr, vt: &VarTable) -> bool {
5400 fn side(e: &Expr, vt: &VarTable) -> bool {
5401 bool_const(e).is_some() || bool_valued(e, vt)
5402 }
5403 match e {
5404 Expr::Local { var, .. } => is_bool_var(*var, vt),
5405 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
5406 Expr::InstanceOf { .. } => true,
5407 Expr::Un { op: UnOp::Not, e: x, .. } => bool_valued(x, vt) || bool_const(x).is_some(),
5408 Expr::Bin { op, l, r, .. } => match op {
5409 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le => true,
5410 BinOp::LogAnd | BinOp::LogOr => {
5413 side(l, vt) && side(r, vt)
5414 }
5415 _ => false,
5416 },
5417 Expr::Cond { t, f, .. } => side(t, vt) && side(f, vt),
5418 _ => false,
5419 }
5420 }
5421 fn lone_assign(
5430 b: &Stmt,
5431 v: u32,
5432 vt: &VarTable,
5433 an: &VarAnalysis,
5434 consumed: &mut HashSet<u32>,
5435 ) -> Option<Expr> {
5436 let l = flat_list(b);
5437 if l.len() == 2 {
5438 let (u, e): (u32, &Expr) = match l[0] {
5439 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e),
5440 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
5441 match &**target {
5442 Expr::Local { var, .. } => (*var, &**value),
5443 _ => return None,
5444 }
5445 }
5446 _ => return None,
5447 };
5448 if u == v || consumed.contains(&u) {
5449 return None;
5450 }
5451 if an.assigns.get(u as usize).copied().unwrap_or(0) != 1
5458 || an.reads.get(u as usize).copied().unwrap_or(0) != 1
5459 {
5460 return None;
5461 }
5462 let (tv, rv) = match l[1] {
5463 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
5464 match (&**target, &**value) {
5465 (Expr::Local { var: t, .. }, Expr::Local { var: r, .. }) => (*t, *r),
5466 _ => return None,
5467 }
5468 }
5469 Stmt::LocalDef { var, init: Some(Expr::Local { var: r, .. }), .. } => (*var, *r),
5470 _ => return None,
5471 };
5472 if tv != v || rv != u {
5473 return None;
5474 }
5475 if reads_var(e, v) || !(bool_valued(e, vt) || bool_const(e).is_some()) {
5476 return None;
5477 }
5478 consumed.insert(u);
5479 return Some(e.clone());
5480 }
5481 if l.len() != 1 {
5482 return None;
5483 }
5484 let (target, value) = match l[0] {
5485 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => (target, value),
5486 Stmt::LocalDef { var, init: Some(value), .. } if *var == v => {
5487 if reads_var(value, v) || !(bool_valued(value, vt) || bool_const(value).is_some()) {
5488 return None;
5489 }
5490 return Some(value.clone());
5491 }
5492 _ => return None,
5493 };
5494 let Expr::Local { var: tv, .. } = &**target else { return None };
5495 if *tv != v || reads_var(value, v) {
5496 return None;
5497 }
5498 if !(bool_valued(value, vt) || bool_const(value).is_some()) {
5499 return None;
5500 }
5501 Some((**value).clone())
5502 }
5503 fn not(e: Expr) -> Expr {
5504 Expr::Un { op: UnOp::Not, e: Box::new(e) }
5505 }
5506 fn simplify_bool(e: Expr) -> Expr {
5511 match e {
5512 Expr::Un { op: UnOp::Not, e: inner, .. } => match *inner {
5513 Expr::Bin { op: BinOp::Ne, l, r, ty } => {
5518 simplify_bool(Expr::Bin { op: BinOp::Eq, l, r, ty })
5519 }
5520 Expr::Bin { op: BinOp::Eq, l, r, ty } => {
5521 simplify_bool(Expr::Bin { op: BinOp::Ne, l, r, ty })
5522 }
5523 other => Expr::Un { op: UnOp::Not, e: Box::new(simplify_bool(other)) },
5524 },
5525 Expr::Bin { op: op @ (BinOp::Eq | BinOp::Ne), l, r, ty } => {
5526 fn cond01(e: &Expr) -> Option<Expr> {
5528 if let Expr::Cond { c, t, f } = e {
5529 if matches!(&**t, Expr::Const(ConstVal::Int(1)))
5530 && matches!(&**f, Expr::Const(ConstVal::Int(0)))
5531 {
5532 return Some((**c).clone());
5533 }
5534 }
5535 None
5536 }
5537 let (lc, rc) = (cond01(&l), cond01(&r));
5538 match (lc, rc) {
5539 (Some(c), None) => match (&*r, op) {
5540 (Expr::Const(ConstVal::Int(1)), BinOp::Eq) => c,
5541 (Expr::Const(ConstVal::Int(1)), BinOp::Ne) => not(c),
5542 (Expr::Const(ConstVal::Int(0)), BinOp::Eq) => not(c),
5543 (Expr::Const(ConstVal::Int(0)), BinOp::Ne) => c,
5544 _ => Expr::Bin { op, l: Box::new(c), r, ty },
5545 },
5546 (None, Some(c)) => match (&*l, op) {
5547 (Expr::Const(ConstVal::Int(1)), BinOp::Eq) => c,
5548 (Expr::Const(ConstVal::Int(1)), BinOp::Ne) => not(c),
5549 (Expr::Const(ConstVal::Int(0)), BinOp::Eq) => not(c),
5550 (Expr::Const(ConstVal::Int(0)), BinOp::Ne) => c,
5551 _ => Expr::Bin { op, l, r: Box::new(c), ty },
5552 },
5553 _ => Expr::Bin { op, l, r, ty },
5554 }
5555 }
5556 other => other,
5557 }
5558 }
5559 fn bin(op: BinOp, l: Expr, r: Expr) -> Expr {
5560 Expr::Bin { op, l: Box::new(l), r: Box::new(r), ty: None }
5561 }
5562
5563 for _round in 0..4 {
5564 let mut changed = false;
5565 let mut analysis = VarAnalysis {
5566 assigns: Vec::new(),
5567 reads: Vec::new(),
5568 values: Vec::new(),
5569 };
5570 analyze_vars(body, &mut analysis);
5571 let mut consumed: HashSet<u32> = HashSet::default();
5572 walk_mut_deep(body, &mut |st| {
5573 let Stmt::Block(list) = st else { return };
5574 let mut i = 0usize;
5575 while i + 1 < list.len() {
5576 let folded = try_at(list, i, vt, &is_bool_var, &bool_const, &reads_var, &lone_assign, &analysis, &mut consumed);
5577 match folded {
5578 Some(expr) => {
5579 let is_def = matches!(&list[i], Stmt::LocalDef { .. });
5580 let var = match &list[i] {
5581 Stmt::LocalDef { var, .. } => *var,
5582 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
5583 match &**target { Expr::Local { var, .. } => *var, _ => unreachable!() }
5584 }
5585 _ => unreachable!(),
5586 };
5587 let (is_final, force_type) = match &list[i] {
5588 Stmt::LocalDef { is_final, force_type, .. } => (*is_final, *force_type),
5589 _ => (false, false),
5590 };
5591 list[i] = if is_def {
5592 Stmt::LocalDef { var, init: Some(expr), is_final, force_type }
5593 } else {
5594 Stmt::ExprStmt(Expr::Assign {
5595 target: Box::new(Expr::Local {
5596 var,
5597 ty: TypeRef::J(JavaType::Boolean),
5598 }),
5599 op: AssignOp::Plain,
5600 value: Box::new(expr),
5601 })
5602 };
5603 list.remove(i + 1);
5604 changed = true;
5605 }
5608 None => i += 1,
5609 }
5610 }
5611 });
5612 if !changed {
5613 break;
5614 }
5615 }
5616
5617 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
5618 fn try_at(
5619 list: &[Stmt],
5620 i: usize,
5621 vt: &VarTable,
5622 is_bool_var: &dyn Fn(u32, &VarTable) -> bool,
5623 bool_const: &dyn Fn(&Expr) -> Option<bool>,
5624 reads_var: &dyn Fn(&Expr, u32) -> bool,
5625 lone_assign: &dyn Fn(&Stmt, u32, &VarTable, &VarAnalysis, &mut HashSet<u32>) -> Option<Expr>,
5626 an: &VarAnalysis,
5627 consumed: &mut HashSet<u32>,
5628 ) -> Option<Expr> {
5629 let (v, base) = match &list[i] {
5631 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
5632 match &**target {
5633 Expr::Local { var, .. } => {
5634 let b = bool_const(value)?;
5637 if !is_bool_var(*var, vt) { return None; }
5638 (*var, b)
5639 }
5640 _ => return None,
5641 }
5642 }
5643 Stmt::LocalDef { var, init: Some(e), .. } => {
5644 let b = bool_const(e)?;
5645 if !is_bool_var(*var, vt) { return None; }
5646 (*var, b)
5647 }
5648 _ => return None,
5649 };
5650 let Stmt::If { cond, then_stmt, else_stmt } = &list[i + 1] else {
5651 return None;
5652 };
5653 if reads_var(cond, v) {
5654 return None;
5655 }
5656 let mut touches_v = false;
5657 visit_exprs(cond, &mut |x| {
5658 if let Expr::Assign { target, .. } = x {
5659 if let Expr::Local { var, .. } = &**target {
5660 if *var == v { touches_v = true; }
5661 }
5662 }
5663 });
5664 if touches_v {
5665 return None;
5666 }
5667 let (cond_n, then_arm, else_arm): (Expr, &Stmt, Option<&Stmt>) =
5670 if flat_list(then_stmt).is_empty() && else_stmt.is_some() {
5671 (not(cond.clone()), else_stmt.as_ref().unwrap(), None)
5672 } else {
5673 (cond.clone(), then_stmt.as_ref(), else_stmt.as_deref())
5674 };
5675 let e1 = lone_assign(then_arm, v, vt, an, consumed)?;
5676 let e2 = match else_arm {
5677 None => None,
5678 Some(b) => Some(lone_assign(b, v, vt, an, consumed)?),
5679 };
5680 let e1c = bool_const(&e1);
5681 let c = simplify_bool(cond_n);
5682 Some(match e2 {
5683 None => {
5684 if !base {
5685 match e1c {
5686 Some(true) => c,
5687 Some(false) => return None, None => bin(BinOp::LogAnd, c, e1),
5689 }
5690 } else {
5691 match e1c {
5692 Some(false) => not(c),
5693 Some(true) => return None,
5694 None => bin(BinOp::LogOr, not(c), e1),
5695 }
5696 }
5697 }
5698 Some(e2) => {
5699 if e1 == e2 {
5701 return None; }
5703 Expr::Cond { c: Box::new(c), t: Box::new(e1), f: Box::new(e2) }
5704 }
5705 })
5706 }
5707}
5708
5709pub fn idiom_compounds(body: &mut Stmt, vt: &VarTable) {
5710 walk_mut_deep(body, &mut |st| {
5711 let Stmt::ExprStmt(e) = st else { return };
5712 let Expr::Assign { target, op: AssignOp::Plain, value } = &mut *e else {
5713 return;
5714 };
5715 enum Tgt {
5718 Loc(u32),
5719 Fld,
5720 }
5721 let tgt = match &**target {
5722 Expr::Local { var, .. } => Tgt::Loc(*var),
5723 Expr::Field { owner, .. }
5724 if owner.is_none()
5725 || matches!(&**owner.as_ref().unwrap(), Expr::This) =>
5726 {
5727 Tgt::Fld
5728 }
5729 _ => return,
5730 };
5731 let Expr::Bin { op, l, r, .. } = &mut **value else {
5732 return;
5733 };
5734 match (&tgt, &**l) {
5736 (Tgt::Loc(v), Expr::Local { var, .. }) if *var == *v => {}
5737 (Tgt::Fld, Expr::Field { cls: c2, name: n2, .. }) => {
5738 let Expr::Field { cls: c1, name: n1, .. } = &**target else {
5739 return;
5740 };
5741 if c1 != c2 || n1 != n2 {
5742 return;
5743 }
5744 }
5745 _ => return,
5746 }
5747 let one = match &**r {
5749 Expr::Const(ConstVal::Int(1)) => Some(1i64),
5750 Expr::Const(ConstVal::Int(-1)) => Some(-1),
5751 Expr::Const(ConstVal::Long(1)) => Some(1),
5752 Expr::Const(ConstVal::Long(-1)) => Some(-1),
5753 _ => None,
5754 };
5755 if *op == BinOp::Add || *op == BinOp::Sub {
5756 if let Some(mut d) = one {
5757 if *op == BinOp::Sub {
5758 d = -d;
5759 }
5760 let tgt_ty = match &tgt {
5761 Tgt::Loc(v) => vt.var(*v).ty.erased(),
5762 Tgt::Fld => {
5763 let Expr::Field { ty, .. } = &**target else {
5764 return;
5765 };
5766 ty.erased()
5767 }
5768 };
5769 let numeric = matches!(
5770 tgt_ty,
5771 JavaType::Int
5772 | JavaType::Long
5773 | JavaType::Short
5774 | JavaType::Byte
5775 | JavaType::Char
5776 | JavaType::Float
5777 | JavaType::Double
5778 );
5779 if numeric {
5780 let wide = matches!(tgt_ty, JavaType::Long);
5781 let inner = match &tgt {
5782 Tgt::Loc(v) => Expr::Local {
5783 var: *v,
5784 ty: vt.var(*v).ty.clone(),
5785 },
5786 Tgt::Fld => (**target).clone(),
5787 };
5788 *e = Expr::PostIncDec {
5789 e: Box::new(inner),
5790 delta: d,
5791 wide,
5792 };
5793 return;
5794 }
5795 }
5796 }
5797 let cop = match op {
5798 BinOp::Add => AssignOp::Add,
5799 BinOp::Sub => AssignOp::Sub,
5800 BinOp::Mul => AssignOp::Mul,
5801 BinOp::Div => AssignOp::Div,
5802 BinOp::Rem => AssignOp::Rem,
5803 BinOp::Shl => AssignOp::Shl,
5804 BinOp::Shr => AssignOp::Shr,
5805 BinOp::Ushr => AssignOp::Ushr,
5806 BinOp::And => AssignOp::And,
5807 BinOp::Or => AssignOp::Or,
5808 BinOp::Xor => AssignOp::Xor,
5809 _ => return,
5810 };
5811 let rhs = std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
5812 let new_target = match &tgt {
5813 Tgt::Loc(v) => Box::new(Expr::Local {
5814 var: *v,
5815 ty: vt.var(*v).ty.clone(),
5816 }),
5817 Tgt::Fld => target.clone(),
5818 };
5819 *e = Expr::Assign {
5820 target: new_target,
5821 op: cop,
5822 value: rhs,
5823 };
5824 });
5825}
5826
5827pub fn fix_bool_xor(body: &mut Stmt, vt: &VarTable, ret_bool: bool) {
5828 fn is_one(e: &Expr) -> bool {
5829 matches!(e, Expr::Const(ConstVal::Int(1)))
5830 }
5831 fn bty(e: &Expr, vt: &VarTable) -> bool {
5832 matches!(
5833 match e {
5834 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
5835 other => other.type_ref().erased(),
5836 },
5837 JavaType::Boolean
5838 )
5839 }
5840 fn inv_of_xor(x: &Expr, vt: &VarTable) -> Option<Expr> {
5845 if let Expr::Bin { op: BinOp::Xor, l, r, .. } = x {
5846 let (int_side, one_side) = if is_one(r) {
5847 (l, r)
5848 } else if is_one(l) {
5849 (r, l)
5850 } else {
5851 return None;
5852 };
5853 let _ = one_side;
5854 if !bty(int_side, vt) {
5855 return Some(Expr::Bin {
5856 op: BinOp::Eq,
5857 l: int_side.clone(),
5858 r: Box::new(Expr::Const(ConstVal::Int(0))),
5859 ty: Some(TypeRef::J(JavaType::Boolean)),
5860 });
5861 }
5862 }
5863 None
5864 }
5865 let tgt_bool = |target: &Expr, vt: &VarTable| -> bool {
5866 match target {
5867 Expr::Local { var, .. } => matches!(vt.var(*var).ty.erased(), JavaType::Boolean),
5868 Expr::Field { ty, .. } => matches!(ty.erased(), JavaType::Boolean),
5869 _ => false,
5870 }
5871 };
5872 walk_mut_deep(body, &mut |st| match st {
5873 Stmt::Return(Some(e)) if ret_bool => {
5874 if let Some(r) = inv_of_xor(e, vt) {
5875 *e = r;
5876 }
5877 }
5878 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain, .. }) => {
5879 if tgt_bool(target, vt) {
5880 if let Some(r) = inv_of_xor(value, vt) {
5881 **value = r;
5882 }
5883 }
5884 }
5885 Stmt::LocalDef { var, init: Some(value), .. }
5886 if matches!(vt.var(*var).ty.erased(), JavaType::Boolean) =>
5887 {
5888 if let Some(r) = inv_of_xor(value, vt) {
5889 *value = r;
5890 }
5891 }
5892 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
5893 if let Some(r) = inv_of_xor(cond, vt) {
5894 *cond = r;
5895 }
5896 }
5897 _ => {}
5898 });
5899 fn ity(e: &Expr, vt: &VarTable) -> bool {
5900 matches!(
5901 match e {
5902 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
5903 other => other.type_ref().erased(),
5904 },
5905 JavaType::Int | JavaType::Short | JavaType::Byte | JavaType::Char
5906 )
5907 }
5908 fn mixed_rewrite(e: &mut Expr, vt: &VarTable) {
5917 deep_rewrite(e, &mut |x| {
5918 if let Expr::Bin { op, l, r, ty } = x {
5919 match op {
5920 BinOp::Xor => {
5921 if is_one(r) && bty(l, vt) {
5922 let taken =
5923 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
5924 *x = Expr::Un { op: UnOp::Not, e: taken };
5925 } else if is_one(l) && bty(r, vt) {
5926 let taken =
5927 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
5928 *x = Expr::Un { op: UnOp::Not, e: taken };
5929 }
5930 }
5931 BinOp::Or | BinOp::And => {
5932 if bty(l, vt) && ity(r, vt) {
5933 let taken =
5934 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
5935 **r = Expr::Bin {
5936 op: BinOp::Ne,
5937 l: taken,
5938 r: Box::new(Expr::Const(ConstVal::Int(0))),
5939 ty: Some(TypeRef::J(JavaType::Boolean)),
5940 };
5941 *ty = Some(TypeRef::J(JavaType::Boolean));
5942 } else if ity(l, vt) && bty(r, vt) {
5943 let taken =
5944 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
5945 **l = Expr::Bin {
5946 op: BinOp::Ne,
5947 l: taken,
5948 r: Box::new(Expr::Const(ConstVal::Int(0))),
5949 ty: Some(TypeRef::J(JavaType::Boolean)),
5950 };
5951 *ty = Some(TypeRef::J(JavaType::Boolean));
5952 }
5953 }
5954 _ => {}
5955 }
5956 }
5957 });
5958 }
5959 walk_mut_deep(body, &mut |st| match st {
5962 Stmt::Return(Some(e)) if ret_bool => mixed_rewrite(e, vt),
5963 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain, .. }) => {
5964 if tgt_bool(target, vt) {
5965 mixed_rewrite(value, vt);
5966 }
5967 }
5968 Stmt::LocalDef { var, init: Some(e), .. }
5969 if matches!(vt.var(*var).ty.erased(), JavaType::Boolean) =>
5970 {
5971 mixed_rewrite(e, vt);
5972 }
5973 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
5974 mixed_rewrite(cond, vt);
5975 }
5976 _ => {}
5977 });
5978 walk_stmt_exprs(body, &mut |e| {
5981 deep_rewrite(e, &mut |x| {
5982 if let Expr::Cond { c, .. } = x {
5983 mixed_rewrite(c, vt);
5984 }
5985 });
5986 });
5987}
5988
5989pub fn booleanize(vt: &mut VarTable, body: &mut Stmt, ret_bool: bool) -> usize {
5995 let n0 = vt.vars.len();
6002 let mut derefed = vec![false; n0];
6003 mark_derefs(body, &mut derefed);
6004 let mut total = 0usize;
6005 for _ in 0..4 {
6006 let n = booleanize_round(vt, body, ret_bool, &derefed);
6007 if n == 0 {
6008 break;
6009 }
6010 total += n;
6011 }
6012 total
6013}
6014
6015fn mark_deref_expr(e: &Expr, derefed: &mut [bool]) {
6019 match e {
6020 Expr::Field { owner: Some(o), is_static: false, .. }
6021 | Expr::Method { owner: Some(o), .. } => {
6022 if let Expr::Local { var, .. } = &**o {
6023 if (*var as usize) < derefed.len() {
6024 derefed[*var as usize] = true;
6025 }
6026 }
6027 }
6028 Expr::ArrayIndex { array, .. } => {
6029 if let Expr::Local { var, .. } = &**array {
6030 if (*var as usize) < derefed.len() {
6031 derefed[*var as usize] = true;
6032 }
6033 }
6034 }
6035 _ => {}
6036 }
6037 for_each_child(e, &mut |c| mark_deref_expr(c, derefed));
6038}
6039
6040fn mark_derefs(body: &Stmt, derefed: &mut [bool]) {
6041 walk_all(body, &mut |st| {
6042 match st {
6043 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
6044 mark_deref_expr(e, derefed);
6045 }
6046 Stmt::Return(Some(e)) => mark_deref_expr(e, derefed),
6047 Stmt::LocalDef { init: Some(e), .. } => mark_deref_expr(e, derefed),
6048 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
6049 mark_deref_expr(cond, derefed);
6050 }
6051 Stmt::For { init, .. } => {
6052 for x in init {
6053 mark_derefs(x, derefed);
6054 }
6055 }
6056 Stmt::Switch { selector, .. } => mark_deref_expr(selector, derefed),
6057 Stmt::ForEach { iterable, .. } => mark_deref_expr(iterable, derefed),
6058 _ => {}
6059 }
6060 });
6061}
6062
6063fn booleanize_round(
6064 vt: &mut VarTable,
6065 body: &mut Stmt,
6066 ret_bool: bool,
6067 derefed: &[bool],
6068) -> usize {
6069 let n = vt.vars.len();
6070 let mut in_cond = vec![false; n];
6071
6072 walk_all(body, &mut |st| {
6075 let cond: Option<&Expr> = match st {
6076 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
6077 Some(cond)
6078 }
6079 _ => None,
6080 };
6081 if let Some(c) = cond {
6082 if let Expr::Bin {
6083 op: BinOp::Eq | BinOp::Ne,
6084 l,
6085 r,
6086 ..
6087 } = c
6088 {
6089 for (x, other) in [(l, r), (r, l)] {
6090 if let (Expr::Local { var, .. }, Expr::Const(ConstVal::Int(0))) =
6091 (&**x, &**other)
6092 {
6093 if (*var as usize) < n {
6094 in_cond[*var as usize] = true;
6095 }
6096 }
6097 }
6098 }
6099 }
6100 if ret_bool {
6105 if let Stmt::Return(Some(Expr::Local { var, .. })) = st {
6106 if (*var as usize) < n {
6107 in_cond[*var as usize] = true;
6108 }
6109 }
6110 }
6111 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = st {
6118 if let Expr::Field { ty, .. } = &**target {
6119 if ty.erased() == JavaType::Boolean {
6120 if let Expr::Local { var, .. } = &**value {
6121 if (*var as usize) < n {
6122 in_cond[*var as usize] = true;
6123 }
6124 }
6125 }
6126 }
6127 }
6128 let val: Option<&Expr> = match st {
6129 Stmt::ExprStmt(Expr::Assign { value, .. }) => Some(value),
6130 Stmt::ExprStmt(e) => Some(e),
6133 Stmt::LocalDef { init: Some(e), .. } => Some(e),
6134 Stmt::Return(Some(e)) => Some(e),
6135 _ => None,
6136 };
6137 if let Some(v) = val {
6138 visit_exprs(v, &mut |x| {
6139 if let Expr::Method { desc, args, .. } = x {
6145 for (i, a) in args.iter().enumerate() {
6146 if desc.args.get(i) == Some(&JavaType::Boolean) {
6147 if let Expr::Local { var, .. } = a {
6148 if (*var as usize) < n {
6149 in_cond[*var as usize] = true;
6150 }
6151 }
6152 }
6153 }
6154 }
6155 if let Expr::Bin { op, l, r, .. } = x {
6156 if matches!(
6157 op,
6158 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
6159 ) {
6160 for side in [l, r] {
6161 if let Expr::Local { var, .. } = &**side {
6162 if (*var as usize) < n {
6163 in_cond[*var as usize] = true;
6164 }
6165 }
6166 }
6167 }
6168 if matches!(op, BinOp::Or | BinOp::And) {
6174 let l_bool = l.type_ref().erased() == JavaType::Boolean;
6175 let r_bool = r.type_ref().erased() == JavaType::Boolean;
6176 if l_bool != r_bool {
6177 let side = if l_bool { r } else { l };
6178 if let Expr::Local { var, .. } = &**side {
6179 if (*var as usize) < n {
6180 in_cond[*var as usize] = true;
6181 }
6182 }
6183 }
6184 }
6185 }
6186 });
6187 }
6188 });
6189
6190 let mut assigned_any = vec![false; n];
6197 let mut all_bool = vec![true; n];
6198 let mut edges: Vec<(usize, usize)> = Vec::new();
6199 walk_all(body, &mut |st| {
6200 let (var, value) = match st {
6201 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
6202 Expr::Local { var, .. } => (var, &**value),
6203 _ => return,
6204 },
6205 Stmt::LocalDef {
6206 var, init: Some(e), ..
6207 } => (var, e),
6208 _ => return,
6209 };
6210 let i = *var as usize;
6211 if i < n {
6212 assigned_any[i] = true;
6213 if !is_boolean_valued(value, vt) {
6214 all_bool[i] = false;
6215 }
6216 if let Expr::Local { var: src, .. } = value {
6222 if (*src as usize) < n {
6223 edges.push((i, *src as usize));
6224 }
6225 }
6226 }
6227 });
6228 loop {
6233 let mut changed = false;
6234 for &(tgt, src) in &edges {
6235 if in_cond[tgt] && !in_cond[src] && all_bool[src] {
6236 in_cond[src] = true;
6237 changed = true;
6238 }
6239 if in_cond[src] && !in_cond[tgt] && all_bool[tgt] {
6245 in_cond[tgt] = true;
6246 changed = true;
6247 }
6248 }
6249 if !changed {
6250 break;
6251 }
6252 }
6253 let reads = count_locals_stmts(std::slice::from_ref(body));
6254 let mut boolean_vars: HashSet<u32> = HashSet::default();
6255 for i in 0..n {
6256 if vt.vars[i].is_param {
6257 continue;
6258 }
6259 if derefed[i] {
6260 continue;
6261 }
6262 if !in_cond[i] && !(reads.get(&(i as u32)).copied().unwrap_or(0) == 0) {
6263 continue;
6264 }
6265 if assigned_any[i] && all_bool[i] {
6266 boolean_vars.insert(i as u32);
6267 }
6268 }
6269 if boolean_vars.is_empty() {
6270 return 0;
6271 }
6272 for v in &boolean_vars {
6273 vt.vars[*v as usize].ty = TypeRef::J(JavaType::Boolean);
6274 }
6275 let types: Vec<&TypeRef> = vt.vars.iter().map(|v| &v.ty).collect();
6276 rewrite_exprs(body, &mut |e| {
6277 deep_rewrite(e, &mut |x| {
6278 if let Expr::Local { var, ty } = x {
6279 if let Some(want) = types.get(*var as usize) {
6280 if ty != *want {
6281 *ty = (*want).clone();
6282 }
6283 }
6284 }
6285 });
6286 });
6287
6288 fold_bool_conditions(body, &boolean_vars);
6290 boolean_vars.len()
6291}
6292
6293fn is_boolean_valued(e: &Expr, vt: &VarTable) -> bool {
6294 match e {
6295 Expr::Const(ConstVal::Int(0)) | Expr::Const(ConstVal::Int(1)) => true,
6296 Expr::Bin { op: BinOp::Or | BinOp::And, l, r, .. } => {
6303 is_boolean_valued(l, vt) || is_boolean_valued(r, vt)
6304 }
6305 Expr::Bin {
6313 op: BinOp::Xor,
6314 l,
6315 r,
6316 ..
6317 } => is_boolean_valued(l, vt) && is_boolean_valued(r, vt),
6318 Expr::Bin { op, .. } => matches!(
6319 op,
6320 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
6321 ),
6322 Expr::InstanceOf { .. } => true,
6323 Expr::Local { var, ty } => {
6329 let t = if (*var as usize) < vt.vars.len() {
6330 vt.var(*var).ty.erased()
6331 } else {
6332 ty.erased()
6333 };
6334 t == JavaType::Boolean
6335 }
6336 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
6337 Expr::Cond { t, f, .. } => is_boolean_valued(t, vt) && is_boolean_valued(f, vt),
6338 Expr::Un { op: UnOp::Not, .. } => true,
6339 Expr::Field { ty, .. } => ty.erased() == JavaType::Boolean,
6343 _ => false,
6344 }
6345}
6346
6347fn fold_bool_conditions(s: &mut Stmt, bv: &HashSet<u32>) {
6348 let fold = |e: &mut Expr| {
6349 deep_rewrite(e, &mut |x| {
6350 if let Expr::Bin { op, l, r, .. } = x {
6351 let (var_side, const_side) = match (&**l, &**r) {
6352 (Expr::Local { .. }, Expr::Const(ConstVal::Int(_))) => (l, 0),
6353 (Expr::Const(ConstVal::Int(_)), Expr::Local { .. }) => (r, 1),
6354 _ => return,
6355 };
6356 let var = match &**var_side {
6357 Expr::Local { var, .. } => *var,
6358 _ => return,
6359 };
6360 if !bv.contains(&var) {
6361 return;
6362 }
6363 let _ = const_side;
6364 match op {
6365 BinOp::Ne => {
6366 let inner = var_side.clone();
6367 *x = *inner;
6368 }
6369 BinOp::Eq => {
6370 let inner = var_side.clone();
6371 *x = Expr::Un {
6372 op: UnOp::Not,
6373 e: Box::new(*inner),
6374 };
6375 }
6376 _ => {}
6377 }
6378 }
6379 });
6380 };
6381 match s {
6382 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => fold(cond),
6383 _ => {}
6384 }
6385 walk_mut_deep(s, &mut |st| {
6386 let cond = match st {
6387 Stmt::If { cond, .. } => Some(cond),
6388 Stmt::While { cond, .. } => Some(cond),
6389 Stmt::DoWhile { cond, .. } => Some(cond),
6390 _ => None,
6391 };
6392 if let Some(c) = cond {
6393 fold(c);
6394 }
6395 });
6396}
6397
6398pub fn flip_const_compares(s: &mut Stmt) {
6400 rewrite_exprs(s, &mut |e| {
6401 deep_rewrite(e, &mut |x| {
6402 if let Expr::Bin { op, l, r, .. } = x {
6403 if matches!(
6404 op,
6405 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
6406 ) {
6407 let l_const = matches!(&**l, Expr::Const(_));
6408 let r_var = matches!(&**r, Expr::Local { .. });
6409 if l_const && r_var {
6410 std::mem::swap(l, r);
6411 }
6412 }
6413 }
6414 });
6415 });
6416}
6417
6418pub fn null_compares(vt: &VarTable, body: &mut Stmt) {
6420 let obj_vars: HashSet<u32> = vt
6421 .vars
6422 .iter()
6423 .filter(|v| v.ty.erased().is_reference())
6424 .map(|v| v.id)
6425 .collect();
6426 rewrite_exprs(body, &mut |e| {
6427 deep_rewrite(e, &mut |x| {
6428 null_side_rewrite(x, &|v| obj_vars.contains(&v));
6429 });
6430 });
6431}
6432
6433pub fn drop_dead_locals(body: &mut Stmt) {
6442 loop {
6443 let mut reads: Vec<usize> = Vec::new();
6444 count_reads(body, &mut reads);
6445 let mut dropped = 0usize;
6446 walk_mut_deep(body, &mut |st| match st {
6447 Stmt::Block(items) => prune_dead_items(items, &reads, &mut dropped),
6448 Stmt::Switch { cases, .. } => {
6458 for c in cases {
6459 prune_dead_items(&mut c.body, &reads, &mut dropped);
6460 }
6461 }
6462 Stmt::For { init, .. } => prune_dead_items(init, &reads, &mut dropped),
6463 _ => {}
6464 });
6465 if dropped == 0 {
6466 break;
6467 }
6468 }
6469}
6470
6471fn prune_dead_items(items: &mut Vec<Stmt>, reads: &[usize], dropped: &mut usize) {
6476 let dead = |v: u32| reads.get(v as usize).copied().unwrap_or(0) == 0;
6477 items.retain_mut(|x| match x {
6478 Stmt::ExprStmt(Expr::Assign { target, value, op, .. }) => {
6479 let dead_target = matches!(&**target, Expr::Local { var, .. } if dead(*var));
6480 if dead_target && matches!(*op, AssignOp::Plain) {
6481 *dropped += 1;
6482 if has_side_effects(value) {
6483 let e = std::mem::replace(value, Box::new(Expr::This));
6484 *x = Stmt::ExprStmt(*e);
6485 true
6486 } else {
6487 false
6488 }
6489 } else {
6490 true
6491 }
6492 }
6493 Stmt::LocalDef { var, init, .. } if dead(*var) => {
6494 *dropped += 1;
6495 if let Some(e) = init.take() {
6496 if has_side_effects(&e) {
6497 *x = Stmt::ExprStmt(e);
6498 return true;
6499 }
6500 }
6501 false
6502 }
6503 _ => true,
6504 });
6505}
6506
6507fn for_each_child_stmt<'a>(st: &'a Stmt, f: &mut impl FnMut(&'a Stmt)) {
6510 match st {
6511 Stmt::Block(v) => {
6512 for x in v {
6513 f(x);
6514 }
6515 }
6516 Stmt::If {
6517 then_stmt,
6518 else_stmt,
6519 ..
6520 } => {
6521 f(then_stmt);
6522 if let Some(e) = else_stmt {
6523 f(e);
6524 }
6525 }
6526 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => f(body),
6527 Stmt::For { init, body, .. } => {
6528 for x in init {
6529 f(x);
6530 }
6531 f(body);
6532 }
6533 Stmt::ForEach { body, .. } => f(body),
6534 Stmt::Switch { cases, default, .. } => {
6535 for c in cases {
6536 for x in &c.body {
6537 f(x);
6538 }
6539 }
6540 if let Some(d) = default {
6541 f(d);
6542 }
6543 }
6544 Stmt::Try {
6545 body,
6546 catches,
6547 finally,
6548 } => {
6549 f(body);
6550 for c in catches {
6551 f(&c.body);
6552 }
6553 if let Some(fl) = finally {
6554 f(fl);
6555 }
6556 }
6557 Stmt::TryWithResources {
6558 resources,
6559 body,
6560 catches,
6561 finally,
6562 } => {
6563 for r in resources {
6564 f(r);
6565 }
6566 f(body);
6567 for c in catches {
6568 f(&c.body);
6569 }
6570 if let Some(fl) = finally {
6571 f(fl);
6572 }
6573 }
6574 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => f(body),
6575 _ => {}
6576 }
6577}
6578
6579fn stmt_shallow_vars<F: FnMut(u32)>(st: &Stmt, out: &mut F) {
6584 fn ex<F: FnMut(u32)>(e: &Expr, out: &mut F) {
6585 visit_exprs(e, &mut |x| {
6586 if let Expr::Local { var, .. } = x {
6587 out(*var);
6588 }
6589 });
6590 }
6591 match st {
6592 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
6593 ex(e, out)
6594 }
6595 Stmt::TernaryValue { e } => ex(e, out),
6596 Stmt::Return(Some(e)) => ex(e, out),
6597 Stmt::LocalDef { var, init, .. } => {
6598 out(*var);
6599 if let Some(e) = init {
6600 ex(e, out);
6601 }
6602 }
6603 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
6604 ex(cond, out)
6605 }
6606 Stmt::For { cond, update, .. } => {
6607 if let Some(c) = cond {
6608 ex(c, out);
6609 }
6610 for u in update {
6611 ex(u, out);
6612 }
6613 }
6614 Stmt::ForEach { iterable, .. } => ex(iterable, out),
6615 Stmt::Switch { selector, cases, .. } => {
6616 ex(selector, out);
6617 for cg in cases {
6618 if let Some(g) = &cg.guard {
6619 ex(g, out);
6620 }
6621 }
6622 }
6623 Stmt::Assert { cond, msg } => {
6624 ex(cond, out);
6625 if let Some(m) = msg {
6626 ex(m, out);
6627 }
6628 }
6629 Stmt::Synchronized { lock, .. } => ex(lock, out),
6630 _ => {}
6631 }
6632}
6633
6634fn scope_pass_a(st: &Stmt, cur: u32, next: &mut u32, def_block: &mut [u32], sizes: &mut Vec<u32>) {
6637 if let Stmt::LocalDef { var, .. } = st {
6638 let i = *var as usize;
6639 if i < def_block.len() && def_block[i] == u32::MAX {
6640 def_block[i] = cur;
6641 }
6642 }
6643 for_each_child_stmt(st, &mut |c| {
6644 if matches!(c, Stmt::Block(_)) {
6645 let id = *next;
6646 *next += 1;
6647 sizes.push(0);
6648 scope_pass_a(c, id, next, def_block, sizes);
6649 sizes[id as usize] = *next - id;
6650 } else {
6651 scope_pass_a(c, cur, next, def_block, sizes);
6652 }
6653 });
6654}
6655
6656fn scope_pass_b(
6659 st: &Stmt,
6660 cur: u32,
6661 next: &mut u32,
6662 def_block: &[u32],
6663 sizes: &[u32],
6664 within: &mut [u32],
6665 total: &mut [u32],
6666) {
6667 stmt_shallow_vars(st, &mut |v| {
6668 let i = v as usize;
6669 if i < total.len() {
6670 total[i] += 1;
6671 let d = def_block[i];
6672 if d != u32::MAX && d <= cur && cur < d + sizes[d as usize] {
6673 within[i] += 1;
6674 }
6675 }
6676 });
6677 for_each_child_stmt(st, &mut |c| {
6678 if matches!(c, Stmt::Block(_)) {
6679 let id = *next;
6680 *next += 1;
6681 scope_pass_b(c, id, next, def_block, sizes, within, total);
6682 } else {
6683 scope_pass_b(c, cur, next, def_block, sizes, within, total);
6684 }
6685 });
6686}
6687
6688pub fn strip_enum_const_inits(s: &mut Stmt, class: &crate::PoolClass) {
6697 let const_fields: jdc_core::FxHashSet<&str> = class
6698 .static_fields
6699 .iter()
6700 .filter(|f| f.access & crate::access::ACC_ENUM != 0)
6701 .map(|f| f.name.as_str())
6702 .collect();
6703 if const_fields.is_empty() {
6704 return;
6705 }
6706 if let Stmt::Block(stmts) = s {
6707 stmts.retain(|st| {
6708 !matches!(
6709 st,
6710 Stmt::ExprStmt(Expr::Assign { target, value, .. })
6711 if matches!(&**target,
6712 Expr::Field { cls, name, is_static: true, .. }
6713 if cls.as_ref() == class.name
6714 && const_fields.contains(&**name))
6715 && matches!(&**value, Expr::New { cls: ncls, .. } if ncls.as_ref() == class.name)
6716 )
6717 });
6718 }
6719}
6720
6721fn contains_this_access(e: &Expr) -> bool {
6730 let mut hit = false;
6731 visit_exprs(e, &mut |x| {
6732 if hit {
6733 return;
6734 }
6735 match x {
6736 Expr::This => hit = true,
6737 Expr::Field { owner, is_static: false, .. } => {
6738 if owner.is_none() || matches!(&**owner.as_ref().unwrap(), Expr::This) {
6739 hit = true;
6740 }
6741 }
6742 Expr::Method { owner, is_static: false, .. } => {
6743 if owner.is_none() || matches!(&**owner.as_ref().unwrap(), Expr::This) {
6744 hit = true;
6745 }
6746 }
6747 _ => {}
6748 }
6749 });
6750 hit
6751}
6752
6753fn var_occurs_in(stmts: &[Stmt], var: u32) -> bool {
6768 stmts.iter().any(|st| {
6769 let mut c = st.clone();
6770 let mut hit = false;
6771 walk_stmt_exprs(&mut c, &mut |e| {
6772 if !hit {
6773 deep_rewrite(e, &mut |x| {
6774 if let Expr::Local { var: v, .. } = x {
6775 if *v == var {
6776 hit = true;
6777 }
6778 }
6779 });
6780 }
6781 });
6782 hit
6783 })
6784}
6785
6786pub struct CtorHelper {
6796 pub name: String,
6797 pub ret: JavaType,
6798 pub params: Vec<(JavaType, String)>,
6799 pub body: Stmt,
6800 pub vt: VarTable,
6801}
6802
6803fn ctor_helpers() -> &'static std::sync::Mutex<std::collections::HashMap<String, Vec<CtorHelper>>> {
6804 static H: std::sync::OnceLock<std::sync::Mutex<std::collections::HashMap<String, Vec<CtorHelper>>>> =
6805 std::sync::OnceLock::new();
6806 H.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::default()))
6807}
6808
6809fn helper_name(class: &str, desc: &str) -> String {
6812 let mut h: u64 = 0xcbf29ce484222325;
6813 for b in class.as_bytes().iter().chain(b"::").chain(desc.as_bytes()) {
6814 h ^= *b as u64;
6815 h = h.wrapping_mul(0x100000001b3);
6816 }
6817 format!("resolve${:x}", (h >> 16) & 0xffff_ffff)
6818}
6819
6820pub fn take_ctor_helpers(class: &str) -> Vec<CtorHelper> {
6823 let mut m = ctor_helpers().lock().unwrap_or_else(|e| e.into_inner());
6824 let mut v = m.remove(class).unwrap_or_default();
6825 v.sort_by(|a, b| a.name.cmp(&b.name));
6826 v
6827}
6828
6829fn strip_delegations_deep(stmts: &mut Vec<Stmt>) {
6830 stmts.retain(|s| !is_bare_ctor_call(s));
6831 for s in stmts.iter_mut() {
6832 strip_delegation_one(s);
6833 }
6834}
6835
6836fn strip_delegation_one(st: &mut Stmt) {
6837 if is_bare_ctor_call(st) {
6838 *st = Stmt::Block(Vec::new());
6839 return;
6840 }
6841 match st {
6842 Stmt::Block(v) => strip_delegations_deep(v),
6843 Stmt::If { then_stmt, else_stmt, .. } => {
6844 strip_delegation_one(then_stmt);
6845 if let Some(e) = else_stmt {
6846 strip_delegation_one(e);
6847 }
6848 }
6849 Stmt::While { body, .. }
6850 | Stmt::DoWhile { body, .. }
6851 | Stmt::ForEach { body, .. }
6852 | Stmt::Synchronized { body, .. }
6853 | Stmt::Labeled { body, .. } => strip_delegation_one(body),
6854 Stmt::For { init, body, .. } => {
6855 strip_delegations_deep(init);
6856 strip_delegation_one(body);
6857 }
6858 Stmt::Switch { cases, default, .. } => {
6859 for c in cases.iter_mut() {
6860 strip_delegations_deep(&mut c.body);
6861 }
6862 if let Some(d) = default {
6863 strip_delegation_one(d);
6864 }
6865 }
6866 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
6867 strip_delegation_one(body);
6868 for c in catches.iter_mut() {
6869 strip_delegation_one(&mut c.body);
6870 }
6871 if let Some(f) = finally {
6872 strip_delegation_one(f);
6873 }
6874 }
6875 _ => {}
6876 }
6877}
6878
6879#[allow(clippy::ptr_arg)]
6880fn rewrite_bare_returns(stmts: &mut Vec<Stmt>, val: &Expr) {
6881 for s in stmts.iter_mut() {
6882 rewrite_bare_returns_one(s, val);
6883 }
6884}
6885
6886fn rewrite_bare_returns_one(st: &mut Stmt, val: &Expr) {
6887 match st {
6888 Stmt::Return(None) => *st = Stmt::Return(Some(val.clone())),
6889 Stmt::Block(v) => rewrite_bare_returns(v, val),
6890 Stmt::If { then_stmt, else_stmt, .. } => {
6891 rewrite_bare_returns_one(then_stmt, val);
6892 if let Some(e) = else_stmt {
6893 rewrite_bare_returns_one(e, val);
6894 }
6895 }
6896 Stmt::While { body, .. }
6897 | Stmt::DoWhile { body, .. }
6898 | Stmt::ForEach { body, .. }
6899 | Stmt::Synchronized { body, .. }
6900 | Stmt::Labeled { body, .. } => rewrite_bare_returns_one(body, val),
6901 Stmt::For { init, body, .. } => {
6902 rewrite_bare_returns(init, val);
6903 rewrite_bare_returns_one(body, val);
6904 }
6905 Stmt::Switch { cases, default, .. } => {
6906 for c in cases.iter_mut() {
6907 rewrite_bare_returns(&mut c.body, val);
6908 }
6909 if let Some(d) = default {
6910 rewrite_bare_returns_one(d, val);
6911 }
6912 }
6913 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
6914 rewrite_bare_returns_one(body, val);
6915 for c in catches.iter_mut() {
6916 rewrite_bare_returns_one(&mut c.body, val);
6917 }
6918 if let Some(f) = finally {
6919 rewrite_bare_returns_one(f, val);
6920 }
6921 }
6922 _ => {}
6923 }
6924}
6925
6926fn collect_def_vars(stmts: &[Stmt], out: &mut jdc_core::FxHashSet<u32>) {
6927 for st in stmts {
6928 match st {
6929 Stmt::LocalDef { var, .. } => {
6930 out.insert(*var);
6931 }
6932 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
6933 if let Expr::Local { var, .. } = &**target {
6934 out.insert(*var);
6935 }
6936 }
6937 Stmt::Block(v) => collect_def_vars(v, out),
6938 Stmt::If { then_stmt, else_stmt, .. } => {
6939 collect_def_vars(std::slice::from_ref(then_stmt.as_ref()), out);
6940 if let Some(e) = else_stmt {
6941 collect_def_vars(std::slice::from_ref(e.as_ref()), out);
6942 }
6943 }
6944 Stmt::While { body, .. }
6945 | Stmt::DoWhile { body, .. }
6946 | Stmt::Synchronized { body, .. }
6947 | Stmt::Labeled { body, .. } => {
6948 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
6949 }
6950 Stmt::ForEach { var, body, .. } => {
6951 out.insert(*var);
6952 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
6953 }
6954 Stmt::For { init, body, .. } => {
6955 collect_def_vars(init, out);
6956 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
6957 }
6958 Stmt::Switch { cases, default, .. } => {
6959 for c in cases {
6960 collect_def_vars(&c.body, out);
6961 }
6962 if let Some(d) = default {
6963 collect_def_vars(std::slice::from_ref(d.as_ref()), out);
6964 }
6965 }
6966 Stmt::Try { body, catches, finally }
6967 | Stmt::TryWithResources { body, catches, finally, .. } => {
6968 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
6969 for c in catches {
6970 out.insert(c.var);
6971 collect_def_vars(std::slice::from_ref(c.body.as_ref()), out);
6972 }
6973 if let Some(f) = finally {
6974 collect_def_vars(std::slice::from_ref(f.as_ref()), out);
6975 }
6976 }
6977 _ => {}
6978 }
6979 }
6980}
6981
6982fn extraction_shape_ok(stmts: &[Stmt]) -> bool {
6985 stmts.iter().all(|st| match st {
6986 Stmt::Block(v) => extraction_shape_ok(v),
6987 Stmt::LocalDef { .. }
6988 | Stmt::ExprStmt(_)
6989 | Stmt::Return(_)
6990 | Stmt::Throw(_)
6991 | Stmt::Assert { .. }
6992 | Stmt::Break(_)
6993 | Stmt::Continue(_) => true,
6994 Stmt::If { then_stmt, else_stmt, .. } => {
6995 ok_one(then_stmt)
6996 && else_stmt.as_deref().map(ok_one).unwrap_or(true)
6997 }
6998 Stmt::While { body, .. }
6999 | Stmt::DoWhile { body, .. }
7000 | Stmt::ForEach { body, .. }
7001 | Stmt::Synchronized { body, .. } => ok_one(body),
7002 Stmt::Labeled { body, .. } => ok_one(body),
7003 Stmt::For { init, body, .. } => extraction_shape_ok(init) && ok_one(body),
7004 Stmt::Switch { cases, default, .. } => {
7005 cases.iter().all(|c| extraction_shape_ok(&c.body))
7006 && default.as_deref().map(ok_one).unwrap_or(true)
7007 }
7008 Stmt::Try { body, catches, finally }
7009 | Stmt::TryWithResources { body, catches, finally, .. } => {
7010 ok_one(body)
7011 && catches.iter().all(|c| ok_one(&c.body))
7012 && finally.as_deref().map(ok_one).unwrap_or(true)
7013 }
7014 _ => false,
7017 })
7018}
7019
7020fn ok_one(st: &Stmt) -> bool {
7021 extraction_shape_ok(std::slice::from_ref(st))
7022}
7023
7024fn static_safe(stmts: &[Stmt], this_id: Option<u32>) -> bool {
7025 let mut ok = true;
7026 for st in stmts {
7027 let mut c = st.clone();
7028 walk_stmt_exprs(&mut c, &mut |e| {
7029 deep_rewrite(e, &mut |x| {
7030 match x {
7031 Expr::This => ok = false,
7032 Expr::Local { var, .. } => {
7038 if Some(*var) == this_id {
7039 ok = false;
7040 }
7041 }
7042 Expr::Field { owner: None, is_static: false, .. } => ok = false,
7044 Expr::Method { owner: None, is_static: false, .. } => ok = false,
7045 _ => {}
7046 }
7047 });
7048 });
7049 }
7050 ok
7051}
7052
7053pub fn hoist_branch_delegations(body: &mut Stmt, vt: &VarTable) {
7066 let Stmt::Block(stmts) = body else { return };
7067 if stmts.first().is_some_and(is_bare_ctor_call) {
7068 return; }
7070 let mut dels: Vec<Expr> = Vec::new();
7071 for st in stmts.iter() {
7072 let mut c = st.clone();
7073 walk_stmt_exprs(&mut c, &mut |e| {
7074 if is_delegation_expr(e) {
7075 dels.push(e.clone());
7076 }
7077 });
7078 }
7079 if dels.is_empty() {
7080 return;
7081 }
7082 if !dels.iter().all(|d| *d == dels[0]) {
7083 return;
7084 }
7085 let mut bad = false;
7086 visit_exprs(&dels[0], &mut |x| {
7087 if let Expr::Local { var, .. } = x {
7088 if (*var as usize) >= vt.vars.len() || !vt.vars[*var as usize].is_param {
7089 bad = true;
7090 }
7091 }
7092 });
7093 if bad {
7094 return;
7095 }
7096 let del = dels[0].clone();
7097 strip_delegations_deep(stmts);
7098 stmts.insert(0, Stmt::ExprStmt(del));
7099}
7100
7101pub fn extract_branched_delegation_helper(
7135 body: &mut Stmt,
7136 vt: &VarTable,
7137 pool: &DexPool,
7138 class: &crate::PoolClass,
7139 desc_str: &str,
7140) {
7141 if class.is_enum() {
7142 return;
7143 }
7144 if class.nesting.enclosing_class.is_some() && !crate::ctx::nested_is_static(pool, class) {
7149 return;
7150 }
7151 let Stmt::Block(stmts) = body else { return };
7152 let mut dels: Vec<Expr> = Vec::new();
7153 for st in stmts.iter() {
7154 let mut c = st.clone();
7155 walk_stmt_exprs(&mut c, &mut |e| {
7156 if is_delegation_expr(e) {
7157 dels.push(e.clone());
7158 }
7159 });
7160 }
7161 if dels.is_empty() {
7162 return;
7163 }
7164 if dels.len() == 1 && stmts.first().is_some_and(is_bare_ctor_call) {
7165 return; }
7167 let template = dels[0].clone();
7168 let Expr::Method { args: t_args, .. } = &template else {
7169 return;
7170 };
7171 enum Mode {
7175 Single(u32),
7176 Position(usize),
7177 }
7178 let mode = if dels.iter().all(|d| *d == dels[0]) {
7179 let mut carrier: Option<u32> = None;
7180 let mut reads = 0usize;
7181 let mut multi = false;
7182 for a in t_args.iter() {
7183 let c = a.clone();
7184 visit_exprs(&c, &mut |x| {
7185 if let Expr::Local { var, .. } = x {
7186 if (*var as usize) < vt.vars.len() && !vt.vars[*var as usize].is_param {
7187 reads += 1;
7188 match carrier {
7189 Some(v) if v == *var => {}
7190 Some(_) => multi = true,
7191 None => carrier = Some(*var),
7192 }
7193 }
7194 }
7195 });
7196 }
7197 match carrier {
7198 Some(cv) if !multi && reads == 1 => Mode::Single(cv),
7199 _ => return,
7200 }
7201 } else {
7202 let arity = t_args.len();
7203 if dels
7207 .iter()
7208 .any(|d| !matches!(d, Expr::Method { args, .. } if args.len() == arity))
7209 {
7210 return;
7211 }
7212 let mut diff: Vec<usize> = Vec::new();
7213 for j in 0..arity {
7214 if dels.iter().any(|d| match d {
7215 Expr::Method { args, .. } => args.get(j) != t_args.get(j),
7216 _ => true,
7217 }) {
7218 diff.push(j);
7219 }
7220 }
7221 if diff.len() != 1 {
7222 return;
7223 }
7224 let k = diff[0];
7225 let mut base0 = dels[0].clone();
7229 if let Expr::Method { args, .. } = &mut base0 {
7230 args[k] = Expr::Const(ConstVal::Null);
7231 }
7232 for d in dels.iter() {
7233 let mut c = d.clone();
7234 if let Expr::Method { args, .. } = &mut c {
7235 args[k] = Expr::Const(ConstVal::Null);
7236 }
7237 if c != base0 {
7238 return;
7239 }
7240 }
7241 let mut carrier_ty: Option<JavaType> = None;
7244 for d in dels.iter() {
7245 let Expr::Method { args, .. } = d else {
7246 return;
7247 };
7248 let Expr::Local { var, .. } = &args[k] else {
7249 return;
7250 };
7251 if (*var as usize) >= vt.vars.len() || vt.vars[*var as usize].is_param {
7252 return;
7253 }
7254 let ty = vt.vars[*var as usize].ty.erased();
7255 match &carrier_ty {
7256 Some(t) if *t == ty => {}
7257 Some(_) => return,
7258 None => carrier_ty = Some(ty),
7259 }
7260 for (j, a) in args.iter().enumerate() {
7266 if j == k {
7267 continue;
7268 }
7269 let mut bad = false;
7270 visit_exprs(a, &mut |x| {
7271 if let Expr::Local { var: v2, .. } = x {
7272 if (*v2 as usize) >= vt.vars.len() || !vt.vars[*v2 as usize].is_param {
7273 bad = true;
7274 }
7275 }
7276 });
7277 if bad {
7278 return;
7279 }
7280 }
7281 }
7282 Mode::Position(k)
7283 };
7284 let l = stmts.iter().rposition(is_bare_ctor_call);
7285 if l == Some(0) {
7286 return;
7287 }
7288 let (mut extracted, mut tail): (Vec<Stmt>, Vec<Stmt>) = match l {
7289 Some(l) => (stmts[..l].to_vec(), stmts[l + 1..].to_vec()),
7290 None => (stmts.clone(), Vec::new()),
7291 };
7292 if !extraction_shape_ok(&extracted) {
7293 return;
7294 }
7295 let mut site_tails: Vec<Vec<Stmt>> = Vec::new();
7302 {
7303 let mut probe = extracted.clone();
7304 site_tails_walk(&mut probe, &mut site_tails, false);
7305 }
7306 let hoist = !site_tails.is_empty() && {
7307 let n0 = normalize_tail(&site_tails[0]);
7308 site_tails.iter().all(|t| normalize_tail(t) == n0)
7309 && (l.is_none() || normalize_tail(&tail) == n0)
7310 };
7311 if hoist {
7312 site_tails_walk(&mut extracted, &mut Vec::new(), true);
7313 if l.is_none() {
7314 tail = normalize_tail(&site_tails[0]);
7315 }
7316 }
7317 let primary_del: Expr = match l {
7320 Some(l) => match &stmts[l] {
7321 Stmt::ExprStmt(e) => e.clone(),
7322 _ => return,
7323 },
7324 None => dels[0].clone(),
7325 };
7326 let ret = match mode {
7327 Mode::Single(cv) => vt.var(cv).ty.erased(),
7328 Mode::Position(k) => match &primary_del {
7329 Expr::Method { args, .. } => match args.get(k) {
7330 Some(Expr::Local { var, .. }) => vt.var(*var).ty.erased(),
7331 _ => return,
7332 },
7333 _ => return,
7334 },
7335 };
7336 match mode {
7337 Mode::Single(cv) => {
7338 strip_delegations_deep(&mut extracted);
7339 let cv_local = Expr::Local {
7340 var: cv,
7341 ty: vt.var(cv).ty.clone(),
7342 };
7343 rewrite_bare_returns(&mut extracted, &cv_local);
7344 if !always_returns(&extracted) {
7345 extracted.push(Stmt::Return(Some(cv_local)));
7346 }
7347 }
7348 Mode::Position(k) => {
7349 fill_returns_by_position(&mut extracted, k);
7353 let fallback = match &primary_del {
7354 Expr::Method { args, .. } => args[k].clone(),
7355 _ => return,
7356 };
7357 rewrite_bare_returns(&mut extracted, &fallback);
7358 if !always_returns(&extracted) {
7359 extracted.push(Stmt::Return(Some(fallback)));
7360 }
7361 }
7362 }
7363 let this_id = vt
7364 .vars
7365 .iter()
7366 .find(|v| v.is_param && v.name == "this")
7367 .map(|v| v.id);
7368 if !static_safe(&extracted, this_id) {
7369 return;
7370 }
7371 let mut defs: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
7372 collect_def_vars(&extracted, &mut defs);
7373 if defs.iter().any(|v| var_occurs_in(&tail, *v)) {
7374 return;
7375 }
7376 let params: Vec<(u32, JavaType, String)> = vt
7377 .vars
7378 .iter()
7379 .filter(|v| v.is_param && v.name != "this")
7380 .map(|v| (v.id, v.ty.erased(), v.name.clone()))
7381 .collect();
7382 let name = helper_name(&class.name, desc_str);
7383 let call = Expr::Method {
7384 owner: None,
7385 cls: std::sync::Arc::from(class.name.as_str()),
7386 name: std::sync::Arc::from(name.as_str()),
7387 desc: std::sync::Arc::new(jdc_core::types::MethodDescriptor {
7388 ret: ret.clone(),
7389 args: params.iter().map(|p| p.1.clone()).collect(),
7390 }),
7391 args: params
7392 .iter()
7393 .map(|(id, ty, _)| Expr::Local {
7394 var: *id,
7395 ty: TypeRef::J(ty.clone()),
7396 })
7397 .collect(),
7398 is_static: true,
7399 is_interface: false,
7400 is_special: false,
7401 is_super: false,
7402 is_dynamic: false,
7403 type_args: Vec::new(),
7404 };
7405 let mut new_del = primary_del.clone();
7406 match mode {
7407 Mode::Single(cv) => {
7408 deep_rewrite(&mut new_del, &mut |x| {
7409 if let Expr::Local { var, .. } = x {
7410 if *var == cv {
7411 *x = call.clone();
7412 }
7413 }
7414 });
7415 }
7416 Mode::Position(k) => {
7417 let Expr::Method { args, .. } = &mut new_del else {
7418 return;
7419 };
7420 if k >= args.len() {
7421 return;
7422 }
7423 args[k] = call.clone();
7424 }
7425 }
7426 {
7427 let mut m = ctor_helpers().lock().unwrap_or_else(|e| e.into_inner());
7428 let v = m.entry(class.name.clone()).or_default();
7429 if !v.iter().any(|h| h.name == name) {
7430 let mut hbody = Stmt::Block(extracted);
7434 invert_empty_thens(&mut hbody);
7435 v.push(CtorHelper {
7436 name,
7437 ret,
7438 params: params.into_iter().map(|(_, ty, nm)| (ty, nm)).collect(),
7439 body: hbody,
7440 vt: vt.clone(),
7441 });
7442 } else {
7443 return; }
7445 }
7446 *stmts = std::iter::once(Stmt::ExprStmt(new_del)).chain(tail).collect();
7447}
7448
7449fn fill_returns_by_position(stmts: &mut Vec<Stmt>, k: usize) {
7456 let mut i = 0;
7457 while i < stmts.len() {
7458 if is_bare_ctor_call(&stmts[i]) {
7459 let karg = match &stmts[i] {
7460 Stmt::ExprStmt(Expr::Method { args, .. }) => args.get(k).cloned(),
7461 _ => None,
7462 };
7463 if let Some(v) = karg {
7464 let mut j = i + 1;
7465 let mut found = None;
7466 while j < stmts.len() {
7467 if matches!(stmts[j], Stmt::Return(None)) {
7468 found = Some(j);
7469 break;
7470 }
7471 if is_bare_ctor_call(&stmts[j]) {
7472 break; }
7474 j += 1;
7475 }
7476 match found {
7477 Some(j) => {
7478 stmts[j] = Stmt::Return(Some(v));
7479 stmts.remove(i);
7480 continue;
7481 }
7482 None => {
7483 stmts[i] = Stmt::Return(Some(v));
7484 stmts.truncate(i + 1);
7485 break;
7486 }
7487 }
7488 }
7489 }
7490 fill_returns_one(&mut stmts[i], k);
7491 i += 1;
7492 }
7493}
7494
7495fn fill_returns_one(st: &mut Stmt, k: usize) {
7496 match st {
7497 Stmt::Block(v) => fill_returns_by_position(v, k),
7498 Stmt::If { then_stmt, else_stmt, .. } => {
7499 fill_returns_stmt(then_stmt, k);
7500 if let Some(e) = else_stmt {
7501 fill_returns_stmt(e, k);
7502 }
7503 }
7504 Stmt::While { body, .. }
7505 | Stmt::DoWhile { body, .. }
7506 | Stmt::ForEach { body, .. }
7507 | Stmt::Synchronized { body, .. }
7508 | Stmt::Labeled { body, .. } => fill_returns_stmt(body, k),
7509 Stmt::For { init, body, .. } => {
7510 fill_returns_by_position(init, k);
7511 fill_returns_stmt(body, k);
7512 }
7513 Stmt::Switch { cases, default, .. } => {
7514 for c in cases.iter_mut() {
7515 fill_returns_by_position(&mut c.body, k);
7516 }
7517 if let Some(d) = default {
7518 fill_returns_stmt(d, k);
7519 }
7520 }
7521 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
7522 fill_returns_stmt(body, k);
7523 for c in catches.iter_mut() {
7524 fill_returns_stmt(&mut c.body, k);
7525 }
7526 if let Some(f) = finally {
7527 fill_returns_stmt(f, k);
7528 }
7529 }
7530 _ => {}
7531 }
7532}
7533
7534fn fill_returns_stmt(st: &mut Stmt, k: usize) {
7535 if is_bare_ctor_call(st) {
7536 if let Stmt::ExprStmt(Expr::Method { args, .. }) = st {
7539 if let Some(v) = args.get(k).cloned() {
7540 *st = Stmt::Return(Some(v));
7541 return;
7542 }
7543 }
7544 }
7545 fill_returns_one(st, k);
7546}
7547
7548fn normalize_tail(t: &[Stmt]) -> Vec<Stmt> {
7549 let mut v = t.to_vec();
7550 while matches!(v.last(), Some(Stmt::Return(None))) {
7551 v.pop();
7552 }
7553 v
7554}
7555
7556fn site_tails_walk(stmts: &mut Vec<Stmt>, out: &mut Vec<Vec<Stmt>>, carve: bool) {
7561 let mut i = 0;
7562 while i < stmts.len() {
7563 if is_bare_ctor_call(&stmts[i]) {
7564 let mut j = i + 1;
7565 let mut end = stmts.len();
7566 while j < stmts.len() {
7567 if matches!(stmts[j], Stmt::Return(None)) {
7568 end = j;
7569 break;
7570 }
7571 if is_bare_ctor_call(&stmts[j]) {
7572 end = j;
7573 break;
7574 }
7575 j += 1;
7576 }
7577 out.push(stmts[i + 1..end].to_vec());
7578 if carve {
7579 stmts.splice(i + 1..end, []);
7580 }
7581 i += 1;
7582 continue;
7583 }
7584 site_tails_one(&mut stmts[i], out, carve);
7585 i += 1;
7586 }
7587}
7588
7589fn site_tails_one(st: &mut Stmt, out: &mut Vec<Vec<Stmt>>, carve: bool) {
7590 if is_bare_ctor_call(st) {
7591 out.push(Vec::new());
7592 return;
7593 }
7594 match st {
7595 Stmt::Block(v) => site_tails_walk(v, out, carve),
7596 Stmt::If { then_stmt, else_stmt, .. } => {
7597 site_tails_one(then_stmt, out, carve);
7598 if let Some(e) = else_stmt {
7599 site_tails_one(e, out, carve);
7600 }
7601 }
7602 Stmt::While { body, .. }
7603 | Stmt::DoWhile { body, .. }
7604 | Stmt::ForEach { body, .. }
7605 | Stmt::Synchronized { body, .. }
7606 | Stmt::Labeled { body, .. } => site_tails_one(body, out, carve),
7607 Stmt::For { init, body, .. } => {
7608 site_tails_walk(init, out, carve);
7609 site_tails_one(body, out, carve);
7610 }
7611 Stmt::Switch { cases, default, .. } => {
7612 for c in cases.iter_mut() {
7613 site_tails_walk(&mut c.body, out, carve);
7614 }
7615 if let Some(d) = default {
7616 site_tails_one(d, out, carve);
7617 }
7618 }
7619 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
7620 site_tails_one(body, out, carve);
7621 for c in catches.iter_mut() {
7622 site_tails_one(&mut c.body, out, carve);
7623 }
7624 if let Some(f) = finally {
7625 site_tails_one(f, out, carve);
7626 }
7627 }
7628 _ => {}
7629 }
7630}
7631
7632fn always_returns(stmts: &[Stmt]) -> bool {
7638 let Some(last) = stmts
7639 .iter()
7640 .rev()
7641 .find(|s| !matches!(s, Stmt::Block(v) if v.is_empty()))
7642 else {
7643 return false;
7644 };
7645 match last {
7646 Stmt::Return(Some(_)) | Stmt::Throw(_) => true,
7647 Stmt::Block(v) => always_returns(v),
7648 Stmt::If { then_stmt, else_stmt, .. } => match else_stmt {
7649 Some(e) => always_returns_one(then_stmt) && always_returns_one(e),
7650 None => false,
7651 },
7652 Stmt::Labeled { body, .. } | Stmt::Synchronized { body, .. } => {
7653 always_returns_one(body)
7654 }
7655 Stmt::Switch { cases, default, .. } => {
7656 default.as_ref().is_some_and(|d| always_returns_one(d))
7657 && cases.iter().all(|c| always_returns(&c.body))
7658 }
7659 Stmt::Try { body, catches, finally }
7660 | Stmt::TryWithResources { body, catches, finally, .. } => {
7661 finally.as_ref().is_some_and(|f| always_returns_one(f))
7662 || (always_returns_one(body)
7663 && catches.iter().all(|c| always_returns_one(&c.body)))
7664 }
7665 _ => false,
7666 }
7667}
7668
7669fn always_returns_one(st: &Stmt) -> bool {
7670 always_returns(std::slice::from_ref(st))
7671}
7672
7673fn inline_ctor_arg_defs(
7674 call: &mut Stmt,
7675 prefix: &[&Stmt],
7676 vt: &VarTable,
7677 consumed: &mut Vec<(u32, usize)>,
7678) -> bool {
7679 let Stmt::ExprStmt(cexpr) = call else {
7680 return false;
7681 };
7682 fn subst(e: &mut Expr, v: u32, val: &Option<Expr>) -> bool {
7685 let mut ok = true;
7686 deep_rewrite(e, &mut |x| {
7687 if let Expr::Local { var, .. } = x {
7688 if *var == v {
7689 match val {
7690 Some(e2) => *x = e2.clone(),
7691 None => ok = false,
7692 }
7693 }
7694 }
7695 });
7696 ok
7697 }
7698 let mut seen: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
7699 for _ in 0..32 {
7700 let mut need: Option<u32> = None;
7701 visit_exprs(cexpr, &mut |x| {
7702 if need.is_none() {
7703 if let Expr::Local { var, .. } = x {
7704 if (*var as usize) < vt.vars.len() && !vt.vars[*var as usize].is_param {
7705 need = Some(*var);
7706 }
7707 }
7708 }
7709 });
7710 let Some(v) = need else { break };
7711 if !seen.insert(v) {
7712 return false; }
7714 let mut val: Option<Expr> = None;
7726 let mut ok = true;
7727 for (si, st) in prefix.iter().enumerate() {
7728 match st {
7729 Stmt::LocalDef { var, init: None, .. } if *var == v => {}
7733 Stmt::LocalDef { var, init, .. } if *var == v => {
7734 let Some(e) = init else { ok = false; break };
7735 let mut e2 = (*e).clone();
7736 if !subst(&mut e2, v, &val) {
7737 ok = false;
7738 break;
7739 }
7740 val = Some(e2);
7741 consumed.push((v, si));
7742 }
7743 Stmt::ExprStmt(Expr::Assign { target, op, .. })
7744 if matches!(&**target, Expr::Local { var: tv, .. } if *tv == v) =>
7745 {
7746 if !matches!(op, AssignOp::Plain) {
7747 ok = false; break;
7749 }
7750 let Stmt::ExprStmt(Expr::Assign { value, .. }) = st else {
7751 unreachable!()
7752 };
7753 let mut e2 = value.as_ref().clone();
7754 if !subst(&mut e2, v, &val) {
7755 ok = false;
7756 break;
7757 }
7758 val = Some(e2);
7759 consumed.push((v, si));
7760 }
7761 _ => {
7762 let mut touched = false;
7763 visit_all_exprs(st, &mut |x| {
7764 if let Expr::Local { var, .. } = x {
7765 if *var == v {
7766 touched = true;
7767 }
7768 }
7769 });
7770 if touched {
7771 ok = false;
7772 break;
7773 }
7774 }
7775 }
7776 }
7777 if !ok {
7778 return false;
7779 }
7780 let Some(de) = val else { return false };
7781 if contains_this_access(&de) {
7782 return false;
7783 }
7784 let mut de_nodes = 0usize;
7786 visit_exprs(&de, &mut |_| de_nodes += 1);
7787 if de_nodes > 256 {
7788 return false;
7789 }
7790 deep_rewrite(cexpr, &mut |x| {
7791 if let Expr::Local { var, .. } = x {
7792 if *var == v {
7793 *x = de.clone();
7794 }
7795 }
7796 });
7797 let mut nodes = 0usize;
7802 visit_exprs(cexpr, &mut |_| nodes += 1);
7803 if nodes > 2_048 {
7804 return false;
7805 }
7806 }
7807 true
7808}
7809
7810fn fold_sb_chain(stmts: &mut Vec<Stmt>, pos: usize) -> usize {
7823 let mut sbv: Option<u32> = None;
7825 let mut def_i: Option<usize> = None;
7826 let mut def_init: Option<Expr> = None;
7827 for (i, st) in stmts[..pos].iter().enumerate() {
7828 let cand: Option<(u32, &Expr)> = match st {
7829 Stmt::LocalDef {
7830 var,
7831 init: Some(e),
7832 ..
7833 } => Some((*var, e)),
7834 Stmt::ExprStmt(Expr::Assign {
7835 target,
7836 value,
7837 op: AssignOp::Plain,
7838 ..
7839 }) => match &**target {
7840 Expr::Local { var, .. } => Some((*var, value.as_ref())),
7841 _ => None,
7842 },
7843 _ => None,
7844 };
7845 if let Some((v, init)) = cand {
7846 if let Expr::New { cls, .. } = init {
7847 if cls.as_ref() == "java/lang/StringBuilder" {
7848 sbv = Some(v);
7849 def_i = Some(i);
7850 def_init = Some(init.clone());
7851 break;
7852 }
7853 }
7854 }
7855 }
7856 let (Some(sbv), Some(def_i), Some(init)) = (sbv, def_i, def_init) else {
7857 return pos;
7858 };
7859 if contains_this_access(&init) {
7860 return pos;
7861 }
7862 let mut appends: Vec<usize> = Vec::new();
7864 for (i, st) in stmts[..pos].iter().enumerate() {
7865 if let Stmt::ExprStmt(Expr::Method {
7866 owner: Some(o),
7867 cls,
7868 name,
7869 ..
7870 }) = st
7871 {
7872 if matches!(&**o, Expr::Local { var, .. } if *var == sbv)
7873 && cls.as_ref() == "java/lang/StringBuilder"
7874 && &**name == "append"
7875 {
7876 appends.push(i);
7877 }
7878 }
7879 }
7880 let mut post_read = false;
7882 for st in stmts.iter().skip(pos + 1) {
7883 visit_all_exprs(st, &mut |e| {
7884 if let Expr::Local { var, .. } = e {
7885 if *var == sbv {
7886 post_read = true;
7887 }
7888 }
7889 });
7890 }
7891 if post_read {
7892 return pos;
7893 }
7894 for (i, st) in stmts[..pos].iter().enumerate() {
7897 if i == def_i || appends.contains(&i) {
7898 if let Stmt::ExprStmt(Expr::Method { args, .. }) = st {
7900 if args.iter().any(contains_this_access) {
7901 return pos;
7902 }
7903 }
7904 continue;
7905 }
7906 let mut leak = false;
7907 visit_all_exprs(st, &mut |e| {
7908 if let Expr::Local { var, .. } = e {
7909 if *var == sbv {
7910 leak = true;
7911 }
7912 }
7913 });
7914 if leak {
7915 return pos;
7916 }
7917 }
7918 let mut local_reads = 0usize;
7920 let mut tostring_reads = 0usize;
7921 visit_all_exprs(&stmts[pos], &mut |e| match e {
7922 Expr::Local { var, .. } if *var == sbv => local_reads += 1,
7923 Expr::Method {
7924 owner: Some(o),
7925 name,
7926 args,
7927 ..
7928 } if args.is_empty()
7929 && &**name == "toString"
7930 && matches!(&**o, Expr::Local { var, .. } if *var == sbv) =>
7931 {
7932 tostring_reads += 1;
7933 }
7934 _ => {}
7935 });
7936 if local_reads != 1 || tostring_reads != 1 {
7937 return pos;
7938 }
7939 let mut chain = init;
7941 for ai in &appends {
7942 if let Stmt::ExprStmt(Expr::Method {
7943 owner: _,
7944 cls,
7945 name,
7946 desc,
7947 args,
7948 is_static,
7949 is_interface,
7950 is_special,
7951 is_super,
7952 is_dynamic,
7953 type_args,
7954 ..
7955 }) = &stmts[*ai]
7956 {
7957 chain = Expr::Method {
7958 owner: Some(Box::new(chain)),
7959 cls: cls.clone(),
7960 name: name.clone(),
7961 desc: desc.clone(),
7962 args: args.clone(),
7963 is_static: *is_static,
7964 is_interface: *is_interface,
7965 is_special: *is_special,
7966 is_super: *is_super,
7967 is_dynamic: *is_dynamic,
7968 type_args: type_args.clone(),
7969 };
7970 }
7971 }
7972 let mut replaced = false;
7973 walk_stmt_exprs(&mut stmts[pos], &mut |e| {
7974 deep_rewrite(e, &mut |x| {
7975 if let Expr::Method {
7976 owner: Some(o),
7977 name,
7978 args,
7979 ..
7980 } = x
7981 {
7982 if args.is_empty()
7983 && &**name == "toString"
7984 && matches!(&**o, Expr::Local { var, .. } if *var == sbv)
7985 {
7986 **o = chain.clone();
7987 replaced = true;
7988 }
7989 }
7990 });
7991 });
7992 if !replaced {
7993 return pos; }
7995 let mut removed: Vec<usize> = appends.clone();
7997 removed.push(def_i);
7998 removed.sort_unstable();
7999 removed.dedup();
8000 for i in removed.iter().rev() {
8001 stmts.remove(*i);
8002 }
8003 pos - removed.len()
8004}
8005
8006pub fn fix_ctor_super_first(body: &mut Stmt, vt: &VarTable) {
8007 let Stmt::Block(stmts) = body else { return };
8008 if stmts.is_empty() || is_bare_ctor_call(stmts.first().unwrap()) {
8009 return;
8010 }
8011 if let Some(pos0) = stmts.iter().position(is_bare_ctor_call) {
8013 let pos = fold_sb_chain(stmts, pos0);
8014 let mut call = stmts[pos].clone();
8015 let prefix: Vec<&Stmt> = stmts[..pos].iter().collect();
8016 let mut consumed: Vec<(u32, usize)> = Vec::new();
8017 if !inline_ctor_arg_defs(&mut call, &prefix, vt, &mut consumed) {
8018 return;
8019 }
8020 stmts.remove(pos);
8021 let mut drop_idx: Vec<usize> = consumed
8028 .iter()
8029 .filter(|(v, _)| !var_occurs_in(&stmts[pos..], *v))
8030 .map(|(_, k)| *k)
8031 .collect();
8032 drop_idx.sort_unstable();
8033 drop_idx.dedup();
8034 for k in drop_idx.iter().rev() {
8035 stmts.remove(*k);
8036 }
8037 stmts.insert(0, call);
8038 return;
8039 }
8040 let mut found: Option<(usize, usize)> = None;
8050 for (i, st) in stmts.iter().enumerate() {
8051 if let Stmt::Block(inner) = st {
8052 if let Some(p) = inner.iter().position(is_bare_ctor_call) {
8053 if inner[..p]
8054 .iter()
8055 .all(|s| matches!(s, Stmt::LocalDef { .. } | Stmt::ExprStmt(_)))
8056 {
8057 found = Some((i, p));
8058 break;
8059 }
8060 }
8061 }
8062 }
8063 if let Some((i, p)) = found {
8064 let mut call = match &stmts[i] {
8065 Stmt::Block(inner) => inner[p].clone(),
8066 _ => unreachable!(),
8067 };
8068 let mut prefix: Vec<&Stmt> = stmts[..i].iter().collect();
8069 if let Stmt::Block(inner) = &stmts[i] {
8070 prefix.extend(inner[..p].iter());
8071 }
8072 let mut consumed: Vec<(u32, usize)> = Vec::new();
8073 if !inline_ctor_arg_defs(&mut call, &prefix, vt, &mut consumed) {
8074 return;
8075 }
8076 let mut ks: Vec<usize> = Vec::new();
8083 {
8084 let inner_tail: &[Stmt] = match &stmts[i] {
8085 Stmt::Block(inner) if p < inner.len() => &inner[p + 1..],
8086 _ => &[],
8087 };
8088 for (v, k) in &consumed {
8089 if !var_occurs_in(inner_tail, *v) && !var_occurs_in(&stmts[i + 1..], *v) {
8090 ks.push(*k);
8091 }
8092 }
8093 }
8094 ks.sort_unstable();
8095 ks.dedup();
8096 if let Stmt::Block(inner) = &mut stmts[i] {
8097 inner.remove(p);
8098 for k in ks.iter().rev().filter(|k| **k >= i) {
8099 inner.remove(k - i);
8100 }
8101 }
8102 for k in ks.iter().rev().filter(|k| **k < i) {
8103 stmts.remove(*k);
8104 }
8105 stmts.insert(0, call);
8106 }
8107}
8108
8109fn is_delegation_expr(e: &Expr) -> bool {
8118 matches!(
8119 e,
8120 Expr::Method { name, is_special: true, owner, .. }
8121 if &**name == "<init>"
8122 && (owner.is_none() || matches!(owner.as_deref(), Some(Expr::This)))
8123 )
8124}
8125
8126pub(crate) fn is_bare_ctor_call(s: &Stmt) -> bool {
8127 matches!(s, Stmt::ExprStmt(e) if is_delegation_expr(e))
8128}
8129
8130pub(crate) fn local_is_written(body: &Stmt, var: u32) -> bool {
8134 let mut hit = false;
8135 let mut c = body.clone();
8136 walk_stmt_exprs(&mut c, &mut |e| {
8137 if !hit {
8138 deep_rewrite(e, &mut |x| {
8139 if hit {
8140 return;
8141 }
8142 match x {
8143 Expr::Assign { target, .. }
8144 if matches!(&**target, Expr::Local { var: v, .. } if *v == var) =>
8145 {
8146 hit = true;
8147 }
8148 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. }
8149 if matches!(&**e, Expr::Local { var: v, .. } if *v == var) =>
8150 {
8151 hit = true;
8152 }
8153 _ => {}
8154 }
8155 });
8156 }
8157 });
8158 hit
8159}
8160
8161pub(crate) fn rewrite_inner_ctor_outer_param(
8171 body: &mut Stmt,
8172 param0: u32,
8173 outer_display: &str,
8174 outer_ty: &TypeRef,
8175 eligible: &[String],
8176) {
8177 walk_stmt_exprs(body, &mut |e| {
8181 if let Expr::Method { name, cls, args, .. } = e {
8182 if &**name == "<init>"
8183 && eligible.iter().any(|c| c.as_str() == cls.as_ref())
8184 && args.first().is_some_and(|a| matches!(a, Expr::Local { var: v, .. } if *v == param0))
8185 {
8186 args.remove(0);
8187 }
8188 }
8189 });
8190 let text = format!("{outer_display}.this");
8192 let ty = outer_ty.clone();
8193 walk_stmt_exprs(body, &mut |e| {
8194 deep_rewrite_reads(e, &mut |x| {
8195 if let Expr::Local { var: v, .. } = x {
8196 if *v == param0 {
8197 *x = Expr::RawT(text.clone(), ty.clone());
8198 }
8199 }
8200 });
8201 });
8202}
8203
8204fn stmts_count_writes(stmts: &[Stmt], var: u32) -> usize {
8252 let mut n = 0usize;
8253 for s in stmts {
8254 let mut c = s.clone();
8255 walk_stmt_exprs(&mut c, &mut |e| {
8256 deep_rewrite(e, &mut |x| match x {
8257 Expr::Assign { target, .. }
8258 if matches!(&**target, Expr::Local { var: v, .. } if *v == var) =>
8259 {
8260 n += 1;
8261 }
8262 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. }
8263 if matches!(&**e, Expr::Local { var: v, .. } if *v == var) =>
8264 {
8265 n += 1;
8266 }
8267 _ => {}
8268 });
8269 });
8270 }
8271 n
8272}
8273
8274fn prefix_written_vars(stmts: &[Stmt]) -> jdc_core::FxHashSet<u32> {
8278 let mut set = jdc_core::FxHashSet::default();
8279 for s in stmts {
8280 let mut c = s.clone();
8281 walk_stmt_exprs(&mut c, &mut |e| {
8282 deep_rewrite(e, &mut |x| match x {
8283 Expr::Assign { target, .. } => {
8284 if let Expr::Local { var, .. } = &**target {
8285 set.insert(*var);
8286 }
8287 }
8288 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => {
8289 if let Expr::Local { var, .. } = &**e {
8290 set.insert(*var);
8291 }
8292 }
8293 _ => {}
8294 });
8295 });
8296 if let Stmt::LocalDef { var, .. } = s {
8297 set.insert(*var);
8298 }
8299 }
8300 set
8301}
8302
8303fn pre_this_safe(e: &Expr, prefix_vars: &jdc_core::FxHashSet<u32>) -> bool {
8307 match e {
8308 Expr::Local { var, .. } => !prefix_vars.contains(var),
8309 Expr::Const(_) => true,
8310 Expr::Cast { e, .. } => pre_this_safe(e, prefix_vars),
8311 Expr::Bin { l, r, .. } => {
8312 pre_this_safe(l, prefix_vars) && pre_this_safe(r, prefix_vars)
8313 }
8314 Expr::Cond { c, t, f } => {
8315 pre_this_safe(c, prefix_vars)
8316 && pre_this_safe(t, prefix_vars)
8317 && pre_this_safe(f, prefix_vars)
8318 }
8319 Expr::Field { owner, is_static, .. } => {
8320 *is_static
8321 && owner
8322 .as_ref()
8323 .is_none_or(|o| pre_this_safe(o, prefix_vars))
8324 }
8325 Expr::Method {
8326 owner,
8327 args,
8328 is_static,
8329 ..
8330 } => {
8331 args.iter().all(|a| pre_this_safe(a, prefix_vars))
8332 && match owner {
8333 Some(o) => pre_this_safe(o, prefix_vars),
8334 None => *is_static,
8335 }
8336 }
8337 Expr::New { args, .. } => args.iter().all(|a| pre_this_safe(a, prefix_vars)),
8345 Expr::NewArray { dims, init, .. } => {
8346 dims.iter().all(|d| pre_this_safe(d, prefix_vars))
8347 && init
8348 .as_ref()
8349 .map(|l| l.iter().all(|x| pre_this_safe(x, prefix_vars)))
8350 .unwrap_or(true)
8351 }
8352 Expr::Un { e, .. } | Expr::InstanceOf { e, .. } => pre_this_safe(e, prefix_vars),
8354 Expr::ArrayIndex { array, index } => {
8355 pre_this_safe(array, prefix_vars) && pre_this_safe(index, prefix_vars)
8356 }
8357 _ => false,
8358 }
8359}
8360
8361fn single_assign_to_v(s: &Stmt, v: u32) -> Option<Expr> {
8365 match s {
8366 Stmt::ExprStmt(Expr::Assign {
8367 target,
8368 op: AssignOp::Plain,
8369 value,
8370 }) if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v) => {
8371 Some((**value).clone())
8372 }
8373 Stmt::Block(xs) if xs.len() == 1 => single_assign_to_v(&xs[0], v),
8374 _ => None,
8375 }
8376}
8377
8378pub(crate) fn flatten_top_blocks(stmts: &mut Vec<Stmt>) {
8400 fn rec(s: Stmt, out: &mut Vec<Stmt>) {
8401 match s {
8402 Stmt::Block(inner) => {
8403 for x in inner {
8404 rec(x, out);
8405 }
8406 }
8407 other => out.push(other),
8408 }
8409 }
8410 if !stmts.iter().any(|s| matches!(s, Stmt::Block(_))) {
8411 return;
8412 }
8413 let mut out: Vec<Stmt> = Vec::with_capacity(stmts.len());
8414 for s in std::mem::take(stmts) {
8415 rec(s, &mut out);
8416 }
8417 *stmts = out;
8418}
8419
8420fn fold_default_arg_bridge(stmts: &mut Vec<Stmt>) -> bool {
8421 flatten_top_blocks(stmts);
8422 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
8423 return false;
8424 };
8425 if pos == 0 {
8426 return false;
8427 }
8428 let Stmt::ExprStmt(Expr::Method { args, .. }) = &stmts[pos] else {
8429 return false;
8430 };
8431 let mut arg_vars: Vec<u32> = Vec::new();
8432 let mut reads = 0usize;
8433 for a in args {
8434 let mut c = a.clone();
8435 deep_rewrite_reads(&mut c, &mut |x| {
8436 if let Expr::Local { var, .. } = x {
8437 reads += 1;
8438 if !arg_vars.contains(var) {
8439 arg_vars.push(*var);
8440 }
8441 }
8442 });
8443 }
8444 if arg_vars.is_empty() || reads != arg_vars.len() {
8445 return false; }
8447 for &v in &arg_vars {
8450 if stmts_read_var(&stmts[pos + 1..], v) != 0
8451 || stmts_count_writes(&stmts[pos + 1..], v) != 0
8452 {
8453 return false;
8454 }
8455 }
8456 let prefix_vars = prefix_written_vars(&stmts[..pos]);
8457 let mut repl: Vec<(u32, Expr)> = Vec::new();
8458 let mut drop: Vec<usize> = Vec::new();
8459 for &v in &arg_vars {
8460 let mut base: Option<(usize, Expr)> = None;
8462 let mut decl: Option<usize> = None;
8463 let mut condf: Option<(usize, Expr, Option<Expr>, Option<Expr>)> = None;
8464 let mut ok = true;
8465 for (i, s) in stmts[..pos].iter().enumerate() {
8466 match s {
8467 Stmt::LocalDef { var, init, .. } if *var == v => match init {
8468 None => {
8469 if decl.is_some() {
8470 ok = false;
8471 break;
8472 }
8473 decl = Some(i);
8474 }
8475 Some(e) => {
8476 if base.is_some() {
8477 ok = false;
8478 break;
8479 }
8480 base = Some((i, e.clone()));
8481 }
8482 },
8483 Stmt::ExprStmt(Expr::Assign {
8484 target,
8485 op: AssignOp::Plain,
8486 value,
8487 }) if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v) => {
8488 if base.is_some() {
8489 ok = false;
8490 break;
8491 }
8492 base = Some((i, (**value).clone()));
8493 }
8494 Stmt::If {
8495 cond,
8496 then_stmt,
8497 else_stmt,
8498 } => {
8499 let t = single_assign_to_v(then_stmt, v);
8500 let f = else_stmt.as_ref().and_then(|b| single_assign_to_v(b, v));
8501 let then_touches = t.is_some()
8502 || stmts_read_var(std::slice::from_ref(&**then_stmt), v) != 0
8503 || stmts_count_writes(std::slice::from_ref(&**then_stmt), v) != 0;
8504 let else_touches = else_stmt.as_ref().is_some_and(|b| {
8505 f.is_some()
8506 || stmts_read_var(std::slice::from_ref(b), v) != 0
8507 || stmts_count_writes(std::slice::from_ref(b), v) != 0
8508 });
8509 if !then_touches && !else_touches {
8510 continue; }
8512 if condf.is_some()
8517 || base.is_none()
8518 || (then_touches && t.is_none())
8519 || (else_touches && f.is_none())
8520 {
8521 ok = false;
8522 break;
8523 }
8524 let cond_stmt = Stmt::ExprStmt(cond.clone());
8525 if stmts_read_var(std::slice::from_ref(&cond_stmt), v) != 0 {
8526 ok = false;
8527 break;
8528 }
8529 condf = Some((i, cond.clone(), t, f));
8530 }
8531 _ => {}
8532 }
8533 }
8534 if !ok {
8535 continue;
8536 }
8537 let Some((bi, be)) = base else { continue };
8538 let want_writes = 1
8541 + condf
8542 .as_ref()
8543 .map(|(_, _, t, f)| t.is_some() as usize + f.is_some() as usize)
8544 .unwrap_or(0);
8545 if stmts_count_writes(&stmts[..pos], v) != want_writes
8546 || stmts_read_var(&stmts[..pos], v) != 0
8547 {
8548 continue;
8549 }
8550 let rep = match condf {
8551 Some((ci, c, t, f)) => {
8552 drop.push(ci);
8553 match (t, f) {
8554 (Some(t), Some(f)) => Expr::Cond {
8555 c: Box::new(c),
8556 t: Box::new(t),
8557 f: Box::new(f),
8558 },
8559 (Some(t), None) => Expr::Cond {
8560 c: Box::new(c),
8561 t: Box::new(t),
8562 f: Box::new(be.clone()),
8563 },
8564 (None, Some(f)) => Expr::Cond {
8565 c: Box::new(c),
8566 t: Box::new(be.clone()),
8567 f: Box::new(f),
8568 },
8569 (None, None) => unreachable!("condf requires a branch assign"),
8570 }
8571 }
8572 None => be.clone(),
8573 };
8574 if !pre_this_safe(&rep, &prefix_vars) {
8575 continue;
8576 }
8577 drop.push(bi);
8578 if let Some(d) = decl {
8579 drop.push(d);
8580 }
8581 repl.push((v, rep));
8582 }
8583 if repl.is_empty() {
8584 return false;
8585 }
8586 if let Stmt::ExprStmt(Expr::Method { args, .. }) = &mut stmts[pos] {
8587 for a in args.iter_mut() {
8588 deep_rewrite_reads(a, &mut |x| {
8589 if let Expr::Local { var, .. } = x {
8590 if let Some((_, e)) = repl.iter().find(|(vv, _)| vv == var) {
8591 *x = e.clone();
8592 }
8593 }
8594 });
8595 }
8596 }
8597 drop.sort_unstable();
8598 drop.dedup();
8599 for i in drop.into_iter().rev() {
8600 stmts.remove(i);
8601 }
8602 true
8603}
8604
8605pub fn rewrite_kotlin_facades(body: &mut Stmt, pool: &crate::DexPool) {
8613 let map = pool.kotlin_facade_map();
8614 if map.is_empty() {
8615 return;
8616 }
8617 walk_stmt_exprs(body, &mut |e| {
8618 match e {
8619 Expr::Method {
8620 cls,
8621 name,
8622 desc,
8623 is_static,
8624 ..
8625 } if *is_static => {
8626 if let Some((base, cover)) = map.get(cls.as_ref()) {
8627 let hit = match cover {
8628 None => true,
8629 Some(c) => c.0.contains(&(name.to_string(), desc.to_string())),
8630 };
8631 if hit {
8632 *cls = std::sync::Arc::from(base.as_str());
8633 }
8634 }
8635 }
8636 Expr::Field {
8637 cls,
8638 name,
8639 is_static,
8640 ..
8641 } if *is_static => {
8642 if let Some((base, cover)) = map.get(cls.as_ref()) {
8643 let hit = match cover {
8644 None => true,
8645 Some(c) => c.1.contains(name.as_ref()),
8646 };
8647 if hit {
8648 *cls = std::sync::Arc::from(base.as_str());
8649 }
8650 }
8651 }
8652 _ => {}
8653 }
8654 });
8655}
8656
8657pub fn fold_enum_default_arg_bridge(body: &mut Stmt) {
8665 let Stmt::Block(stmts) = body else { return };
8666 fold_default_arg_bridge(stmts);
8667}
8668
8669pub fn fix_ctor_delegation_arg_defs(body: &mut Stmt) {
8670 let Stmt::Block(stmts) = body else { return };
8671 fold_default_arg_bridge(stmts);
8674 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
8678 return;
8679 };
8680 if pos == 0 {
8681 return;
8682 }
8683 let mut arg_vars: Vec<u32> = Vec::new();
8686 let mut total_reads = 0usize;
8687 {
8688 let Stmt::ExprStmt(Expr::Method { args, .. }) = &stmts[pos] else {
8689 return;
8690 };
8691 for a in args {
8692 let mut c = a.clone();
8693 deep_rewrite_reads(&mut c, &mut |x| {
8694 if let Expr::Local { var: v, .. } = x {
8695 total_reads += 1;
8696 if !arg_vars.contains(v) {
8697 arg_vars.push(*v);
8698 }
8699 }
8700 });
8701 }
8702 }
8703 if arg_vars.is_empty() || total_reads != arg_vars.len() {
8704 return;
8705 }
8706 let prefix_defs: Vec<u32> = stmts[..pos]
8709 .iter()
8710 .filter_map(|s| match s {
8711 Stmt::LocalDef { var, init: Some(_), .. } => Some(*var),
8712 _ => None,
8713 })
8714 .collect();
8715 let mut plan: Vec<(u32, usize, Option<usize>)> = Vec::new();
8720 for v in arg_vars {
8721 let defs: Vec<usize> = stmts[..pos]
8722 .iter()
8723 .enumerate()
8724 .filter(|(i, _)| {
8725 matches!(&stmts[*i], Stmt::LocalDef { var, init: Some(_), .. } if *var == v)
8726 })
8727 .map(|(i, _)| i)
8728 .collect();
8729 if defs.len() > 1 {
8730 return; }
8732 let Some(&q) = defs.first() else {
8733 continue; };
8735 let init = match &stmts[q] {
8736 Stmt::LocalDef { init: Some(e), .. } => e.clone(),
8737 _ => return,
8738 };
8739 if stmts_read_var(&stmts[q + 1..pos], v) != 0 {
8740 return; }
8742 let mut init_vars: Vec<u32> = Vec::new();
8745 deep_rewrite_reads(&mut { init.clone() }, &mut |x| {
8746 if let Expr::Local { var: vv, .. } = x {
8747 if !init_vars.contains(vv) {
8748 init_vars.push(*vv);
8749 }
8750 }
8751 });
8752 if init_vars.iter().any(|iv| prefix_defs.contains(iv)) {
8753 return;
8754 }
8755 match stmts[pos + 1..]
8756 .iter()
8757 .position(|s| {
8758 matches!(s, Stmt::ExprStmt(Expr::Assign { target, .. })
8759 if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v))
8760 }) {
8761 Some(w) => {
8762 if stmts_read_var(&stmts[pos + 1..pos + 1 + w], v) != 0 {
8764 return;
8765 }
8766 plan.push((v, q, Some(pos + 1 + w)));
8767 }
8768 None => {
8769 if stmts_read_var(&stmts[pos + 1..], v) != 0 {
8771 return;
8772 }
8773 plan.push((v, q, None));
8774 }
8775 }
8776 }
8777 if plan.is_empty() {
8778 return;
8779 }
8780 let values: Vec<(u32, Expr)> = plan
8782 .iter()
8783 .map(|(_v, q, _)| match &stmts[*q] {
8784 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e.clone()),
8785 _ => unreachable!("plan entries carry a LocalDef with init"),
8786 })
8787 .collect();
8788 if let Stmt::ExprStmt(Expr::Method { args, .. }) = &mut stmts[pos] {
8789 for a in args {
8790 deep_rewrite_reads(a, &mut |x| {
8791 if let Expr::Local { var: v, .. } = x {
8792 if let Some((_, e)) = values.iter().find(|(vv, _)| vv == v) {
8793 *x = e.clone();
8794 }
8795 }
8796 });
8797 }
8798 }
8799 for (v, _, w) in &plan {
8803 if let Some(w) = w {
8804 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = &mut stmts[*w] {
8805 if matches!(&**target, Expr::Local { var: vv, .. } if vv == v) {
8806 let taken = std::mem::replace(&mut **value, Expr::Const(ConstVal::Null));
8807 stmts[*w] = Stmt::LocalDef {
8808 var: *v,
8809 init: Some(taken),
8810 is_final: false,
8811 force_type: false,
8812 };
8813 }
8814 }
8815 }
8816 }
8817 let mut order: Vec<usize> = plan.iter().map(|(_, q, _)| *q).collect();
8818 order.sort_unstable();
8819 order.reverse();
8820 for q in order {
8821 stmts.remove(q);
8822 }
8823}
8824
8825pub fn fix_bool_returns(vt: &VarTable, body: &mut Stmt) {
8836 walk_mut_deep(body, &mut |st| {
8837 if let Stmt::Return(Some(e)) = st {
8838 let numeric = match &*e {
8839 Expr::Local { var, ty } => {
8840 let t = if (*var as usize) < vt.vars.len() {
8841 vt.var(*var).ty.erased()
8842 } else {
8843 ty.erased()
8844 };
8845 matches!(
8846 t,
8847 JavaType::Int
8848 | JavaType::Long
8849 | JavaType::Short
8850 | JavaType::Byte
8851 | JavaType::Char
8852 )
8853 }
8854 other => matches!(
8855 other.type_ref().erased(),
8856 JavaType::Int | JavaType::Long | JavaType::Short | JavaType::Byte
8857 | JavaType::Char
8858 ),
8859 };
8860 if numeric {
8861 let taken = e.clone();
8862 *e = Expr::Bin {
8863 op: BinOp::Ne,
8864 l: Box::new(taken),
8865 r: Box::new(Expr::Const(ConstVal::Int(0))),
8866 ty: Some(TypeRef::J(JavaType::Boolean)),
8867 };
8868 }
8869 }
8870 });
8871}
8872
8873pub fn fix_int_returns(vt: &VarTable, body: &mut Stmt) {
8874 walk_mut_deep(body, &mut |st| {
8875 if let Stmt::Return(Some(e)) = st {
8876 let is_bool = match e {
8877 Expr::Local { var, .. } => {
8878 (*var as usize) < vt.vars.len()
8879 && vt.vars[*var as usize].ty.erased() == JavaType::Boolean
8880 }
8881 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
8882 _ => false,
8883 };
8884 if is_bool {
8885 *e = Expr::Cond {
8886 c: Box::new(e.clone()),
8887 t: Box::new(Expr::Const(ConstVal::Int(1))),
8888 f: Box::new(Expr::Const(ConstVal::Int(0))),
8889 };
8890 }
8891 }
8892 });
8893}
8894
8895pub fn split_generations(vt: &mut VarTable, body: &mut Stmt, pool: &DexPool) {
8911 let mut counter: u32 = 0;
8912 let mut gen: std::collections::HashMap<u32, u32> = std::collections::HashMap::default();
8913 split_walk_stmt(body, vt, &mut gen, &mut counter, pool);
8914}
8915
8916fn split_needed(vt: &VarTable, target: u32, value: &Expr, pool: &DexPool) -> bool {
8918 let n = vt.vars.len();
8919 if target as usize >= n {
8920 return false;
8921 }
8922 let declared = vt.vars[target as usize].ty.erased();
8923 let vt_is_ref = |t: &JavaType| matches!(t, JavaType::Object(_) | JavaType::Array(_));
8924 fn side_bool(e: &Expr, vt: &VarTable) -> bool {
8933 match e {
8934 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
8935 matches!(vt.var(*var).ty.erased(), JavaType::Boolean)
8936 }
8937 Expr::Const(ConstVal::Int(0 | 1)) => true,
8938 Expr::Bin { op: BinOp::Or | BinOp::And | BinOp::Xor, l, r, .. } => {
8939 side_bool(l, vt) && side_bool(r, vt)
8940 }
8941 other => matches!(other.type_ref().erased(), JavaType::Boolean),
8942 }
8943 }
8944 let value_ty = match value {
8945 Expr::Local { var, ty } if (*var as usize) < vt.vars.len() => vt.var(*var).ty.erased(),
8946 Expr::Bin {
8947 op: BinOp::Or | BinOp::And | BinOp::Xor,
8948 l,
8949 r,
8950 ..
8951 } if side_bool(l, vt) && side_bool(r, vt) => JavaType::Boolean,
8952 other => other.type_ref().erased(),
8953 };
8954 if vt_is_ref(&declared) && vt_is_ref(&value_ty) {
8955 let obj = JavaType::Object("java/lang/Object".into());
8965 if declared == value_ty || declared == obj || value_ty == obj {
8966 return false;
8967 }
8968 if let (JavaType::Object(v), JavaType::Object(d)) = (&value_ty, &declared) {
8981 if matches!(value, Expr::New { .. })
8982 && (pool.get(v).is_none() || pool.get(d).is_none())
8983 {
8984 return false;
8985 }
8986 }
8987 true
8988 } else {
8989 if vt_is_ref(&declared) != vt_is_ref(&value_ty) {
8993 return true;
8994 }
8995 let d_bool = matches!(declared, JavaType::Boolean);
9003 let v_bool = matches!(value_ty, JavaType::Boolean);
9004 let zero_one = matches!(value, Expr::Const(ConstVal::Int(0 | 1)));
9005 d_bool != v_bool && !zero_one
9006 }
9007}
9008
9009fn rewrite_gen_reads(e: &mut Expr, gen: &std::collections::HashMap<u32, u32>, vt: &VarTable) {
9010 deep_rewrite(e, &mut |x| {
9011 if let Expr::Local { var, ty } = x {
9012 if let Some(&to) = gen.get(var) {
9013 if to != *var {
9014 *var = to;
9015 *ty = vt.var(to).ty.clone();
9016 }
9017 }
9018 }
9019 });
9020}
9021
9022fn do_split(
9023 vt: &mut VarTable,
9024 target: &mut Expr,
9025 old: u32,
9026 value_ty: &JavaType,
9027 gen: &mut std::collections::HashMap<u32, u32>,
9028 counter: &mut u32,
9029) {
9030 *counter += 1;
9031 let info = &vt.vars[old as usize];
9032 let id = vt.vars.len() as u32;
9033 let name = format!("{}_g{}", info.name, counter);
9034 let slot = info.slot;
9035 let is_param = false;
9043 vt.vars.push(jdc_core::var::VarInfo {
9044 id,
9045 slot,
9046 name,
9047 ty: TypeRef::J(value_ty.clone()),
9048 is_param,
9049 range_start: 0,
9050 range_end: u16::MAX,
9051 synthetic_name: true,
9052 });
9053 while vt.by_slot.len() <= slot as usize {
9054 vt.by_slot.push(Vec::new());
9055 }
9056 vt.by_slot[slot as usize].push((0, u16::MAX, id));
9057 gen.insert(old, id);
9058 if let Expr::Local { var, ty, .. } = target {
9059 *var = id;
9060 *ty = TypeRef::J(value_ty.clone());
9061 }
9062}
9063
9064fn rename_gen_back(s: &mut Stmt, from: u32, to: u32, vt: &VarTable) {
9076 if from == to || (to as usize) >= vt.vars.len() {
9077 return;
9078 }
9079 let to_ty = vt.vars[to as usize].ty.clone();
9080 walk_mut_deep(s, &mut |st| {
9081 if let Stmt::LocalDef { var, .. } = st {
9082 if *var == from {
9083 *var = to;
9084 }
9085 }
9086 });
9087 rewrite_exprs(s, &mut |e| {
9088 deep_rewrite(e, &mut |x| {
9089 if let Expr::Local { var, ty } = x {
9090 if *var == from {
9091 *var = to;
9092 *ty = to_ty.clone();
9093 }
9094 }
9095 });
9096 });
9097}
9098
9099fn drop_gen_slot(vt: &mut VarTable, dead: u32) {
9103 if (dead as usize) >= vt.vars.len() {
9104 return;
9105 }
9106 let slot = vt.vars[dead as usize].slot as usize;
9107 if let Some(seg) = vt.by_slot.get_mut(slot) {
9108 seg.retain(|&(_, _, id)| id != dead);
9109 }
9110}
9111
9112fn undo_type_safe(vt: &VarTable, k: u32, v: u32) -> bool {
9121 if (k as usize) >= vt.vars.len() || (v as usize) >= vt.vars.len() {
9122 return false;
9123 }
9124 let kt = vt.vars[k as usize].ty.erased();
9125 let vt_ty = vt.vars[v as usize].ty.erased();
9126 if kt == vt_ty {
9127 return true;
9128 }
9129 let is_prim = |t: &JavaType| {
9130 matches!(
9131 t,
9132 JavaType::Int
9133 | JavaType::Long
9134 | JavaType::Short
9135 | JavaType::Byte
9136 | JavaType::Char
9137 | JavaType::Float
9138 | JavaType::Double
9139 | JavaType::Boolean
9140 )
9141 };
9142 is_prim(&kt) && is_prim(&vt_ty)
9143}
9144
9145fn split_walk_stmt(
9146 st: &mut Stmt,
9147 vt: &mut VarTable,
9148 gen: &mut std::collections::HashMap<u32, u32>,
9149 counter: &mut u32,
9150 pool: &DexPool,
9151) {
9152 match st {
9153 Stmt::Block(v) => {
9154 for x in v.iter_mut() {
9155 split_walk_stmt(x, vt, gen, counter, pool);
9156 }
9157 }
9158 Stmt::ExprStmt(e) => {
9159 rewrite_gen_reads(e, gen, vt);
9160 if let Expr::Assign {
9161 target,
9162 value,
9163 op: AssignOp::Plain,
9164 ..
9165 } = e
9166 {
9167 if let Expr::Local { var, .. } = &**target {
9168 let old = *var;
9169 if split_needed(vt, old, value, pool) {
9170 let value_ty = value.type_ref().erased();
9171 do_split(vt, target, old, &value_ty, gen, counter);
9172 }
9173 }
9174 }
9175 }
9176 Stmt::LocalDef { var, init, .. } => {
9177 if let Some(e) = init {
9178 rewrite_gen_reads(e, gen, vt);
9179 if split_needed(vt, *var, e, pool) {
9180 let value_ty = e.type_ref().erased();
9184 let old = *var;
9185 let mut fake = Expr::Local {
9186 var: old,
9187 ty: vt.vars[old as usize].ty.clone(),
9188 };
9189 do_split(vt, &mut fake, old, &value_ty, gen, counter);
9190 if let Expr::Local { var: nv, .. } = fake {
9191 *var = nv;
9192 }
9193 }
9194 }
9195 }
9196 Stmt::If {
9197 cond,
9198 then_stmt,
9199 else_stmt,
9200 ..
9201 } => {
9202 rewrite_gen_reads(cond, gen, vt);
9203 let then_floor = vt.vars.len() as u32;
9204 let mut g_then = gen.clone();
9205 split_walk_stmt(then_stmt, vt, &mut g_then, counter, pool);
9206 let else_floor = vt.vars.len() as u32;
9207 let mut g_else = gen.clone();
9208 if let Some(e) = else_stmt {
9209 split_walk_stmt(e, vt, &mut g_else, counter, pool);
9210 }
9211 let mut undo_then: Vec<(u32, u32)> = g_then
9228 .iter()
9229 .filter(|(&k, &v)| {
9230 v >= then_floor
9231 && v < else_floor
9232 && g_else.get(&k).copied() == gen.get(&k).copied()
9233 && undo_type_safe(vt, k, v)
9234 && stmts_read_var(std::slice::from_ref(&**then_stmt), v) == 0
9235 })
9236 .map(|(&k, &v)| (v, k))
9237 .collect();
9238 undo_then.sort_unstable_by_key(|&(v, _)| std::cmp::Reverse(v));
9239 for &(v, k) in &undo_then {
9240 rename_gen_back(then_stmt, v, k, vt);
9241 drop_gen_slot(vt, v);
9242 }
9243 if let Some(e) = else_stmt.as_deref_mut() {
9244 let mut undo_else: Vec<(u32, u32)> = g_else
9245 .iter()
9246 .filter(|(&k, &v)| {
9247 v >= else_floor
9248 && g_then.get(&k).copied() == gen.get(&k).copied()
9249 && undo_type_safe(vt, k, v)
9250 && stmts_read_var(std::slice::from_ref(&*e), v) == 0
9251 })
9252 .map(|(&k, &v)| (v, k))
9253 .collect();
9254 undo_else.sort_unstable_by_key(|&(v, _)| std::cmp::Reverse(v));
9255 for &(v, k) in &undo_else {
9256 rename_gen_back(e, v, k, vt);
9257 drop_gen_slot(vt, v);
9258 }
9259 }
9260 gen.retain(|k, v| g_then.get(k) == Some(v) && g_else.get(k) == Some(v));
9263 }
9264 Stmt::While { cond, body, .. } | Stmt::DoWhile { cond, body, .. } => {
9265 let entry = gen.clone();
9269 for _ in 0..3 {
9270 rewrite_gen_reads(cond, gen, vt);
9271 split_walk_stmt(body, vt, gen, counter, pool);
9272 }
9273 *gen = entry;
9276 }
9277 Stmt::For { init, body, .. } => {
9278 for x in init.iter_mut() {
9279 split_walk_stmt(x, vt, gen, counter, pool);
9280 }
9281 let entry = gen.clone();
9282 for _ in 0..3 {
9283 split_walk_stmt(body, vt, gen, counter, pool);
9284 }
9285 *gen = entry;
9286 }
9287 Stmt::ForEach { body, .. } => {
9288 let entry = gen.clone();
9289 for _ in 0..3 {
9290 split_walk_stmt(body, vt, gen, counter, pool);
9291 }
9292 *gen = entry;
9293 }
9294 Stmt::Switch { cases, default, .. } => {
9295 let entry = gen.clone();
9296 for c in cases.iter_mut() {
9297 let mut g_case = entry.clone();
9298 for x in c.body.iter_mut() {
9299 split_walk_stmt(x, vt, &mut g_case, counter, pool);
9300 }
9301 gen.retain(|k, v| g_case.get(k) == Some(v));
9302 }
9303 if let Some(d) = default {
9304 let mut g_def = entry.clone();
9305 split_walk_stmt(d, vt, &mut g_def, counter, pool);
9306 gen.retain(|k, v| g_def.get(k) == Some(v));
9307 }
9308 }
9309 Stmt::Try {
9310 body,
9311 catches,
9312 finally,
9313 } => {
9314 let entry = gen.clone();
9315 let mut g_try = entry.clone();
9316 split_walk_stmt(body, vt, &mut g_try, counter, pool);
9317 let mut g_all = g_try.clone();
9318 for c in catches.iter_mut() {
9319 let mut g_c = entry.clone();
9322 split_walk_stmt(&mut c.body, vt, &mut g_c, counter, pool);
9323 g_all.retain(|k, v| g_c.get(k) == Some(v));
9324 }
9325 *gen = g_all;
9326 if let Some(f) = finally {
9327 split_walk_stmt(f, vt, gen, counter, pool);
9328 }
9329 }
9330 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => {
9331 split_walk_stmt(body, vt, gen, counter, pool);
9332 }
9333 Stmt::Return(Some(e)) | Stmt::Throw(e) => {
9334 rewrite_gen_reads(e, gen, vt);
9335 }
9336 _ => {}
9337 }
9338}
9339
9340fn fw_super(c: &str) -> Option<&'static str> {
9347 crate::fwdb::super_of(c)
9348}
9349
9350fn exc_subtype(sub: &str, sup: &str, pool: &DexPool) -> bool {
9356 if sub == sup {
9357 return true;
9358 }
9359 if pool.is_subtype(sub, sup) {
9360 return true;
9361 }
9362 let mut cur = sub;
9363 for _ in 0..24 {
9364 let Some(next) = fw_super(cur) else { break };
9365 if next == sup {
9366 return true;
9367 }
9368 cur = next;
9369 }
9370 false
9371}
9372
9373pub fn dedupe_multicatch(s: &mut Stmt, pool: &DexPool) {
9379 dedupe_multicatch_walk(s, pool);
9380}
9381
9382fn dedupe_multicatch_walk(s: &mut Stmt, pool: &DexPool) {
9383 match s {
9384 Stmt::Try {
9385 body,
9386 catches,
9387 finally,
9388 }
9389 | Stmt::TryWithResources {
9390 body,
9391 catches,
9392 finally,
9393 ..
9394 } => {
9395 dedupe_multicatch_walk(body, pool);
9396 for c in catches.iter_mut() {
9397 if c.exc.len() > 1 {
9398 let mut keep: Vec<std::sync::Arc<str>> = Vec::with_capacity(c.exc.len());
9399 for e in c.exc.iter() {
9400 let shadowed = c.exc.iter().any(|o| {
9401 o.as_ref() != e.as_ref() && exc_subtype(e, o, pool)
9402 });
9403 if !shadowed && !keep.iter().any(|k| k.as_ref() == e.as_ref()) {
9404 keep.push(e.clone());
9405 }
9406 }
9407 c.exc = keep;
9408 }
9409 dedupe_multicatch_walk(&mut c.body, pool);
9410 }
9411 if let Some(f) = finally {
9412 dedupe_multicatch_walk(f, pool);
9413 }
9414 }
9415 Stmt::Block(v) => {
9416 for x in v.iter_mut() {
9417 dedupe_multicatch_walk(x, pool);
9418 }
9419 }
9420 Stmt::If {
9421 then_stmt,
9422 else_stmt,
9423 ..
9424 } => {
9425 dedupe_multicatch_walk(then_stmt, pool);
9426 if let Some(e) = else_stmt {
9427 dedupe_multicatch_walk(e, pool);
9428 }
9429 }
9430 Stmt::While { body, .. }
9431 | Stmt::DoWhile { body, .. }
9432 | Stmt::For { body, .. }
9433 | Stmt::ForEach { body, .. }
9434 | Stmt::Labeled { body, .. }
9435 | Stmt::Synchronized { body, .. } => dedupe_multicatch_walk(body, pool),
9436 Stmt::Switch { cases, default, .. } => {
9437 for c in cases.iter_mut() {
9438 for x in c.body.iter_mut() {
9439 dedupe_multicatch_walk(x, pool);
9440 }
9441 }
9442 if let Some(d) = default {
9443 dedupe_multicatch_walk(d, pool);
9444 }
9445 }
9446 _ => {}
9447 }
9448}
9449
9450pub fn resolve_dangling_gotos(s: &mut Stmt) {
9462 let mut labels: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
9463 let probe = s.clone();
9464 walk_all(&probe, &mut |st| {
9465 if let Stmt::Label(id) = st {
9466 labels.insert(*id);
9467 }
9468 });
9469 resolve_gotos_walk(s, &labels, 0);
9470}
9471
9472fn resolve_gotos_walk(s: &mut Stmt, labels: &jdc_core::FxHashSet<u32>, loop_depth: u32) {
9473 let depth = match s {
9474 Stmt::While { .. } | Stmt::DoWhile { .. } | Stmt::For { .. } | Stmt::ForEach { .. } => {
9475 loop_depth + 1
9476 }
9477 _ => loop_depth,
9478 };
9479 match s {
9480 Stmt::Goto(id) => {
9481 if labels.contains(id) {
9482 return; }
9484 if depth > 0 {
9485 *s = Stmt::Break(None);
9486 } else {
9487 *s = Stmt::Comment(format!(
9488 "$DDC: unresolved jump to block {} (irreducible control flow)",
9489 id
9490 ));
9491 }
9492 }
9493 Stmt::Block(v) => {
9494 let mut i = 0usize;
9495 while i < v.len() {
9496 let dangling_here =
9501 depth == 0 && matches!(&v[i], Stmt::Goto(id) if !labels.contains(id));
9502 resolve_gotos_walk(&mut v[i], labels, depth);
9503 if dangling_here {
9504 v.truncate(i + 1);
9505 break;
9506 }
9507 i += 1;
9508 }
9509 }
9510 Stmt::If { then_stmt, else_stmt, .. } => {
9511 resolve_gotos_walk(then_stmt, labels, depth);
9512 if let Some(e) = else_stmt {
9513 resolve_gotos_walk(e, labels, depth);
9514 }
9515 }
9516 Stmt::While { body, .. }
9517 | Stmt::DoWhile { body, .. }
9518 | Stmt::For { body, .. }
9519 | Stmt::ForEach { body, .. }
9520 | Stmt::Labeled { body, .. }
9521 | Stmt::Synchronized { body, .. } => resolve_gotos_walk(body, labels, depth),
9522 Stmt::Try {
9523 body,
9524 catches,
9525 finally,
9526 } => {
9527 resolve_gotos_walk(body, labels, depth);
9528 for c in catches.iter_mut() {
9529 resolve_gotos_walk(&mut c.body, labels, depth);
9530 }
9531 if let Some(f) = finally {
9532 resolve_gotos_walk(f, labels, depth);
9533 }
9534 }
9535 Stmt::Switch { cases, default, .. } => {
9536 for c in cases.iter_mut() {
9537 for x in c.body.iter_mut() {
9538 resolve_gotos_walk(x, labels, depth);
9539 }
9540 }
9541 if let Some(d) = default {
9542 resolve_gotos_walk(d, labels, depth);
9543 }
9544 }
9545 _ => {}
9546 }
9547}
9548
9549fn stmts_read_var(stmts: &[Stmt], v: u32) -> usize {
9551 let mut n = 0usize;
9552 for s in stmts {
9553 let mut c = s.clone();
9554 walk_stmt_exprs(&mut c, &mut |e| {
9555 deep_rewrite_reads(e, &mut |x| {
9556 if let Expr::Local { var: vv, .. } = x {
9557 if *vv == v {
9558 n += 1;
9559 }
9560 }
9561 });
9562 });
9563 }
9564 n
9565}
9566
9567pub fn fix_ctor_conditional_super(body: &mut Stmt) {
9568 if !matches!(body, Stmt::Block(_)) {
9571 let inner = std::mem::replace(body, Stmt::Block(Vec::new()));
9572 *body = Stmt::Block(vec![inner]);
9573 }
9574 let Stmt::Block(stmts) = body else { return };
9575 while stmts.len() == 1 {
9579 let Stmt::Block(inner) = &stmts[0] else { break };
9580 *stmts = inner.clone();
9581 }
9582 if stmts.is_empty() {
9583 return;
9584 }
9585 if is_bare_ctor_call(stmts.first().unwrap()) {
9586 dedupe_ctor_delegations(body);
9587 return;
9588 }
9589 if try_linear_super_inline(stmts) {
9590 return;
9591 }
9592 try_merge_branched_super(stmts);
9593}
9594
9595pub fn strip_secondary_delegations(body: &mut Stmt) {
9619 let Stmt::Block(stmts) = body else { return };
9620 if !stmts.first().is_some_and(is_bare_ctor_call) {
9621 return;
9622 }
9623 fn strip_in(s: &mut Stmt) {
9624 if is_bare_ctor_call(s) {
9625 *s = Stmt::Block(Vec::new());
9626 return;
9627 }
9628 match s {
9629 Stmt::Block(v) => v.iter_mut().for_each(strip_in),
9630 Stmt::If {
9631 then_stmt,
9632 else_stmt,
9633 ..
9634 } => {
9635 strip_in(then_stmt);
9636 if let Some(e) = else_stmt {
9637 strip_in(e);
9638 }
9639 }
9640 Stmt::While { body, .. }
9641 | Stmt::DoWhile { body, .. }
9642 | Stmt::ForEach { body, .. }
9643 | Stmt::Synchronized { body, .. }
9644 | Stmt::Labeled { body, .. } => strip_in(body),
9645 Stmt::For { init, body, .. } => {
9646 init.iter_mut().for_each(strip_in);
9647 strip_in(body);
9648 }
9649 Stmt::Switch { cases, default, .. } => {
9650 for c in cases.iter_mut() {
9651 c.body.iter_mut().for_each(strip_in);
9652 }
9653 if let Some(d) = default {
9654 strip_in(d);
9655 }
9656 }
9657 Stmt::Try {
9658 body,
9659 catches,
9660 finally,
9661 }
9662 | Stmt::TryWithResources {
9663 body,
9664 catches,
9665 finally,
9666 ..
9667 } => {
9668 strip_in(body);
9669 for c in catches.iter_mut() {
9670 strip_in(&mut c.body);
9671 }
9672 if let Some(f) = finally {
9673 strip_in(f);
9674 }
9675 }
9676 _ => {}
9677 }
9678 }
9679 for st in stmts.iter_mut().skip(1) {
9680 strip_in(st);
9681 }
9682}
9683
9684pub fn dedupe_ctor_delegations(body: &mut Stmt) {
9685 let Stmt::Block(stmts) = body else { return };
9686 let Some(d0) = stmts.first().and_then(|s| match s {
9687 Stmt::ExprStmt(e) if is_delegation_expr(e) => Some(e.clone()),
9688 _ => None,
9689 }) else {
9690 return;
9691 };
9692 fn rec(s: &mut Stmt, d0: &Expr) {
9693 match s {
9694 Stmt::Block(v) => {
9695 v.retain(|x| {
9696 !matches!(x, Stmt::ExprStmt(e) if is_delegation_expr(e) && same_delegation(e, d0))
9697 });
9698 for x in v.iter_mut() {
9699 rec(x, d0);
9700 }
9701 }
9702 Stmt::If { then_stmt, else_stmt, .. } => {
9703 rec(then_stmt, d0);
9704 if let Some(e) = else_stmt {
9705 rec(e, d0);
9706 }
9707 }
9708 _ => {}
9709 }
9710 }
9711 {
9715 let d0r = &d0;
9716 let mut idx = 0usize;
9717 stmts.retain(|s| {
9718 idx += 1;
9719 idx == 1
9720 || !(matches!(s, Stmt::ExprStmt(e)
9721 if is_delegation_expr(e) && same_delegation(e, d0r)))
9722 });
9723 }
9724 for s in stmts.iter_mut().skip(1) {
9725 rec(s, &d0);
9726 }
9727}
9728
9729fn same_delegation(a: &Expr, b: &Expr) -> bool {
9733 match (a, b) {
9734 (
9735 Expr::Method { name: n1, cls: c1, is_super: s1, args: a1, .. },
9736 Expr::Method { name: n2, cls: c2, is_super: s2, args: a2, .. },
9737 ) => {
9738 &**n1 == "<init>"
9739 && &**n2 == "<init>"
9740 && c1 == c2
9741 && s1 == s2
9742 && (a1 == a2 || (a1.is_empty() && a2.is_empty()))
9743 }
9744 _ => false,
9745 }
9746}
9747
9748fn ctor_call_expr(s: &Stmt) -> Option<&Expr> {
9750 match s {
9751 Stmt::ExprStmt(e) if is_delegation_expr(e) => Some(e),
9752 _ => None,
9753 }
9754}
9755
9756fn same_ctor_kind(a: &Expr, b: &Expr) -> bool {
9758 match (a, b) {
9759 (
9760 Expr::Method { cls: c1, is_super: s1, owner: o1, .. },
9761 Expr::Method { cls: c2, is_super: s2, owner: o2, .. },
9762 ) => c1 == c2 && s1 == s2 && o1 == o2,
9763 _ => false,
9764 }
9765}
9766
9767fn flat_list(s: &Stmt) -> Vec<&Stmt> {
9772 fn rec<'a>(s: &'a Stmt, out: &mut Vec<&'a Stmt>) {
9773 match s {
9774 Stmt::Block(v) => {
9775 for x in v {
9776 rec(x, out);
9777 }
9778 }
9779 other => out.push(other),
9780 }
9781 }
9782 let mut out = Vec::new();
9783 rec(s, &mut out);
9784 out
9785}
9786
9787fn strip_lost_alloc_owners(e: &mut Expr) {
9793 deep_rewrite(e, &mut |x| {
9794 if let Expr::Method { name, is_special: true, owner: Some(o), .. } = x {
9795 if &**name == "<init>" && !matches!(**o, Expr::This) {
9796 **o = Expr::Const(jdc_core::ir::expr::ConstVal::Int(0));
9797 }
9798 }
9799 });
9800}
9801
9802fn count_locals_expr(e: &Expr) -> std::collections::HashMap<u32, usize> {
9805 let mut m = std::collections::HashMap::new();
9806 let mut probe = e.clone();
9807 strip_lost_alloc_owners(&mut probe);
9808 deep_rewrite(&mut probe, &mut |x| {
9809 if let Expr::Local { var: v, .. } = x {
9810 *m.entry(*v).or_insert(0) += 1;
9811 }
9812 });
9813 m
9814}
9815
9816pub(crate) fn count_locals_stmts(ss: &[Stmt]) -> std::collections::HashMap<u32, usize> {
9817 let mut m = std::collections::HashMap::new();
9818 for s in ss {
9819 let mut c = s.clone();
9820 walk_stmt_exprs(&mut c, &mut |e| {
9821 strip_lost_alloc_owners(e);
9822 deep_rewrite(e, &mut |x| {
9823 if let Expr::Local { var: v, .. } = x {
9824 *m.entry(*v).or_insert(0) += 1;
9825 }
9826 });
9827 });
9828 }
9829 m
9830}
9831
9832fn cnt(m: &std::collections::HashMap<u32, usize>, v: u32) -> usize {
9833 m.get(&v).copied().unwrap_or(0)
9834}
9835
9836fn inline_locals(
9840 e: &Expr,
9841 map: &std::collections::HashMap<u32, Expr>,
9842 depth: u32,
9843) -> Option<Expr> {
9844 if depth > 4 {
9845 return None;
9846 }
9847 let mut out = e.clone();
9848 let mut fail = false;
9849 deep_rewrite(&mut out, &mut |x| {
9850 if let Expr::Local { var, .. } = x {
9851 if let Some(def) = map.get(var) {
9852 match inline_locals(def, map, depth + 1) {
9853 Some(r) => *x = r,
9854 None => fail = true,
9855 }
9856 }
9857 }
9858 });
9859 if fail {
9860 None
9861 } else {
9862 Some(out)
9863 }
9864}
9865
9866fn def_of(s: &Stmt) -> Option<(u32, &Expr)> {
9868 match s {
9869 Stmt::LocalDef { var, init: Some(e), .. } => Some((*var, e)),
9870 Stmt::ExprStmt(Expr::Assign {
9871 target,
9872 value,
9873 op: jdc_core::ir::expr::AssignOp::Plain,
9874 ..
9875 }) => match &**target {
9876 Expr::Local { var, .. } => Some((*var, value)),
9877 _ => None,
9878 },
9879 _ => None,
9880 }
9881}
9882
9883fn try_linear_super_inline(stmts: &mut Vec<Stmt>) -> bool {
9885 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
9886 return false;
9887 };
9888 if pos == 0 {
9889 return false; }
9891 if !stmts[..pos]
9893 .iter()
9894 .all(|s| matches!(s, Stmt::LocalDef { .. } | Stmt::ExprStmt(_)))
9895 {
9896 return false;
9897 }
9898 let Some(call) = ctor_call_expr(&stmts[pos]) else {
9899 return false;
9900 };
9901 let Expr::Method { args, .. } = call else {
9902 return false;
9903 };
9904 let mut map: std::collections::HashMap<u32, Expr> =
9906 std::collections::HashMap::new();
9907 let mut def_idx: std::collections::HashMap<u32, usize> =
9908 std::collections::HashMap::new();
9909 for (i, s) in stmts[..pos].iter().enumerate() {
9910 if let Some((v, e)) = def_of(s) {
9911 map.insert(v, e.clone());
9912 def_idx.insert(v, i);
9913 }
9914 }
9915 let mut needed: Vec<u32> = Vec::new();
9917 for a in args {
9918 let mut probe = a.clone();
9919 strip_lost_alloc_owners(&mut probe);
9920 deep_rewrite(&mut probe, &mut |x| {
9921 if let Expr::Local { var, .. } = x {
9922 if map.contains_key(var) && !needed.contains(var) {
9923 needed.push(*var);
9924 }
9925 }
9926 });
9927 }
9928 if needed.is_empty() {
9929 return false; }
9931 let mut consumed: Vec<usize> = Vec::new();
9934 let use_counts = count_locals_stmts(stmts);
9935 for &v in &needed {
9936 let def = &map[&v];
9937 let uses = cnt(&use_counts, v);
9938 if jdc_core::ir::build::has_side_effects(def) && uses != 1 {
9939 return false;
9940 }
9941 if uses == 1 {
9942 consumed.push(def_idx[&v]);
9943 }
9944 }
9945 let mut new_args: Vec<Expr> = Vec::with_capacity(args.len());
9947 for a in args {
9948 match inline_locals(a, &map, 0) {
9949 Some(r) => new_args.push(r),
9950 None => return false,
9951 }
9952 }
9953 let mut call_stmt = stmts.remove(pos);
9956 if let Stmt::ExprStmt(Expr::Method { args: slot, .. }) = &mut call_stmt {
9957 *slot = new_args;
9958 }
9959 let mut rest: Vec<Stmt> = Vec::with_capacity(stmts.len() + 1);
9960 rest.push(call_stmt);
9961 for (i, s) in stmts.drain(..).enumerate() {
9962 let orig = if i >= pos { i + 1 } else { i };
9965 if !consumed.contains(&orig) {
9966 rest.push(s);
9967 }
9968 }
9969 *stmts = rest;
9970 true
9971}
9972
9973fn try_merge_branched_super(stmts: &mut Vec<Stmt>) -> bool {
9975 let if_positions: Vec<usize> = stmts
9979 .iter()
9980 .enumerate()
9981 .filter(|(_, s)| matches!(s, Stmt::If { .. }))
9982 .map(|(i, _)| i)
9983 .collect();
9984 for if_pos in if_positions {
9985 let gate = match &stmts[if_pos] {
9991 Stmt::If { then_stmt, else_stmt, .. } => {
9992 contains_delegation(then_stmt)
9993 || else_stmt.as_ref().is_some_and(|e| contains_delegation(e))
9994 }
9995 _ => false,
9996 };
9997 if gate && merge_at(stmts, if_pos) {
9998 return true;
9999 }
10000 }
10001 false
10002}
10003
10004fn contains_delegation(s: &Stmt) -> bool {
10007 match s {
10008 Stmt::ExprStmt(e) => is_delegation_expr(e),
10009 Stmt::Block(v) => v.iter().any(contains_delegation),
10010 Stmt::If { then_stmt, else_stmt, .. } => {
10011 contains_delegation(then_stmt)
10012 || else_stmt.as_ref().is_some_and(|e| contains_delegation(e))
10013 }
10014 _ => false,
10015 }
10016}
10017
10018
10019
10020fn merge_at(stmts: &mut Vec<Stmt>, if_pos: usize) -> bool {
10033 enum Br {
10038 Call(usize, Vec<(u32, Expr)>, Vec<usize>),
10040 Clean,
10041 Dirty,
10042 }
10043 fn split_branch(list: &[&Stmt]) -> Br {
10044 let call_pos = list.iter().position(|s| ctor_call_expr(s).is_some());
10045 let Some(c) = call_pos else {
10046 return if list.iter().any(|s| contains_delegation(s)) {
10049 Br::Dirty
10050 } else {
10051 Br::Clean
10052 };
10053 };
10054 if list.iter().skip(c + 1).any(|s| ctor_call_expr(s).is_some()) {
10055 return Br::Dirty; }
10057 let mut defs: Vec<(u32, Expr)> = Vec::new();
10058 let mut def_idxs: Vec<usize> = Vec::new();
10059 for (i, s) in list.iter().enumerate().take(c) {
10060 match def_of(s) {
10061 Some((v, e)) => {
10062 defs.push((v, e.clone()));
10063 def_idxs.push(i);
10064 }
10065 None => match s {
10066 Stmt::ExprStmt(_) | Stmt::LocalDef { .. } => {}
10069 _ => return Br::Dirty, },
10071 }
10072 }
10073 Br::Call(c, defs, def_idxs)
10074 }
10075 fn cond_def_of(s: &Stmt, scope: &[Stmt]) -> Option<(u32, Expr)> {
10077 let Stmt::If { cond, then_stmt, else_stmt: Some(e), .. } = s else {
10078 return None;
10079 };
10080 fn one<'x>(b: &'x Stmt, scope: &[Stmt]) -> Option<(u32, &'x Expr)> {
10081 let l = flat_list(b);
10082 if l.len() == 1 {
10083 return def_of(l[0]);
10084 }
10085 if l.len() == 2 {
10094 let (w, ex) = def_of(l[0])?;
10095 let Stmt::ExprStmt(Expr::Assign {
10096 target,
10097 op: AssignOp::Plain,
10098 value,
10099 }) = l[1]
10100 else {
10101 return None;
10102 };
10103 let Expr::Local { var: tv, .. } = &**target else {
10104 return None;
10105 };
10106 let Expr::Local { var: rv, .. } = &**value else {
10107 return None;
10108 };
10109 if *rv != w || *tv == w {
10110 return None;
10111 }
10112 let mut ldef_writes = 0usize;
10116 for st in scope {
10117 walk_all(st, &mut |x| {
10118 if let Stmt::LocalDef {
10119 var,
10120 init: Some(_),
10121 ..
10122 } = x
10123 {
10124 if *var == w {
10125 ldef_writes += 1;
10126 }
10127 }
10128 });
10129 }
10130 if stmts_read_var(scope, w) != 1
10131 || stmts_count_writes(scope, w) + ldef_writes != 1
10132 {
10133 return None;
10134 }
10135 return Some((*tv, ex));
10136 }
10137 None
10138 }
10139 let (v1, e1) = one(then_stmt, scope)?;
10140 let (v2, e2) = one(e, scope)?;
10141 if v1 != v2 {
10142 return None;
10143 }
10144 Some((
10145 v1,
10146 Expr::Cond {
10147 c: Box::new(cond.clone()),
10148 t: Box::new(e1.clone()),
10149 f: Box::new(e2.clone()),
10150 },
10151 ))
10152 }
10153
10154 let mut pmap: std::collections::HashMap<u32, Expr> =
10156 std::collections::HashMap::new();
10157 let mut pidx: std::collections::HashMap<u32, usize> =
10158 std::collections::HashMap::new();
10159 for (i, s) in stmts[..if_pos].iter().enumerate() {
10160 match s {
10161 Stmt::LocalDef { .. } | Stmt::ExprStmt(_) => {
10162 if let Some((v, e)) = def_of(s) {
10163 pmap.insert(v, e.clone());
10164 pidx.insert(v, i);
10165 }
10166 }
10167 Stmt::If { .. } => match cond_def_of(s, stmts) {
10168 Some((v, e)) => {
10169 pmap.insert(v, e);
10170 pidx.insert(v, i);
10171 }
10172 None => return false,
10173 },
10174 _ => return false,
10175 }
10176 }
10177 let (cond, then_v, else_v, had_else) = match &stmts[if_pos] {
10178 Stmt::If { cond, then_stmt, else_stmt, .. } => (
10179 cond.clone(),
10180 then_stmt.as_ref().clone(),
10181 else_stmt.as_ref().map_or_else(|| Stmt::Block(Vec::new()), |e| e.as_ref().clone()),
10182 else_stmt.is_some(),
10183 ),
10184 _ => return false,
10185 };
10186 fn normalize_branch(b: &mut Stmt) {
10192 let slot: &mut Stmt = match b {
10197 Stmt::Block(v) if v.len() == 1 => &mut v[0],
10198 other => other,
10199 };
10200 if !matches!(slot, Stmt::If { .. }) || !contains_delegation(slot) {
10201 return;
10202 }
10203 let taken = std::mem::replace(slot, Stmt::Block(Vec::new()));
10204 let mut tmp = vec![taken];
10205 if merge_at(&mut tmp, 0) {
10206 *slot = Stmt::Block(tmp);
10207 } else {
10208 *slot = tmp.pop().unwrap();
10209 }
10210 }
10211 let mut then_v = then_v;
10212 let mut else_v = else_v;
10213 normalize_branch(&mut then_v);
10214 normalize_branch(&mut else_v);
10215 let then_list = flat_list(&then_v);
10216 let else_list = flat_list(&else_v);
10217
10218 enum Side {
10221 Call {
10222 at: usize,
10223 defs: Vec<(u32, Expr)>,
10224 def_idxs: Vec<usize>,
10225 },
10226 Clean,
10227 }
10228 let (t_side, e_side, two_sided) = match (split_branch(&then_list), split_branch(&else_list)) {
10229 (Br::Call(tc, td, ti), Br::Call(ec, ed, ei)) => (
10230 Side::Call { at: tc, defs: td, def_idxs: ti },
10231 Side::Call { at: ec, defs: ed, def_idxs: ei },
10232 true,
10233 ),
10234 (Br::Call(tc, td, ti), Br::Clean) => (
10235 Side::Call { at: tc, defs: td, def_idxs: ti },
10236 Side::Clean,
10237 false,
10238 ),
10239 (Br::Clean, Br::Call(ec, ed, ei)) => (
10240 Side::Clean,
10241 Side::Call { at: ec, defs: ed, def_idxs: ei },
10242 false,
10243 ),
10244 _ => return false,
10245 };
10246 let (t_call, e_call) = match (&t_side, &e_side) {
10248 (Side::Call { at: ta, .. }, Side::Call { at: ea, .. }) => (
10249 ctor_call_expr(then_list[*ta]),
10250 ctor_call_expr(else_list[*ea]),
10251 ),
10252 (Side::Call { at: ta, .. }, Side::Clean) => {
10253 (ctor_call_expr(then_list[*ta]), None)
10254 }
10255 (Side::Clean, Side::Call { at: ea, .. }) => {
10256 (None, ctor_call_expr(else_list[*ea]))
10257 }
10258 _ => return false,
10259 };
10260 let (call_list, call_at) = match &t_side {
10262 Side::Call { at, .. } => (&then_list, *at),
10263 Side::Clean => match &e_side {
10264 Side::Call { at, .. } => (&else_list, *at),
10265 Side::Clean => return false,
10266 },
10267 };
10268 let (base_args, other_args) = if two_sided {
10269 let (Some(tc), Some(ec)) = (t_call, e_call) else {
10270 return false;
10271 };
10272 if !same_ctor_kind(tc, ec) {
10273 return false;
10274 }
10275 let (Expr::Method { args: a1, .. }, Expr::Method { args: a2, .. }) = (tc, ec) else {
10276 return false;
10277 };
10278 if a1.len() != a2.len() {
10279 return false;
10280 }
10281 (a1, Some(a2))
10282 } else {
10283 let c = t_call.or(e_call);
10284 let Some(c) = c else { return false };
10285 let Expr::Method { args, .. } = c else {
10286 return false;
10287 };
10288 (args, None)
10289 };
10290
10291 let mut tmap: std::collections::HashMap<u32, Expr> = pmap.clone();
10293 let mut emap: std::collections::HashMap<u32, Expr> = pmap.clone();
10294 if let Side::Call { defs, .. } = &t_side {
10295 for (v, e) in defs {
10296 tmap.insert(*v, e.clone());
10297 }
10298 }
10299 if let Side::Call { defs, .. } = &e_side {
10300 for (v, e) in defs {
10301 emap.insert(*v, e.clone());
10302 }
10303 }
10304 let Some(cond_i) = inline_locals(&cond, &pmap, 0) else {
10305 return false;
10306 };
10307 let mut then_args: Vec<Expr> = Vec::with_capacity(base_args.len());
10309 let mut else_args: Vec<Expr> = Vec::new();
10310 if two_sided {
10311 for a in base_args {
10312 match inline_locals(a, &tmap, 0) {
10313 Some(r) => then_args.push(r),
10314 None => return false,
10315 }
10316 }
10317 for a in other_args.unwrap() {
10318 match inline_locals(a, &emap, 0) {
10319 Some(r) => else_args.push(r),
10320 None => return false,
10321 }
10322 }
10323 } else {
10324 let from_then = matches!(&t_side, Side::Call { .. });
10327 for a in base_args {
10328 let r = if from_then {
10329 inline_locals(a, &tmap, 0)
10330 } else {
10331 inline_locals(a, &emap, 0)
10332 };
10333 match r {
10334 Some(r) => then_args.push(r),
10335 None => return false,
10336 }
10337 }
10338 }
10339 let primary_args = base_args;
10340 let mut merged: Vec<Expr> = Vec::with_capacity(then_args.len());
10342 let mut differ = 0usize;
10343 for (j, t) in then_args.iter().enumerate() {
10344 if two_sided {
10345 let e = &else_args[j];
10346 if t == e {
10347 merged.push(t.clone());
10348 } else {
10349 differ += 1;
10350 merged.push(Expr::Cond {
10351 c: Box::new(cond_i.clone()),
10352 t: Box::new(t.clone()),
10353 f: Box::new(e.clone()),
10354 });
10355 }
10356 } else {
10357 merged.push(t.clone());
10358 }
10359 }
10360
10361 let vars_in = |e: &Expr| -> Vec<u32> {
10363 let mut vs = Vec::new();
10364 let mut probe = e.clone();
10365 strip_lost_alloc_owners(&mut probe);
10366 deep_rewrite(&mut probe, &mut |x| {
10367 if let Expr::Local { var, .. } = x {
10368 if !vs.contains(var) {
10369 vs.push(*var);
10370 }
10371 }
10372 });
10373 vs
10374 };
10375 let branch_remainder = |list: &[&Stmt], side: &Side| -> Vec<Stmt> {
10383 match side {
10384 Side::Clean => list.iter().map(|s| (*s).clone()).collect(),
10385 Side::Call { at, def_idxs, .. } => {
10386 list.iter()
10387 .enumerate()
10388 .filter(|(i, _)| *i != *at && !def_idxs.contains(i))
10389 .map(|(_, s)| (*s).clone())
10390 .collect()
10391 }
10392 }
10393 };
10394 let tail_then_v = branch_remainder(&then_list, &t_side);
10395 let tail_else_v = branch_remainder(&else_list, &e_side);
10396 let post = &stmts[if_pos + 1..];
10397 let mut inlined: Vec<u32> = vars_in(&cond);
10398 for a in primary_args.iter().chain(else_args.iter()) {
10399 for v in vars_in(a) {
10400 if !inlined.contains(&v) {
10401 inlined.push(v);
10402 }
10403 }
10404 }
10405 inlined.retain(|v| pmap.contains_key(v) || tmap.contains_key(v) || emap.contains_key(v));
10406 for &v in &inlined {
10408 let in_branch = match (&t_side, &e_side) {
10409 (Side::Call { defs: td, .. }, Side::Call { defs: ed, .. }) => {
10410 td.iter().any(|(d, _)| *d == v) || ed.iter().any(|(d, _)| *d == v)
10411 }
10412 (Side::Call { defs, .. }, Side::Clean)
10413 | (Side::Clean, Side::Call { defs, .. }) => {
10414 defs.iter().any(|(d, _)| *d == v)
10415 }
10416 _ => false,
10417 };
10418 if pmap.contains_key(&v) && in_branch {
10419 return false;
10420 }
10421 }
10422 type Counts = std::collections::HashMap<u32, usize>;
10423 type GuardCounts = (
10424 Counts,
10425 Vec<Counts>,
10426 Vec<Counts>,
10427 Counts,
10428 Counts,
10429 Counts,
10430 Vec<(u32, Counts)>,
10431 );
10432 let (cond_counts, a1_counts, a2_counts, tail1_counts, tail2_counts, post_counts, def_counts): GuardCounts =
10433 if inlined.is_empty() {
10434 Default::default()
10435 } else {
10436 let all_defs: Vec<(u32, &Expr)> = pmap
10437 .iter()
10438 .map(|(k, v)| (*k, v))
10439 .chain(match &t_side {
10440 Side::Call { defs, .. } => defs.iter().map(|(k, v)| (*k, v)).collect(),
10441 Side::Clean => Vec::new(),
10442 })
10443 .chain(match &e_side {
10444 Side::Call { defs, .. } => defs.iter().map(|(k, v)| (*k, v)).collect(),
10445 Side::Clean => Vec::new(),
10446 })
10447 .collect();
10448 (
10449 count_locals_expr(&cond),
10450 primary_args.iter().map(count_locals_expr).collect(),
10451 else_args.iter().map(count_locals_expr).collect(),
10452 count_locals_stmts(&tail_then_v),
10453 count_locals_stmts(&tail_else_v),
10454 count_locals_stmts(post),
10455 all_defs.into_iter().map(|(v, e)| (v, count_locals_expr(e))).collect(),
10456 )
10457 };
10458 for &v in &inlined {
10459 let in_t = matches!(&t_side, Side::Call { defs, .. } if defs.iter().any(|(d, _)| *d == v));
10460 let in_e = matches!(&e_side, Side::Call { defs, .. } if defs.iter().any(|(d, _)| *d == v));
10461 let effectful = [
10462 in_t.then(|| tmap.get(&v)).flatten(),
10463 in_e.then(|| emap.get(&v)).flatten(),
10464 pmap.get(&v),
10465 ]
10466 .into_iter()
10467 .flatten()
10468 .any(jdc_core::ir::build::has_side_effects);
10469 if !effectful {
10470 continue;
10471 }
10472 let u_cond = cnt(&cond_counts, v);
10473 let u_post = cnt(&post_counts, v);
10474 let u_t1 = cnt(&tail1_counts, v);
10475 let u_t2 = cnt(&tail2_counts, v);
10476 let mut u_odef = 0usize;
10477 for (dv, dc) in &def_counts {
10478 if *dv != v && inlined.contains(dv) {
10479 u_odef += cnt(dc, v);
10480 }
10481 }
10482 let mut emit = u_cond * (1 + differ);
10483 let mut uses_args = 0usize;
10484 for j in 0..primary_args.len() {
10485 let c1 = cnt(&a1_counts[j], v);
10486 let c2 = if two_sided && j < a2_counts.len() {
10487 cnt(&a2_counts[j], v)
10488 } else {
10489 0
10490 };
10491 uses_args += c1 + c2;
10492 emit += if two_sided && then_args[j] != else_args[j] {
10493 c1 + c2
10494 } else {
10495 c1.max(c2)
10496 };
10497 }
10498 let ok = if in_t || in_e {
10499 u_cond == 0
10500 && u_post == 0
10501 && u_t1 == 0
10502 && u_t2 == 0
10503 && u_odef == 0
10504 && a1_counts.iter().map(|c| cnt(c, v)).sum::<usize>() <= 1
10505 && a2_counts.iter().map(|c| cnt(c, v)).sum::<usize>() <= 1
10506 && emit <= 2
10507 && (in_t || a1_counts.iter().all(|c| cnt(c, v) == 0))
10508 && (in_e || a2_counts.iter().all(|c| cnt(c, v) == 0))
10509 } else {
10510 u_post == 0
10511 && u_t1 == 0
10512 && u_t2 == 0
10513 && u_odef == 0
10514 && ((u_cond == 1 && uses_args == 0 && emit <= 2)
10515 || (u_cond == 0 && emit <= 1))
10516 };
10517 if !ok {
10518 return false;
10519 }
10520 }
10521
10522 let mut call_stmt = call_list[call_at].clone();
10524 if let Stmt::ExprStmt(Expr::Method { args: slot, .. }) = &mut call_stmt {
10525 *slot = merged;
10526 }
10527 let residual = |v: u32| -> usize {
10528 cnt(&tail1_counts, v) + cnt(&tail2_counts, v) + cnt(&post_counts, v)
10529 };
10530 let mut kept_prelude: Vec<Stmt> = Vec::new();
10531 for (i, s) in stmts[..if_pos].iter().enumerate() {
10532 let consumed = match s {
10533 Stmt::LocalDef { var, .. } => Some(*var),
10534 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
10535 Expr::Local { var, .. } => Some(*var),
10536 _ => None,
10537 },
10538 Stmt::If { .. } => cond_def_of(s, stmts).map(|(v, _)| v),
10539 _ => None,
10540 };
10541 if let Some(v) = consumed {
10542 if inlined.contains(&v) && pidx.get(&v) == Some(&i) && residual(v) == 0 {
10543 continue;
10544 }
10545 }
10546 kept_prelude.push(s.clone());
10547 }
10548 let build_keep = |list: &[&Stmt], side: &Side| -> Vec<Stmt> {
10549 match side {
10550 Side::Clean => list.iter().map(|s| (*s).clone()).collect(),
10551 Side::Call { at, .. } => {
10552 let mut out = Vec::new();
10553 for (i, s) in list.iter().enumerate() {
10554 if i == *at {
10555 continue;
10556 }
10557 if i < *at {
10558 if let Some((v, _)) = def_of(s) {
10559 if inlined.contains(&v) && residual(v) == 0 {
10560 continue;
10561 }
10562 }
10563 }
10564 out.push((*s).clone());
10565 }
10566 out
10567 }
10568 }
10569 };
10570 let then_keep = build_keep(&then_list, &t_side);
10571 let else_keep = build_keep(&else_list, &e_side);
10572 let else_part = if else_keep.is_empty() && !had_else {
10573 None
10574 } else {
10575 Some(Box::new(Stmt::Block(else_keep)))
10576 };
10577 let new_if = Stmt::If {
10578 cond: cond_i,
10579 then_stmt: Box::new(Stmt::Block(then_keep)),
10580 else_stmt: else_part,
10581 };
10582 let if_empty = match &new_if {
10583 Stmt::If { then_stmt, else_stmt, .. } => {
10584 let be = |s: &Stmt| matches!(s, Stmt::Block(v) if v.is_empty());
10585 be(then_stmt) && else_stmt.as_ref().is_none_or(|e| be(e))
10586 }
10587 _ => false,
10588 };
10589 let mut out: Vec<Stmt> = Vec::with_capacity(stmts.len());
10590 out.push(call_stmt);
10591 out.extend(kept_prelude);
10592 if !if_empty {
10593 out.push(new_if);
10594 }
10595 out.extend(stmts[if_pos + 1..].to_vec());
10596 *stmts = out;
10597 true
10598}
10599
10600
10601pub fn strip_enum_ctor_super(body: &mut Stmt) {
10602 let is_enum_super = |s: &Stmt| {
10603 matches!(
10604 s,
10605 Stmt::ExprStmt(Expr::Method { name, is_super: true, .. }) if &**name == "<init>"
10606 )
10607 };
10608 let Stmt::Block(stmts) = body else {
10609 return;
10610 };
10611 stmts.retain(|s| !is_enum_super(s));
10612 for st in stmts.iter_mut() {
10613 if let Stmt::Block(inner) = st {
10614 inner.retain(|s| !is_enum_super(s));
10615 }
10616 }
10617}
10618
10619pub fn ensure_declared(body: &mut Stmt, vt: &VarTable) {
10620 let n = vt.vars.len().max(1);
10624
10625 let mut seen: HashSet<u32> = HashSet::default();
10628 demote_dup_decls(body, &mut seen, vt);
10629
10630 let mut def_block: Vec<u32> = vec![u32::MAX; n];
10638 let mut sizes: Vec<u32> = vec![0];
10639 let mut next_id: u32 = 1;
10640 scope_pass_a(body, 0, &mut next_id, &mut def_block, &mut sizes);
10641 sizes[0] = next_id;
10642 let mut within: Vec<u32> = vec![0; n];
10643 let mut total: Vec<u32> = vec![0; n];
10644 let mut next_b: u32 = 1;
10645 scope_pass_b(body, 0, &mut next_b, &def_block, &sizes, &mut within, &mut total);
10646 let mut hoist: HashSet<u32> = HashSet::default();
10647 for v in 0..n {
10648 if def_block[v] != u32::MAX && def_block[v] != 0 && within[v] < total[v] {
10649 hoist.insert(v as u32);
10650 }
10651 }
10652 if !hoist.is_empty() {
10653 let mut seed: HashSet<u32> = hoist.iter().copied().collect();
10654 demote_dup_decls(body, &mut seed, vt);
10655 }
10656
10657 let mut declared: Vec<bool> = vec![false; n];
10658 let mut is_param: Vec<bool> = vec![false; n];
10659 for v in &vt.vars {
10660 if v.is_param && (v.id as usize) < n {
10661 is_param[v.id as usize] = true;
10662 }
10663 }
10664 let mut is_catch: Vec<bool> = vec![false; n];
10670 walk_all(body, &mut |st| {
10671 if let Stmt::Try { catches, .. } = st {
10672 for c in catches {
10673 if (c.var as usize) < n {
10674 is_catch[c.var as usize] = true;
10675 }
10676 }
10677 }
10678 });
10679 walk_all(body, &mut |st| {
10680 if let Stmt::LocalDef { var, .. } = st {
10681 if (*var as usize) < n {
10682 declared[*var as usize] = true;
10683 }
10684 }
10685 });
10686 let mut used: HashSet<u32> = HashSet::default();
10687 stmt_collect_vars(body, &mut used, true);
10691
10692 let mut needs_decl: Vec<u32> = used
10695 .into_iter()
10696 .filter(|v| {
10697 let i = *v as usize;
10698 i >= n
10699 || ((!declared[i] && !is_param[i] && !is_catch[i]) || hoist.contains(v))
10700 })
10701 .collect();
10702 needs_decl.sort_unstable();
10703 needs_decl.dedup();
10704
10705 if !needs_decl.is_empty() {
10706 let mut decls: Vec<Stmt> = needs_decl
10707 .into_iter()
10708 .map(|v| Stmt::LocalDef {
10709 var: v,
10710 init: None,
10711 is_final: false,
10712 force_type: true,
10713 })
10714 .collect();
10715 decls.reverse();
10716 match body {
10717 Stmt::Block(v) => {
10718 for d in decls {
10719 v.insert(0, d);
10720 }
10721 }
10722 other => {
10723 let inner = std::mem::replace(other, Stmt::Block(vec![]));
10724 *other = Stmt::Block({
10725 let mut vs = decls;
10726 vs.push(inner);
10727 vs
10728 });
10729 }
10730 }
10731 }
10732}
10733
10734fn demote_dup_decls(s: &mut Stmt, seen: &mut HashSet<u32>, vt: &VarTable) {
10735 match s {
10736 Stmt::Block(v) => {
10739 for x in v.iter_mut() {
10740 demote_dup_decls(x, seen, vt);
10741 }
10742 }
10743 Stmt::LocalDef { var, init, .. } => {
10744 if seen.contains(var) {
10745 let ty = vt.var(*var).ty.clone();
10746 if let Some(e) = init.take() {
10747 *s = Stmt::ExprStmt(Expr::Assign {
10748 target: Box::new(Expr::Local { var: *var, ty }),
10749 op: AssignOp::Plain,
10750 value: Box::new(e),
10751 });
10752 } else {
10753 *s = Stmt::Block(vec![]);
10754 }
10755 } else {
10756 seen.insert(*var);
10757 }
10758 }
10759 other => {
10760 walk_mut(other, &mut |x| demote_dup_decls(x, seen, vt));
10761 }
10762 }
10763}
10764
10765#[allow(dead_code)]
10766fn unused(_: &Vec<CaseGroup>, _: &Catch) {}
10767
10768pub fn apply_local_names(vt: &mut VarTable, body: &Stmt) {
10782 use std::collections::{HashMap, HashSet};
10783
10784 let mut by_call: HashMap<u32, Vec<String>> = HashMap::new();
10787 let mut by_intrinsics: HashMap<u32, String> = HashMap::new();
10788
10789 visit_all_exprs(body, &mut |e| {
10790 if let Expr::Method { cls, name, args, .. } = e {
10791 if (cls.as_ref() == "kotlin/jvm/internal/Intrinsics"
10792 || cls.as_ref() == "kotlin/jvm/internal/IntrinsicsKt")
10793 && matches!(name.as_ref(), "checkNotNullParameter" | "checkParameterIsNotNull")
10794 && args.len() == 2
10795 {
10796 if let (Expr::Local { var, .. }, Expr::Const(ConstVal::Str(s))) = (&args[0], &args[1])
10797 {
10798 by_intrinsics
10799 .entry(*var)
10800 .or_insert_with(|| sanitize_name(s).unwrap_or_default());
10801 }
10802 }
10803 }
10804 });
10805
10806 walk_all(body, &mut |st| {
10807 let (var, value) = match st {
10808 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e),
10809 Stmt::ExprStmt(Expr::Assign { target, value, .. })
10810 if matches!(&**target, Expr::Local { .. }) =>
10811 {
10812 let Expr::Local { var, .. } = &**target else { return };
10813 (*var, value.as_ref())
10814 }
10815 _ => return,
10816 };
10817 if let Some(n) = defining_call_name(value) {
10818 by_call.entry(var).or_default().push(n);
10819 }
10820 });
10821
10822 let sanit = |n: &str| crate::classdec::java_ident(n).into_owned();
10833 let mut taken: HashSet<String> = HashSet::default();
10834 for v in vt.vars.iter() {
10839 if v.synthetic_name {
10844 taken.insert(sanit(&v.name));
10845 }
10846 }
10847 for v in vt.vars.iter_mut() {
10848 if v.synthetic_name {
10849 continue;
10850 }
10851 if !taken.insert(sanit(&v.name)) {
10852 let base = sanit(&v.name);
10853 for i in 2.. {
10854 let cand = format!("{base}{i}");
10855 if taken.insert(cand.clone()) {
10856 v.name = cand;
10857 break;
10858 }
10859 }
10860 }
10861 }
10862 let claim = |taken: &mut HashSet<String>, want: &str| -> String {
10863 let w = crate::classdec::java_ident(want).into_owned();
10864 if taken.insert(w.clone()) {
10865 return w;
10866 }
10867 for i in 2.. {
10868 let cand = format!("{w}{i}");
10869 if taken.insert(cand.clone()) {
10870 return cand;
10871 }
10872 }
10873 w
10874 };
10875
10876 for info in vt.vars.iter_mut() {
10877 if !info.synthetic_name {
10878 continue;
10879 }
10880 if let Some(n) = by_intrinsics.get(&info.id) {
10883 if !n.is_empty() {
10884 info.name = claim(&mut taken, n);
10885 continue;
10886 }
10887 }
10888 if let Some(names) = by_call.get(&info.id) {
10890 let unique: HashSet<&String> = names.iter().collect();
10891 if unique.len() == 1 {
10892 let n = unique.into_iter().next().unwrap();
10893 if !is_java_keyword(n) {
10894 info.name = claim(&mut taken, n);
10895 continue;
10896 }
10897 }
10898 }
10899 let want = type_alias(&info.ty).map(str::to_string).or_else(|| simple_type_name(&info.ty));
10902 if let Some(n) = want {
10903 info.name = claim(&mut taken, &n);
10904 }
10905 }
10906
10907 let mut seen: HashSet<String> = HashSet::default();
10912 for v in vt.vars.iter_mut() {
10913 if !seen.insert(sanit(&v.name)) {
10914 let base = sanit(&v.name);
10915 for i in 2.. {
10916 let cand = format!("{base}{i}");
10917 if seen.insert(cand.clone()) {
10918 v.name = cand;
10919 break;
10920 }
10921 }
10922 }
10923 }
10924}
10925
10926fn simple_type_name(ty: &TypeRef) -> Option<String> {
10928 let TypeRef::J(JavaType::Object(n)) = ty else {
10929 return None;
10930 };
10931 let simple = n.rsplit('/').next().unwrap_or(n);
10932 let simple = simple.rsplit('$').next().unwrap_or(simple);
10933 let mut chars = simple.chars();
10936 let mut out = String::with_capacity(simple.len());
10937 let mut lowered = false;
10938 for c in chars.by_ref() {
10939 if c.is_ascii_alphabetic() && !lowered {
10940 out.push(c.to_ascii_lowercase());
10941 lowered = true;
10942 } else {
10943 out.push(c);
10944 }
10945 if lowered {
10946 break;
10947 }
10948 }
10949 out.extend(chars);
10950 sanitize_name(&out)
10951}
10952
10953fn defining_call_name(e: &Expr) -> Option<String> {
10957 match e {
10958 Expr::Method { name, .. } => {
10959 let base = name
10960 .strip_prefix("get")
10961 .or_else(|| name.strip_prefix("is"))
10962 .filter(|rest| rest.chars().next().is_some_and(|c| c.is_ascii_uppercase()))
10963 .unwrap_or(name);
10964 let first = base.chars().next()?;
10965 let mut out = String::with_capacity(base.len());
10966 out.push(first.to_ascii_lowercase());
10967 out.extend(base.chars().skip(1));
10968 sanitize_name(&out)
10969 }
10970 Expr::New { cls, .. } => {
10971 let simple = cls.rsplit('/').next().unwrap_or(cls);
10972 let simple = simple.rsplit('$').next().unwrap_or(simple);
10973 let first = simple.chars().next()?;
10974 let mut out = String::with_capacity(simple.len());
10975 out.push(first.to_ascii_lowercase());
10976 out.extend(simple.chars().skip(1));
10977 sanitize_name(&out)
10978 }
10979 _ => None,
10980 }
10981}
10982
10983fn sanitize_name(s: &str) -> Option<String> {
10985 let mut out = String::with_capacity(s.len());
10986 for c in s.chars() {
10987 if c.is_ascii_alphanumeric() || c == '_' || c == '$' {
10988 out.push(c);
10989 } else {
10990 out.push('_');
10991 }
10992 }
10993 if out.len() < 2
10994 || out.chars().next().is_some_and(|c| c.is_ascii_digit())
10995 || is_java_keyword(&out)
10996 {
10997 return None;
10998 }
10999 Some(out)
11000}
11001
11002fn type_alias(ty: &TypeRef) -> Option<&'static str> {
11005 let TypeRef::J(JavaType::Object(n)) = ty else {
11006 return None;
11007 };
11008 Some(match n.as_ref() {
11009 "java/lang/String" | "kotlin/String" => "str",
11010 "java/lang/Class" => "cls",
11011 "java/lang/Throwable" => "th",
11012 "java/lang/Object" | "kotlin/Any" => "obj",
11013 "java/util/Iterator" | "kotlin/collections/Iterator" => "it",
11014 "java/lang/Boolean" => "bool",
11015 "java/lang/Integer" => "num",
11016 "java/lang/Character" => "ch",
11017 "java/lang/Byte" => "b",
11018 "java/lang/Short" => "sh",
11019 "java/lang/Float" => "f",
11020 "java/lang/Double" => "d",
11021 "java/lang/Long" => "i",
11022 "java/lang/StringBuilder" => "sb",
11023 "java/util/ArrayList" => "list",
11024 "java/util/HashMap" => "map",
11025 "android/content/Context" => "context",
11026 "android/content/Intent" => "intent",
11027 "android/os/Bundle" => "bundle",
11028 "android/view/View" => "view",
11029 "android/graphics/Bitmap" => "bitmap",
11030 "android/view/ViewGroup" => "viewGroup",
11031 _ => return None,
11032 })
11033}
11034
11035pub(crate) fn visit_all_exprs<F: FnMut(&Expr)>(s: &Stmt, f: &mut F) {
11037 walk_all(s, &mut |st| {
11038 let exprs: Vec<&Expr> = match st {
11039 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
11040 let mut v: Vec<&Expr> = vec![value];
11041 if !matches!(&**target, Expr::Local { .. }) {
11042 v.push(target);
11043 }
11044 v
11045 }
11046 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
11047 vec![e]
11048 }
11049 Stmt::Return(Some(e)) => vec![e],
11050 Stmt::LocalDef { init: Some(e), .. } => vec![e],
11051 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
11052 vec![cond]
11053 }
11054 _ => Vec::new(),
11055 };
11056 for e in exprs {
11057 visit_exprs(e, f);
11058 }
11059 });
11060}
11061
11062pub fn platform_constants(body: &mut Stmt) {
11067 if !crate::platform::installed() {
11068 return;
11069 }
11070 rewrite_exprs(body, &mut |e| {
11071 if let Expr::Method { cls, name, desc, args, .. } = e {
11072 for a in args.iter_mut() {
11073 let (value, is_long) = match a {
11074 Expr::Const(ConstVal::Int(v)) => (*v as i64, false),
11075 Expr::Const(ConstVal::Long(v)) => (*v, true),
11076 _ => continue,
11077 };
11078 if let Some(named) =
11079 crate::platform::named_constant(cls, name, &desc_to_string(desc), 0, value)
11080 {
11081 let _ = is_long;
11082 *a = Expr::Raw(named);
11083 }
11084 }
11085 }
11086 });
11087}
11088
11089fn desc_to_string(d: &jdc_core::types::MethodDescriptor) -> String {
11091 let mut out = String::from("(");
11092 for a in &d.args {
11093 type_to_desc(a, &mut out);
11094 }
11095 out.push(')');
11096 type_to_desc(&d.ret, &mut out);
11097 out
11098}
11099
11100fn type_to_desc(t: &jdc_core::types::JavaType, out: &mut String) {
11101 match t {
11102 JavaType::Void => out.push('V'),
11103 JavaType::Boolean => out.push('Z'),
11104 JavaType::Byte => out.push('B'),
11105 JavaType::Char => out.push('C'),
11106 JavaType::Short => out.push('S'),
11107 JavaType::Int => out.push('I'),
11108 JavaType::Float => out.push('F'),
11109 JavaType::Long => out.push('J'),
11110 JavaType::Double => out.push('D'),
11111 JavaType::Object(n) => {
11112 out.push('L');
11113 out.push_str(n);
11114 out.push(';');
11115 }
11116 JavaType::Array(inner) => {
11117 out.push('[');
11118 type_to_desc(inner, out);
11119 }
11120 }
11121}
11122
11123pub fn remove_kotlin_checks(s: &mut Stmt) {
11129 walk_mut_deep(s, &mut |st| {
11130 let Stmt::Block(v) = st else { return };
11131 v.retain(|x| !is_kotlin_check(x));
11132 });
11133}
11134
11135fn is_kotlin_check(s: &Stmt) -> bool {
11136 let Stmt::ExprStmt(e) = s else { return false };
11137 let Expr::Method { cls, name, .. } = e else { return false };
11138 (cls.as_ref() == "kotlin/jvm/internal/Intrinsics" || cls.as_ref() == "kotlin/jvm/internal/IntrinsicsKt__Jdk7Kt")
11139 && matches!(
11140 name.as_ref(),
11141 "checkNotNullParameter"
11142 | "checkParameterIsNotNull"
11143 | "checkExpressionValueIsNotNull"
11144 | "checkNotNullExpressionValue"
11145 | "checkReturnedValueIsNotNull"
11146 | "checkFieldIsNotNull"
11147 | "checkNotNull"
11148 )
11149}
11150
11151fn inline_ref_ok(pool: &DexPool, host: &str, target: &str, member: Option<(&str, bool)>) -> bool {
11171 let hpkg = match host.rfind('/') {
11172 Some(i) => &host[..i],
11173 None => "",
11174 };
11175 let mut cur = target;
11176 loop {
11177 let tpkg = match cur.rfind('/') {
11178 Some(i) => &cur[..i],
11179 None => "",
11180 };
11181 if tpkg != hpkg {
11182 if let Some(pc) = pool.get(cur) {
11183 if pc.access & crate::access::ACC_PUBLIC == 0 {
11184 return false;
11185 }
11186 if cur == target {
11187 if let Some((mname, is_method)) = member {
11188 let ok = if is_method {
11189 pc.all_methods().any(|m| {
11190 &*m.name == mname
11191 && m.access & crate::access::ACC_PUBLIC != 0
11192 })
11193 } else {
11194 pc.static_fields
11195 .iter()
11196 .chain(pc.instance_fields.iter())
11197 .any(|f| {
11198 f.name == mname
11199 && f.access & crate::access::ACC_PUBLIC != 0
11200 })
11201 };
11202 if !ok
11207 && (if is_method {
11208 pc.all_methods().any(|m| &*m.name == mname)
11209 } else {
11210 pc.static_fields
11211 .iter()
11212 .chain(pc.instance_fields.iter())
11213 .any(|f| f.name == mname)
11214 })
11215 {
11216 return false;
11217 }
11218 }
11219 }
11220 }
11221 }
11222 match cur.rfind('$') {
11223 Some(i) if i > 0 => cur = &cur[..i],
11224 _ => break,
11225 }
11226 }
11227 true
11228}
11229
11230pub fn inline_accessors(s: &mut Stmt, pool: &DexPool, host: &str) {
11231 rewrite_exprs(s, &mut |e| {
11232 let Expr::Method { cls, name, desc, args, is_static, .. } = e else { return };
11233 if !*is_static || args.is_empty() {
11234 return;
11235 }
11236 let Some(target) = pool.get(cls) else { return };
11237 let desc_s = desc_to_string(desc);
11238 let Some(m) = target.find_method(name, &desc_s) else { return };
11239 if !(m.is_static() && m.access & access::ACC_SYNTHETIC != 0) {
11240 return;
11241 }
11242 let Some(dex) = pool.dex(m.dex_idx) else { return };
11243 let code = match pool.accessor_code(m.dex_idx, m.code_off) {
11248 Some(bytes) => ddc_dex::CodeItem::parse(&bytes, 0),
11249 None => dex.code_at(m.code_off),
11250 };
11251 let Some(code) = code else { return };
11252 let insns: Vec<&Insn> =
11253 code.insns.iter().filter(|i| !matches!(i.kind, InsnKind::Nop)).collect();
11254 let core: Vec<&Insn> = insns
11256 .iter()
11257 .copied()
11258 .filter(|i| !is_const_only_trace(&i.kind, &insns))
11259 .collect();
11260 match accessor_shape(&dex, &core, &code) {
11261 Some(Shape::Identity(arg_i)) => {
11262 if let Some(a) = args.get(arg_i) {
11263 *e = a.clone();
11264 }
11265 }
11266 Some(Shape::FieldRead { arg, cls: field_cls, field, field_ty }) => {
11267 if !inline_ref_ok(pool, host, &field_cls, Some((&field, false))) {
11268 return;
11269 }
11270 if let Some(owner) = args.get(arg).cloned().map(Box::new) {
11271 let ty = TypeRef::J(desc_type(&field_ty));
11272 let name = jdc_core::rename::field_display(
11278 &field_cls,
11279 &field,
11280 &field_ty,
11281 )
11282 .map(std::sync::Arc::from)
11283 .unwrap_or_else(|| field.into());
11284 *e = Expr::Field {
11285 owner: Some(owner),
11286 cls: field_cls.into(),
11287 name,
11288 ty,
11289 is_static: false,
11290 };
11291 }
11292 }
11293 Some(Shape::Forward { cls: tcls, name: tname, desc: tdesc, instance }) => {
11294 if !inline_ref_ok(pool, host, &tcls, Some((&tname, true))) {
11295 return;
11296 }
11297 let disp = jdc_core::rename::field_display(
11301 &tcls,
11302 &tname,
11303 &tdesc.to_string(),
11304 )
11305 .map(std::sync::Arc::from)
11306 .unwrap_or_else(|| tname.into());
11307 *cls = tcls.into();
11308 *name = disp;
11309 *desc = std::sync::Arc::new(tdesc);
11310 if instance && !args.is_empty() {
11311 let recv = args.remove(0);
11313 if let Expr::Method { owner, .. } = e {
11314 *owner = Some(Box::new(recv));
11315 }
11316 }
11317 }
11318 None => {}
11319 }
11320 });
11321}
11322
11323enum Shape {
11325 Identity(usize),
11327 FieldRead { arg: usize, cls: String, field: String, field_ty: String },
11329 Forward { cls: String, name: String, desc: MethodDescriptor, instance: bool },
11332}
11333
11334fn field_of(dex: &ddc_dex::DexFile, field_idx: u32) -> Option<(String, String, String)> {
11336 let f = dex.field(field_idx);
11337 Some((
11338 dex.class_name(f.class_idx),
11341 dex.string(f.name_idx).to_string(),
11342 dex.type_name(f.type_idx).to_string(),
11344 ))
11345}
11346
11347fn param_index(code: &ddc_dex::CodeItem, reg: u16) -> Option<usize> {
11350 let rs = code.registers_size as usize;
11351 let ins = code.ins_size as usize;
11352 let r = reg as usize;
11353 let first = rs.checked_sub(ins)?;
11354 (r >= first && r < rs).then(|| r - first)
11355}
11356
11357fn accessor_shape(
11358 dex: &ddc_dex::DexFile,
11359 core: &[&Insn],
11360 code: &ddc_dex::CodeItem,
11361) -> Option<Shape> {
11362 let core: &[&Insn] = match core.split_first() {
11365 Some((first, rest)) if matches!(first.kind, InsnKind::Const { .. }) => rest,
11366 _ => core,
11367 };
11368 let core: &[&Insn] = match core.split_last() {
11369 Some((last, rest)) if matches!(last.kind, InsnKind::Const { .. }) => rest,
11370 _ => core,
11371 };
11372 match core {
11373 [Insn { kind: InsnKind::Return { src }, .. }] => {
11375 let idx = param_index(code, *src)?;
11376 Some(Shape::Identity(idx))
11377 }
11378 [
11380 Insn { kind: InsnKind::IGet { dst, obj, field_idx }, .. },
11381 Insn { kind: InsnKind::Return { src }, .. },
11382 ] if dst == src => {
11383 let arg = param_index(code, *obj)?;
11384 let (cls, field, field_ty) = field_of(dex, *field_idx)?;
11385 Some(Shape::FieldRead { arg, cls, field, field_ty })
11386 }
11387 [Insn { kind: InsnKind::Invoke { method_idx, regs, kind, .. }, .. }, rest @ ..] if rest.len() <= 2 => {
11389 if !regs.iter().all(|r| param_index(code, *r).is_some()) || regs.is_empty() {
11391 return None;
11392 }
11393 if rest.len() == 2 {
11396 let ok = matches!(rest[0].kind, InsnKind::MoveResult { .. })
11397 && matches!(rest[1].kind, InsnKind::Return { .. } | InsnKind::ReturnVoid);
11398 if !ok {
11399 return None;
11400 }
11401 }
11402 let mid = dex.method(*method_idx);
11403 let cls = dex.class_name(mid.class_idx);
11406 let name = dex.string(mid.name_idx).to_string();
11407 let mut s = String::from("(");
11408 for t in dex.proto_params(mid.proto_idx) {
11409 s.push_str(dex.type_name(*t));
11410 }
11411 s.push(')');
11412 s.push_str(dex.type_name(dex.proto(mid.proto_idx).return_type_idx));
11413 let desc = parse_method_descriptor(&s)?;
11414 Some(Shape::Forward { cls, name, desc, instance: !matches!(kind, InvokeKind::Static) })
11415 }
11416 _ => None,
11417 }
11418}
11419
11420
11421fn is_const_only_trace(kind: &InsnKind, insns: &[&Insn]) -> bool {
11427 let InsnKind::Invoke { kind, regs, .. } = kind else { return false };
11428 if !matches!(kind, InvokeKind::Static) || regs.is_empty() {
11429 return false;
11430 }
11431 regs.iter().all(|r| const_loaded(*r, insns, 0))
11432}
11433
11434fn const_loaded(r: u16, insns: &[&Insn], depth: u8) -> bool {
11437 if depth > 3 {
11438 return false;
11439 }
11440 insns.iter().any(|i| match &i.kind {
11441 InsnKind::Const { dst, .. } => *dst == r,
11442 InsnKind::Move { dst, src } => *dst == r && const_loaded(*src, insns, depth + 1),
11443 _ => false,
11444 })
11445}