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, .. }) => Some(*var),
725 Some(Stmt::ExprStmt(Expr::Assign { target, .. })) => match &**target {
726 Expr::Local { var, .. } => Some(*var),
727 _ => None,
728 },
729 _ => None,
730 },
731 _ => None,
732 };
733 if let Some(v) = stored {
734 let exc = c
735 .exc
736 .first()
737 .cloned()
738 .unwrap_or_else(|| "java/lang/Throwable".into());
739 let slot = vt.var(v).slot;
740 let name = "e".to_string();
741 let new_var =
742 vt.add_catch_var(slot, name, TypeRef::J(JavaType::Object(exc)));
743 if let Stmt::Block(vs) = c.body.as_mut() {
744 vs.remove(0);
745 }
746 rewrite_local_refs(c.body.as_mut(), v, new_var);
747 c.var = new_var;
748 } else if let Some(v) = first_undefined_local_read(c.body.as_ref(), defined)
749 .filter(|v| {
750 let in_catch = count_locals_stmts(std::slice::from_ref(c.body.as_ref()));
756 reads_all.get(v).copied().unwrap_or(0)
757 == in_catch.get(v).copied().unwrap_or(0)
758 })
759 .filter(|v| {
760 let ty = vt.var(*v).ty.erased();
768 let exc = c
769 .exc
770 .first()
771 .cloned()
772 .unwrap_or_else(|| "java/lang/Throwable".into());
773 matches!(&ty, JavaType::Object(o)
774 if o.as_ref() == exc.as_ref()
775 || o.as_ref() == "java/lang/Throwable")
776 })
777 {
778 c.var = v;
788 fallback_bound.push(v);
789 } else if let Some(v) =
790 first_thrown_unassigned_local(c.body.as_ref(), vt, assigned)
791 .filter(|v| {
792 let in_catch =
793 count_locals_stmts(std::slice::from_ref(c.body.as_ref()));
794 reads_all.get(v).copied().unwrap_or(0)
795 == in_catch.get(v).copied().unwrap_or(0)
796 })
797 .filter(|v| {
798 let ty = vt.var(*v).ty.erased();
802 let exc = c
803 .exc
804 .first()
805 .cloned()
806 .unwrap_or_else(|| "java/lang/Throwable".into());
807 matches!(&ty, JavaType::Object(o)
808 if o.as_ref() == exc.as_ref()
809 || o.as_ref() == "java/lang/Throwable")
810 })
811 {
812 c.var = v;
818 fallback_bound.push(v);
819 }
820 }
821 bind_catches_walk(&mut c.body, vt, defined, reads_all, assigned, fallback_bound);
822 }
823 if let Some(f) = finally {
824 bind_catches_walk(f.as_mut(), vt, defined, reads_all, assigned, fallback_bound);
825 }
826 }
827 Stmt::Block(v) => {
828 for x in v.iter_mut() {
829 bind_catches_walk(x, vt, defined, reads_all, assigned, fallback_bound);
830 }
831 }
832 Stmt::If {
833 then_stmt,
834 else_stmt,
835 ..
836 } => {
837 bind_catches_walk(then_stmt, vt, defined, reads_all, assigned, fallback_bound);
838 if let Some(e) = else_stmt {
839 bind_catches_walk(e, vt, defined, reads_all, assigned, fallback_bound);
840 }
841 }
842 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound),
843 Stmt::For { init, body, .. } => {
844 for x in init.iter_mut() {
845 bind_catches_walk(x, vt, defined, reads_all, assigned, fallback_bound);
846 }
847 bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound);
848 }
849 Stmt::ForEach { body, .. }
850 | Stmt::Labeled { body, .. }
851 | Stmt::Synchronized { body, .. } => bind_catches_walk(body, vt, defined, reads_all, assigned, fallback_bound),
852 Stmt::Switch { cases, default, .. } => {
853 for c in cases.iter_mut() {
854 for x in c.body.iter_mut() {
855 bind_catches_walk(x, vt, defined, reads_all, assigned, fallback_bound);
856 }
857 }
858 if let Some(d) = default {
859 bind_catches_walk(d, vt, defined, reads_all, assigned, fallback_bound);
860 }
861 }
862 _ => {}
863 }
864}
865
866fn rewrite_local_refs(s: &mut Stmt, from: u32, to: u32) {
867 rewrite_exprs(s, &mut |e| {
868 deep_rewrite(e, &mut |x| {
869 if let Expr::Local { var, .. } = x {
870 if *var == from {
871 *var = to;
872 }
873 }
874 });
875 });
876}
877
878pub fn cleanup(s: &mut Stmt) {
880 jdc_core::ir::stmt::flatten(s);
881 strip_empty(s);
882 merge_singleton_stmts(s);
883}
884
885fn strip_empty(s: &mut Stmt) {
886 match s {
887 Stmt::Block(v) => {
888 v.retain(|x| !x.is_empty_block());
889 for x in v.iter_mut() {
890 strip_empty(x);
891 }
892 }
893 Stmt::If {
894 then_stmt,
895 else_stmt,
896 ..
897 } => {
898 strip_empty(then_stmt);
899 if let Some(e) = else_stmt {
900 strip_empty(e);
901 if e.is_empty_block() {
902 *else_stmt = None;
903 }
904 }
905 }
906 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => strip_empty(body),
907 Stmt::For { init, body, .. } => {
908 init.retain(|x| !x.is_empty_block());
909 strip_empty(body);
910 }
911 Stmt::ForEach { body, .. } => strip_empty(body),
912 Stmt::Switch { cases, default, .. } => {
913 for c in cases {
914 c.body.retain(|x| !x.is_empty_block());
915 }
916 if let Some(d) = default {
917 strip_empty(d);
918 }
919 }
920 Stmt::Try {
921 body,
922 catches,
923 finally,
924 } => {
925 strip_empty(body);
926 for c in catches {
927 strip_empty(&mut c.body);
928 }
929 if let Some(f) = finally {
930 strip_empty(f);
931 }
932 }
933 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => strip_empty(body),
934 _ => {}
935 }
936}
937
938fn merge_singleton_stmts(s: &mut Stmt) {
939 if let Stmt::Block(v) = s {
940 for x in v.iter_mut() {
941 merge_singleton_stmts(x);
942 }
943 }
944}
945
946pub fn prune_unreachable(s: &mut Stmt) {
948 if let Stmt::Block(v) = s {
949 let mut cut: Option<usize> = None;
950 for (i, x) in v.iter().enumerate() {
951 if matches!(x, Stmt::Return(_) | Stmt::Throw(_)) {
952 cut = Some(i + 1);
953 break;
954 }
955 }
956 if let Some(c) = cut {
957 if c < v.len() {
958 v.truncate(c);
959 }
960 }
961 for x in v.iter_mut() {
962 prune_unreachable(x);
963 }
964 } else {
965 walk_mut(s, &mut prune_unreachable);
966 }
967}
968
969fn walk_mut<F: FnMut(&mut Stmt)>(s: &mut Stmt, f: &mut F) {
970 match s {
971 Stmt::Block(v) => {
972 for x in v.iter_mut() {
973 f(x);
974 }
975 }
976 Stmt::If {
977 then_stmt,
978 else_stmt,
979 ..
980 } => {
981 f(then_stmt);
982 if let Some(e) = else_stmt {
983 f(e);
984 }
985 }
986 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => f(body),
987 Stmt::For { init, body, .. } => {
988 for x in init.iter_mut() {
989 f(x);
990 }
991 f(body);
992 }
993 Stmt::ForEach { body, .. } => f(body),
994 Stmt::Switch { cases, default, .. } => {
995 for c in cases {
996 for x in c.body.iter_mut() {
997 f(x);
998 }
999 }
1000 if let Some(d) = default {
1001 f(d);
1002 }
1003 }
1004 Stmt::Try {
1005 body,
1006 catches,
1007 finally,
1008 } => {
1009 f(body);
1010 for c in catches {
1011 f(&mut c.body);
1012 }
1013 if let Some(fl) = finally {
1014 f(fl);
1015 }
1016 }
1017 Stmt::TryWithResources {
1018 resources,
1019 body,
1020 catches,
1021 finally,
1022 } => {
1023 for r in resources.iter_mut() {
1024 f(r);
1025 }
1026 f(body);
1027 for c in catches {
1028 f(&mut c.body);
1029 }
1030 if let Some(fl) = finally {
1031 f(fl);
1032 }
1033 }
1034 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => f(body),
1035 _ => {}
1036 }
1037}
1038
1039pub fn count_localdefs(s: &Stmt, out: &mut usize) {
1041 if matches!(s, Stmt::LocalDef { .. }) {
1042 *out += 1;
1043 }
1044 walk_all(s, &mut |x| {
1045 if matches!(x, Stmt::LocalDef { .. }) {
1046 *out += 1;
1047 }
1048 });
1049}
1050
1051pub fn strip_trailing_void_return(s: &mut Stmt) {
1053 if let Stmt::Block(v) = s {
1054 loop {
1055 match v.last_mut() {
1056 Some(Stmt::Return(None)) => {
1057 v.pop();
1058 }
1059 Some(Stmt::Block(_)) => {
1064 let last = v.last_mut().unwrap();
1065 let before = match last {
1066 Stmt::Block(b) => b.len(),
1067 _ => 0,
1068 };
1069 strip_trailing_void_return(last);
1070 let after = match last {
1071 Stmt::Block(b) => b.len(),
1072 _ => 0,
1073 };
1074 if after == before {
1075 break; }
1077 }
1078 _ => break,
1079 }
1080 }
1081 }
1082}
1083
1084pub fn invert_empty_thens(s: &mut Stmt) {
1089 walk_mut_deep(s, &mut |st| {
1090 if let Stmt::If {
1091 cond,
1092 then_stmt,
1093 else_stmt,
1094 } = st
1095 {
1096 let then_empty = matches!(&**then_stmt, Stmt::Block(v) if v.is_empty());
1097 if then_empty {
1098 if let Some(els) = else_stmt.take() {
1099 let c = std::mem::replace(cond, Expr::Const(ConstVal::Int(0)));
1100 *cond = jdc_core::convert::negate(c);
1101 *then_stmt = els;
1102 }
1103 }
1104 }
1105 });
1106}
1107
1108pub fn fold_short_circuits(s: &mut Stmt) {
1119 for _ in 0..8 {
1120 let mut changed = false;
1121 walk_mut_deep(s, &mut |st| {
1122 if try_fold_diamond(st) {
1123 changed = true;
1124 }
1125 });
1126 if !changed {
1127 break;
1128 }
1129 }
1130}
1131
1132fn try_fold_diamond(st: &mut Stmt) -> bool {
1133 enum Shape {
1134 ThenAnd,
1135 ThenAndNot,
1136 ElseOr,
1137 ElseOrNot,
1138 BareAnd,
1139 BareAndNot,
1140 }
1141 let shape = match &*st {
1145 Stmt::If {
1148 then_stmt,
1149 else_stmt: None,
1150 ..
1151 } => match &**then_stmt {
1152 Stmt::If {
1153 then_stmt: t2,
1154 else_stmt: None,
1155 ..
1156 } if !matches!(&**t2, Stmt::Block(v) if v.is_empty()) => Some(Shape::BareAnd),
1157 Stmt::If {
1158 then_stmt: t2,
1159 else_stmt: Some(_),
1160 ..
1161 } if matches!(&**t2, Stmt::Block(v) if v.is_empty()) => Some(Shape::BareAndNot),
1162 _ => None,
1163 },
1164 Stmt::If {
1165 then_stmt,
1166 else_stmt: Some(els),
1167 ..
1168 } => match (&**then_stmt, &**els) {
1169 (Stmt::If { else_stmt: Some(f2), .. }, _) if f2.as_ref() == els.as_ref() => {
1170 Some(Shape::ThenAnd)
1171 }
1172 (Stmt::If { then_stmt: t2, else_stmt: Some(_), .. }, _)
1173 if t2.as_ref() == els.as_ref() =>
1174 {
1175 Some(Shape::ThenAndNot)
1176 }
1177 (_, Stmt::If { then_stmt: t2, else_stmt: Some(_), .. })
1178 if t2.as_ref() == then_stmt.as_ref() =>
1179 {
1180 Some(Shape::ElseOr)
1181 }
1182 (_, Stmt::If { else_stmt: Some(f2), .. })
1183 if f2.as_ref() == then_stmt.as_ref() =>
1184 {
1185 Some(Shape::ElseOrNot)
1186 }
1187 _ => None,
1188 },
1189 _ => None,
1190 };
1191 let Some(shape) = shape else {
1192 return false;
1193 };
1194
1195 let taken = std::mem::replace(st, Stmt::Block(vec![]));
1196 if matches!(shape, Shape::BareAnd | Shape::BareAndNot) {
1198 let Stmt::If {
1199 cond: c1,
1200 then_stmt,
1201 else_stmt: None,
1202 } = taken
1203 else {
1204 unreachable!()
1205 };
1206 let Stmt::If {
1207 cond: c2,
1208 then_stmt: t2,
1209 else_stmt: f2,
1210 } = *then_stmt
1211 else {
1212 unreachable!()
1213 };
1214 let sc = |op: BinOp, l: Expr, r: Expr| Expr::Bin {
1215 op,
1216 l: Box::new(l),
1217 r: Box::new(r),
1218 ty: None,
1219 };
1220 match shape {
1221 Shape::BareAnd => {
1223 *st = Stmt::If {
1224 cond: sc(BinOp::LogAnd, c1, c2),
1225 then_stmt: t2,
1226 else_stmt: None,
1227 };
1228 }
1229 _ => {
1231 let body = f2.unwrap();
1232 *st = Stmt::If {
1233 cond: sc(
1234 BinOp::LogAnd,
1235 c1,
1236 Expr::Un {
1237 op: UnOp::Not,
1238 e: Box::new(c2),
1239 },
1240 ),
1241 then_stmt: body,
1242 else_stmt: None,
1243 };
1244 }
1245 }
1246 return true;
1247 }
1248 let Stmt::If {
1249 cond: c1,
1250 then_stmt,
1251 else_stmt: Some(els),
1252 } = taken
1253 else {
1254 unreachable!("shape probe guaranteed an If with else")
1255 };
1256 let sc = |op: BinOp, l: Expr, r: Expr| Expr::Bin {
1257 op,
1258 l: Box::new(l),
1259 r: Box::new(r),
1260 ty: None,
1261 };
1262 let not = |e: Expr| Expr::Un {
1263 op: UnOp::Not,
1264 e: Box::new(e),
1265 };
1266 match shape {
1267 Shape::BareAnd | Shape::BareAndNot => unreachable!("handled above"),
1268 Shape::ThenAnd | Shape::ThenAndNot => {
1269 let Stmt::If {
1270 cond: c2,
1271 then_stmt: t2,
1272 else_stmt: Some(f2),
1273 } = *then_stmt
1274 else {
1275 unreachable!()
1276 };
1277 let (nc, body, alt) = match shape {
1278 Shape::ThenAnd => (sc(BinOp::LogAnd, c1, c2), t2, f2),
1280 _ => (sc(BinOp::LogAnd, c1, not(c2)), f2, t2),
1282 };
1283 *st = Stmt::If {
1284 cond: nc,
1285 then_stmt: body,
1286 else_stmt: Some(alt),
1287 };
1288 }
1289 Shape::ElseOr | Shape::ElseOrNot => {
1290 let Stmt::If {
1291 cond: c2,
1292 then_stmt: t2,
1293 else_stmt: Some(f2),
1294 } = *els
1295 else {
1296 unreachable!()
1297 };
1298 let body = then_stmt;
1301 let (nc, alt) = match shape {
1302 Shape::ElseOr => (sc(BinOp::LogOr, c1, c2), f2),
1304 _ => (sc(BinOp::LogOr, c1, not(c2)), t2),
1306 };
1307 *st = Stmt::If {
1308 cond: nc,
1309 then_stmt: body,
1310 else_stmt: Some(alt),
1311 };
1312 }
1313 }
1314 true
1315}
1316
1317pub fn strip_clinit_returns(s: &mut Stmt) {
1323 walk_mut_deep(s, &mut |st| {
1324 if let Stmt::Block(v) = st {
1325 v.retain(|x| !matches!(x, Stmt::Return(None)));
1326 }
1327 });
1328}
1329
1330pub fn prepend_comment(s: &mut Stmt, text: String) {
1331 let stmt = Stmt::Comment(text);
1332 match s {
1333 Stmt::Block(v) => v.insert(0, stmt),
1334 other => {
1335 let inner = std::mem::replace(other, Stmt::Block(vec![]));
1336 *other = Stmt::Block(vec![stmt, inner]);
1337 }
1338 }
1339}
1340
1341fn is_obj_expr(e: &Expr, obj_var: &dyn Fn(u32) -> bool) -> bool {
1349 match e {
1350 Expr::Local { var, .. } => obj_var(*var),
1351 Expr::Const(_) => false,
1352 _ => e.type_ref().erased().is_reference(),
1353 }
1354}
1355
1356fn null_side_rewrite(x: &mut Expr, obj_var: &dyn Fn(u32) -> bool) {
1361 if let Expr::Bin { op, l, r, .. } = x {
1362 if !matches!(op, BinOp::Eq | BinOp::Ne) {
1363 return;
1364 }
1365 let l_zero = matches!(&**l, Expr::Const(ConstVal::Int(0)));
1366 let r_zero = matches!(&**r, Expr::Const(ConstVal::Int(0)));
1367 if l_zero && !r_zero && is_obj_expr(r, obj_var) {
1368 **l = Expr::Const(ConstVal::Null);
1369 } else if r_zero && !l_zero && is_obj_expr(l, obj_var) {
1370 **r = Expr::Const(ConstVal::Null);
1371 }
1372 }
1373}
1374
1375pub fn fused_expr_rewrites(s: &mut Stmt, vt: &VarTable) {
1376 let mut obj_vars: Vec<bool> = Vec::with_capacity(vt.vars.len());
1378 for v in &vt.vars {
1379 obj_vars.push(v.ty.erased().is_reference());
1380 }
1381 rewrite_exprs(s, &mut |e| {
1382 deep_rewrite(e, &mut |x| {
1383 if let Expr::Invokedynamic { name, args, .. } = x {
1385 if name.starts_with('\0') && args.len() == 2 {
1386 let (cls, ty) = match name.as_str() {
1387 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
1388 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
1389 _ => ("java/lang/Double", JavaType::Double),
1390 };
1391 let desc = MethodDescriptor {
1392 args: vec![ty.clone(), ty],
1393 ret: JavaType::Int,
1394 };
1395 *x = Expr::Method {
1396 owner: None,
1397 cls: cls.into(),
1398 name: "compare".into(),
1399 desc: std::sync::Arc::new(desc),
1400 args: args.clone(),
1401 is_static: true,
1402 is_interface: false,
1403 is_special: false,
1404 is_super: false,
1405 is_dynamic: false,
1406 type_args: vec![],
1407 };
1408 return;
1409 }
1410 }
1411 null_side_rewrite(x, &|v| obj_vars.get(v as usize).copied().unwrap_or(false));
1414 if let Expr::Bin { op, l, r, .. } = x {
1416 if matches!(
1417 op,
1418 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
1419 ) {
1420 let l_const = matches!(&**l, Expr::Const(_));
1421 let r_var = matches!(&**r, Expr::Local { .. });
1422 if l_const && r_var {
1423 std::mem::swap(l, r);
1424 }
1425 }
1426 }
1427 });
1428 });
1429 walk_mut_deep(s, &mut |st| {
1437 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = st {
1438 let tgt_is_obj = match &**target {
1439 Expr::Local { var, .. } => {
1440 obj_vars.get(*var as usize).copied().unwrap_or(false)
1441 }
1442 other => other.type_ref().erased().is_reference(),
1443 };
1444 if tgt_is_obj {
1445 if let Expr::Const(ConstVal::Int(0)) = &**value {
1446 **value = Expr::Const(ConstVal::Null);
1447 }
1448 }
1449 } else if let Stmt::LocalDef { var, init, .. } = st {
1450 if obj_vars.get(*var as usize).copied().unwrap_or(false) {
1451 if let Some(iv) = init {
1452 if let Expr::Const(ConstVal::Int(0)) = iv {
1453 *iv = Expr::Const(ConstVal::Null);
1454 }
1455 }
1456 }
1457 }
1458 });
1459}
1460
1461#[allow(dead_code)]
1464pub fn desugar_cmp_residuals(s: &mut Stmt) {
1465 rewrite_exprs(s, &mut |e| {
1466 deep_rewrite(e, &mut |x| {
1467 if let Expr::Invokedynamic { name, args, .. } = x {
1468 if name.starts_with('\0') && args.len() == 2 {
1469 let (cls, ty) = match name.as_str() {
1470 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
1471 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
1472 _ => ("java/lang/Double", JavaType::Double),
1473 };
1474 let desc = jdc_core::types::MethodDescriptor {
1475 args: vec![ty.clone(), ty],
1476 ret: JavaType::Int,
1477 };
1478 *x = Expr::Method {
1479 owner: None,
1480 cls: cls.into(),
1481 name: "compare".into(),
1482 desc: std::sync::Arc::new(desc),
1483 args: args.clone(),
1484 is_static: true,
1485 is_interface: false,
1486 is_special: false,
1487 is_super: false,
1488 is_dynamic: false,
1489 type_args: vec![],
1490 };
1491 }
1492 }
1493 });
1494 });
1495}
1496
1497pub fn ternary_fold(s: &mut Stmt) {
1499 let mut changed = true;
1500 let mut guard = 0;
1501 while changed && guard < 8 {
1502 changed = false;
1503 guard += 1;
1504 ternary_fold_walk(s, &mut changed);
1505 strip_empty(s);
1506 }
1507}
1508
1509fn ternary_fold_walk(s: &mut Stmt, changed: &mut bool) {
1510 if let Stmt::Block(v) = s {
1511 for x in v.iter_mut() {
1512 ternary_fold_walk(x, changed);
1513 }
1514 return;
1515 }
1516 let Stmt::If {
1517 cond,
1518 then_stmt,
1519 else_stmt,
1520 } = s
1521 else {
1522 return;
1523 };
1524 ternary_fold_walk(then_stmt, changed);
1525 if let Some(e) = else_stmt {
1526 ternary_fold_walk(e, changed);
1527 }
1528 let Some(else_b) = else_stmt else { return };
1529 let target_of = |st: &Stmt| -> Option<u32> {
1530 let Stmt::ExprStmt(Expr::Assign { target, .. }) = st else {
1531 return None;
1532 };
1533 if let Expr::Local { var, .. } = &**target {
1534 Some(*var)
1535 } else {
1536 None
1537 }
1538 };
1539 let (Stmt::Block(tv), Stmt::Block(ev)) = (then_stmt.as_mut(), else_b.as_mut()) else {
1540 return;
1541 };
1542 if tv.len() != 1 || ev.len() != 1 {
1543 return;
1544 }
1545 let (Some(vt_), Some(ve)) = (target_of(&tv[0]), target_of(&ev[0])) else {
1546 return;
1547 };
1548 if vt_ != ve {
1549 return;
1550 }
1551 let mut touched = HashSet::default();
1555 let (Stmt::ExprStmt(a_then), Stmt::ExprStmt(a_else)) = (&tv[0], &ev[0]) else {
1556 return;
1557 };
1558 if let Expr::Assign { value, .. } = a_then {
1559 collect_vars(value, &mut touched);
1560 }
1561 if let Expr::Assign { value, .. } = a_else {
1562 collect_vars(value, &mut touched);
1563 }
1564 if touched.contains(&vt_) {
1565 return;
1566 }
1567 let value_then = match a_then {
1568 Expr::Assign { value, .. } => value.clone(),
1569 _ => return,
1570 };
1571 let value_else = match a_else {
1572 Expr::Assign { value, .. } => value.clone(),
1573 _ => return,
1574 };
1575 let cond_e = cond.clone();
1576 let folded = Stmt::ExprStmt(Expr::Assign {
1577 target: Box::new(Expr::Local {
1578 var: vt_,
1579 ty: vt_join_ty(&value_then, &value_else),
1580 }),
1581 op: AssignOp::Plain,
1582 value: Box::new(Expr::Cond {
1583 c: Box::new(cond_e),
1584 t: value_then,
1585 f: value_else,
1586 }),
1587 });
1588 *s = Stmt::Block(vec![folded]);
1589 *changed = true;
1590}
1591
1592fn vt_join_ty(a: &Expr, b: &Expr) -> TypeRef {
1593 let ta = a.type_ref();
1594 let tb = b.type_ref();
1595 if ta.erased() == tb.erased() {
1596 return ta;
1597 }
1598 TypeRef::J(JavaType::Object("java/lang/Object".into()))
1599}
1600
1601pub fn fold_string_builders(s: &mut Stmt, vt: &VarTable) {
1608 let n_vars = vt.vars.len().max(1);
1610 let mut counts = vec![0usize; n_vars];
1611 count_assignments(s, &mut counts);
1612 let mut values: Vec<Option<Expr>> = vec![None; n_vars];
1613 record_assignments(s, &counts, &mut values);
1614
1615 let mut appended_stmts: HashSet<u32> = HashSet::default();
1617 walk_all(s, &mut |st| {
1618 if let Stmt::ExprStmt(Expr::Method {
1619 cls,
1620 name,
1621 owner,
1622 args,
1623 ..
1624 }) = st
1625 {
1626 if name.as_ref() == "append" && is_string_builder(cls) && args.len() == 1 {
1627 if let Some(Expr::Local { var, .. }) = owner.as_deref() {
1628 appended_stmts.insert(*var);
1629 }
1630 }
1631 }
1632 });
1633
1634 rewrite_exprs(s, &mut |e| {
1635 fold_concat_in_expr(e, &values, &appended_stmts);
1636 });
1637
1638 for _ in 0..8 {
1642 let mut reads: HashSet<u32> = HashSet::default();
1643 stmt_collect_vars(s, &mut reads, false);
1644 if drop_unused_assigns(s, &reads) == 0 {
1645 break;
1646 }
1647 }
1648}
1649
1650pub fn count_stmts_deep(s: &Stmt, out: &mut usize) {
1652 *out += 1;
1653 walk_all(s, &mut |_| {
1654 *out += 1;
1655 });
1656}
1657
1658struct VarAnalysis {
1660 assigns: Vec<usize>,
1661 reads: Vec<usize>,
1662 values: Vec<Option<Expr>>,
1664}
1665
1666fn analyze_vars(s: &Stmt, a: &mut VarAnalysis) {
1667 walk_all(s, &mut |st| {
1668 let exprs: Vec<&Expr> = match st {
1669 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
1670 if let Expr::Local { var, .. } = &**target {
1671 grow_to(&mut a.assigns, *var);
1672 let idx = *var as usize;
1673 a.assigns[idx] += 1;
1674 if a.assigns[idx] == 1 {
1675 if idx >= a.values.len() {
1676 a.values.resize(idx + 1, None);
1677 }
1678 a.values[idx] = Some((**value).clone());
1679 } else if idx < a.values.len() {
1680 a.values[idx] = None;
1681 }
1682 }
1683 let mut v: Vec<&Expr> = vec![value];
1684 if !matches!(&**target, Expr::Local { .. }) {
1685 v.push(target);
1686 }
1687 v
1688 }
1689 Stmt::ExprStmt(e @ (Expr::PreIncDec { .. } | Expr::PostIncDec { .. })) => {
1690 let inner = match e {
1695 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => e,
1696 _ => unreachable!(),
1697 };
1698 if let Expr::Local { var, .. } = &**inner {
1699 grow_to(&mut a.assigns, *var);
1700 let idx = *var as usize;
1701 a.assigns[idx] += 1;
1702 if idx < a.values.len() {
1703 a.values[idx] = None;
1704 }
1705 return;
1706 }
1707 vec![e]
1708 }
1709 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
1710 vec![e]
1711 }
1712 Stmt::Return(Some(e)) => vec![e],
1713 Stmt::LocalDef {
1714 var, init: Some(e), ..
1715 } => {
1716 grow_to(&mut a.assigns, *var);
1717 let idx = *var as usize;
1718 a.assigns[idx] += 1;
1719 if a.assigns[idx] == 1 {
1720 if idx >= a.values.len() {
1721 a.values.resize(idx + 1, None);
1722 }
1723 a.values[idx] = Some(e.clone());
1724 } else if idx < a.values.len() {
1725 a.values[idx] = None;
1726 }
1727 vec![e]
1728 }
1729 Stmt::If { cond, .. } => vec![cond],
1730 Stmt::While { cond, .. } => vec![cond],
1731 Stmt::DoWhile { cond, .. } => vec![cond],
1732 _ => Vec::new(),
1733 };
1734 for e in exprs {
1735 visit_exprs(e, &mut |x| {
1736 if let Expr::Local { var, .. } = x {
1737 grow_to(&mut a.reads, *var);
1738 a.reads[*var as usize] += 1;
1739 }
1740 });
1741 }
1742 });
1743 let n = a.assigns.len().max(a.reads.len()).max(a.values.len());
1745 a.assigns.resize(n, 0);
1746 a.reads.resize(n, 0);
1747 a.values.resize(n, None);
1748}
1749
1750fn grow_to(v: &mut Vec<usize>, var: u32) {
1751 if var as usize >= v.len() {
1752 v.resize(var as usize + 1, 0);
1753 }
1754}
1755
1756fn count_assignments(s: &Stmt, counts: &mut Vec<usize>) {
1757 walk_all(s, &mut |st| match st {
1758 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
1759 if let Expr::Local { var, .. } = &**target {
1760 grow_to(counts, *var);
1761 counts[*var as usize] += 1;
1762 }
1763 }
1764 Stmt::LocalDef {
1765 var, init: Some(_), ..
1766 } => {
1767 grow_to(counts, *var);
1768 counts[*var as usize] += 1;
1769 }
1770 _ => {}
1771 });
1772}
1773
1774fn record_assignments(s: &Stmt, counts: &[usize], out: &mut Vec<Option<Expr>>) {
1775 walk_all(s, &mut |st| match st {
1776 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
1777 if let Expr::Local { var, .. } = &**target {
1778 if counts.get(*var as usize).copied().unwrap_or(0) == 1 {
1779 if *var as usize >= out.len() {
1780 out.resize(*var as usize + 1, None);
1781 }
1782 out[*var as usize] = Some((**value).clone());
1783 }
1784 }
1785 }
1786 Stmt::LocalDef {
1787 var, init: Some(e), ..
1788 } if counts.get(*var as usize).copied().unwrap_or(0) == 1 => {
1789 if *var as usize >= out.len() {
1790 out.resize(*var as usize + 1, None);
1791 }
1792 out[*var as usize] = Some(e.clone());
1793 }
1794 _ => {}
1795 });
1796}
1797
1798fn fold_concat_in_expr(e: &mut Expr, values: &[Option<Expr>], appended: &HashSet<u32>) {
1799 deep_rewrite(e, &mut |x| {
1800 if let Expr::Method {
1801 cls,
1802 name,
1803 args,
1804 owner,
1805 ..
1806 } = x
1807 {
1808 if name.as_ref() == "toString" && args.is_empty() && is_string_builder(cls) {
1809 if let Some(o) = owner {
1810 let mut has_stmt_appends = false;
1813 visit_exprs(o, &mut |y| {
1814 if let Expr::Local { var, .. } = y {
1815 if appended.contains(var) {
1816 has_stmt_appends = true;
1817 }
1818 }
1819 });
1820 if has_stmt_appends {
1821 return;
1822 }
1823 let resolved = resolve_local(o, values);
1824 if let Some(parts) = collect_sb_parts(&resolved, values, 0) {
1825 if !parts.is_empty() {
1834 *x = Expr::StringConcat(parts);
1835 }
1836 }
1837 }
1838 }
1839 }
1840 });
1841}
1842
1843fn is_string_builder(cls: &str) -> bool {
1844 cls == "java/lang/StringBuilder" || cls == "java/lang/StringBuffer"
1845}
1846
1847fn resolve_local(e: &Expr, values: &[Option<Expr>]) -> Expr {
1848 match e {
1849 Expr::Local { var, ty } => values
1850 .get(*var as usize)
1851 .and_then(|o| o.clone())
1852 .unwrap_or_else(|| Expr::Local {
1853 var: *var,
1854 ty: ty.clone(),
1855 }),
1856 other => other.clone(),
1857 }
1858}
1859
1860fn collect_sb_parts(e: &Expr, values: &[Option<Expr>], depth: u32) -> Option<Vec<ConcatPart>> {
1862 if depth > 16 {
1863 return None;
1864 }
1865 match e {
1866 Expr::Method {
1867 cls,
1868 name,
1869 args,
1870 owner,
1871 ..
1872 } if name.as_ref() == "append" && is_string_builder(cls) && args.len() == 1 => {
1873 let mut parts =
1874 collect_sb_parts(&resolve_local(owner.as_deref()?, values), values, depth + 1)?;
1875 parts.push(ConcatPart::Str(args[0].clone()));
1876 Some(parts)
1877 }
1878 Expr::New {
1879 cls,
1880 args,
1881 raw: false,
1882 ..
1883 } if is_string_builder(cls) => {
1884 let mut parts = Vec::new();
1885 for a in args {
1886 if let Expr::Const(ConstVal::Str(sv)) = a {
1887 parts.push(ConcatPart::Const(sv.to_string()));
1888 } else {
1889 parts.push(ConcatPart::Str(a.clone()));
1890 }
1891 }
1892 Some(parts)
1893 }
1894 _ => None,
1895 }
1896}
1897
1898fn drop_unused_assigns(s: &mut Stmt, reads: &HashSet<u32>) -> usize {
1899 let mut dropped = 0usize;
1900 walk_mut_deep(s, &mut |st| {
1901 if let Stmt::Block(v) = st {
1902 let before = v.len();
1903 v.retain(|x| match x {
1904 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
1907 Expr::Local { var, .. } => reads.contains(var) || !is_sbish(value),
1908 _ => true,
1909 },
1910 Stmt::LocalDef { var, init, .. } => {
1911 reads.contains(var) || !init.as_ref().map(is_sbish).unwrap_or(false)
1912 }
1913 _ => true,
1914 });
1915 dropped += before - v.len();
1916 }
1917 });
1918 dropped
1919}
1920
1921fn is_sbish(e: &Expr) -> bool {
1922 match e {
1923 Expr::New {
1924 cls, raw: false, ..
1925 } => is_string_builder(cls),
1926 Expr::Method { name, cls, .. } => name.as_ref() == "append" && is_string_builder(cls),
1927 _ => false,
1928 }
1929}
1930
1931fn walk_mut_deep<F: FnMut(&mut Stmt)>(s: &mut Stmt, f: &mut F) {
1932 f(s);
1933 walk_mut(s, &mut |x| walk_mut_deep(x, f));
1934}
1935
1936pub fn drop_dead_raw_news(body: &mut Stmt) {
1948 fn is_orphan(s: &Stmt) -> bool {
1949 matches!(s, Stmt::ExprStmt(Expr::New { raw: true, .. }))
1950 }
1951 walk_mut_deep(body, &mut |st| match st {
1952 Stmt::Block(v) => v.retain(|x| !is_orphan(x)),
1953 Stmt::Switch { cases, .. } => {
1954 for c in cases.iter_mut() {
1955 c.body.retain(|x| !is_orphan(x));
1956 }
1957 }
1958 Stmt::For { init, .. } => init.retain(|x| !is_orphan(x)),
1959 Stmt::TryWithResources { resources, .. } => resources.retain(|x| !is_orphan(x)),
1960 _ => {}
1961 });
1962}
1963
1964pub fn fold_synchronized(s: &mut Stmt) {
1968 fold_sync_walk(s);
1969}
1970
1971fn fold_sync_walk(s: &mut Stmt) {
1972 walk_mut_deep(s, &mut |st| {
1973 let Stmt::Block(v) = st else { return };
1974 let mut i = 0;
1975 while i < v.len() {
1976 if i + 1 < v.len() {
1977 if let Some(sync) = try_sync_at(v, i) {
1978 let _ = v.drain(i..i + 2);
1979 v.insert(i, sync);
1980 continue;
1981 }
1982 }
1983 i += 1;
1984 }
1985 });
1986}
1987
1988fn try_sync_at(v: &[Stmt], i: usize) -> Option<Stmt> {
1989 let Stmt::MonitorEnter(lock) = &v[i] else {
1990 return None;
1991 };
1992 let Stmt::Try {
1993 body,
1994 catches,
1995 finally,
1996 } = &v[i + 1]
1997 else {
1998 return None;
1999 };
2000 if finally.is_some() || catches.len() != 1 {
2001 return None;
2002 }
2003 let c = &catches[0];
2004 if !c.exc.is_empty() {
2005 return None;
2006 }
2007 let Stmt::Block(cv) = c.body.as_ref() else {
2009 return None;
2010 };
2011 let exit_matches = cv.len() >= 2
2012 && matches!(&cv[0], Stmt::MonitorExit(e) if expr_local_var(e) == expr_local_var(lock));
2013 let throws = cv.len() >= 2 && matches!(&cv[1], Stmt::Throw(_));
2014 if !(exit_matches && throws) {
2015 return None;
2016 }
2017 Some(Stmt::Synchronized {
2018 lock: lock.clone(),
2019 body: body.clone(),
2020 })
2021}
2022
2023fn expr_local_var(e: &Expr) -> Option<u32> {
2024 if let Expr::Local { var, .. } = e {
2025 Some(*var)
2026 } else {
2027 None
2028 }
2029}
2030
2031pub fn forward_single_use(s: &mut Stmt, _vt: &VarTable) {
2036 let mut analysis = VarAnalysis {
2040 assigns: Vec::new(),
2041 reads: Vec::new(),
2042 values: Vec::new(),
2043 };
2044 analyze_vars(s, &mut analysis);
2045 let mut assigns = analysis.assigns;
2046 let mut reads = analysis.reads;
2047 let n_vars = assigns.len().max(_vt.vars.len()).max(1);
2050 assigns.resize(n_vars, 0);
2051 reads.resize(n_vars, 0);
2052 let mut values = analysis.values;
2053 values.resize(n_vars, None);
2054
2055 let mut cand = vec![false; n_vars];
2060 let mut edges: Vec<Vec<u32>> = vec![Vec::new(); n_vars];
2061 for (v, cand_v) in cand.iter_mut().enumerate() {
2062 if assigns.get(v).copied().unwrap_or(0) != 1 || reads.get(v).copied().unwrap_or(0) != 1 {
2063 continue;
2064 }
2065 if let Some(val) = values.get(v).and_then(|o| o.as_ref()) {
2066 let mut refs = HashSet::default();
2067 collect_vars(val, &mut refs);
2068 if refs.iter().any(|r| assigns[*r as usize] > 1) {
2069 continue;
2070 }
2071 edges[v] = refs.into_iter().collect();
2075 }
2076 *cand_v = true;
2077 }
2078
2079 let mut color = vec![0u8; n_vars]; let mut bad = vec![false; n_vars];
2097 for root in 0..n_vars {
2098 if color[root] != 0 || edges[root].is_empty() {
2099 continue;
2100 }
2101 color[root] = 1;
2102 let mut stack: Vec<(usize, usize)> = vec![(root, 0)];
2103 while let Some(top) = stack.last_mut() {
2104 let v = top.0;
2105 let i = top.1;
2106 top.1 += 1;
2107 if i < edges[v].len() {
2108 let r = edges[v][i] as usize;
2109 if color[r] == 1 {
2110 for &(pn, _) in &stack {
2111 bad[pn] = true;
2112 }
2113 } else if color[r] == 0 {
2114 color[r] = 1;
2115 stack.push((r, 0));
2116 } else if bad[r] {
2117 for &(pn, _) in &stack {
2118 bad[pn] = true;
2119 }
2120 }
2121 } else {
2122 color[v] = 2;
2123 stack.pop();
2124 }
2125 }
2126 }
2127 let single: Vec<bool> = (0..n_vars).map(|v| cand[v] && !bad[v]).collect();
2128 if !single.iter().any(|&b| b) {
2129 return;
2130 }
2131 let pure: Vec<bool> = (0..n_vars)
2132 .map(|v| {
2133 single[v]
2134 && values
2135 .get(v)
2136 .and_then(|o| o.as_ref())
2137 .map(|e| !has_side_effects(e))
2138 .unwrap_or(false)
2139 })
2140 .collect();
2141 let impure: Vec<bool> = (0..n_vars).map(|v| single[v] && !pure[v]).collect();
2142
2143 if pure.iter().any(|&b| b) {
2145 let vals: Vec<Option<Expr>> = (0..n_vars)
2146 .map(|v| {
2147 if pure[v] {
2148 values.get(v).cloned().flatten()
2149 } else {
2150 None
2151 }
2152 })
2153 .collect();
2154 rewrite_exprs(s, &mut |e| {
2155 deep_rewrite_reads(e, &mut |x| {
2156 if let Expr::Local { var, .. } = x {
2157 if let Some(Some(v)) = vals.get(*var as usize) {
2158 *x = v.clone();
2159 }
2160 }
2161 });
2162 });
2163 drop_defs(s, &pure);
2164 }
2165
2166 if impure.iter().any(|&b| b) {
2169 forward_adjacent_impure(s, &values, &impure);
2170 }
2171}
2172
2173fn count_reads(s: &Stmt, out: &mut Vec<usize>) {
2175 walk_all(s, &mut |st| {
2176 let exprs: Vec<&Expr> = match st {
2177 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
2180 let mut v: Vec<&Expr> = vec![value];
2181 if !matches!(&**target, Expr::Local { .. }) {
2182 v.push(target);
2183 }
2184 v
2185 }
2186 Stmt::ExprStmt(e @ (Expr::PreIncDec { .. } | Expr::PostIncDec { .. })) => {
2189 let inner = match e {
2190 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => e,
2191 _ => unreachable!(),
2192 };
2193 if matches!(**inner, Expr::Local { .. }) {
2194 Vec::new()
2195 } else {
2196 vec![e]
2197 }
2198 }
2199 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
2200 vec![e]
2201 }
2202 Stmt::Return(Some(e)) => vec![e],
2203 Stmt::LocalDef { init: Some(e), .. } => vec![e],
2204 Stmt::If { cond, .. } => vec![cond],
2205 Stmt::While { cond, .. } => vec![cond],
2206 Stmt::DoWhile { cond, .. } => vec![cond],
2207 Stmt::Switch { selector, .. } => vec![selector],
2213 Stmt::ForEach { iterable, .. } => vec![iterable],
2214 Stmt::Synchronized { lock, .. } => vec![lock],
2215 Stmt::For { cond, update, .. } => {
2216 let mut v: Vec<&Expr> = Vec::with_capacity(update.len() + 1);
2217 if let Some(c) = cond {
2218 v.push(c);
2219 }
2220 v.extend(update.iter());
2221 v
2222 }
2223 Stmt::Assert { cond, msg } => {
2224 let mut v: Vec<&Expr> = vec![cond];
2225 if let Some(m) = msg {
2226 v.push(m);
2227 }
2228 v
2229 }
2230 _ => Vec::new(),
2231 };
2232 for e in exprs {
2233 visit_exprs(e, &mut |x| {
2234 if let Expr::Local { var, .. } = x {
2235 grow_to(out, *var);
2236 out[*var as usize] += 1;
2237 }
2238 });
2239 }
2240 });
2241}
2242
2243fn drop_defs(s: &mut Stmt, vars: &[bool]) {
2244 walk_mut_deep(s, &mut |st| {
2245 if let Stmt::Block(v) = st {
2246 v.retain(|x| match x {
2247 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
2248 Expr::Local { var, .. } => !vars.get(*var as usize).copied().unwrap_or(false),
2249 _ => true,
2250 },
2251 Stmt::LocalDef { var, .. } => !vars.get(*var as usize).copied().unwrap_or(false),
2252 _ => true,
2253 });
2254 }
2255 });
2256}
2257
2258fn forward_adjacent_impure(s: &mut Stmt, values: &[Option<Expr>], impure: &[bool]) {
2260 if let Stmt::Block(v) = s {
2261 let mut i = 0;
2262 while i + 1 < v.len() {
2263 let def_var = match &v[i] {
2264 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
2265 Expr::Local { var, .. } => Some(*var),
2266 _ => None,
2267 },
2268 Stmt::LocalDef { var, .. } => Some(*var),
2269 _ => None,
2270 };
2271 if let Some(var) = def_var {
2272 if impure.get(var as usize).copied().unwrap_or(false) {
2273 let mut reads_here = vec![0usize; values.len().max(1)];
2275 count_reads(&v[i + 1], &mut reads_here);
2276 if reads_here.get(var as usize).copied().unwrap_or(0) == 1 {
2277 let val = match &v[i] {
2281 Stmt::ExprStmt(Expr::Assign { value, .. }) => (**value).clone(),
2282 Stmt::LocalDef { init: Some(e), .. } => (*e).clone(),
2283 _ => {
2284 i += 1;
2285 continue;
2286 }
2287 };
2288 rewrite_exprs(&mut v[i + 1], &mut |e| {
2289 deep_rewrite_reads(e, &mut |x| {
2290 if let Expr::Local { var: v2, .. } = x {
2291 if *v2 == var {
2292 *x = val.clone();
2293 }
2294 }
2295 });
2296 });
2297 v.remove(i);
2298 i = i.saturating_sub(1);
2301 continue;
2302 }
2303 }
2304 }
2305 i += 1;
2306 }
2307 }
2308 walk_mut(s, &mut |x| forward_adjacent_impure(x, values, impure));
2309}
2310
2311pub fn infer_types(vt: &mut VarTable, body: &mut Stmt, ret: &JavaType, env: &MethodEnv) {
2317 let n = vt.vars.len();
2318 let mut evidence: Vec<Vec<(JavaType, bool)>> = vec![Vec::new(); n];
2326 let ev = |evidence: &mut Vec<Vec<(JavaType, bool)>>, var: u32, t: JavaType, strong: bool| {
2327 if (var as usize) < evidence.len() {
2328 evidence[var as usize].push((t, strong));
2329 }
2330 };
2331
2332 walk_all(body, &mut |st| match st {
2333 Stmt::LocalDef { var, init, .. } => {
2334 if let Some(e) = init {
2335 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
2336 let strong = match e {
2344 Expr::Const(_) => false,
2345 Expr::Local { var: src, .. } => {
2346 let src_ty = vt.vars.get(*src as usize).map(|v| v.ty.erased());
2347 let tgt_ty = vt.vars.get(*var as usize).map(|v| v.ty.erased());
2348 match (src_ty, tgt_ty) {
2349 (Some(s), Some(g)) => {
2350 (s.is_numeric() && g.is_numeric())
2351 || (s.is_reference() && g.is_reference())
2352 }
2353 _ => false,
2354 }
2355 }
2356 _ => true,
2357 };
2358 ev(&mut evidence, *var, e.type_ref().erased(), strong);
2359 }
2360 }
2361 Stmt::ExprStmt(e) => {
2362 if let Expr::Assign { target, value, .. } = e {
2369 if let Expr::Field { owner: Some(o), cls, is_static: false, .. } = &**target {
2376 if cls.is_empty() {
2377 } else if let Expr::Local { var, .. } = &**o {
2379 ev(
2380 &mut evidence,
2381 *var,
2382 JavaType::Object(cls.clone()),
2383 true,
2384 );
2385 }
2386 }
2387 if let Expr::Local { var, .. } = &**target {
2388 let strong = match &**value {
2389 Expr::Const(_) => false,
2390 Expr::Local { var: src, .. } => {
2391 let src_ty = vt.vars.get(*src as usize).map(|v| v.ty.erased());
2398 let tgt_ty = vt.vars.get(*var as usize).map(|v| v.ty.erased());
2399 match (src_ty, tgt_ty) {
2400 (Some(s), Some(g)) => {
2401 (s.is_numeric() && g.is_numeric())
2402 || (s.is_reference() && g.is_reference())
2403 }
2404 _ => false,
2405 }
2406 }
2407 _ => true,
2408 };
2409 ev(&mut evidence, *var, value.type_ref().erased(), strong);
2410 }
2411 }
2412 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
2413 }
2414 Stmt::Throw(e) => {
2415 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
2416 if let Expr::Local { var, .. } = e {
2417 ev(
2418 &mut evidence,
2419 *var,
2420 JavaType::Object("java/lang/Throwable".into()),
2421 true,
2422 );
2423 }
2424 }
2425 Stmt::Return(Some(e)) => {
2426 expr_evidence(e, &mut |v, t| ev(&mut evidence, v, t, false));
2427 if let Expr::Local { var, .. } = e {
2428 ev(&mut evidence, *var, ret.clone(), true);
2429 }
2430 }
2431 Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
2432 if let Expr::Local { var, .. } = e {
2433 ev(
2434 &mut evidence,
2435 *var,
2436 JavaType::Object("java/lang/Object".into()),
2437 true,
2438 );
2439 }
2440 }
2441 Stmt::ForEach {
2442 var,
2443 iterable,
2444 is_array,
2445 ..
2446 } => {
2447 if let Expr::Local { var: v0, .. } = iterable {
2448 if *is_array {
2449 ev(&mut evidence, *v0, JavaType::Array(Box::new(JavaType::Int)), true);
2450 } else {
2451 ev(
2452 &mut evidence,
2453 *v0,
2454 JavaType::Object("java/lang/Object".into()),
2455 true,
2456 );
2457 }
2458 }
2459 let _ = var;
2460 }
2461 _ => {}
2462 });
2463 let _ = env;
2464
2465 let mut derefed: Vec<bool> = vec![false; vt.vars.len()];
2475 mark_derefs(body, &mut derefed);
2476 let mut arr_derefed: Vec<bool> = vec![false; vt.vars.len()];
2481 visit_all_exprs(body, &mut |x| {
2482 if let Expr::ArrayIndex { array, .. } = x {
2483 if let Expr::Local { var, .. } = &**array {
2484 if (*var as usize) < arr_derefed.len() {
2485 arr_derefed[*var as usize] = true;
2486 }
2487 }
2488 }
2489 if let Expr::Bin { op, l, r, .. } = x {
2497 if matches!(op, BinOp::Eq | BinOp::Ne)
2498 && (matches!(&**r, Expr::Const(ConstVal::Null))
2499 || matches!(&**l, Expr::Const(ConstVal::Null)))
2500 {
2501 for side in [l, r] {
2502 if let Expr::Local { var, .. } = &**side {
2503 ev(
2504 &mut evidence,
2505 *var,
2506 JavaType::Object("java/lang/Object".into()),
2507 true,
2508 );
2509 }
2510 }
2511 }
2512 }
2513 });
2514 const FINAL_JDK: &[&str] = &[
2515 "java/lang/Boolean",
2516 "java/lang/String",
2517 "java/lang/Integer",
2518 "java/lang/Long",
2519 "java/lang/Short",
2520 "java/lang/Byte",
2521 "java/lang/Character",
2522 "java/lang/Float",
2523 "java/lang/Double",
2524 ];
2525 for (i, evs) in evidence.iter().enumerate() {
2526 let info = &vt.vars[i];
2527 if info.is_param {
2528 continue;
2529 }
2530 let strong: Vec<JavaType> = evs
2531 .iter()
2532 .filter(|(_, s)| *s)
2533 .map(|(t, _)| t.clone())
2534 .collect();
2535 let all: Vec<JavaType> = evs.iter().map(|(t, _)| t.clone()).collect();
2536 if info.ty.erased().is_reference() {
2537 if strong.is_empty() {
2543 let refs: std::collections::HashSet<&str> = all
2544 .iter()
2545 .filter_map(|t| match t {
2546 JavaType::Object(n) if n.as_ref() != "java/lang/Object" => {
2547 Some(n.as_ref())
2548 }
2549 _ => None,
2550 })
2551 .collect();
2552 let mixed = all.iter().any(|t| t.is_numeric());
2553 if refs.len() >= 2 || (mixed && !refs.is_empty()) || (mixed && all.iter().any(|t| matches!(t, JavaType::Array(_)))) {
2554 vt.vars[i].ty =
2555 TypeRef::J(JavaType::Object("java/lang/Object".into()));
2556 continue;
2557 }
2558 }
2559 if arr_derefed[i] {
2562 let arr = strong
2563 .iter()
2564 .chain(all.iter())
2565 .find(|t| matches!(t, JavaType::Array(_)))
2566 .cloned();
2567 if let Some(t) = arr {
2568 vt.vars[i].ty = TypeRef::J(t);
2569 continue;
2570 }
2571 }
2572 let deref_pick = || {
2576 if !derefed[i] {
2577 return None;
2578 }
2579 strong
2580 .iter()
2581 .find(|t| {
2582 matches!(t, JavaType::Object(n)
2583 if !n.is_empty()
2584 && n.as_ref() != "java/lang/Object"
2585 && !FINAL_JDK.contains(&n.as_ref()))
2586 })
2587 .cloned()
2588 };
2589 if let Some(t) = deref_pick()
2590 .or_else(|| pick_object(&strong))
2591 .or_else(|| pick_object(&all))
2592 {
2593 vt.vars[i].ty = TypeRef::J(t);
2594 }
2595 } else if let Some(t) = pick_object(&strong) {
2596 vt.vars[i].ty = TypeRef::J(t);
2600 } else if strong.contains(&JavaType::Boolean) {
2601 vt.vars[i].ty = TypeRef::J(JavaType::Boolean);
2605 } else if !all.is_empty() && all.iter().all(|t| t.is_numeric()) {
2606 if let Some(t) = pick_numeric(&all) {
2607 vt.vars[i].ty = TypeRef::J(t);
2608 }
2609 } else {
2610 let refs = all.iter().filter(|t| t.is_reference()).count();
2614 let nums = all.iter().filter(|t| t.is_numeric()).count();
2615 if refs > 0 && nums > 0 {
2616 vt.vars[i].ty =
2617 TypeRef::J(JavaType::Object("java/lang/Object".into()));
2618 } else if let Some(t) = pick_object(&strong).or_else(|| pick_object(&all)) {
2619 vt.vars[i].ty = TypeRef::J(t);
2620 }
2621 }
2622 }
2623
2624 let types: Vec<&TypeRef> = vt.vars.iter().map(|v| &v.ty).collect();
2629 rewrite_exprs(body, &mut |e| {
2630 deep_rewrite(e, &mut |x| {
2631 if let Expr::Local { var, ty } = x {
2632 if let Some(want) = types.get(*var as usize) {
2633 if ty != *want {
2634 *ty = (*want).clone();
2635 }
2636 }
2637 }
2638 });
2639 });
2640
2641 coerce_num_consts(body, &types);
2644}
2645
2646fn coerce_num_consts(body: &mut Stmt, types: &[&TypeRef]) {
2647 walk_mut_deep(body, &mut |st| {
2648 let (var, val): (u32, &mut Expr) = match st {
2649 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &mut **target {
2650 Expr::Local { var, .. } => (*var, value),
2651 _ => return,
2652 },
2653 Stmt::LocalDef {
2654 var, init: Some(e), ..
2655 } => (*var, e),
2656 _ => return,
2657 };
2658 let want = match types.get(var as usize) {
2659 Some(TypeRef::J(t)) => t.clone(),
2660 _ => return,
2661 };
2662 let rewritten = match &*val {
2663 Expr::Const(ConstVal::Long(l)) => match &want {
2664 JavaType::Double => Some(Expr::Const(ConstVal::Double(*l as f64))),
2665 JavaType::Float => Some(Expr::Const(ConstVal::Float(*l as f32))),
2666 _ => None,
2667 },
2668 Expr::Const(ConstVal::Int(i)) => match &want {
2669 JavaType::Double => Some(Expr::Const(ConstVal::Double(*i as f64))),
2670 JavaType::Float => Some(Expr::Const(ConstVal::Float(*i as f32))),
2671 _ => None,
2672 },
2673 _ => None,
2674 };
2675 if let Some(e) = rewritten {
2676 *val = e;
2677 }
2678 });
2679}
2680
2681fn pick_object(evs: &[JavaType]) -> Option<JavaType> {
2682 evs.iter()
2683 .find(|t| {
2684 t.is_reference()
2685 && !matches!(t, JavaType::Object(n) if n.is_empty() || n.as_ref() == "java/lang/Object")
2686 })
2687 .cloned()
2688}
2689
2690fn pick_numeric(evs: &[JavaType]) -> Option<JavaType> {
2691 if evs.is_empty() {
2692 return None;
2693 }
2694 let mut t = evs[0].clone();
2695 for x in &evs[1..] {
2696 t = join_numeric(&t, x);
2697 }
2698 Some(t)
2699}
2700
2701fn join_numeric(a: &JavaType, b: &JavaType) -> JavaType {
2702 if a == b {
2703 return a.clone();
2704 }
2705 match (a, b) {
2706 (JavaType::Double, _) | (_, JavaType::Double) => JavaType::Double,
2707 (JavaType::Float, _) | (_, JavaType::Float) => JavaType::Float,
2708 (JavaType::Long, _) | (_, JavaType::Long) => JavaType::Long,
2709 (JavaType::Int, x) if x.is_integral() => JavaType::Int,
2710 (x, JavaType::Int) if x.is_integral() => JavaType::Int,
2711 (x, y) if x.is_integral() && y.is_integral() => JavaType::Int,
2712 _ => JavaType::Int,
2713 }
2714}
2715
2716fn expr_evidence<F: FnMut(u32, JavaType)>(e: &Expr, f: &mut F) {
2718 visit_exprs(e, &mut |x| match x {
2719 Expr::Method {
2720 owner,
2721 args,
2722 desc,
2723 is_static,
2724 cls,
2725 ..
2726 } => {
2727 if !*is_static {
2728 if let Some(o) = owner {
2729 if let Expr::Local { var, .. } = &**o {
2730 f(*var, JavaType::Object(cls.clone()));
2731 }
2732 }
2733 }
2734 for (i, a) in args.iter().enumerate() {
2735 if let Expr::Local { var, .. } = a {
2736 if let Some(t) = desc.args.get(i) {
2737 f(*var, t.clone());
2738 }
2739 }
2740 }
2741 }
2742 Expr::Field {
2743 owner,
2744 cls,
2745 ty,
2746 is_static,
2747 ..
2748 } => {
2749 if !*is_static && !cls.is_empty() {
2750 if let Some(o) = owner {
2751 if let Expr::Local { var, .. } = &**o {
2752 f(*var, JavaType::Object(cls.clone()));
2762 }
2763 }
2764 }
2765 let _ = ty;
2766 }
2767 Expr::Bin { op, l, r, .. }
2772 if matches!(op, BinOp::Eq | BinOp::Ne)
2773 && (matches!(&**r, Expr::Const(ConstVal::Null))
2774 || matches!(&**l, Expr::Const(ConstVal::Null))) =>
2775 {
2776 for side in [l, r] {
2777 if let Expr::Local { var, .. } = &**side {
2778 f(*var, JavaType::Object("java/lang/Object".into()));
2779 }
2780 }
2781 }
2782 Expr::ArrayIndex { array, .. } => {
2783 if let Expr::Local { var, ty, .. } = &**array {
2784 let t = ty.erased();
2789 if matches!(t, JavaType::Array(_)) {
2790 f(*var, t);
2791 } else {
2792 f(*var, JavaType::Array(Box::new(JavaType::Int)));
2793 }
2794 }
2795 }
2796 Expr::Assign { .. } => {
2797 }
2800 Expr::Cast { e: inner, .. } => {
2801 let _ = inner;
2807 }
2808 Expr::InstanceOf { e: inner, .. } => {
2809 let _ = inner;
2810 }
2811 _ => {}
2812 });
2813}
2814
2815pub fn mark_field_owner_concrete(vt: &mut VarTable, body: &mut Stmt) {
2827 let mut owners: HashSet<u32> = HashSet::default();
2828 rewrite_exprs(body, &mut |e| {
2829 visit_exprs(e, &mut |x| {
2830 if let Expr::Field {
2831 owner: Some(o),
2832 is_static: false,
2833 ..
2834 } = x
2835 {
2836 if let Expr::Local { var, .. } = &**o {
2837 owners.insert(*var);
2838 }
2839 }
2840 });
2841 });
2842 for var in owners {
2843 let i = var as usize;
2844 if i >= vt.vars.len() {
2845 continue;
2846 }
2847 if let JavaType::Object(cls) = vt.vars[i].ty.erased() {
2848 if let Some(last) = cls.rsplit('$').next() {
2849 if !last.is_empty() && last.chars().all(|c| c.is_ascii_digit()) {
2850 vt.force_concrete_vars.insert(var);
2851 }
2852 }
2853 }
2854 }
2855 let concrete: Vec<u32> = vt
2864 .vars
2865 .iter()
2866 .filter(|v| {
2867 if let JavaType::Object(cls) = v.ty.erased() {
2868 cls.rsplit('$')
2869 .next()
2870 .is_some_and(|l| !l.is_empty() && l.chars().all(|c| c.is_ascii_digit()))
2871 } else {
2872 false
2873 }
2874 })
2875 .map(|v| v.id)
2876 .collect();
2877 for var in concrete {
2878 vt.force_concrete_vars.insert(var);
2879 }
2880}
2881
2882pub fn insert_object_narrowing_casts(
2894 vt: &VarTable,
2895 body: &mut Stmt,
2896 pool: &DexPool,
2897 ret: &JavaType,
2898) {
2899 let value_ty = |e: &Expr| -> JavaType {
2902 match e {
2903 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
2904 other => other.type_ref().erased(),
2905 }
2906 };
2907 let is_top_object = |e: &Expr| -> bool {
2908 value_ty(e) == JavaType::Object("java/lang/Object".into())
2909 };
2910 let specific_ref = |ty: &TypeRef| -> Option<TypeRef> {
2914 match ty.erased() {
2915 JavaType::Object(c) if c.as_ref() != "java/lang/Object" => Some(ty.clone()),
2916 JavaType::Array(_) => Some(ty.clone()),
2917 _ => None,
2918 }
2919 };
2920 let castable = |e: &Expr| -> bool {
2921 is_top_object(e)
2922 && !matches!(e, Expr::Const(_) | Expr::Cast { .. } | Expr::InstanceOf { .. })
2923 };
2924 let needs_downcast = |e: &Expr, tt: &JavaType| -> bool {
2934 if matches!(e, Expr::Const(_) | Expr::Cast { .. } | Expr::InstanceOf { .. }) {
2935 return false;
2936 }
2937 let v = value_ty(e);
2938 let (JavaType::Object(vn), JavaType::Object(tn)) = (&v, tt) else {
2939 return false;
2940 };
2941 if vn.as_ref() == "java/lang/Object" || tn.as_ref() == "java/lang/Object" {
2942 return false;
2943 }
2944 vn != tn && !pool.is_subtype(vn, tn)
2945 };
2946 walk_mut_deep(body, &mut |st| match st {
2947 Stmt::ExprStmt(Expr::Assign {
2948 target,
2949 value,
2950 op: AssignOp::Plain,
2951 ..
2952 }) => {
2953 let tgt = match &**target {
2954 Expr::Local { var, .. } => vt.var(*var).ty.clone(),
2955 Expr::Field { ty, .. } => ty.clone(),
2956 Expr::ArrayIndex { array, .. } => {
2963 let aty = match &**array {
2964 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
2965 other => other.type_ref().erased(),
2966 };
2967 match aty {
2968 JavaType::Array(inner) => TypeRef::J(*inner),
2969 _ => return,
2970 }
2971 }
2972 _ => return,
2973 };
2974 if let Some(t) = specific_ref(&tgt) {
2975 if castable(value) || needs_downcast(value, &tgt.erased()) {
2976 let v = std::mem::replace(value, Box::new(Expr::This));
2977 **value = Expr::Cast { ty: t, e: v };
2978 }
2979 }
2980 }
2981 Stmt::LocalDef { var, init: Some(value), .. } => {
2982 if let Some(t) = specific_ref(&vt.var(*var).ty) {
2983 let tt = vt.var(*var).ty.erased();
2984 if castable(value) || needs_downcast(value, &tt) {
2985 let v = std::mem::replace(value, Expr::This);
2986 *value = Expr::Cast {
2987 ty: t,
2988 e: Box::new(v),
2989 };
2990 }
2991 }
2992 }
2993 Stmt::Return(Some(value)) => {
2994 if let Some(t) = specific_ref(&TypeRef::J(ret.clone())) {
2995 if castable(value) || needs_downcast(value, ret) {
2996 let v = std::mem::replace(value, Expr::This);
2997 *value = Expr::Cast { ty: t, e: Box::new(v) };
2998 }
2999 }
3000 }
3001 _ => {}
3002 });
3003 walk_stmt_exprs(body, &mut |e| {
3010 deep_rewrite(e, &mut |x| {
3011 let (args, arg_tys): (&mut Vec<Expr>, &[JavaType]) = match x {
3012 Expr::Method { desc, args, is_dynamic: false, .. } => {
3013 (args, desc.args.as_slice())
3014 }
3015 Expr::New { arg_tys, args, .. } => (args, arg_tys.as_slice()),
3019 _ => return,
3020 };
3021 for (i, a) in args.iter_mut().enumerate() {
3022 let Some(formal) = arg_tys.get(i) else {
3023 continue;
3024 };
3025 let Some(t) = specific_ref(&TypeRef::J(formal.clone())) else {
3026 continue;
3027 };
3028 if !(castable(a) || needs_downcast(a, formal)) {
3029 continue;
3030 }
3031 let v = std::mem::replace(a, Expr::Const(ConstVal::Null));
3032 *a = Expr::Cast { ty: t, e: Box::new(v) };
3033 }
3034 });
3035 });
3036}
3037
3038pub fn fix_primitive_assign_casts(vt: &VarTable, body: &mut Stmt, ret_ty: &JavaType) {
3047 fn numlike(t: &JavaType) -> bool {
3048 matches!(
3049 t,
3050 JavaType::Byte
3051 | JavaType::Short
3052 | JavaType::Int
3053 | JavaType::Char
3054 | JavaType::Long
3055 | JavaType::Float
3056 | JavaType::Double
3057 )
3058 }
3059 fn val_ty(e: &Expr, vt: &VarTable) -> JavaType {
3060 match e {
3061 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
3062 vt.var(*var).ty.erased()
3063 }
3064 other => other.type_ref().erased(),
3065 }
3066 }
3067 fn wrap(ty: &JavaType, value: &mut Expr) {
3068 if matches!(value, Expr::Cast { .. }) {
3069 return;
3070 }
3071 if val_ty_static(value) == *ty {
3072 return;
3073 }
3074 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
3075 *value = Expr::Cast {
3076 ty: TypeRef::J(ty.clone()),
3077 e: Box::new(taken),
3078 };
3079 }
3080 fn val_ty_static(e: &Expr) -> JavaType {
3081 e.type_ref().erased()
3082 }
3083 fn coerce(tt: &JavaType, value: &mut Expr, vt: &VarTable) {
3091 match (&*value, tt) {
3096 (Expr::Const(ConstVal::Int(b)), JavaType::Float) => {
3097 *value = Expr::Const(ConstVal::Float(f32::from_bits(*b as u32)));
3098 return;
3099 }
3100 (Expr::Const(ConstVal::Long(b)), JavaType::Double) => {
3101 *value = Expr::Const(ConstVal::Double(f64::from_bits(*b as u64)));
3102 return;
3103 }
3104 (Expr::Const(ConstVal::Float(f)), JavaType::Int) => {
3105 *value = Expr::Const(ConstVal::Int(f.to_bits() as i32));
3106 return;
3107 }
3108 (Expr::Const(ConstVal::Double(d)), JavaType::Long) => {
3109 *value = Expr::Const(ConstVal::Long(d.to_bits() as i64));
3110 return;
3111 }
3112 _ => {}
3113 }
3114 let vt_val = val_ty(value, vt);
3115 let tt_bool = matches!(tt, JavaType::Boolean);
3116 let val_bool = matches!(vt_val, JavaType::Boolean);
3117 if tt_bool && !val_bool && numlike(&vt_val) {
3118 if matches!(
3119 value,
3120 Expr::Const(_)
3121 | Expr::Bin { op: BinOp::Eq | BinOp::Ne, .. }
3122 | Expr::Un { op: UnOp::Not, .. }
3123 ) {
3124 return;
3129 }
3130 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
3131 *value = Expr::Bin {
3132 op: BinOp::Ne,
3133 l: Box::new(taken),
3134 r: Box::new(Expr::Const(ConstVal::Int(0))),
3135 ty: Some(TypeRef::J(JavaType::Boolean)),
3136 };
3137 } else if !tt_bool && val_bool && numlike(tt) {
3138 if matches!(value, Expr::Cast { .. }) {
3139 return;
3140 }
3141 let taken = std::mem::replace(value, Expr::Const(ConstVal::Null));
3142 *value = Expr::Cast {
3143 ty: TypeRef::J(tt.clone()),
3144 e: Box::new(Expr::Cond {
3145 c: Box::new(taken),
3146 t: Box::new(Expr::Const(ConstVal::Int(1))),
3147 f: Box::new(Expr::Const(ConstVal::Int(0))),
3148 }),
3149 };
3150 } else if numlike(tt) && numlike(&vt_val) {
3151 wrap(tt, value);
3152 }
3153 }
3154 walk_mut_deep(body, &mut |st| match st {
3155 Stmt::ExprStmt(Expr::Assign {
3156 target,
3157 op: AssignOp::Plain,
3158 value,
3159 }) => {
3160 let tt = match &**target {
3161 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
3162 vt.var(*var).ty.erased()
3163 }
3164 Expr::Field { ty, .. } => ty.erased(),
3165 Expr::ArrayIndex { array, .. } => match array.type_ref().erased() {
3169 JavaType::Array(inner) => *inner,
3170 _ => return,
3171 },
3172 _ => return,
3173 };
3174 coerce(&tt, value, vt);
3175 }
3176 Stmt::LocalDef {
3177 var,
3178 init: Some(value),
3179 ..
3180 } => {
3181 if (*var as usize) >= vt.vars.len() {
3182 return;
3183 }
3184 let tt = vt.var(*var).ty.erased();
3185 coerce(&tt, value, vt);
3186 }
3187 Stmt::Return(Some(e)) => {
3188 if !numlike(ret_ty) || !numlike(&val_ty(e, vt)) {
3189 return;
3190 }
3191 wrap(ret_ty, e);
3192 }
3193 _ => {}
3194 });
3195}
3196
3197pub fn fix_field_owner_downcasts(body: &mut Stmt, vt: &VarTable, pool: &DexPool) {
3216 fn owner_internal(o: &Expr, vt: &VarTable) -> Option<std::sync::Arc<str>> {
3217 let t = match o {
3218 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
3219 other => other.type_ref().erased(),
3220 };
3221 match t {
3222 JavaType::Object(n) => Some(n),
3223 _ => None,
3224 }
3225 }
3226 fn declares_up(pool: &DexPool, class: &str, name: &str) -> bool {
3227 let mut cur = Some(class.to_string());
3228 let mut hops = 0;
3229 while let Some(c) = cur {
3230 hops += 1;
3231 if hops > 64 {
3232 return false;
3233 }
3234 let Some(pc) = pool.get(&c) else { return false };
3235 if pc
3236 .instance_fields
3237 .iter()
3238 .chain(pc.static_fields.iter())
3239 .any(|f| f.name.as_str() == name)
3240 {
3241 return true;
3242 }
3243 cur = pc.super_name.clone();
3244 }
3245 false
3246 }
3247 walk_stmt_exprs(body, &mut |e| {
3248 deep_rewrite(e, &mut |x| {
3249 if let Expr::Field {
3250 owner: Some(o),
3251 cls,
3252 name,
3253 is_static: false,
3254 ..
3255 } = x
3256 {
3257 let Some(ot) = owner_internal(o, vt) else {
3258 return;
3259 };
3260 if ot.as_ref() == cls.as_ref() || !pool.is_subtype(cls, &ot) {
3261 return;
3262 }
3263 if declares_up(pool, &ot, name) || !declares_up(pool, cls, name) {
3264 return;
3265 }
3266 let ty = TypeRef::J(JavaType::Object(cls.clone()));
3267 if let Expr::Cast { ty: ct, .. } = &mut **o {
3268 *ct = ty;
3269 } else {
3270 let taken = std::mem::replace(&mut **o, Expr::This);
3271 **o = Expr::Cast { ty, e: Box::new(taken) };
3272 }
3273 }
3274 let (tgt_ty, value) = match x {
3276 Expr::Assign { target, op: AssignOp::Plain, value } => {
3277 let t = match &**target {
3278 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
3279 Expr::Field { ty, .. } => ty.erased(),
3280 _ => return,
3281 };
3282 (t, value)
3283 }
3284 _ => return,
3285 };
3286 let JavaType::Object(sn) = tgt_ty else { return };
3287 if let Expr::Cast { ty, .. } = &mut **value {
3288 if let TypeRef::J(JavaType::Object(tn)) = ty {
3289 if tn.as_ref() != sn.as_ref() && pool.is_subtype(&sn, tn) {
3290 *ty = TypeRef::J(JavaType::Object(sn.clone()));
3291 }
3292 }
3293 }
3294 });
3295 });
3296 walk_mut_deep(body, &mut |st| {
3298 if let Stmt::LocalDef { var, init: Some(value), .. } = st {
3299 let tt = if (*var as usize) < vt.vars.len() {
3300 vt.vars[*var as usize].ty.erased()
3301 } else {
3302 return;
3303 };
3304 let JavaType::Object(sn) = tt else { return };
3305 if let Expr::Cast { ty, .. } = value {
3306 if let TypeRef::J(JavaType::Object(tn)) = ty {
3307 if tn.as_ref() != sn.as_ref() && pool.is_subtype(&sn, tn) {
3308 *ty = TypeRef::J(JavaType::Object(sn.clone()));
3309 }
3310 }
3311 }
3312 }
3313 });
3314}
3315
3316pub fn fix_incomparable_equality(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
3323 fn side_ty(e: &Expr, vt: &VarTable) -> JavaType {
3324 match e {
3325 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
3326 vt.var(*var).ty.erased()
3327 }
3328 other => other.type_ref().erased(),
3329 }
3330 }
3331 let incomparable = |a: &JavaType, b: &JavaType| -> bool {
3332 use JavaType::{Array, Object};
3333 match (a, b) {
3334 (Object(x), Object(y)) => {
3335 if x == y || x.as_ref() == "java/lang/Object" || y.as_ref() == "java/lang/Object"
3336 {
3337 return false;
3338 }
3339 let xi = pool.get(x).map(|c| c.is_interface()).unwrap_or(true);
3340 let yi = pool.get(y).map(|c| c.is_interface()).unwrap_or(true);
3341 if xi || yi {
3345 return false;
3346 }
3347 !pool.is_subtype(x, y) && !pool.is_subtype(y, x)
3348 }
3349 (Array(_), Object(y)) | (Object(y), Array(_)) => {
3350 y.as_ref() != "java/lang/Object"
3351 && y.as_ref() != "java/lang/Cloneable"
3352 && y.as_ref() != "java/io/Serializable"
3353 && !pool.get(y).map(|c| c.is_interface()).unwrap_or(false)
3354 }
3355 _ => false,
3356 }
3357 };
3358 let is_ref = |t: &JavaType| matches!(t, JavaType::Object(_) | JavaType::Array(_));
3359 walk_stmt_exprs(body, &mut |e| {
3360 deep_rewrite(e, &mut |x| {
3361 if let Expr::Bin {
3362 op: BinOp::Eq | BinOp::Ne,
3363 l,
3364 r,
3365 ..
3366 } = x
3367 {
3368 let lt = side_ty(l, vt);
3369 let rt = side_ty(r, vt);
3370 if is_ref(<) && matches!(&**r, Expr::Const(ConstVal::Int(0))) {
3376 **r = Expr::Const(ConstVal::Null);
3377 return;
3378 }
3379 if is_ref(&rt) && matches!(&**l, Expr::Const(ConstVal::Int(0))) {
3380 **l = Expr::Const(ConstVal::Null);
3381 return;
3382 }
3383 if incomparable(<, &rt) {
3384 let taken = std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
3385 **l = Expr::Cast {
3386 ty: TypeRef::J(JavaType::Object("java/lang/Object".into())),
3387 e: taken,
3388 };
3389 }
3390 }
3391 });
3392 });
3393}
3394
3395pub fn fix_primitive_arg_bridges(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
3409 fn val_ty(e: &Expr, vt: &VarTable) -> JavaType {
3410 match e {
3411 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
3412 other => other.type_ref().erased(),
3413 }
3414 }
3415 #[allow(clippy::type_complexity)]
3416 let is_bool = |t: &JavaType| matches!(t, JavaType::Boolean);
3417 let is_num = |t: &JavaType| {
3418 matches!(
3419 t,
3420 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
3421 | JavaType::Long | JavaType::Float | JavaType::Double
3422 )
3423 };
3424 fn bridge(actual: &JavaType, formal: &JavaType, arg: &Expr) -> Option<Expr> {
3429 match (arg, formal) {
3439 (Expr::Const(ConstVal::Int(b)), JavaType::Float) => {
3440 return Some(Expr::Const(ConstVal::Float(f32::from_bits(*b as u32))));
3441 }
3442 (Expr::Const(ConstVal::Long(b)), JavaType::Double) => {
3443 return Some(Expr::Const(ConstVal::Double(f64::from_bits(*b as u64))));
3444 }
3445 (Expr::Const(ConstVal::Float(f)), JavaType::Int) => {
3446 return Some(Expr::Const(ConstVal::Int(f.to_bits() as i32)));
3447 }
3448 (Expr::Const(ConstVal::Double(d)), JavaType::Long) => {
3449 return Some(Expr::Const(ConstVal::Long(d.to_bits() as i64)));
3450 }
3451 _ => {}
3452 }
3453 if matches!(actual, JavaType::Boolean)
3454 && matches!(
3455 formal,
3456 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
3457 | JavaType::Long | JavaType::Float | JavaType::Double
3458 )
3459 {
3460 Some(Expr::Cast {
3461 ty: TypeRef::J(formal.clone()),
3462 e: Box::new(Expr::Cond {
3463 c: Box::new(arg.clone()),
3464 t: Box::new(Expr::Const(ConstVal::Int(1))),
3465 f: Box::new(Expr::Const(ConstVal::Int(0))),
3466 }),
3467 })
3468 } else if matches!(formal, JavaType::Boolean)
3469 && matches!(
3470 actual,
3471 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
3472 | JavaType::Long | JavaType::Float | JavaType::Double
3473 )
3474 {
3475 Some(Expr::Bin {
3476 op: BinOp::Ne,
3477 l: Box::new(arg.clone()),
3478 r: Box::new(Expr::Const(ConstVal::Int(0))),
3479 ty: Some(TypeRef::J(JavaType::Boolean)),
3480 })
3481 } else if matches!(
3482 actual,
3483 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
3484 | JavaType::Long | JavaType::Float | JavaType::Double
3485 ) && matches!(
3486 formal,
3487 JavaType::Byte | JavaType::Short | JavaType::Int | JavaType::Char
3488 | JavaType::Long | JavaType::Float | JavaType::Double
3489 ) && !implicit_widening(actual, formal)
3490 {
3491 Some(Expr::Cast {
3500 ty: TypeRef::J(formal.clone()),
3501 e: Box::new(arg.clone()),
3502 })
3503 } else {
3504 None
3505 }
3506 }
3507 fn fw_resolvable(n: &str) -> bool {
3522 n.starts_with("java/")
3523 || n.starts_with("javax/")
3524 || n.starts_with("android/")
3525 || n.starts_with("dalvik/")
3526 || n.starts_with("junit/")
3527 || n.starts_with("org/json/")
3528 || n.starts_with("org/w3c/")
3529 || n.starts_with("org/xml/")
3530 || n.starts_with("org/xmlpull/")
3531 || n.starts_with("org/apache/http/")
3532 }
3533 fn null_arg_cast(a: &mut Expr, f: &JavaType, pool: &DexPool) -> bool {
3534 if !matches!(&*a, Expr::Const(ConstVal::Null)) {
3535 return false;
3536 }
3537 if let JavaType::Object(n) = f {
3538 let resolvable = pool.get(n).is_some() || fw_resolvable(n);
3544 if n.as_ref() != "java/lang/Object" && resolvable {
3545 *a = Expr::Cast {
3546 ty: TypeRef::J(f.clone()),
3547 e: Box::new(Expr::Const(ConstVal::Null)),
3548 };
3549 return true;
3550 }
3551 } else if let JavaType::Array(_) = f {
3552 let mut e: &JavaType = f;
3559 while let JavaType::Array(inner) = e {
3560 e = &**inner;
3561 }
3562 let resolvable = match e {
3563 JavaType::Object(n) => pool.get(n).is_some() || fw_resolvable(n),
3564 _ => true, };
3566 if resolvable {
3567 *a = Expr::Cast {
3568 ty: TypeRef::J(f.clone()),
3569 e: Box::new(Expr::Const(ConstVal::Null)),
3570 };
3571 return true;
3572 }
3573 }
3574 false
3577 }
3578 fn implicit_widening(a: &JavaType, f: &JavaType) -> bool {
3581 use JavaType::*;
3582 match (a, f) {
3583 (Byte, Short) | (Byte, Int) | (Byte, Long) | (Byte, Float) | (Byte, Double) => true,
3584 (Short, Int) | (Short, Long) | (Short, Float) | (Short, Double) => true,
3585 (Char, Int) | (Char, Long) | (Char, Float) | (Char, Double) => true,
3586 (Int, Long) | (Int, Float) | (Int, Double) => true,
3587 (Long, Float) | (Long, Double) => true,
3588 (Float, Double) => true,
3589 (x, y) => x == y,
3590 }
3591 }
3592 let overload_competes = |cls: &str, name: &str, args: &[JavaType]| -> bool {
3601 let Some(c) = pool.get(cls) else {
3602 return false;
3603 };
3604 c.all_methods().any(|m| {
3605 &*m.name == name
3606 && m.parsed_desc()
3607 .is_some_and(|d| d.args.len() == args.len() && d.args != args)
3608 })
3609 };
3610 let ctor_formals = |cls: &str, n: usize| -> Option<Vec<JavaType>> {
3613 let c = pool.get(cls)?;
3614 let mut out: Option<Vec<JavaType>> = None;
3615 for m in c.all_methods() {
3616 if &*m.name != "<init>" {
3617 continue;
3618 }
3619 let Some(d) = m.parsed_desc() else { continue };
3620 if d.args.len() != n {
3621 continue;
3622 }
3623 match &out {
3624 None => out = Some(d.args.clone()),
3625 Some(prev) if *prev != d.args => return None,
3626 Some(_) => {}
3627 }
3628 }
3629 out
3630 };
3631 walk_stmt_exprs(body, &mut |e| {
3632 deep_rewrite(e, &mut |x| match x {
3633 Expr::Method {
3634 cls,
3635 name,
3636 desc,
3637 args,
3638 is_static,
3639 ..
3640 } => {
3641 if args.len() != desc.args.len() {
3642 return;
3643 }
3644 let _ = is_static;
3645 let mut competes: Option<bool> = None;
3646 for (a, f) in args.iter_mut().zip(desc.args.iter()) {
3647 if null_arg_cast(a, f, pool) {
3648 continue;
3649 }
3650 if matches!(a, Expr::Cast { .. }) {
3651 continue;
3652 }
3653 let subtype_arg = match (&val_ty(a, vt), f) {
3664 (JavaType::Object(an), JavaType::Object(fn_)) => {
3665 (an != fn_ && pool.is_subtype(an, fn_))
3670 || (fn_.as_ref() == "java/lang/Object"
3671 && an.as_ref() != "java/lang/Object")
3672 }
3673 (JavaType::Array(_), JavaType::Object(fn_))
3674 if fn_.as_ref() == "java/lang/Object" =>
3675 {
3676 true
3677 }
3678 _ => false,
3679 };
3680 let null_obj =
3694 matches!(a, Expr::Const(ConstVal::Null)) && matches!(f, JavaType::Object(_));
3695 let framework_owner = pool.get(cls.as_ref()).is_none();
3696 let fw_null_ambiguous = framework_owner
3697 && matches!(
3698 (cls.as_ref(), name.as_ref()),
3699 ("java/lang/StringBuilder", "append")
3700 | ("java/lang/StringBuffer", "append")
3701 | ("java/io/PrintStream", "println")
3702 | ("java/io/PrintStream", "print")
3703 | ("java/io/PrintWriter", "println")
3704 | ("java/io/PrintWriter", "print")
3705 | ("java/lang/String", "valueOf")
3706 );
3707 let fw_subtype_pinnable = framework_owner
3718 && subtype_arg
3719 && !matches!(f, JavaType::Object(fn_) if fn_.as_ref() == "java/lang/Object")
3720 && matches!(
3721 (cls.as_ref(), name.as_ref()),
3722 ("android/content/Intent", "putExtra")
3723 | ("java/util/concurrent/ScheduledExecutorService", "schedule")
3724 | ("java/util/concurrent/ExecutorService", "submit")
3725 | ("java/util/concurrent/ExecutorService", "invokeAll")
3726 );
3727 if fw_subtype_pinnable
3728 || (subtype_arg
3729 && *competes.get_or_insert_with(|| {
3730 overload_competes(cls, name, &desc.args)
3731 }))
3732 || (null_obj
3733 && (fw_null_ambiguous
3734 || *competes.get_or_insert_with(|| {
3735 overload_competes(cls, name, &desc.args)
3736 })))
3737 {
3738 let old =
3739 std::mem::replace(a, Expr::Const(ConstVal::Null));
3740 *a = Expr::Cast {
3741 ty: TypeRef::J(f.clone()),
3742 e: Box::new(old),
3743 };
3744 continue;
3745 }
3746 if !is_bool(&val_ty(a, vt)) && !is_num(&val_ty(a, vt)) {
3747 continue;
3748 }
3749 if !is_bool(f) && !is_num(f) {
3750 continue;
3751 }
3752 if let Some(b) = bridge(&val_ty(a, vt), f, a) {
3753 *a = b;
3754 }
3755 }
3756 }
3757 Expr::New { cls, args, arg_tys, .. } => {
3758 let formals_owned = if arg_tys.len() == args.len() && !arg_tys.is_empty() {
3759 Some(arg_tys.clone())
3760 } else {
3761 ctor_formals(cls, args.len())
3762 };
3763 if let Some(formals) = formals_owned {
3764 for (a, f) in args.iter_mut().zip(formals.iter()) {
3765 if null_arg_cast(a, f, pool) {
3766 continue;
3767 }
3768 if matches!(a, Expr::Cast { .. }) {
3769 continue;
3770 }
3771 if !is_bool(f) && !is_num(f) {
3772 continue;
3773 }
3774 if let Some(b) = bridge(&val_ty(a, vt), f, a) {
3775 *a = b;
3776 }
3777 }
3778 }
3779 }
3780 _ => {}
3781 });
3782 });
3783}
3784
3785pub fn deshadow_locals(vt: &mut VarTable, pool: &crate::DexPool, class_name: &str, body: &Stmt) {
3797 let segs = pool.root_pkg_segs();
3798 let obscured = crate::classdec::obscured_simples_snapshot();
3802 let self_pkg = class_name.rfind('/').map(|i| &class_name[..i]).unwrap_or("");
3809 let mut same_pkg_simples: jdc_core::FxHashSet<String> = jdc_core::FxHashSet::default();
3810 visit_all_exprs(body, &mut |x| {
3811 let cls = match x {
3812 Expr::Field { cls, is_static: true, .. } => Some(cls.as_ref()),
3813 Expr::Method { cls, is_static: true, .. } => Some(cls.as_ref()),
3814 _ => None,
3815 };
3816 if let Some(c) = cls {
3817 let pkg = c.rfind('/').map(|i| &c[..i]).unwrap_or("");
3818 if pkg == self_pkg {
3819 let simple = &c[c.rfind('/').map(|i| i + 1).unwrap_or(0)..];
3820 let head = simple.split('$').next().unwrap_or(simple);
3821 if !head.is_empty() {
3822 same_pkg_simples.insert(head.to_string());
3823 }
3824 }
3825 }
3826 });
3827 let hit = |name: &str| {
3828 segs.contains(name) || obscured.contains(name) || same_pkg_simples.contains(name)
3829 };
3830 if (segs.is_empty() && obscured.is_empty() && same_pkg_simples.is_empty())
3831 || !vt.vars.iter().any(|v| hit(&v.name))
3832 {
3833 return;
3834 }
3835 let mut taken: jdc_core::FxHashSet<String> =
3836 vt.vars.iter().map(|v| v.name.clone()).collect();
3837 for v in vt.vars.iter_mut() {
3838 if !hit(&v.name) {
3839 continue;
3840 }
3841 let mut k = 0u32;
3842 let cand = loop {
3843 k += 1;
3844 let c = if k == 1 {
3845 format!("{}x", v.name)
3846 } else {
3847 format!("{}x{}", v.name, k)
3848 };
3849 if !taken.contains(&c) {
3850 break c;
3851 }
3852 };
3853 taken.insert(cand.clone());
3854 v.name = cand;
3855 }
3856}
3857
3858pub fn fix_ctor_this_aliases(body: &mut Stmt, vt: &VarTable) {
3884 let Some(this_var) = vt
3885 .vars
3886 .iter()
3887 .find(|v| v.is_param && v.name == "this")
3888 .map(|v| v.id)
3889 else {
3890 return;
3891 };
3892 let mut analysis = VarAnalysis {
3893 assigns: Vec::new(),
3894 reads: Vec::new(),
3895 values: Vec::new(),
3896 };
3897 analyze_vars(body, &mut analysis);
3898 let n = vt.vars.len().max(analysis.assigns.len());
3899 let mut alias = vec![false; n];
3900 for _ in 0..4 {
3901 let mut changed = false;
3902 for v in 0..n as u32 {
3903 if alias[v as usize] {
3904 continue;
3905 }
3906 if analysis.assigns.get(v as usize).copied().unwrap_or(0) != 1 {
3907 continue;
3908 }
3909 let Some(Expr::Local { var: src, .. }) = analysis.values.get(v as usize).and_then(|o| o.as_ref()) else {
3910 continue;
3911 };
3912 if *src == this_var
3913 || (*src < n as u32 && alias[*src as usize])
3914 {
3915 alias[v as usize] = true;
3916 changed = true;
3917 }
3918 }
3919 if !changed {
3920 break;
3921 }
3922 }
3923 if !alias.iter().any(|&b| b) {
3924 return;
3925 }
3926 walk_stmt_exprs(body, &mut |e| {
3927 deep_rewrite_reads(e, &mut |x| {
3928 if let Expr::Local { var, .. } = x {
3929 if (*var as usize) < n && alias[*var as usize] {
3930 *x = Expr::This;
3931 }
3932 }
3933 });
3934 });
3935}
3936
3937pub fn fix_this0_owners(body: &mut Stmt, class_name: &str) {
3938 walk_stmt_exprs(body, &mut |e| {
3939 deep_rewrite(e, &mut |x| {
3940 if let Expr::Field { owner: Some(_), cls, name, .. } = x {
3941 if name.as_ref() == "this$0" && cls.as_ref() == class_name {
3942 if let Expr::Field { owner, .. } = x {
3943 *owner = None;
3944 }
3945 }
3946 }
3947 });
3948 });
3949}
3950
3951pub fn rescue_primitive_receivers(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
3952 fn prim(t: &JavaType) -> bool {
3953 match t {
3954 JavaType::Int
3955 | JavaType::Long
3956 | JavaType::Short
3957 | JavaType::Byte
3958 | JavaType::Char
3959 | JavaType::Float
3960 | JavaType::Double
3961 | JavaType::Boolean => true,
3962 JavaType::Object(n) => matches!(
3966 n.as_ref(),
3967 "java/lang/Boolean"
3968 | "java/lang/String"
3969 | "java/lang/Integer"
3970 | "java/lang/Long"
3971 | "java/lang/Short"
3972 | "java/lang/Byte"
3973 | "java/lang/Character"
3974 | "java/lang/Float"
3975 | "java/lang/Double"
3976 ),
3977 _ => false,
3978 }
3979 }
3980 walk_stmt_exprs(body, &mut |e| {
3981 deep_rewrite(e, &mut |x| {
3982 let recv_var = {
3984 let o: &Expr = match &*x {
3985 Expr::Method { owner: Some(o), .. } => o,
3986 Expr::Field { owner: Some(o), is_static: false, .. } => o,
3987 _ => return,
3988 };
3989 let Expr::Local { var, ty } = o else {
3990 return;
3991 };
3992 let vt_ty = if (*var as usize) < vt.vars.len() {
3993 vt.var(*var).ty.erased()
3994 } else {
3995 ty.erased()
3996 };
3997 if !prim(&vt_ty) {
3998 return;
3999 }
4000 *var
4001 };
4002 let cls: std::sync::Arc<str> = match &*x {
4003 Expr::Method { cls, .. } | Expr::Field { cls, .. } => cls.clone(),
4004 _ => return,
4005 };
4006 let mut cands: Vec<u32> = Vec::new();
4008 for v in &vt.vars {
4009 if let JavaType::Object(n) = v.ty.erased() {
4010 if n.as_ref() == cls.as_ref()
4011 || pool.is_subtype(n.as_ref(), cls.as_ref())
4012 {
4013 cands.push(v.id);
4014 }
4015 }
4016 }
4017 let id = if cands.len() == 1 {
4018 cands[0]
4019 } else if cands.len() > 1 {
4020 let slot = if (recv_var as usize) < vt.vars.len() {
4025 Some(vt.var(recv_var).slot)
4026 } else {
4027 None
4028 };
4029 let mut hits: Vec<u32> = match slot {
4030 Some(sl) => cands
4031 .iter()
4032 .copied()
4033 .filter(|&c| {
4034 c != recv_var
4035 && (c as usize) < vt.vars.len()
4036 && vt.var(c).slot == sl
4037 })
4038 .collect(),
4039 None => Vec::new(),
4040 };
4041 if hits.is_empty() {
4042 hits = cands
4045 .iter()
4046 .copied()
4047 .filter(|&c| c != recv_var)
4048 .collect();
4049 }
4050 hits.sort_unstable();
4051 hits.dedup();
4052 if hits.is_empty() {
4053 return;
4054 }
4055 *hits.last().unwrap()
4056 } else {
4057 if cands.is_empty() {
4064 return;
4065 }
4066 let slot = if (recv_var as usize) < vt.vars.len() {
4067 vt.var(recv_var).slot
4068 } else {
4069 return;
4070 };
4071 let mut hits: Vec<u32> = cands
4072 .into_iter()
4073 .filter(|&c| {
4074 c != recv_var
4075 && (c as usize) < vt.vars.len()
4076 && vt.var(c).slot == slot
4077 })
4078 .collect();
4079 hits.sort_unstable();
4080 hits.dedup();
4081 if hits.len() != 1 {
4082 return;
4083 }
4084 hits[0]
4085 };
4086 let o: &mut Box<Expr> = match x {
4088 Expr::Method { owner: Some(o), .. } => o,
4089 Expr::Field { owner: Some(o), is_static: false, .. } => o,
4090 _ => return,
4091 };
4092 if let Expr::Local { var, ty } = &mut **o {
4093 if *var == recv_var {
4094 *var = id;
4095 *ty = vt.vars[id as usize].ty.clone();
4096 }
4097 }
4098 });
4099 });
4100
4101}
4102
4103pub fn rescue_arg_return_swaps(
4109 body: &mut Stmt,
4110 vt: &VarTable,
4111 pool: &crate::DexPool,
4112 ret_ty: &JavaType,
4113) {
4114 let mut top_ids: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
4122 for v in &vt.vars {
4123 if v.is_param {
4124 top_ids.insert(v.id);
4125 }
4126 }
4127 if let Stmt::Block(stmts) = body {
4128 for st in stmts.iter() {
4129 if let Stmt::LocalDef { var, .. } = st {
4130 top_ids.insert(*var);
4131 }
4132 }
4133 }
4134 let ret_wants_ref = match ret_ty {
4135 JavaType::Object(n) => n.as_ref() != "java/lang/Object",
4136 JavaType::Array(_) => true,
4137 _ => false,
4138 };
4139 let try_swap = |a: &mut Expr, want: &JavaType| {
4140 let Expr::Local { var, ty } = &mut *a else {
4141 return;
4142 };
4143 if (*var as usize) >= vt.vars.len() {
4144 return;
4145 }
4146 let avt = vt.var(*var).ty.erased();
4147 let avt_ref = matches!(avt, JavaType::Object(_) | JavaType::Array(_));
4148 let want_arr = matches!(want, JavaType::Array(_));
4149 if avt_ref && !(avt == JavaType::Object("java/lang/Object".into())) {
4152 return;
4153 }
4154 if avt_ref && !matches!(want, JavaType::Object(_)) {
4155 return;
4156 }
4157 let slot = vt.var(*var).slot;
4158 let mut hits: Vec<u32> = Vec::new();
4159 for v in &vt.vars {
4160 if v.slot != slot || !top_ids.contains(&v.id) || v.id == *var {
4161 continue;
4162 }
4163 let t = v.ty.erased();
4164 let ok = match (&t, want) {
4165 (JavaType::Object(n), JavaType::Object(w)) => {
4166 n.as_ref() == w.as_ref()
4167 || pool.is_subtype(n.as_ref(), w.as_ref())
4168 }
4169 (JavaType::Array(_), JavaType::Array(_)) => want_arr,
4170 _ => false,
4171 };
4172 if ok {
4173 hits.push(v.id);
4174 }
4175 }
4176 hits.sort_unstable();
4177 hits.dedup();
4178 if hits.is_empty() {
4179 return;
4180 }
4181 let id = *hits.last().unwrap();
4187 *var = id;
4188 *ty = vt.vars[id as usize].ty.clone();
4189 };
4190 walk_stmt_exprs(body, &mut |e| {
4191 deep_rewrite(e, &mut |x| {
4192 match x {
4193 Expr::Method { desc, args, is_dynamic: false, .. } => {
4194 for (i, a) in args.iter_mut().enumerate() {
4195 if let Some(f) = desc.args.get(i) {
4196 if matches!(f, JavaType::Object(n) if n.as_ref() != "java/lang/Object")
4197 || matches!(f, JavaType::Array(_))
4198 {
4199 try_swap(a, f);
4200 }
4201 }
4202 }
4203 }
4204 Expr::New { arg_tys, args, .. } => {
4205 for (i, a) in args.iter_mut().enumerate() {
4206 if let Some(f) = arg_tys.get(i) {
4207 if matches!(f, JavaType::Object(n) if n.as_ref() != "java/lang/Object")
4208 || matches!(f, JavaType::Array(_))
4209 {
4210 try_swap(a, f);
4211 }
4212 }
4213 }
4214 }
4215 _ => {}
4216 }
4217 });
4218 });
4219 if ret_wants_ref {
4220 walk_mut_deep(body, &mut |st| {
4221 if let Stmt::Return(Some(e)) = st {
4222 try_swap(e, ret_ty);
4223 }
4224 });
4225 }
4226}
4227
4228pub fn fix_ref_null_assigns(vt: &VarTable, body: &mut Stmt) {
4243 let is_ref_var = |var: u32| -> bool {
4244 (var as usize) < vt.vars.len()
4245 && matches!(
4246 vt.vars[var as usize].ty.erased(),
4247 JavaType::Object(_) | JavaType::Array(_)
4248 )
4249 };
4250 walk_mut_deep(body, &mut |st| match st {
4251 Stmt::ExprStmt(Expr::Assign {
4252 target,
4253 value,
4254 op: AssignOp::Plain,
4255 ..
4256 }) => {
4257 if let Expr::Local { var, .. } = &**target {
4258 if is_ref_var(*var) && matches!(&**value, Expr::Const(ConstVal::Int(0))) {
4259 **value = Expr::Const(ConstVal::Null);
4260 }
4261 }
4262 }
4263 Stmt::LocalDef {
4264 var,
4265 init: Some(e),
4266 ..
4267 } => {
4268 if is_ref_var(*var) && matches!(e, Expr::Const(ConstVal::Int(0))) {
4269 *e = Expr::Const(ConstVal::Null);
4270 }
4271 }
4272 _ => {}
4273 });
4274}
4275
4276pub fn fix_ref_array_null_consts(body: &mut Stmt) {
4277 walk_stmt_exprs(body, &mut |e| {
4278 deep_rewrite(e, &mut |x| {
4279 if let Expr::NewArray { elem, init: Some(list), .. } = x {
4280 if matches!(elem.erased(), JavaType::Object(_) | JavaType::Array(_)) {
4281 for slot in list.iter_mut() {
4282 if matches!(&*slot, Expr::Const(ConstVal::Int(0))) {
4283 *slot = Expr::Const(ConstVal::Null);
4284 }
4285 }
4286 }
4287 }
4288 });
4289 });
4290}
4291
4292pub fn fix_null_sentinels(body: &mut Stmt, vt: &VarTable, ret: &JavaType) {
4331 #[derive(Clone, Copy, PartialEq)]
4332 enum WK {
4333 Zero,
4334 Copy(u32),
4335 Other,
4336 }
4337 fn is_ref_jt(t: &JavaType) -> bool {
4338 matches!(t, JavaType::Object(_) | JavaType::Array(_))
4339 }
4340 fn cls_write(e: &Expr) -> WK {
4341 match e {
4342 Expr::Const(ConstVal::Int(0)) => WK::Zero,
4343 Expr::Local { var, .. } => WK::Copy(*var),
4344 _ => WK::Other,
4345 }
4346 }
4347 fn expr_writes(e: &Expr, writes: &mut std::collections::HashMap<u32, Vec<WK>>) {
4348 visit_exprs(e, &mut |x| match x {
4349 Expr::Assign { target, op, value } => {
4350 if let Expr::Local { var, .. } = &**target {
4351 let k = if matches!(op, AssignOp::Plain) {
4352 cls_write(value)
4353 } else {
4354 WK::Other
4355 };
4356 writes.entry(*var).or_default().push(k);
4357 }
4358 }
4359 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
4360 if let Expr::Local { var, .. } = &**inner {
4361 writes.entry(*var).or_default().push(WK::Other);
4362 }
4363 }
4364 _ => {}
4365 });
4366 }
4367 fn collect_writes(
4368 s: &Stmt,
4369 writes: &mut std::collections::HashMap<u32, Vec<WK>>,
4370 has_zero: &mut bool,
4371 ) {
4372 fn note(e: &Expr, has_zero: &mut bool, writes: &mut std::collections::HashMap<u32, Vec<WK>>) {
4373 visit_exprs(e, &mut |x| {
4374 if let Expr::Const(ConstVal::Int(0)) = x {
4375 *has_zero = true;
4376 }
4377 });
4378 expr_writes(e, writes);
4379 }
4380 match s {
4381 Stmt::Block(v) => {
4382 for x in v {
4383 collect_writes(x, writes, has_zero);
4384 }
4385 }
4386 Stmt::LocalDef { var, init, .. } => {
4387 if let Some(e) = init {
4388 if matches!(e, Expr::Const(ConstVal::Int(0))) {
4389 *has_zero = true;
4390 }
4391 writes.entry(*var).or_default().push(cls_write(e));
4392 }
4393 }
4394 Stmt::ExprStmt(e) => note(e, has_zero, writes),
4395 Stmt::Return(e) => {
4396 if let Some(x) = e {
4397 note(x, has_zero, writes);
4398 }
4399 }
4400 Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
4401 note(e, has_zero, writes);
4402 }
4403 Stmt::TernaryValue { e } => note(e, has_zero, writes),
4404 Stmt::If { cond, then_stmt, else_stmt } => {
4405 note(cond, has_zero, writes);
4406 collect_writes(then_stmt, writes, has_zero);
4407 if let Some(b) = else_stmt {
4408 collect_writes(b, writes, has_zero);
4409 }
4410 }
4411 Stmt::While { cond, body } | Stmt::DoWhile { body, cond } => {
4412 note(cond, has_zero, writes);
4413 collect_writes(body, writes, has_zero);
4414 }
4415 Stmt::For { init, cond, update, body } => {
4416 for x in init {
4417 collect_writes(x, writes, has_zero);
4418 }
4419 if let Some(c) = cond {
4420 note(c, has_zero, writes);
4421 }
4422 for u in update {
4423 note(u, has_zero, writes);
4424 }
4425 collect_writes(body, writes, has_zero);
4426 }
4427 Stmt::ForEach { var, iterable, body, .. } => {
4428 writes.entry(*var).or_default().push(WK::Other);
4429 note(iterable, has_zero, writes);
4430 collect_writes(body, writes, has_zero);
4431 }
4432 Stmt::Switch { selector, cases, default, .. } => {
4433 note(selector, has_zero, writes);
4434 for c in cases {
4435 if let Some(g) = &c.guard {
4436 note(g, has_zero, writes);
4437 }
4438 for x in &c.body {
4439 collect_writes(x, writes, has_zero);
4440 }
4441 }
4442 if let Some(d) = default {
4443 collect_writes(d, writes, has_zero);
4444 }
4445 }
4446 Stmt::Try { body, catches, finally } => {
4447 collect_writes(body, writes, has_zero);
4448 for c in catches {
4449 writes.entry(c.var).or_default().push(WK::Other);
4450 collect_writes(&c.body, writes, has_zero);
4451 }
4452 if let Some(f) = finally {
4453 collect_writes(f, writes, has_zero);
4454 }
4455 }
4456 Stmt::TryWithResources { resources, body, catches, finally, .. } => {
4457 for r in resources {
4458 collect_writes(r, writes, has_zero);
4459 }
4460 collect_writes(body, writes, has_zero);
4461 for c in catches {
4462 writes.entry(c.var).or_default().push(WK::Other);
4463 collect_writes(&c.body, writes, has_zero);
4464 }
4465 if let Some(f) = finally {
4466 collect_writes(f, writes, has_zero);
4467 }
4468 }
4469 Stmt::Assert { cond, msg } => {
4470 note(cond, has_zero, writes);
4471 if let Some(m) = msg {
4472 note(m, has_zero, writes);
4473 }
4474 }
4475 Stmt::Synchronized { lock, body } => {
4476 note(lock, has_zero, writes);
4477 collect_writes(body, writes, has_zero);
4478 }
4479 Stmt::Labeled { body, .. } => collect_writes(body, writes, has_zero),
4480 _ => {}
4481 }
4482 }
4483
4484 let mut writes: std::collections::HashMap<u32, Vec<WK>> =
4485 std::collections::HashMap::new();
4486 let mut has_zero = false;
4487 collect_writes(body, &mut writes, &mut has_zero);
4488
4489 let mut eligible: HashSet<u32> = HashSet::default();
4491 let int_typed = |v: u32| {
4492 (v as usize) < vt.vars.len() && vt.vars[v as usize].ty.erased() == JavaType::Int
4493 };
4494 loop {
4495 let mut changed = false;
4496 for (&v, ws) in writes.iter() {
4497 if eligible.contains(&v) || ws.is_empty() || !int_typed(v) {
4498 continue;
4499 }
4500 if ws.iter().all(|k| match k {
4501 WK::Zero => true,
4502 WK::Copy(u) => *u != v && eligible.contains(u),
4503 WK::Other => false,
4504 }) {
4505 eligible.insert(v);
4506 changed = true;
4507 }
4508 }
4509 if !changed {
4510 break;
4511 }
4512 }
4513 if eligible.is_empty() && !has_zero {
4514 return; }
4516
4517 #[derive(Clone, Copy, PartialEq)]
4518 enum Mode {
4519 Scan,
4520 Rewrite,
4521 }
4522 struct St<'a> {
4523 mode: Mode,
4524 vt: &'a VarTable,
4525 eligible: &'a HashSet<u32>,
4526 ref_read: HashSet<u32>,
4527 dead: HashSet<u32>,
4528 sentinels: HashSet<u32>,
4529 n_lit: usize,
4530 n_var: usize,
4531 }
4532 fn arg_ctx(t: &JavaType) -> Option<JavaType> {
4536 match t {
4537 JavaType::Object(n) if n.as_ref() == "java/lang/Object" => None,
4538 t if is_ref_jt(t) => Some(t.clone()),
4539 _ => None,
4540 }
4541 }
4542 fn walk_expr(e: &mut Expr, ctx: Option<JavaType>, st: &mut St) {
4543 walk_expr_c(e, ctx, false, st);
4544 }
4545 fn walk_expr_c(e: &mut Expr, ctx: Option<JavaType>, cast_arg: bool, st: &mut St) {
4551 let slot_ref = ctx.as_ref().is_some_and(is_ref_jt);
4552 fn mk_null(ctx: &Option<JavaType>, cast_arg: bool) -> Expr {
4553 match (cast_arg, ctx) {
4554 (true, Some(t)) => Expr::Cast {
4555 ty: TypeRef::J(t.clone()),
4556 e: Box::new(Expr::Const(ConstVal::Null)),
4557 },
4558 _ => Expr::Const(ConstVal::Null),
4559 }
4560 }
4561 match e {
4562 Expr::Const(ConstVal::Int(0)) if slot_ref => {
4563 if st.mode == Mode::Rewrite {
4564 *e = mk_null(&ctx, cast_arg);
4565 st.n_lit += 1;
4566 }
4567 return;
4568 }
4569 Expr::Local { var, .. } => {
4570 if st.eligible.contains(var) {
4571 if slot_ref {
4572 st.ref_read.insert(*var);
4573 } else {
4574 st.dead.insert(*var);
4575 }
4576 }
4577 if st.mode == Mode::Rewrite && slot_ref && st.sentinels.contains(var) {
4578 *e = mk_null(&ctx, cast_arg);
4579 st.n_var += 1;
4580 }
4581 return;
4582 }
4583 _ => {}
4584 }
4585 match e {
4586 Expr::Method { owner, desc, args, .. } => {
4587 if let Some(o) = owner {
4588 walk_expr(o, None, st);
4589 }
4590 for (i, a) in args.iter_mut().enumerate() {
4591 let c = desc.args.get(i).and_then(arg_ctx);
4592 walk_expr_c(a, c, true, st);
4593 }
4594 }
4595 Expr::Invokedynamic { desc, args, .. } => {
4596 for (i, a) in args.iter_mut().enumerate() {
4597 let c = desc.args.get(i).and_then(arg_ctx);
4598 walk_expr_c(a, c, true, st);
4599 }
4600 }
4601 Expr::New { args, arg_tys, .. } => {
4602 for (i, a) in args.iter_mut().enumerate() {
4603 let c = arg_tys.get(i).and_then(arg_ctx);
4604 walk_expr_c(a, c, true, st);
4605 }
4606 }
4607 Expr::AnonNew { args, .. } => {
4608 for a in args.iter_mut() {
4609 walk_expr(a, None, st);
4610 }
4611 }
4612 Expr::Assign { target, value, .. } => {
4613 if let Expr::Local { var: u, .. } = &**target {
4617 if st.eligible.contains(u) {
4618 return;
4619 }
4620 }
4621 let vctx = match &**target {
4622 Expr::Field { ty, .. } => {
4623 let t = ty.erased();
4624 is_ref_jt(&t).then_some(t)
4625 }
4626 Expr::ArrayIndex { array, .. } => match array.type_ref().erased() {
4627 JavaType::Array(inner) if is_ref_jt(&inner) => Some(*inner),
4628 _ => None,
4629 },
4630 _ => None,
4631 };
4632 walk_expr(target, None, st);
4633 walk_expr(value, vctx, st);
4634 }
4635 Expr::Cast { ty, e: inner } => {
4636 let t = ty.erased();
4637 let c = is_ref_jt(&t).then_some(t);
4638 walk_expr(inner, c, st);
4639 }
4640 Expr::InstanceOf { e: inner, .. } => {
4641 walk_expr(inner, Some(JavaType::Object("java/lang/Object".into())), st);
4642 }
4643 Expr::Cond { c, t, f } => {
4644 walk_expr(c, None, st);
4645 let tc = ctx.clone();
4646 walk_expr(t, tc, st);
4647 walk_expr(f, ctx, st);
4648 }
4649 Expr::NewArray { elem, dims, init, .. } => {
4650 for d in dims.iter_mut() {
4651 walk_expr(d, None, st);
4652 }
4653 if let Some(list) = init {
4654 let et = elem.erased();
4655 let c = is_ref_jt(&et).then_some(et);
4656 for slot in list.iter_mut() {
4657 walk_expr(slot, c.clone(), st);
4658 }
4659 }
4660 }
4661 Expr::NewMultiArray { dims, .. } => {
4662 for d in dims.iter_mut() {
4663 walk_expr(d, None, st);
4664 }
4665 }
4666 Expr::ArrayIndex { array, index } => {
4667 walk_expr(array, None, st);
4668 walk_expr(index, None, st);
4669 }
4670 Expr::Field { owner, .. } => {
4671 if let Some(o) = owner {
4672 walk_expr(o, None, st);
4673 }
4674 }
4675 Expr::Bin { l, r, .. } => {
4676 walk_expr(l, None, st);
4677 walk_expr(r, None, st);
4678 }
4679 Expr::Un { e: inner, .. } => walk_expr(inner, None, st),
4680 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
4681 walk_expr(inner, None, st);
4682 }
4683 Expr::StringConcat(parts) => {
4684 for p in parts.iter_mut() {
4685 if let ConcatPart::Str(x) = p {
4686 walk_expr(x, None, st);
4687 }
4688 }
4689 }
4690 Expr::Lambda(l) => {
4691 for c in l.captures.iter_mut() {
4692 walk_expr(c, None, st);
4693 }
4694 }
4695 _ => {}
4696 }
4697 }
4698 fn walk_stmt(s: &mut Stmt, st: &mut St, ret: &JavaType) {
4699 match s {
4700 Stmt::Block(v) => {
4701 for x in v.iter_mut() {
4702 walk_stmt(x, st, ret);
4703 }
4704 }
4705 Stmt::ExprStmt(e) => walk_expr(e, None, st),
4706 Stmt::LocalDef { var, init, .. } => {
4707 if let Some(e) = init {
4708 let ctx = if (*var as usize) < st.vt.vars.len() {
4709 let t = st.vt.vars[*var as usize].ty.erased();
4710 is_ref_jt(&t).then_some(t)
4711 } else {
4712 None
4713 };
4714 walk_expr(e, ctx, st);
4715 }
4716 }
4717 Stmt::Return(e) => {
4718 if let Some(x) = e {
4719 let c = is_ref_jt(ret).then(|| ret.clone());
4720 walk_expr(x, c, st);
4721 }
4722 }
4723 Stmt::Throw(e) => {
4724 walk_expr(e, Some(JavaType::Object("java/lang/Throwable".into())), st);
4725 }
4726 Stmt::If { cond, then_stmt, else_stmt } => {
4727 walk_expr(cond, None, st);
4728 walk_stmt(then_stmt, st, ret);
4729 if let Some(b) = else_stmt {
4730 walk_stmt(b, st, ret);
4731 }
4732 }
4733 Stmt::While { cond, body } => {
4734 walk_expr(cond, None, st);
4735 walk_stmt(body, st, ret);
4736 }
4737 Stmt::DoWhile { body, cond } => {
4738 walk_stmt(body, st, ret);
4739 walk_expr(cond, None, st);
4740 }
4741 Stmt::For { init, cond, update, body } => {
4742 for x in init.iter_mut() {
4743 walk_stmt(x, st, ret);
4744 }
4745 if let Some(c) = cond {
4746 walk_expr(c, None, st);
4747 }
4748 for u in update.iter_mut() {
4749 walk_expr(u, None, st);
4750 }
4751 walk_stmt(body, st, ret);
4752 }
4753 Stmt::ForEach { iterable, body, .. } => {
4754 walk_expr(iterable, None, st);
4755 walk_stmt(body, st, ret);
4756 }
4757 Stmt::Switch { selector, cases, default, .. } => {
4758 walk_expr(selector, None, st);
4759 for c in cases.iter_mut() {
4760 if let Some(g) = &mut c.guard {
4761 walk_expr(g, None, st);
4762 }
4763 for x in c.body.iter_mut() {
4764 walk_stmt(x, st, ret);
4765 }
4766 }
4767 if let Some(d) = default {
4768 walk_stmt(d, st, ret);
4769 }
4770 }
4771 Stmt::Try { body, catches, finally } => {
4772 walk_stmt(body, st, ret);
4773 for c in catches.iter_mut() {
4774 walk_stmt(&mut c.body, st, ret);
4775 }
4776 if let Some(f) = finally {
4777 walk_stmt(f, st, ret);
4778 }
4779 }
4780 Stmt::TryWithResources { resources, body, catches, finally, .. } => {
4781 for r in resources.iter_mut() {
4782 walk_stmt(r, st, ret);
4783 }
4784 walk_stmt(body, st, ret);
4785 for c in catches.iter_mut() {
4786 walk_stmt(&mut c.body, st, ret);
4787 }
4788 if let Some(f) = finally {
4789 walk_stmt(f, st, ret);
4790 }
4791 }
4792 Stmt::Assert { cond, msg } => {
4793 walk_expr(cond, None, st);
4794 if let Some(m) = msg {
4795 walk_expr(m, None, st);
4796 }
4797 }
4798 Stmt::Synchronized { lock, body } => {
4799 walk_expr(lock, Some(JavaType::Object("java/lang/Object".into())), st);
4800 walk_stmt(body, st, ret);
4801 }
4802 Stmt::TernaryValue { e } => walk_expr(e, None, st),
4803 Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
4804 walk_expr(e, Some(JavaType::Object("java/lang/Object".into())), st);
4805 }
4806 Stmt::Labeled { body, .. } => walk_stmt(body, st, ret),
4807 _ => {}
4808 }
4809 }
4810
4811 let mut st = St {
4812 mode: Mode::Scan,
4813 vt,
4814 eligible: &eligible,
4815 ref_read: HashSet::default(),
4816 dead: HashSet::default(),
4817 sentinels: HashSet::default(),
4818 n_lit: 0,
4819 n_var: 0,
4820 };
4821 if !eligible.is_empty() {
4822 walk_stmt(body, &mut st, ret);
4823 st.sentinels = eligible
4824 .iter()
4825 .copied()
4826 .filter(|v| st.ref_read.contains(v) && !st.dead.contains(v))
4827 .collect();
4828 }
4829 if st.sentinels.is_empty() && !has_zero {
4830 return;
4831 }
4832 st.mode = Mode::Rewrite;
4833 walk_stmt(body, &mut st, ret);
4834 if std::env::var_os("DDC_SENT").is_some() {
4835 eprintln!(
4836 "[sent] elig={} confirmed={} lit={} var={}",
4837 eligible.len(),
4838 st.sentinels.len(),
4839 st.n_lit,
4840 st.n_var
4841 );
4842 }
4843}
4844
4845pub fn fix_int_operand_bridges(body: &mut Stmt, vt: &VarTable) {
4853 fn side_bool(e: &Expr, vt: &VarTable) -> bool {
4854 matches!(
4855 match e {
4856 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4857 other => other.type_ref().erased(),
4858 },
4859 JavaType::Boolean
4860 )
4861 }
4862 fn side_int(e: &Expr, vt: &VarTable) -> bool {
4863 matches!(
4864 match e {
4865 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4866 other => other.type_ref().erased(),
4867 },
4868 JavaType::Int | JavaType::Short | JavaType::Byte | JavaType::Char | JavaType::Long
4869 )
4870 }
4871 fn wrap(e: Box<Expr>) -> Box<Expr> {
4872 Box::new(Expr::Cond {
4873 c: e,
4874 t: Box::new(Expr::Const(ConstVal::Int(1))),
4875 f: Box::new(Expr::Const(ConstVal::Int(0))),
4876 })
4877 }
4878 walk_stmt_exprs(body, &mut |e| {
4879 deep_rewrite(e, &mut |x| {
4880 if let Expr::Assign { target, value, op: AssignOp::Plain } = x {
4887 let tgt_ty: Option<JavaType> = match &**target {
4888 Expr::Field { ty, .. } => Some(ty.erased()),
4889 Expr::ArrayIndex { array, .. } => {
4890 match array.type_ref().erased() {
4891 JavaType::Array(el) => Some(el.as_ref().clone()),
4892 _ => None,
4893 }
4894 }
4895 Expr::Local { var, .. } => {
4900 if (*var as usize) < vt.vars.len() {
4901 Some(vt.var(*var).ty.erased())
4902 } else {
4903 None
4904 }
4905 }
4906 _ => None,
4907 };
4908 if let Some(t) = tgt_ty {
4909 let tgt_int = matches!(
4910 t,
4911 JavaType::Int
4912 | JavaType::Short
4913 | JavaType::Byte
4914 | JavaType::Char
4915 | JavaType::Long
4916 );
4917 if tgt_int && side_bool(value, vt) && !side_int(value, vt) {
4918 let taken = std::mem::replace(
4919 value,
4920 Box::new(Expr::Const(ConstVal::Null)),
4921 );
4922 let mut wrapped = wrap(taken);
4923 if !matches!(t, JavaType::Int | JavaType::Long) {
4924 wrapped = Box::new(Expr::Cast {
4925 ty: TypeRef::J(t.clone()),
4926 e: wrapped,
4927 });
4928 }
4929 *value = wrapped;
4930 } else if matches!(t, JavaType::Boolean)
4931 && side_int(value, vt)
4932 && !side_bool(value, vt)
4933 {
4934 let taken = std::mem::replace(
4935 value,
4936 Box::new(Expr::Const(ConstVal::Null)),
4937 );
4938 **value = Expr::Bin {
4939 op: BinOp::Ne,
4940 l: taken,
4941 r: Box::new(Expr::Const(ConstVal::Int(0))),
4942 ty: Some(TypeRef::J(JavaType::Boolean)),
4943 };
4944 }
4945 }
4946 }
4947 if let Expr::Cast { ty, e } = x {
4953 let t = ty.erased();
4954 let t_num = matches!(
4955 t,
4956 JavaType::Int
4957 | JavaType::Short
4958 | JavaType::Byte
4959 | JavaType::Char
4960 | JavaType::Long
4961 | JavaType::Float
4962 | JavaType::Double
4963 );
4964 if t_num && side_bool(e, vt) && !side_int(e, vt) {
4965 let taken = std::mem::replace(e, Box::new(Expr::Const(ConstVal::Null)));
4966 **e = Expr::Cond {
4967 c: taken,
4968 t: Box::new(Expr::Const(ConstVal::Int(1))),
4969 f: Box::new(Expr::Const(ConstVal::Int(0))),
4970 };
4971 } else if matches!(t, JavaType::Boolean)
4972 && side_int(e, vt)
4973 && !side_bool(e, vt)
4974 {
4975 let taken = std::mem::replace(e, Box::new(Expr::Const(ConstVal::Null)));
4976 *x = Expr::Bin {
4977 op: BinOp::Ne,
4978 l: taken,
4979 r: Box::new(Expr::Const(ConstVal::Int(0))),
4980 ty: Some(TypeRef::J(JavaType::Boolean)),
4981 };
4982 }
4983 }
4984 if let Expr::Bin { op, l, r, .. } = x {
4990 if matches!(op, BinOp::Eq | BinOp::Ne) {
4991 if matches!(&**r, Expr::Const(ConstVal::Null))
4992 && side_int(l, vt)
4993 && !side_bool(l, vt)
4994 {
4995 **r = Expr::Const(ConstVal::Int(0));
4996 } else if matches!(&**l, Expr::Const(ConstVal::Null))
4997 && side_int(r, vt)
4998 && !side_bool(r, vt)
4999 {
5000 **l = Expr::Const(ConstVal::Int(0));
5001 }
5002 }
5003 }
5004 if let Expr::ArrayIndex { index, .. } = x {
5008 if side_bool(index, vt) && !side_int(index, vt) {
5009 let taken =
5010 std::mem::replace(index, Box::new(Expr::Const(ConstVal::Null)));
5011 *index = wrap(taken);
5012 }
5013 }
5014 if let Expr::NewArray { dims, .. } = x {
5020 for d in dims.iter_mut() {
5021 if side_bool(d, vt) && !side_int(d, vt) {
5022 let taken =
5023 std::mem::replace(d, Expr::Const(ConstVal::Null));
5024 *d = *wrap(Box::new(taken));
5025 }
5026 }
5027 }
5028 if let Expr::Bin { op, l, r, .. } = x {
5029 let int_kind = matches!(
5030 op,
5031 BinOp::Or
5032 | BinOp::And
5033 | BinOp::Xor
5034 | BinOp::Add
5035 | BinOp::Sub
5036 | BinOp::Mul
5037 | BinOp::Div
5038 | BinOp::Rem
5039 | BinOp::Shl
5040 | BinOp::Shr
5041 | BinOp::Ushr
5042 | BinOp::Eq
5043 | BinOp::Ne
5044 | BinOp::Lt
5045 | BinOp::Ge
5046 | BinOp::Gt
5047 | BinOp::Le
5048 );
5049 if !int_kind {
5050 return;
5051 }
5052 if side_bool(l, vt) && side_int(r, vt) && !side_bool(r, vt) {
5055 let taken = std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
5056 *l = wrap(taken);
5057 } else if side_bool(r, vt) && side_int(l, vt) && !side_bool(l, vt) {
5058 let taken = std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
5059 *r = wrap(taken);
5060 }
5061 }
5062 });
5063 });
5064}
5065
5066pub fn fold_value_diamonds(body: &mut Stmt) {
5104 let Stmt::Block(stmts) = body else { return };
5105 fold_diamonds_in(stmts);
5106}
5107
5108fn diamond_types_ok(a: &Expr, b: &Expr) -> bool {
5109 fn num(t: &JavaType) -> bool {
5110 matches!(
5111 t,
5112 JavaType::Byte
5113 | JavaType::Short
5114 | JavaType::Int
5115 | JavaType::Char
5116 | JavaType::Long
5117 | JavaType::Float
5118 | JavaType::Double
5119 )
5120 }
5121 let ta = a.type_ref().erased();
5122 let tb = b.type_ref().erased();
5123 ta == tb || (num(&ta) && num(&tb))
5124}
5125
5126fn single_local_assign(st: &Stmt) -> Option<(u32, Expr, Expr)> {
5134 let mut inner = st;
5135 while let Stmt::Block(v) = inner {
5136 if v.len() != 1 {
5137 return None;
5138 }
5139 inner = &v[0];
5140 }
5141 match inner {
5142 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain }) => {
5143 match &**target {
5144 Expr::Local { var, .. } => Some((*var, *target.clone(), *value.clone())),
5145 _ => None,
5146 }
5147 }
5148 _ => None,
5149 }
5150}
5151
5152fn mk_ternary_assign(tgt: Expr, c: Expr, a: Expr, b: Expr) -> Stmt {
5153 Stmt::ExprStmt(Expr::Assign {
5154 target: Box::new(tgt),
5155 op: AssignOp::Plain,
5156 value: Box::new(Expr::Cond {
5157 c: Box::new(c),
5158 t: Box::new(a),
5159 f: Box::new(b),
5160 }),
5161 })
5162}
5163
5164fn fold_diamonds_in(stmts: &mut Vec<Stmt>) {
5165 let mut i = 0usize;
5166 while i < stmts.len() {
5167 match &mut stmts[i] {
5168 Stmt::Block(v) => fold_diamonds_in(v),
5169 Stmt::If { then_stmt, else_stmt, .. } => {
5170 if let Stmt::Block(v) = &mut **then_stmt {
5171 fold_diamonds_in(v);
5172 }
5173 if let Some(e) = else_stmt {
5174 if let Stmt::Block(v) = &mut **e {
5175 fold_diamonds_in(v);
5176 }
5177 }
5178 }
5179 _ => {}
5180 }
5181 let shape1 = if let Stmt::If { cond, then_stmt, else_stmt: Some(el), .. } = &stmts[i] {
5183 match (single_local_assign(then_stmt), single_local_assign(el)) {
5184 (Some((v1, tgt, a)), Some((v2, _, b)))
5185 if v1 == v2 && diamond_types_ok(&a, &b) =>
5186 {
5187 Some(mk_ternary_assign(tgt, cond.clone(), a, b))
5188 }
5189 _ => None,
5190 }
5191 } else {
5192 None
5193 };
5194 if let Some(f) = shape1 {
5195 stmts[i] = f;
5196 i += 1;
5197 continue;
5198 }
5199 if i + 1 < stmts.len() {
5201 let shape2 = if let Stmt::If { cond, then_stmt, else_stmt: None, .. } = &stmts[i + 1] {
5202 match (single_local_assign(&stmts[i]), single_local_assign(then_stmt)) {
5203 (Some((v1, tgt, b)), Some((v2, _, a)))
5204 if v1 == v2 && diamond_types_ok(&a, &b) =>
5205 {
5206 Some(mk_ternary_assign(tgt, cond.clone(), a, b))
5207 }
5208 _ => None,
5209 }
5210 } else {
5211 None
5212 };
5213 if let Some(f) = shape2 {
5214 stmts[i] = f;
5215 stmts.remove(i + 1);
5216 continue; }
5218 }
5219 i += 1;
5220 }
5221}
5222
5223pub fn fold_bool_value_diamonds(body: &mut Stmt, vt: &VarTable) {
5224 fn is_bool_var(v: u32, vt: &VarTable) -> bool {
5225 (v as usize) < vt.vars.len()
5226 && matches!(vt.vars[v as usize].ty.erased(), JavaType::Boolean)
5227 }
5228 fn bool_const(e: &Expr) -> Option<bool> {
5229 fn cint(e: &Expr) -> Option<i32> {
5230 match e {
5231 Expr::Const(ConstVal::Int(n)) => Some(*n),
5232 _ => None,
5233 }
5234 }
5235 match e {
5236 Expr::Const(ConstVal::Int(0)) => Some(false),
5237 Expr::Const(ConstVal::Int(1)) => Some(true),
5238 Expr::Bin { op: op @ (BinOp::Eq | BinOp::Ne), l, r, .. } => {
5242 let (a, b) = (cint(l)?, cint(r)?);
5243 let eq = a == b;
5244 Some(if matches!(op, BinOp::Eq) { eq } else { !eq })
5245 }
5246 _ => None,
5247 }
5248 }
5249 fn reads_var(e: &Expr, v: u32) -> bool {
5250 let mut hit = false;
5251 visit_exprs(e, &mut |x| {
5252 if let Expr::Local { var, .. } = x {
5253 if *var == v {
5254 hit = true;
5255 }
5256 }
5257 });
5258 hit
5259 }
5260 fn bool_valued(e: &Expr, vt: &VarTable) -> bool {
5264 fn side(e: &Expr, vt: &VarTable) -> bool {
5265 bool_const(e).is_some() || bool_valued(e, vt)
5266 }
5267 match e {
5268 Expr::Local { var, .. } => is_bool_var(*var, vt),
5269 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
5270 Expr::InstanceOf { .. } => true,
5271 Expr::Un { op: UnOp::Not, e: x, .. } => bool_valued(x, vt) || bool_const(x).is_some(),
5272 Expr::Bin { op, l, r, .. } => match op {
5273 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le => true,
5274 BinOp::LogAnd | BinOp::LogOr => {
5277 side(l, vt) && side(r, vt)
5278 }
5279 _ => false,
5280 },
5281 Expr::Cond { t, f, .. } => side(t, vt) && side(f, vt),
5282 _ => false,
5283 }
5284 }
5285 fn lone_assign(
5294 b: &Stmt,
5295 v: u32,
5296 vt: &VarTable,
5297 an: &VarAnalysis,
5298 consumed: &mut HashSet<u32>,
5299 ) -> Option<Expr> {
5300 let l = flat_list(b);
5301 if l.len() == 2 {
5302 let (u, e): (u32, &Expr) = match l[0] {
5303 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e),
5304 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
5305 match &**target {
5306 Expr::Local { var, .. } => (*var, &**value),
5307 _ => return None,
5308 }
5309 }
5310 _ => return None,
5311 };
5312 if u == v || consumed.contains(&u) {
5313 return None;
5314 }
5315 if an.assigns.get(u as usize).copied().unwrap_or(0) != 1
5322 || an.reads.get(u as usize).copied().unwrap_or(0) != 1
5323 {
5324 return None;
5325 }
5326 let (tv, rv) = match l[1] {
5327 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
5328 match (&**target, &**value) {
5329 (Expr::Local { var: t, .. }, Expr::Local { var: r, .. }) => (*t, *r),
5330 _ => return None,
5331 }
5332 }
5333 Stmt::LocalDef { var, init: Some(Expr::Local { var: r, .. }), .. } => (*var, *r),
5334 _ => return None,
5335 };
5336 if tv != v || rv != u {
5337 return None;
5338 }
5339 if reads_var(e, v) || !(bool_valued(e, vt) || bool_const(e).is_some()) {
5340 return None;
5341 }
5342 consumed.insert(u);
5343 return Some(e.clone());
5344 }
5345 if l.len() != 1 {
5346 return None;
5347 }
5348 let (target, value) = match l[0] {
5349 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => (target, value),
5350 Stmt::LocalDef { var, init: Some(value), .. } if *var == v => {
5351 if reads_var(value, v) || !(bool_valued(value, vt) || bool_const(value).is_some()) {
5352 return None;
5353 }
5354 return Some(value.clone());
5355 }
5356 _ => return None,
5357 };
5358 let Expr::Local { var: tv, .. } = &**target else { return None };
5359 if *tv != v || reads_var(value, v) {
5360 return None;
5361 }
5362 if !(bool_valued(value, vt) || bool_const(value).is_some()) {
5363 return None;
5364 }
5365 Some((**value).clone())
5366 }
5367 fn not(e: Expr) -> Expr {
5368 Expr::Un { op: UnOp::Not, e: Box::new(e) }
5369 }
5370 fn simplify_bool(e: Expr) -> Expr {
5375 match e {
5376 Expr::Un { op: UnOp::Not, e: inner, .. } => match *inner {
5377 Expr::Bin { op: BinOp::Ne, l, r, ty } => {
5382 simplify_bool(Expr::Bin { op: BinOp::Eq, l, r, ty })
5383 }
5384 Expr::Bin { op: BinOp::Eq, l, r, ty } => {
5385 simplify_bool(Expr::Bin { op: BinOp::Ne, l, r, ty })
5386 }
5387 other => Expr::Un { op: UnOp::Not, e: Box::new(simplify_bool(other)) },
5388 },
5389 Expr::Bin { op: op @ (BinOp::Eq | BinOp::Ne), l, r, ty } => {
5390 fn cond01(e: &Expr) -> Option<Expr> {
5392 if let Expr::Cond { c, t, f } = e {
5393 if matches!(&**t, Expr::Const(ConstVal::Int(1)))
5394 && matches!(&**f, Expr::Const(ConstVal::Int(0)))
5395 {
5396 return Some((**c).clone());
5397 }
5398 }
5399 None
5400 }
5401 let (lc, rc) = (cond01(&l), cond01(&r));
5402 match (lc, rc) {
5403 (Some(c), None) => match (&*r, op) {
5404 (Expr::Const(ConstVal::Int(1)), BinOp::Eq) => c,
5405 (Expr::Const(ConstVal::Int(1)), BinOp::Ne) => not(c),
5406 (Expr::Const(ConstVal::Int(0)), BinOp::Eq) => not(c),
5407 (Expr::Const(ConstVal::Int(0)), BinOp::Ne) => c,
5408 _ => Expr::Bin { op, l: Box::new(c), r, ty },
5409 },
5410 (None, Some(c)) => match (&*l, op) {
5411 (Expr::Const(ConstVal::Int(1)), BinOp::Eq) => c,
5412 (Expr::Const(ConstVal::Int(1)), BinOp::Ne) => not(c),
5413 (Expr::Const(ConstVal::Int(0)), BinOp::Eq) => not(c),
5414 (Expr::Const(ConstVal::Int(0)), BinOp::Ne) => c,
5415 _ => Expr::Bin { op, l, r: Box::new(c), ty },
5416 },
5417 _ => Expr::Bin { op, l, r, ty },
5418 }
5419 }
5420 other => other,
5421 }
5422 }
5423 fn bin(op: BinOp, l: Expr, r: Expr) -> Expr {
5424 Expr::Bin { op, l: Box::new(l), r: Box::new(r), ty: None }
5425 }
5426
5427 for _round in 0..4 {
5428 let mut changed = false;
5429 let mut analysis = VarAnalysis {
5430 assigns: Vec::new(),
5431 reads: Vec::new(),
5432 values: Vec::new(),
5433 };
5434 analyze_vars(body, &mut analysis);
5435 let mut consumed: HashSet<u32> = HashSet::default();
5436 walk_mut_deep(body, &mut |st| {
5437 let Stmt::Block(list) = st else { return };
5438 let mut i = 0usize;
5439 while i + 1 < list.len() {
5440 let folded = try_at(list, i, vt, &is_bool_var, &bool_const, &reads_var, &lone_assign, &analysis, &mut consumed);
5441 match folded {
5442 Some(expr) => {
5443 let is_def = matches!(&list[i], Stmt::LocalDef { .. });
5444 let var = match &list[i] {
5445 Stmt::LocalDef { var, .. } => *var,
5446 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
5447 match &**target { Expr::Local { var, .. } => *var, _ => unreachable!() }
5448 }
5449 _ => unreachable!(),
5450 };
5451 let (is_final, force_type) = match &list[i] {
5452 Stmt::LocalDef { is_final, force_type, .. } => (*is_final, *force_type),
5453 _ => (false, false),
5454 };
5455 list[i] = if is_def {
5456 Stmt::LocalDef { var, init: Some(expr), is_final, force_type }
5457 } else {
5458 Stmt::ExprStmt(Expr::Assign {
5459 target: Box::new(Expr::Local {
5460 var,
5461 ty: TypeRef::J(JavaType::Boolean),
5462 }),
5463 op: AssignOp::Plain,
5464 value: Box::new(expr),
5465 })
5466 };
5467 list.remove(i + 1);
5468 changed = true;
5469 }
5472 None => i += 1,
5473 }
5474 }
5475 });
5476 if !changed {
5477 break;
5478 }
5479 }
5480
5481 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
5482 fn try_at(
5483 list: &[Stmt],
5484 i: usize,
5485 vt: &VarTable,
5486 is_bool_var: &dyn Fn(u32, &VarTable) -> bool,
5487 bool_const: &dyn Fn(&Expr) -> Option<bool>,
5488 reads_var: &dyn Fn(&Expr, u32) -> bool,
5489 lone_assign: &dyn Fn(&Stmt, u32, &VarTable, &VarAnalysis, &mut HashSet<u32>) -> Option<Expr>,
5490 an: &VarAnalysis,
5491 consumed: &mut HashSet<u32>,
5492 ) -> Option<Expr> {
5493 let (v, base) = match &list[i] {
5495 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
5496 match &**target {
5497 Expr::Local { var, .. } => {
5498 let b = bool_const(value)?;
5501 if !is_bool_var(*var, vt) { return None; }
5502 (*var, b)
5503 }
5504 _ => return None,
5505 }
5506 }
5507 Stmt::LocalDef { var, init: Some(e), .. } => {
5508 let b = bool_const(e)?;
5509 if !is_bool_var(*var, vt) { return None; }
5510 (*var, b)
5511 }
5512 _ => return None,
5513 };
5514 let Stmt::If { cond, then_stmt, else_stmt } = &list[i + 1] else {
5515 return None;
5516 };
5517 if reads_var(cond, v) {
5518 return None;
5519 }
5520 let mut touches_v = false;
5521 visit_exprs(cond, &mut |x| {
5522 if let Expr::Assign { target, .. } = x {
5523 if let Expr::Local { var, .. } = &**target {
5524 if *var == v { touches_v = true; }
5525 }
5526 }
5527 });
5528 if touches_v {
5529 return None;
5530 }
5531 let (cond_n, then_arm, else_arm): (Expr, &Stmt, Option<&Stmt>) =
5534 if flat_list(then_stmt).is_empty() && else_stmt.is_some() {
5535 (not(cond.clone()), else_stmt.as_ref().unwrap(), None)
5536 } else {
5537 (cond.clone(), then_stmt.as_ref(), else_stmt.as_deref())
5538 };
5539 let e1 = lone_assign(then_arm, v, vt, an, consumed)?;
5540 let e2 = match else_arm {
5541 None => None,
5542 Some(b) => Some(lone_assign(b, v, vt, an, consumed)?),
5543 };
5544 let e1c = bool_const(&e1);
5545 let c = simplify_bool(cond_n);
5546 Some(match e2 {
5547 None => {
5548 if !base {
5549 match e1c {
5550 Some(true) => c,
5551 Some(false) => return None, None => bin(BinOp::LogAnd, c, e1),
5553 }
5554 } else {
5555 match e1c {
5556 Some(false) => not(c),
5557 Some(true) => return None,
5558 None => bin(BinOp::LogOr, not(c), e1),
5559 }
5560 }
5561 }
5562 Some(e2) => {
5563 if e1 == e2 {
5565 return None; }
5567 Expr::Cond { c: Box::new(c), t: Box::new(e1), f: Box::new(e2) }
5568 }
5569 })
5570 }
5571}
5572
5573pub fn idiom_compounds(body: &mut Stmt, vt: &VarTable) {
5574 walk_mut_deep(body, &mut |st| {
5575 let Stmt::ExprStmt(e) = st else { return };
5576 let Expr::Assign { target, op: AssignOp::Plain, value } = &mut *e else {
5577 return;
5578 };
5579 enum Tgt {
5582 Loc(u32),
5583 Fld,
5584 }
5585 let tgt = match &**target {
5586 Expr::Local { var, .. } => Tgt::Loc(*var),
5587 Expr::Field { owner, .. }
5588 if owner.is_none()
5589 || matches!(&**owner.as_ref().unwrap(), Expr::This) =>
5590 {
5591 Tgt::Fld
5592 }
5593 _ => return,
5594 };
5595 let Expr::Bin { op, l, r, .. } = &mut **value else {
5596 return;
5597 };
5598 match (&tgt, &**l) {
5600 (Tgt::Loc(v), Expr::Local { var, .. }) if *var == *v => {}
5601 (Tgt::Fld, Expr::Field { cls: c2, name: n2, .. }) => {
5602 let Expr::Field { cls: c1, name: n1, .. } = &**target else {
5603 return;
5604 };
5605 if c1 != c2 || n1 != n2 {
5606 return;
5607 }
5608 }
5609 _ => return,
5610 }
5611 let one = match &**r {
5613 Expr::Const(ConstVal::Int(1)) => Some(1i64),
5614 Expr::Const(ConstVal::Int(-1)) => Some(-1),
5615 Expr::Const(ConstVal::Long(1)) => Some(1),
5616 Expr::Const(ConstVal::Long(-1)) => Some(-1),
5617 _ => None,
5618 };
5619 if *op == BinOp::Add || *op == BinOp::Sub {
5620 if let Some(mut d) = one {
5621 if *op == BinOp::Sub {
5622 d = -d;
5623 }
5624 let tgt_ty = match &tgt {
5625 Tgt::Loc(v) => vt.var(*v).ty.erased(),
5626 Tgt::Fld => {
5627 let Expr::Field { ty, .. } = &**target else {
5628 return;
5629 };
5630 ty.erased()
5631 }
5632 };
5633 let numeric = matches!(
5634 tgt_ty,
5635 JavaType::Int
5636 | JavaType::Long
5637 | JavaType::Short
5638 | JavaType::Byte
5639 | JavaType::Char
5640 | JavaType::Float
5641 | JavaType::Double
5642 );
5643 if numeric {
5644 let wide = matches!(tgt_ty, JavaType::Long);
5645 let inner = match &tgt {
5646 Tgt::Loc(v) => Expr::Local {
5647 var: *v,
5648 ty: vt.var(*v).ty.clone(),
5649 },
5650 Tgt::Fld => (**target).clone(),
5651 };
5652 *e = Expr::PostIncDec {
5653 e: Box::new(inner),
5654 delta: d,
5655 wide,
5656 };
5657 return;
5658 }
5659 }
5660 }
5661 let cop = match op {
5662 BinOp::Add => AssignOp::Add,
5663 BinOp::Sub => AssignOp::Sub,
5664 BinOp::Mul => AssignOp::Mul,
5665 BinOp::Div => AssignOp::Div,
5666 BinOp::Rem => AssignOp::Rem,
5667 BinOp::Shl => AssignOp::Shl,
5668 BinOp::Shr => AssignOp::Shr,
5669 BinOp::Ushr => AssignOp::Ushr,
5670 BinOp::And => AssignOp::And,
5671 BinOp::Or => AssignOp::Or,
5672 BinOp::Xor => AssignOp::Xor,
5673 _ => return,
5674 };
5675 let rhs = std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
5676 let new_target = match &tgt {
5677 Tgt::Loc(v) => Box::new(Expr::Local {
5678 var: *v,
5679 ty: vt.var(*v).ty.clone(),
5680 }),
5681 Tgt::Fld => target.clone(),
5682 };
5683 *e = Expr::Assign {
5684 target: new_target,
5685 op: cop,
5686 value: rhs,
5687 };
5688 });
5689}
5690
5691pub fn fix_bool_xor(body: &mut Stmt, vt: &VarTable, ret_bool: bool) {
5692 fn is_one(e: &Expr) -> bool {
5693 matches!(e, Expr::Const(ConstVal::Int(1)))
5694 }
5695 fn bty(e: &Expr, vt: &VarTable) -> bool {
5696 matches!(
5697 match e {
5698 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
5699 other => other.type_ref().erased(),
5700 },
5701 JavaType::Boolean
5702 )
5703 }
5704 fn inv_of_xor(x: &Expr, vt: &VarTable) -> Option<Expr> {
5709 if let Expr::Bin { op: BinOp::Xor, l, r, .. } = x {
5710 let (int_side, one_side) = if is_one(r) {
5711 (l, r)
5712 } else if is_one(l) {
5713 (r, l)
5714 } else {
5715 return None;
5716 };
5717 let _ = one_side;
5718 if !bty(int_side, vt) {
5719 return Some(Expr::Bin {
5720 op: BinOp::Eq,
5721 l: int_side.clone(),
5722 r: Box::new(Expr::Const(ConstVal::Int(0))),
5723 ty: Some(TypeRef::J(JavaType::Boolean)),
5724 });
5725 }
5726 }
5727 None
5728 }
5729 let tgt_bool = |target: &Expr, vt: &VarTable| -> bool {
5730 match target {
5731 Expr::Local { var, .. } => matches!(vt.var(*var).ty.erased(), JavaType::Boolean),
5732 Expr::Field { ty, .. } => matches!(ty.erased(), JavaType::Boolean),
5733 _ => false,
5734 }
5735 };
5736 walk_mut_deep(body, &mut |st| match st {
5737 Stmt::Return(Some(e)) if ret_bool => {
5738 if let Some(r) = inv_of_xor(e, vt) {
5739 *e = r;
5740 }
5741 }
5742 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain, .. }) => {
5743 if tgt_bool(target, vt) {
5744 if let Some(r) = inv_of_xor(value, vt) {
5745 **value = r;
5746 }
5747 }
5748 }
5749 Stmt::LocalDef { var, init: Some(value), .. }
5750 if matches!(vt.var(*var).ty.erased(), JavaType::Boolean) =>
5751 {
5752 if let Some(r) = inv_of_xor(value, vt) {
5753 *value = r;
5754 }
5755 }
5756 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
5757 if let Some(r) = inv_of_xor(cond, vt) {
5758 *cond = r;
5759 }
5760 }
5761 _ => {}
5762 });
5763 fn ity(e: &Expr, vt: &VarTable) -> bool {
5764 matches!(
5765 match e {
5766 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
5767 other => other.type_ref().erased(),
5768 },
5769 JavaType::Int | JavaType::Short | JavaType::Byte | JavaType::Char
5770 )
5771 }
5772 fn mixed_rewrite(e: &mut Expr, vt: &VarTable) {
5781 deep_rewrite(e, &mut |x| {
5782 if let Expr::Bin { op, l, r, ty } = x {
5783 match op {
5784 BinOp::Xor => {
5785 if is_one(r) && bty(l, vt) {
5786 let taken =
5787 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
5788 *x = Expr::Un { op: UnOp::Not, e: taken };
5789 } else if is_one(l) && bty(r, vt) {
5790 let taken =
5791 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
5792 *x = Expr::Un { op: UnOp::Not, e: taken };
5793 }
5794 }
5795 BinOp::Or | BinOp::And => {
5796 if bty(l, vt) && ity(r, vt) {
5797 let taken =
5798 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
5799 **r = Expr::Bin {
5800 op: BinOp::Ne,
5801 l: taken,
5802 r: Box::new(Expr::Const(ConstVal::Int(0))),
5803 ty: Some(TypeRef::J(JavaType::Boolean)),
5804 };
5805 *ty = Some(TypeRef::J(JavaType::Boolean));
5806 } else if ity(l, vt) && bty(r, vt) {
5807 let taken =
5808 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
5809 **l = Expr::Bin {
5810 op: BinOp::Ne,
5811 l: taken,
5812 r: Box::new(Expr::Const(ConstVal::Int(0))),
5813 ty: Some(TypeRef::J(JavaType::Boolean)),
5814 };
5815 *ty = Some(TypeRef::J(JavaType::Boolean));
5816 }
5817 }
5818 _ => {}
5819 }
5820 }
5821 });
5822 }
5823 walk_mut_deep(body, &mut |st| match st {
5826 Stmt::Return(Some(e)) if ret_bool => mixed_rewrite(e, vt),
5827 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain, .. }) => {
5828 if tgt_bool(target, vt) {
5829 mixed_rewrite(value, vt);
5830 }
5831 }
5832 Stmt::LocalDef { var, init: Some(e), .. }
5833 if matches!(vt.var(*var).ty.erased(), JavaType::Boolean) =>
5834 {
5835 mixed_rewrite(e, vt);
5836 }
5837 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
5838 mixed_rewrite(cond, vt);
5839 }
5840 _ => {}
5841 });
5842 walk_stmt_exprs(body, &mut |e| {
5845 deep_rewrite(e, &mut |x| {
5846 if let Expr::Cond { c, .. } = x {
5847 mixed_rewrite(c, vt);
5848 }
5849 });
5850 });
5851}
5852
5853pub fn booleanize(vt: &mut VarTable, body: &mut Stmt, ret_bool: bool) -> usize {
5859 let n0 = vt.vars.len();
5866 let mut derefed = vec![false; n0];
5867 mark_derefs(body, &mut derefed);
5868 let mut total = 0usize;
5869 for _ in 0..4 {
5870 let n = booleanize_round(vt, body, ret_bool, &derefed);
5871 if n == 0 {
5872 break;
5873 }
5874 total += n;
5875 }
5876 total
5877}
5878
5879fn mark_deref_expr(e: &Expr, derefed: &mut [bool]) {
5883 match e {
5884 Expr::Field { owner: Some(o), is_static: false, .. }
5885 | Expr::Method { owner: Some(o), .. } => {
5886 if let Expr::Local { var, .. } = &**o {
5887 if (*var as usize) < derefed.len() {
5888 derefed[*var as usize] = true;
5889 }
5890 }
5891 }
5892 Expr::ArrayIndex { array, .. } => {
5893 if let Expr::Local { var, .. } = &**array {
5894 if (*var as usize) < derefed.len() {
5895 derefed[*var as usize] = true;
5896 }
5897 }
5898 }
5899 _ => {}
5900 }
5901 for_each_child(e, &mut |c| mark_deref_expr(c, derefed));
5902}
5903
5904fn mark_derefs(body: &Stmt, derefed: &mut [bool]) {
5905 walk_all(body, &mut |st| {
5906 match st {
5907 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
5908 mark_deref_expr(e, derefed);
5909 }
5910 Stmt::Return(Some(e)) => mark_deref_expr(e, derefed),
5911 Stmt::LocalDef { init: Some(e), .. } => mark_deref_expr(e, derefed),
5912 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
5913 mark_deref_expr(cond, derefed);
5914 }
5915 Stmt::For { init, .. } => {
5916 for x in init {
5917 mark_derefs(x, derefed);
5918 }
5919 }
5920 Stmt::Switch { selector, .. } => mark_deref_expr(selector, derefed),
5921 Stmt::ForEach { iterable, .. } => mark_deref_expr(iterable, derefed),
5922 _ => {}
5923 }
5924 });
5925}
5926
5927fn booleanize_round(
5928 vt: &mut VarTable,
5929 body: &mut Stmt,
5930 ret_bool: bool,
5931 derefed: &[bool],
5932) -> usize {
5933 let n = vt.vars.len();
5934 let mut in_cond = vec![false; n];
5935
5936 walk_all(body, &mut |st| {
5939 let cond: Option<&Expr> = match st {
5940 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
5941 Some(cond)
5942 }
5943 _ => None,
5944 };
5945 if let Some(c) = cond {
5946 if let Expr::Bin {
5947 op: BinOp::Eq | BinOp::Ne,
5948 l,
5949 r,
5950 ..
5951 } = c
5952 {
5953 for (x, other) in [(l, r), (r, l)] {
5954 if let (Expr::Local { var, .. }, Expr::Const(ConstVal::Int(0))) =
5955 (&**x, &**other)
5956 {
5957 if (*var as usize) < n {
5958 in_cond[*var as usize] = true;
5959 }
5960 }
5961 }
5962 }
5963 }
5964 if ret_bool {
5969 if let Stmt::Return(Some(Expr::Local { var, .. })) = st {
5970 if (*var as usize) < n {
5971 in_cond[*var as usize] = true;
5972 }
5973 }
5974 }
5975 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = st {
5982 if let Expr::Field { ty, .. } = &**target {
5983 if ty.erased() == JavaType::Boolean {
5984 if let Expr::Local { var, .. } = &**value {
5985 if (*var as usize) < n {
5986 in_cond[*var as usize] = true;
5987 }
5988 }
5989 }
5990 }
5991 }
5992 let val: Option<&Expr> = match st {
5993 Stmt::ExprStmt(Expr::Assign { value, .. }) => Some(value),
5994 Stmt::ExprStmt(e) => Some(e),
5997 Stmt::LocalDef { init: Some(e), .. } => Some(e),
5998 Stmt::Return(Some(e)) => Some(e),
5999 _ => None,
6000 };
6001 if let Some(v) = val {
6002 visit_exprs(v, &mut |x| {
6003 if let Expr::Method { desc, args, .. } = x {
6009 for (i, a) in args.iter().enumerate() {
6010 if desc.args.get(i) == Some(&JavaType::Boolean) {
6011 if let Expr::Local { var, .. } = a {
6012 if (*var as usize) < n {
6013 in_cond[*var as usize] = true;
6014 }
6015 }
6016 }
6017 }
6018 }
6019 if let Expr::Bin { op, l, r, .. } = x {
6020 if matches!(
6021 op,
6022 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
6023 ) {
6024 for side in [l, r] {
6025 if let Expr::Local { var, .. } = &**side {
6026 if (*var as usize) < n {
6027 in_cond[*var as usize] = true;
6028 }
6029 }
6030 }
6031 }
6032 if matches!(op, BinOp::Or | BinOp::And) {
6038 let l_bool = l.type_ref().erased() == JavaType::Boolean;
6039 let r_bool = r.type_ref().erased() == JavaType::Boolean;
6040 if l_bool != r_bool {
6041 let side = if l_bool { r } else { l };
6042 if let Expr::Local { var, .. } = &**side {
6043 if (*var as usize) < n {
6044 in_cond[*var as usize] = true;
6045 }
6046 }
6047 }
6048 }
6049 }
6050 });
6051 }
6052 });
6053
6054 let mut assigned_any = vec![false; n];
6061 let mut all_bool = vec![true; n];
6062 let mut edges: Vec<(usize, usize)> = Vec::new();
6063 walk_all(body, &mut |st| {
6064 let (var, value) = match st {
6065 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
6066 Expr::Local { var, .. } => (var, &**value),
6067 _ => return,
6068 },
6069 Stmt::LocalDef {
6070 var, init: Some(e), ..
6071 } => (var, e),
6072 _ => return,
6073 };
6074 let i = *var as usize;
6075 if i < n {
6076 assigned_any[i] = true;
6077 if !is_boolean_valued(value, vt) {
6078 all_bool[i] = false;
6079 }
6080 if let Expr::Local { var: src, .. } = value {
6086 if (*src as usize) < n {
6087 edges.push((i, *src as usize));
6088 }
6089 }
6090 }
6091 });
6092 loop {
6097 let mut changed = false;
6098 for &(tgt, src) in &edges {
6099 if in_cond[tgt] && !in_cond[src] && all_bool[src] {
6100 in_cond[src] = true;
6101 changed = true;
6102 }
6103 if in_cond[src] && !in_cond[tgt] && all_bool[tgt] {
6109 in_cond[tgt] = true;
6110 changed = true;
6111 }
6112 }
6113 if !changed {
6114 break;
6115 }
6116 }
6117 let reads = count_locals_stmts(std::slice::from_ref(body));
6118 let mut boolean_vars: HashSet<u32> = HashSet::default();
6119 for i in 0..n {
6120 if vt.vars[i].is_param {
6121 continue;
6122 }
6123 if derefed[i] {
6124 continue;
6125 }
6126 if !in_cond[i] && !(reads.get(&(i as u32)).copied().unwrap_or(0) == 0) {
6127 continue;
6128 }
6129 if assigned_any[i] && all_bool[i] {
6130 boolean_vars.insert(i as u32);
6131 }
6132 }
6133 if boolean_vars.is_empty() {
6134 return 0;
6135 }
6136 for v in &boolean_vars {
6137 vt.vars[*v as usize].ty = TypeRef::J(JavaType::Boolean);
6138 }
6139 let types: Vec<&TypeRef> = vt.vars.iter().map(|v| &v.ty).collect();
6140 rewrite_exprs(body, &mut |e| {
6141 deep_rewrite(e, &mut |x| {
6142 if let Expr::Local { var, ty } = x {
6143 if let Some(want) = types.get(*var as usize) {
6144 if ty != *want {
6145 *ty = (*want).clone();
6146 }
6147 }
6148 }
6149 });
6150 });
6151
6152 fold_bool_conditions(body, &boolean_vars);
6154 boolean_vars.len()
6155}
6156
6157fn is_boolean_valued(e: &Expr, vt: &VarTable) -> bool {
6158 match e {
6159 Expr::Const(ConstVal::Int(0)) | Expr::Const(ConstVal::Int(1)) => true,
6160 Expr::Bin { op: BinOp::Or | BinOp::And, l, r, .. } => {
6167 is_boolean_valued(l, vt) || is_boolean_valued(r, vt)
6168 }
6169 Expr::Bin {
6177 op: BinOp::Xor,
6178 l,
6179 r,
6180 ..
6181 } => is_boolean_valued(l, vt) && is_boolean_valued(r, vt),
6182 Expr::Bin { op, .. } => matches!(
6183 op,
6184 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
6185 ),
6186 Expr::InstanceOf { .. } => true,
6187 Expr::Local { var, ty } => {
6193 let t = if (*var as usize) < vt.vars.len() {
6194 vt.var(*var).ty.erased()
6195 } else {
6196 ty.erased()
6197 };
6198 t == JavaType::Boolean
6199 }
6200 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
6201 Expr::Cond { t, f, .. } => is_boolean_valued(t, vt) && is_boolean_valued(f, vt),
6202 Expr::Un { op: UnOp::Not, .. } => true,
6203 Expr::Field { ty, .. } => ty.erased() == JavaType::Boolean,
6207 _ => false,
6208 }
6209}
6210
6211fn fold_bool_conditions(s: &mut Stmt, bv: &HashSet<u32>) {
6212 let fold = |e: &mut Expr| {
6213 deep_rewrite(e, &mut |x| {
6214 if let Expr::Bin { op, l, r, .. } = x {
6215 let (var_side, const_side) = match (&**l, &**r) {
6216 (Expr::Local { .. }, Expr::Const(ConstVal::Int(_))) => (l, 0),
6217 (Expr::Const(ConstVal::Int(_)), Expr::Local { .. }) => (r, 1),
6218 _ => return,
6219 };
6220 let var = match &**var_side {
6221 Expr::Local { var, .. } => *var,
6222 _ => return,
6223 };
6224 if !bv.contains(&var) {
6225 return;
6226 }
6227 let _ = const_side;
6228 match op {
6229 BinOp::Ne => {
6230 let inner = var_side.clone();
6231 *x = *inner;
6232 }
6233 BinOp::Eq => {
6234 let inner = var_side.clone();
6235 *x = Expr::Un {
6236 op: UnOp::Not,
6237 e: Box::new(*inner),
6238 };
6239 }
6240 _ => {}
6241 }
6242 }
6243 });
6244 };
6245 match s {
6246 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => fold(cond),
6247 _ => {}
6248 }
6249 walk_mut_deep(s, &mut |st| {
6250 let cond = match st {
6251 Stmt::If { cond, .. } => Some(cond),
6252 Stmt::While { cond, .. } => Some(cond),
6253 Stmt::DoWhile { cond, .. } => Some(cond),
6254 _ => None,
6255 };
6256 if let Some(c) = cond {
6257 fold(c);
6258 }
6259 });
6260}
6261
6262pub fn flip_const_compares(s: &mut Stmt) {
6264 rewrite_exprs(s, &mut |e| {
6265 deep_rewrite(e, &mut |x| {
6266 if let Expr::Bin { op, l, r, .. } = x {
6267 if matches!(
6268 op,
6269 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
6270 ) {
6271 let l_const = matches!(&**l, Expr::Const(_));
6272 let r_var = matches!(&**r, Expr::Local { .. });
6273 if l_const && r_var {
6274 std::mem::swap(l, r);
6275 }
6276 }
6277 }
6278 });
6279 });
6280}
6281
6282pub fn null_compares(vt: &VarTable, body: &mut Stmt) {
6284 let obj_vars: HashSet<u32> = vt
6285 .vars
6286 .iter()
6287 .filter(|v| v.ty.erased().is_reference())
6288 .map(|v| v.id)
6289 .collect();
6290 rewrite_exprs(body, &mut |e| {
6291 deep_rewrite(e, &mut |x| {
6292 null_side_rewrite(x, &|v| obj_vars.contains(&v));
6293 });
6294 });
6295}
6296
6297pub fn drop_dead_locals(body: &mut Stmt) {
6306 loop {
6307 let mut reads: Vec<usize> = Vec::new();
6308 count_reads(body, &mut reads);
6309 let mut dropped = 0usize;
6310 walk_mut_deep(body, &mut |st| match st {
6311 Stmt::Block(items) => prune_dead_items(items, &reads, &mut dropped),
6312 Stmt::Switch { cases, .. } => {
6322 for c in cases {
6323 prune_dead_items(&mut c.body, &reads, &mut dropped);
6324 }
6325 }
6326 Stmt::For { init, .. } => prune_dead_items(init, &reads, &mut dropped),
6327 _ => {}
6328 });
6329 if dropped == 0 {
6330 break;
6331 }
6332 }
6333}
6334
6335fn prune_dead_items(items: &mut Vec<Stmt>, reads: &[usize], dropped: &mut usize) {
6340 let dead = |v: u32| reads.get(v as usize).copied().unwrap_or(0) == 0;
6341 items.retain_mut(|x| match x {
6342 Stmt::ExprStmt(Expr::Assign { target, value, op, .. }) => {
6343 let dead_target = matches!(&**target, Expr::Local { var, .. } if dead(*var));
6344 if dead_target && matches!(*op, AssignOp::Plain) {
6345 *dropped += 1;
6346 if has_side_effects(value) {
6347 let e = std::mem::replace(value, Box::new(Expr::This));
6348 *x = Stmt::ExprStmt(*e);
6349 true
6350 } else {
6351 false
6352 }
6353 } else {
6354 true
6355 }
6356 }
6357 Stmt::LocalDef { var, init, .. } if dead(*var) => {
6358 *dropped += 1;
6359 if let Some(e) = init.take() {
6360 if has_side_effects(&e) {
6361 *x = Stmt::ExprStmt(e);
6362 return true;
6363 }
6364 }
6365 false
6366 }
6367 _ => true,
6368 });
6369}
6370
6371fn for_each_child_stmt<'a>(st: &'a Stmt, f: &mut impl FnMut(&'a Stmt)) {
6374 match st {
6375 Stmt::Block(v) => {
6376 for x in v {
6377 f(x);
6378 }
6379 }
6380 Stmt::If {
6381 then_stmt,
6382 else_stmt,
6383 ..
6384 } => {
6385 f(then_stmt);
6386 if let Some(e) = else_stmt {
6387 f(e);
6388 }
6389 }
6390 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => f(body),
6391 Stmt::For { init, body, .. } => {
6392 for x in init {
6393 f(x);
6394 }
6395 f(body);
6396 }
6397 Stmt::ForEach { body, .. } => f(body),
6398 Stmt::Switch { cases, default, .. } => {
6399 for c in cases {
6400 for x in &c.body {
6401 f(x);
6402 }
6403 }
6404 if let Some(d) = default {
6405 f(d);
6406 }
6407 }
6408 Stmt::Try {
6409 body,
6410 catches,
6411 finally,
6412 } => {
6413 f(body);
6414 for c in catches {
6415 f(&c.body);
6416 }
6417 if let Some(fl) = finally {
6418 f(fl);
6419 }
6420 }
6421 Stmt::TryWithResources {
6422 resources,
6423 body,
6424 catches,
6425 finally,
6426 } => {
6427 for r in resources {
6428 f(r);
6429 }
6430 f(body);
6431 for c in catches {
6432 f(&c.body);
6433 }
6434 if let Some(fl) = finally {
6435 f(fl);
6436 }
6437 }
6438 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => f(body),
6439 _ => {}
6440 }
6441}
6442
6443fn stmt_shallow_vars<F: FnMut(u32)>(st: &Stmt, out: &mut F) {
6448 fn ex<F: FnMut(u32)>(e: &Expr, out: &mut F) {
6449 visit_exprs(e, &mut |x| {
6450 if let Expr::Local { var, .. } = x {
6451 out(*var);
6452 }
6453 });
6454 }
6455 match st {
6456 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
6457 ex(e, out)
6458 }
6459 Stmt::TernaryValue { e } => ex(e, out),
6460 Stmt::Return(Some(e)) => ex(e, out),
6461 Stmt::LocalDef { var, init, .. } => {
6462 out(*var);
6463 if let Some(e) = init {
6464 ex(e, out);
6465 }
6466 }
6467 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
6468 ex(cond, out)
6469 }
6470 Stmt::For { cond, update, .. } => {
6471 if let Some(c) = cond {
6472 ex(c, out);
6473 }
6474 for u in update {
6475 ex(u, out);
6476 }
6477 }
6478 Stmt::ForEach { iterable, .. } => ex(iterable, out),
6479 Stmt::Switch { selector, cases, .. } => {
6480 ex(selector, out);
6481 for cg in cases {
6482 if let Some(g) = &cg.guard {
6483 ex(g, out);
6484 }
6485 }
6486 }
6487 Stmt::Assert { cond, msg } => {
6488 ex(cond, out);
6489 if let Some(m) = msg {
6490 ex(m, out);
6491 }
6492 }
6493 Stmt::Synchronized { lock, .. } => ex(lock, out),
6494 _ => {}
6495 }
6496}
6497
6498fn scope_pass_a(st: &Stmt, cur: u32, next: &mut u32, def_block: &mut [u32], sizes: &mut Vec<u32>) {
6501 if let Stmt::LocalDef { var, .. } = st {
6502 let i = *var as usize;
6503 if i < def_block.len() && def_block[i] == u32::MAX {
6504 def_block[i] = cur;
6505 }
6506 }
6507 for_each_child_stmt(st, &mut |c| {
6508 if matches!(c, Stmt::Block(_)) {
6509 let id = *next;
6510 *next += 1;
6511 sizes.push(0);
6512 scope_pass_a(c, id, next, def_block, sizes);
6513 sizes[id as usize] = *next - id;
6514 } else {
6515 scope_pass_a(c, cur, next, def_block, sizes);
6516 }
6517 });
6518}
6519
6520fn scope_pass_b(
6523 st: &Stmt,
6524 cur: u32,
6525 next: &mut u32,
6526 def_block: &[u32],
6527 sizes: &[u32],
6528 within: &mut [u32],
6529 total: &mut [u32],
6530) {
6531 stmt_shallow_vars(st, &mut |v| {
6532 let i = v as usize;
6533 if i < total.len() {
6534 total[i] += 1;
6535 let d = def_block[i];
6536 if d != u32::MAX && d <= cur && cur < d + sizes[d as usize] {
6537 within[i] += 1;
6538 }
6539 }
6540 });
6541 for_each_child_stmt(st, &mut |c| {
6542 if matches!(c, Stmt::Block(_)) {
6543 let id = *next;
6544 *next += 1;
6545 scope_pass_b(c, id, next, def_block, sizes, within, total);
6546 } else {
6547 scope_pass_b(c, cur, next, def_block, sizes, within, total);
6548 }
6549 });
6550}
6551
6552pub fn strip_enum_const_inits(s: &mut Stmt, class: &crate::PoolClass) {
6561 let const_fields: jdc_core::FxHashSet<&str> = class
6562 .static_fields
6563 .iter()
6564 .filter(|f| f.access & crate::access::ACC_ENUM != 0)
6565 .map(|f| f.name.as_str())
6566 .collect();
6567 if const_fields.is_empty() {
6568 return;
6569 }
6570 if let Stmt::Block(stmts) = s {
6571 stmts.retain(|st| {
6572 !matches!(
6573 st,
6574 Stmt::ExprStmt(Expr::Assign { target, value, .. })
6575 if matches!(&**target,
6576 Expr::Field { cls, name, is_static: true, .. }
6577 if cls.as_ref() == class.name
6578 && const_fields.contains(&**name))
6579 && matches!(&**value, Expr::New { cls: ncls, .. } if ncls.as_ref() == class.name)
6580 )
6581 });
6582 }
6583}
6584
6585fn contains_this_access(e: &Expr) -> bool {
6594 let mut hit = false;
6595 visit_exprs(e, &mut |x| {
6596 if hit {
6597 return;
6598 }
6599 match x {
6600 Expr::This => hit = true,
6601 Expr::Field { owner, is_static: false, .. } => {
6602 if owner.is_none() || matches!(&**owner.as_ref().unwrap(), Expr::This) {
6603 hit = true;
6604 }
6605 }
6606 Expr::Method { owner, is_static: false, .. } => {
6607 if owner.is_none() || matches!(&**owner.as_ref().unwrap(), Expr::This) {
6608 hit = true;
6609 }
6610 }
6611 _ => {}
6612 }
6613 });
6614 hit
6615}
6616
6617fn var_occurs_in(stmts: &[Stmt], var: u32) -> bool {
6632 stmts.iter().any(|st| {
6633 let mut c = st.clone();
6634 let mut hit = false;
6635 walk_stmt_exprs(&mut c, &mut |e| {
6636 if !hit {
6637 deep_rewrite(e, &mut |x| {
6638 if let Expr::Local { var: v, .. } = x {
6639 if *v == var {
6640 hit = true;
6641 }
6642 }
6643 });
6644 }
6645 });
6646 hit
6647 })
6648}
6649
6650pub struct CtorHelper {
6660 pub name: String,
6661 pub ret: JavaType,
6662 pub params: Vec<(JavaType, String)>,
6663 pub body: Stmt,
6664 pub vt: VarTable,
6665}
6666
6667fn ctor_helpers() -> &'static std::sync::Mutex<std::collections::HashMap<String, Vec<CtorHelper>>> {
6668 static H: std::sync::OnceLock<std::sync::Mutex<std::collections::HashMap<String, Vec<CtorHelper>>>> =
6669 std::sync::OnceLock::new();
6670 H.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::default()))
6671}
6672
6673fn helper_name(class: &str, desc: &str) -> String {
6676 let mut h: u64 = 0xcbf29ce484222325;
6677 for b in class.as_bytes().iter().chain(b"::").chain(desc.as_bytes()) {
6678 h ^= *b as u64;
6679 h = h.wrapping_mul(0x100000001b3);
6680 }
6681 format!("resolve${:x}", (h >> 16) & 0xffff_ffff)
6682}
6683
6684pub fn take_ctor_helpers(class: &str) -> Vec<CtorHelper> {
6687 let mut m = ctor_helpers().lock().unwrap_or_else(|e| e.into_inner());
6688 let mut v = m.remove(class).unwrap_or_default();
6689 v.sort_by(|a, b| a.name.cmp(&b.name));
6690 v
6691}
6692
6693fn strip_delegations_deep(stmts: &mut Vec<Stmt>) {
6694 stmts.retain(|s| !is_bare_ctor_call(s));
6695 for s in stmts.iter_mut() {
6696 strip_delegation_one(s);
6697 }
6698}
6699
6700fn strip_delegation_one(st: &mut Stmt) {
6701 if is_bare_ctor_call(st) {
6702 *st = Stmt::Block(Vec::new());
6703 return;
6704 }
6705 match st {
6706 Stmt::Block(v) => strip_delegations_deep(v),
6707 Stmt::If { then_stmt, else_stmt, .. } => {
6708 strip_delegation_one(then_stmt);
6709 if let Some(e) = else_stmt {
6710 strip_delegation_one(e);
6711 }
6712 }
6713 Stmt::While { body, .. }
6714 | Stmt::DoWhile { body, .. }
6715 | Stmt::ForEach { body, .. }
6716 | Stmt::Synchronized { body, .. }
6717 | Stmt::Labeled { body, .. } => strip_delegation_one(body),
6718 Stmt::For { init, body, .. } => {
6719 strip_delegations_deep(init);
6720 strip_delegation_one(body);
6721 }
6722 Stmt::Switch { cases, default, .. } => {
6723 for c in cases.iter_mut() {
6724 strip_delegations_deep(&mut c.body);
6725 }
6726 if let Some(d) = default {
6727 strip_delegation_one(d);
6728 }
6729 }
6730 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
6731 strip_delegation_one(body);
6732 for c in catches.iter_mut() {
6733 strip_delegation_one(&mut c.body);
6734 }
6735 if let Some(f) = finally {
6736 strip_delegation_one(f);
6737 }
6738 }
6739 _ => {}
6740 }
6741}
6742
6743#[allow(clippy::ptr_arg)]
6744fn rewrite_bare_returns(stmts: &mut Vec<Stmt>, val: &Expr) {
6745 for s in stmts.iter_mut() {
6746 rewrite_bare_returns_one(s, val);
6747 }
6748}
6749
6750fn rewrite_bare_returns_one(st: &mut Stmt, val: &Expr) {
6751 match st {
6752 Stmt::Return(None) => *st = Stmt::Return(Some(val.clone())),
6753 Stmt::Block(v) => rewrite_bare_returns(v, val),
6754 Stmt::If { then_stmt, else_stmt, .. } => {
6755 rewrite_bare_returns_one(then_stmt, val);
6756 if let Some(e) = else_stmt {
6757 rewrite_bare_returns_one(e, val);
6758 }
6759 }
6760 Stmt::While { body, .. }
6761 | Stmt::DoWhile { body, .. }
6762 | Stmt::ForEach { body, .. }
6763 | Stmt::Synchronized { body, .. }
6764 | Stmt::Labeled { body, .. } => rewrite_bare_returns_one(body, val),
6765 Stmt::For { init, body, .. } => {
6766 rewrite_bare_returns(init, val);
6767 rewrite_bare_returns_one(body, val);
6768 }
6769 Stmt::Switch { cases, default, .. } => {
6770 for c in cases.iter_mut() {
6771 rewrite_bare_returns(&mut c.body, val);
6772 }
6773 if let Some(d) = default {
6774 rewrite_bare_returns_one(d, val);
6775 }
6776 }
6777 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
6778 rewrite_bare_returns_one(body, val);
6779 for c in catches.iter_mut() {
6780 rewrite_bare_returns_one(&mut c.body, val);
6781 }
6782 if let Some(f) = finally {
6783 rewrite_bare_returns_one(f, val);
6784 }
6785 }
6786 _ => {}
6787 }
6788}
6789
6790fn collect_def_vars(stmts: &[Stmt], out: &mut jdc_core::FxHashSet<u32>) {
6791 for st in stmts {
6792 match st {
6793 Stmt::LocalDef { var, .. } => {
6794 out.insert(*var);
6795 }
6796 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
6797 if let Expr::Local { var, .. } = &**target {
6798 out.insert(*var);
6799 }
6800 }
6801 Stmt::Block(v) => collect_def_vars(v, out),
6802 Stmt::If { then_stmt, else_stmt, .. } => {
6803 collect_def_vars(std::slice::from_ref(then_stmt.as_ref()), out);
6804 if let Some(e) = else_stmt {
6805 collect_def_vars(std::slice::from_ref(e.as_ref()), out);
6806 }
6807 }
6808 Stmt::While { body, .. }
6809 | Stmt::DoWhile { body, .. }
6810 | Stmt::Synchronized { body, .. }
6811 | Stmt::Labeled { body, .. } => {
6812 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
6813 }
6814 Stmt::ForEach { var, body, .. } => {
6815 out.insert(*var);
6816 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
6817 }
6818 Stmt::For { init, body, .. } => {
6819 collect_def_vars(init, out);
6820 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
6821 }
6822 Stmt::Switch { cases, default, .. } => {
6823 for c in cases {
6824 collect_def_vars(&c.body, out);
6825 }
6826 if let Some(d) = default {
6827 collect_def_vars(std::slice::from_ref(d.as_ref()), out);
6828 }
6829 }
6830 Stmt::Try { body, catches, finally }
6831 | Stmt::TryWithResources { body, catches, finally, .. } => {
6832 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
6833 for c in catches {
6834 out.insert(c.var);
6835 collect_def_vars(std::slice::from_ref(c.body.as_ref()), out);
6836 }
6837 if let Some(f) = finally {
6838 collect_def_vars(std::slice::from_ref(f.as_ref()), out);
6839 }
6840 }
6841 _ => {}
6842 }
6843 }
6844}
6845
6846fn extraction_shape_ok(stmts: &[Stmt]) -> bool {
6849 stmts.iter().all(|st| match st {
6850 Stmt::Block(v) => extraction_shape_ok(v),
6851 Stmt::LocalDef { .. }
6852 | Stmt::ExprStmt(_)
6853 | Stmt::Return(_)
6854 | Stmt::Throw(_)
6855 | Stmt::Assert { .. }
6856 | Stmt::Break(_)
6857 | Stmt::Continue(_) => true,
6858 Stmt::If { then_stmt, else_stmt, .. } => {
6859 ok_one(then_stmt)
6860 && else_stmt.as_deref().map(ok_one).unwrap_or(true)
6861 }
6862 Stmt::While { body, .. }
6863 | Stmt::DoWhile { body, .. }
6864 | Stmt::ForEach { body, .. }
6865 | Stmt::Synchronized { body, .. } => ok_one(body),
6866 Stmt::Labeled { body, .. } => ok_one(body),
6867 Stmt::For { init, body, .. } => extraction_shape_ok(init) && ok_one(body),
6868 Stmt::Switch { cases, default, .. } => {
6869 cases.iter().all(|c| extraction_shape_ok(&c.body))
6870 && default.as_deref().map(ok_one).unwrap_or(true)
6871 }
6872 Stmt::Try { body, catches, finally }
6873 | Stmt::TryWithResources { body, catches, finally, .. } => {
6874 ok_one(body)
6875 && catches.iter().all(|c| ok_one(&c.body))
6876 && finally.as_deref().map(ok_one).unwrap_or(true)
6877 }
6878 _ => false,
6881 })
6882}
6883
6884fn ok_one(st: &Stmt) -> bool {
6885 extraction_shape_ok(std::slice::from_ref(st))
6886}
6887
6888fn static_safe(stmts: &[Stmt]) -> bool {
6889 let mut ok = true;
6890 for st in stmts {
6891 let mut c = st.clone();
6892 walk_stmt_exprs(&mut c, &mut |e| {
6893 deep_rewrite(e, &mut |x| {
6894 match x {
6895 Expr::This => ok = false,
6896 Expr::Field { owner: None, is_static: false, .. } => ok = false,
6898 Expr::Method { owner: None, is_static: false, .. } => ok = false,
6899 _ => {}
6900 }
6901 });
6902 });
6903 }
6904 ok
6905}
6906
6907pub fn extract_branched_delegation_helper(
6928 body: &mut Stmt,
6929 vt: &VarTable,
6930 class: &crate::PoolClass,
6931 desc_str: &str,
6932) {
6933 if class.is_enum() || class.nesting.enclosing_class.is_some() {
6934 return;
6935 }
6936 let Stmt::Block(stmts) = body else { return };
6937 let mut dels: Vec<Expr> = Vec::new();
6938 for st in stmts.iter() {
6939 let mut c = st.clone();
6940 walk_stmt_exprs(&mut c, &mut |e| {
6941 if is_delegation_expr(e) {
6942 dels.push(e.clone());
6943 }
6944 });
6945 }
6946 if dels.is_empty() {
6947 return;
6948 }
6949 if dels.len() == 1 && stmts.first().is_some_and(is_bare_ctor_call) {
6950 return; }
6952 if !dels.iter().all(|d| *d == dels[0]) {
6953 return;
6954 }
6955 let template = dels[0].clone();
6956 let Expr::Method { args: t_args, .. } = &template else {
6957 return;
6958 };
6959 let mut carrier: Option<u32> = None;
6961 let mut reads = 0usize;
6962 let mut multi = false;
6963 for a in t_args.iter() {
6964 let c = a.clone();
6965 visit_exprs(&c, &mut |x| {
6966 if let Expr::Local { var, .. } = x {
6967 if (*var as usize) < vt.vars.len() && !vt.vars[*var as usize].is_param {
6968 reads += 1;
6969 match carrier {
6970 Some(v) if v == *var => {}
6971 Some(_) => multi = true,
6972 None => carrier = Some(*var),
6973 }
6974 }
6975 }
6976 });
6977 }
6978 let Some(cv) = carrier else { return };
6979 if multi || reads != 1 {
6980 return;
6981 }
6982 let Some(l) = stmts.iter().rposition(is_bare_ctor_call) else {
6983 return;
6984 };
6985 if l == 0 {
6986 return;
6987 }
6988 let mut extracted: Vec<Stmt> = stmts[..l].to_vec();
6989 if !extraction_shape_ok(&extracted) {
6990 return;
6991 }
6992 strip_delegations_deep(&mut extracted);
6993 let cv_local = Expr::Local {
6994 var: cv,
6995 ty: vt.var(cv).ty.clone(),
6996 };
6997 rewrite_bare_returns(&mut extracted, &cv_local);
6998 extracted.push(Stmt::Return(Some(cv_local)));
6999 if !static_safe(&extracted) {
7000 return;
7001 }
7002 let mut defs: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
7003 collect_def_vars(&extracted, &mut defs);
7004 if defs.iter().any(|v| var_occurs_in(&stmts[l + 1..], *v)) {
7005 return;
7006 }
7007 let params: Vec<(u32, JavaType, String)> = vt
7008 .vars
7009 .iter()
7010 .filter(|v| v.is_param && v.name != "this")
7011 .map(|v| (v.id, v.ty.erased(), v.name.clone()))
7012 .collect();
7013 let ret = vt.var(cv).ty.erased();
7014 let name = helper_name(&class.name, desc_str);
7015 let call = Expr::Method {
7016 owner: None,
7017 cls: std::sync::Arc::from(class.name.as_str()),
7018 name: std::sync::Arc::from(name.as_str()),
7019 desc: std::sync::Arc::new(jdc_core::types::MethodDescriptor {
7020 ret: ret.clone(),
7021 args: params.iter().map(|p| p.1.clone()).collect(),
7022 }),
7023 args: params
7024 .iter()
7025 .map(|(id, ty, _)| Expr::Local {
7026 var: *id,
7027 ty: TypeRef::J(ty.clone()),
7028 })
7029 .collect(),
7030 is_static: true,
7031 is_interface: false,
7032 is_special: false,
7033 is_super: false,
7034 is_dynamic: false,
7035 type_args: Vec::new(),
7036 };
7037 let mut new_del = template.clone();
7038 deep_rewrite(&mut new_del, &mut |x| {
7039 if let Expr::Local { var, .. } = x {
7040 if *var == cv {
7041 *x = call.clone();
7042 }
7043 }
7044 });
7045 {
7046 let mut m = ctor_helpers().lock().unwrap_or_else(|e| e.into_inner());
7047 let v = m.entry(class.name.clone()).or_default();
7048 if !v.iter().any(|h| h.name == name) {
7049 let mut hbody = Stmt::Block(extracted);
7053 invert_empty_thens(&mut hbody);
7054 v.push(CtorHelper {
7055 name,
7056 ret,
7057 params: params.into_iter().map(|(_, ty, nm)| (ty, nm)).collect(),
7058 body: hbody,
7059 vt: vt.clone(),
7060 });
7061 } else {
7062 return; }
7064 }
7065 let tail: Vec<Stmt> = stmts[l + 1..].to_vec();
7066 *stmts = std::iter::once(Stmt::ExprStmt(new_del)).chain(tail).collect();
7067}
7068
7069fn inline_ctor_arg_defs(
7070 call: &mut Stmt,
7071 prefix: &[&Stmt],
7072 vt: &VarTable,
7073 consumed: &mut Vec<(u32, usize)>,
7074) -> bool {
7075 let Stmt::ExprStmt(cexpr) = call else {
7076 return false;
7077 };
7078 fn subst(e: &mut Expr, v: u32, val: &Option<Expr>) -> bool {
7081 let mut ok = true;
7082 deep_rewrite(e, &mut |x| {
7083 if let Expr::Local { var, .. } = x {
7084 if *var == v {
7085 match val {
7086 Some(e2) => *x = e2.clone(),
7087 None => ok = false,
7088 }
7089 }
7090 }
7091 });
7092 ok
7093 }
7094 let mut seen: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
7095 for _ in 0..32 {
7096 let mut need: Option<u32> = None;
7097 visit_exprs(cexpr, &mut |x| {
7098 if need.is_none() {
7099 if let Expr::Local { var, .. } = x {
7100 if (*var as usize) < vt.vars.len() && !vt.vars[*var as usize].is_param {
7101 need = Some(*var);
7102 }
7103 }
7104 }
7105 });
7106 let Some(v) = need else { break };
7107 if !seen.insert(v) {
7108 return false; }
7110 let mut val: Option<Expr> = None;
7122 let mut ok = true;
7123 for (si, st) in prefix.iter().enumerate() {
7124 match st {
7125 Stmt::LocalDef { var, init: None, .. } if *var == v => {}
7129 Stmt::LocalDef { var, init, .. } if *var == v => {
7130 let Some(e) = init else { ok = false; break };
7131 let mut e2 = (*e).clone();
7132 if !subst(&mut e2, v, &val) {
7133 ok = false;
7134 break;
7135 }
7136 val = Some(e2);
7137 consumed.push((v, si));
7138 }
7139 Stmt::ExprStmt(Expr::Assign { target, op, .. })
7140 if matches!(&**target, Expr::Local { var: tv, .. } if *tv == v) =>
7141 {
7142 if !matches!(op, AssignOp::Plain) {
7143 ok = false; break;
7145 }
7146 let Stmt::ExprStmt(Expr::Assign { value, .. }) = st else {
7147 unreachable!()
7148 };
7149 let mut e2 = value.as_ref().clone();
7150 if !subst(&mut e2, v, &val) {
7151 ok = false;
7152 break;
7153 }
7154 val = Some(e2);
7155 consumed.push((v, si));
7156 }
7157 _ => {
7158 let mut touched = false;
7159 visit_all_exprs(st, &mut |x| {
7160 if let Expr::Local { var, .. } = x {
7161 if *var == v {
7162 touched = true;
7163 }
7164 }
7165 });
7166 if touched {
7167 ok = false;
7168 break;
7169 }
7170 }
7171 }
7172 }
7173 if !ok {
7174 return false;
7175 }
7176 let Some(de) = val else { return false };
7177 if contains_this_access(&de) {
7178 return false;
7179 }
7180 let mut de_nodes = 0usize;
7182 visit_exprs(&de, &mut |_| de_nodes += 1);
7183 if de_nodes > 256 {
7184 return false;
7185 }
7186 deep_rewrite(cexpr, &mut |x| {
7187 if let Expr::Local { var, .. } = x {
7188 if *var == v {
7189 *x = de.clone();
7190 }
7191 }
7192 });
7193 let mut nodes = 0usize;
7198 visit_exprs(cexpr, &mut |_| nodes += 1);
7199 if nodes > 2_048 {
7200 return false;
7201 }
7202 }
7203 true
7204}
7205
7206fn fold_sb_chain(stmts: &mut Vec<Stmt>, pos: usize) -> usize {
7219 let mut sbv: Option<u32> = None;
7221 let mut def_i: Option<usize> = None;
7222 let mut def_init: Option<Expr> = None;
7223 for (i, st) in stmts[..pos].iter().enumerate() {
7224 let cand: Option<(u32, &Expr)> = match st {
7225 Stmt::LocalDef {
7226 var,
7227 init: Some(e),
7228 ..
7229 } => Some((*var, e)),
7230 Stmt::ExprStmt(Expr::Assign {
7231 target,
7232 value,
7233 op: AssignOp::Plain,
7234 ..
7235 }) => match &**target {
7236 Expr::Local { var, .. } => Some((*var, value.as_ref())),
7237 _ => None,
7238 },
7239 _ => None,
7240 };
7241 if let Some((v, init)) = cand {
7242 if let Expr::New { cls, .. } = init {
7243 if cls.as_ref() == "java/lang/StringBuilder" {
7244 sbv = Some(v);
7245 def_i = Some(i);
7246 def_init = Some(init.clone());
7247 break;
7248 }
7249 }
7250 }
7251 }
7252 let (Some(sbv), Some(def_i), Some(init)) = (sbv, def_i, def_init) else {
7253 return pos;
7254 };
7255 if contains_this_access(&init) {
7256 return pos;
7257 }
7258 let mut appends: Vec<usize> = Vec::new();
7260 for (i, st) in stmts[..pos].iter().enumerate() {
7261 if let Stmt::ExprStmt(Expr::Method {
7262 owner: Some(o),
7263 cls,
7264 name,
7265 ..
7266 }) = st
7267 {
7268 if matches!(&**o, Expr::Local { var, .. } if *var == sbv)
7269 && cls.as_ref() == "java/lang/StringBuilder"
7270 && &**name == "append"
7271 {
7272 appends.push(i);
7273 }
7274 }
7275 }
7276 let mut post_read = false;
7278 for st in stmts.iter().skip(pos + 1) {
7279 visit_all_exprs(st, &mut |e| {
7280 if let Expr::Local { var, .. } = e {
7281 if *var == sbv {
7282 post_read = true;
7283 }
7284 }
7285 });
7286 }
7287 if post_read {
7288 return pos;
7289 }
7290 for (i, st) in stmts[..pos].iter().enumerate() {
7293 if i == def_i || appends.contains(&i) {
7294 if let Stmt::ExprStmt(Expr::Method { args, .. }) = st {
7296 if args.iter().any(contains_this_access) {
7297 return pos;
7298 }
7299 }
7300 continue;
7301 }
7302 let mut leak = false;
7303 visit_all_exprs(st, &mut |e| {
7304 if let Expr::Local { var, .. } = e {
7305 if *var == sbv {
7306 leak = true;
7307 }
7308 }
7309 });
7310 if leak {
7311 return pos;
7312 }
7313 }
7314 let mut local_reads = 0usize;
7316 let mut tostring_reads = 0usize;
7317 visit_all_exprs(&stmts[pos], &mut |e| match e {
7318 Expr::Local { var, .. } if *var == sbv => local_reads += 1,
7319 Expr::Method {
7320 owner: Some(o),
7321 name,
7322 args,
7323 ..
7324 } if args.is_empty()
7325 && &**name == "toString"
7326 && matches!(&**o, Expr::Local { var, .. } if *var == sbv) =>
7327 {
7328 tostring_reads += 1;
7329 }
7330 _ => {}
7331 });
7332 if local_reads != 1 || tostring_reads != 1 {
7333 return pos;
7334 }
7335 let mut chain = init;
7337 for ai in &appends {
7338 if let Stmt::ExprStmt(Expr::Method {
7339 owner: _,
7340 cls,
7341 name,
7342 desc,
7343 args,
7344 is_static,
7345 is_interface,
7346 is_special,
7347 is_super,
7348 is_dynamic,
7349 type_args,
7350 ..
7351 }) = &stmts[*ai]
7352 {
7353 chain = Expr::Method {
7354 owner: Some(Box::new(chain)),
7355 cls: cls.clone(),
7356 name: name.clone(),
7357 desc: desc.clone(),
7358 args: args.clone(),
7359 is_static: *is_static,
7360 is_interface: *is_interface,
7361 is_special: *is_special,
7362 is_super: *is_super,
7363 is_dynamic: *is_dynamic,
7364 type_args: type_args.clone(),
7365 };
7366 }
7367 }
7368 let mut replaced = false;
7369 walk_stmt_exprs(&mut stmts[pos], &mut |e| {
7370 deep_rewrite(e, &mut |x| {
7371 if let Expr::Method {
7372 owner: Some(o),
7373 name,
7374 args,
7375 ..
7376 } = x
7377 {
7378 if args.is_empty()
7379 && &**name == "toString"
7380 && matches!(&**o, Expr::Local { var, .. } if *var == sbv)
7381 {
7382 **o = chain.clone();
7383 replaced = true;
7384 }
7385 }
7386 });
7387 });
7388 if !replaced {
7389 return pos; }
7391 let mut removed: Vec<usize> = appends.clone();
7393 removed.push(def_i);
7394 removed.sort_unstable();
7395 removed.dedup();
7396 for i in removed.iter().rev() {
7397 stmts.remove(*i);
7398 }
7399 pos - removed.len()
7400}
7401
7402pub fn fix_ctor_super_first(body: &mut Stmt, vt: &VarTable) {
7403 let Stmt::Block(stmts) = body else { return };
7404 if stmts.is_empty() || is_bare_ctor_call(stmts.first().unwrap()) {
7405 return;
7406 }
7407 if let Some(pos0) = stmts.iter().position(is_bare_ctor_call) {
7409 let pos = fold_sb_chain(stmts, pos0);
7410 let mut call = stmts[pos].clone();
7411 let prefix: Vec<&Stmt> = stmts[..pos].iter().collect();
7412 let mut consumed: Vec<(u32, usize)> = Vec::new();
7413 if !inline_ctor_arg_defs(&mut call, &prefix, vt, &mut consumed) {
7414 return;
7415 }
7416 stmts.remove(pos);
7417 let mut drop_idx: Vec<usize> = consumed
7424 .iter()
7425 .filter(|(v, _)| !var_occurs_in(&stmts[pos..], *v))
7426 .map(|(_, k)| *k)
7427 .collect();
7428 drop_idx.sort_unstable();
7429 drop_idx.dedup();
7430 for k in drop_idx.iter().rev() {
7431 stmts.remove(*k);
7432 }
7433 stmts.insert(0, call);
7434 return;
7435 }
7436 let mut found: Option<(usize, usize)> = None;
7446 for (i, st) in stmts.iter().enumerate() {
7447 if let Stmt::Block(inner) = st {
7448 if let Some(p) = inner.iter().position(is_bare_ctor_call) {
7449 if inner[..p]
7450 .iter()
7451 .all(|s| matches!(s, Stmt::LocalDef { .. } | Stmt::ExprStmt(_)))
7452 {
7453 found = Some((i, p));
7454 break;
7455 }
7456 }
7457 }
7458 }
7459 if let Some((i, p)) = found {
7460 let mut call = match &stmts[i] {
7461 Stmt::Block(inner) => inner[p].clone(),
7462 _ => unreachable!(),
7463 };
7464 let mut prefix: Vec<&Stmt> = stmts[..i].iter().collect();
7465 if let Stmt::Block(inner) = &stmts[i] {
7466 prefix.extend(inner[..p].iter());
7467 }
7468 let mut consumed: Vec<(u32, usize)> = Vec::new();
7469 if !inline_ctor_arg_defs(&mut call, &prefix, vt, &mut consumed) {
7470 return;
7471 }
7472 let mut ks: Vec<usize> = Vec::new();
7479 {
7480 let inner_tail: &[Stmt] = match &stmts[i] {
7481 Stmt::Block(inner) if p < inner.len() => &inner[p + 1..],
7482 _ => &[],
7483 };
7484 for (v, k) in &consumed {
7485 if !var_occurs_in(inner_tail, *v) && !var_occurs_in(&stmts[i + 1..], *v) {
7486 ks.push(*k);
7487 }
7488 }
7489 }
7490 ks.sort_unstable();
7491 ks.dedup();
7492 if let Stmt::Block(inner) = &mut stmts[i] {
7493 inner.remove(p);
7494 for k in ks.iter().rev().filter(|k| **k >= i) {
7495 inner.remove(k - i);
7496 }
7497 }
7498 for k in ks.iter().rev().filter(|k| **k < i) {
7499 stmts.remove(*k);
7500 }
7501 stmts.insert(0, call);
7502 }
7503}
7504
7505fn is_delegation_expr(e: &Expr) -> bool {
7514 matches!(
7515 e,
7516 Expr::Method { name, is_special: true, owner, .. }
7517 if &**name == "<init>"
7518 && (owner.is_none() || matches!(owner.as_deref(), Some(Expr::This)))
7519 )
7520}
7521
7522pub(crate) fn is_bare_ctor_call(s: &Stmt) -> bool {
7523 matches!(s, Stmt::ExprStmt(e) if is_delegation_expr(e))
7524}
7525
7526pub(crate) fn local_is_written(body: &Stmt, var: u32) -> bool {
7530 let mut hit = false;
7531 let mut c = body.clone();
7532 walk_stmt_exprs(&mut c, &mut |e| {
7533 if !hit {
7534 deep_rewrite(e, &mut |x| {
7535 if hit {
7536 return;
7537 }
7538 match x {
7539 Expr::Assign { target, .. }
7540 if matches!(&**target, Expr::Local { var: v, .. } if *v == var) =>
7541 {
7542 hit = true;
7543 }
7544 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. }
7545 if matches!(&**e, Expr::Local { var: v, .. } if *v == var) =>
7546 {
7547 hit = true;
7548 }
7549 _ => {}
7550 }
7551 });
7552 }
7553 });
7554 hit
7555}
7556
7557pub(crate) fn rewrite_inner_ctor_outer_param(
7567 body: &mut Stmt,
7568 param0: u32,
7569 outer_display: &str,
7570 outer_ty: &TypeRef,
7571 eligible: &[String],
7572) {
7573 walk_stmt_exprs(body, &mut |e| {
7577 if let Expr::Method { name, cls, args, .. } = e {
7578 if &**name == "<init>"
7579 && eligible.iter().any(|c| c.as_str() == cls.as_ref())
7580 && args.first().is_some_and(|a| matches!(a, Expr::Local { var: v, .. } if *v == param0))
7581 {
7582 args.remove(0);
7583 }
7584 }
7585 });
7586 let text = format!("{outer_display}.this");
7588 let ty = outer_ty.clone();
7589 walk_stmt_exprs(body, &mut |e| {
7590 deep_rewrite_reads(e, &mut |x| {
7591 if let Expr::Local { var: v, .. } = x {
7592 if *v == param0 {
7593 *x = Expr::RawT(text.clone(), ty.clone());
7594 }
7595 }
7596 });
7597 });
7598}
7599
7600fn stmts_count_writes(stmts: &[Stmt], var: u32) -> usize {
7648 let mut n = 0usize;
7649 for s in stmts {
7650 let mut c = s.clone();
7651 walk_stmt_exprs(&mut c, &mut |e| {
7652 deep_rewrite(e, &mut |x| match x {
7653 Expr::Assign { target, .. }
7654 if matches!(&**target, Expr::Local { var: v, .. } if *v == var) =>
7655 {
7656 n += 1;
7657 }
7658 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. }
7659 if matches!(&**e, Expr::Local { var: v, .. } if *v == var) =>
7660 {
7661 n += 1;
7662 }
7663 _ => {}
7664 });
7665 });
7666 }
7667 n
7668}
7669
7670fn prefix_written_vars(stmts: &[Stmt]) -> jdc_core::FxHashSet<u32> {
7674 let mut set = jdc_core::FxHashSet::default();
7675 for s in stmts {
7676 let mut c = s.clone();
7677 walk_stmt_exprs(&mut c, &mut |e| {
7678 deep_rewrite(e, &mut |x| match x {
7679 Expr::Assign { target, .. } => {
7680 if let Expr::Local { var, .. } = &**target {
7681 set.insert(*var);
7682 }
7683 }
7684 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => {
7685 if let Expr::Local { var, .. } = &**e {
7686 set.insert(*var);
7687 }
7688 }
7689 _ => {}
7690 });
7691 });
7692 if let Stmt::LocalDef { var, .. } = s {
7693 set.insert(*var);
7694 }
7695 }
7696 set
7697}
7698
7699fn pre_this_safe(e: &Expr, prefix_vars: &jdc_core::FxHashSet<u32>) -> bool {
7703 match e {
7704 Expr::Local { var, .. } => !prefix_vars.contains(var),
7705 Expr::Const(_) => true,
7706 Expr::Cast { e, .. } => pre_this_safe(e, prefix_vars),
7707 Expr::Bin { l, r, .. } => {
7708 pre_this_safe(l, prefix_vars) && pre_this_safe(r, prefix_vars)
7709 }
7710 Expr::Cond { c, t, f } => {
7711 pre_this_safe(c, prefix_vars)
7712 && pre_this_safe(t, prefix_vars)
7713 && pre_this_safe(f, prefix_vars)
7714 }
7715 Expr::Field { owner, is_static, .. } => {
7716 *is_static
7717 && owner
7718 .as_ref()
7719 .is_none_or(|o| pre_this_safe(o, prefix_vars))
7720 }
7721 Expr::Method {
7722 owner,
7723 args,
7724 is_static,
7725 ..
7726 } => {
7727 args.iter().all(|a| pre_this_safe(a, prefix_vars))
7728 && match owner {
7729 Some(o) => pre_this_safe(o, prefix_vars),
7730 None => *is_static,
7731 }
7732 }
7733 Expr::New { args, .. } => args.iter().all(|a| pre_this_safe(a, prefix_vars)),
7741 Expr::NewArray { dims, init, .. } => {
7742 dims.iter().all(|d| pre_this_safe(d, prefix_vars))
7743 && init
7744 .as_ref()
7745 .map(|l| l.iter().all(|x| pre_this_safe(x, prefix_vars)))
7746 .unwrap_or(true)
7747 }
7748 Expr::Un { e, .. } | Expr::InstanceOf { e, .. } => pre_this_safe(e, prefix_vars),
7750 Expr::ArrayIndex { array, index } => {
7751 pre_this_safe(array, prefix_vars) && pre_this_safe(index, prefix_vars)
7752 }
7753 _ => false,
7754 }
7755}
7756
7757fn single_assign_to_v(s: &Stmt, v: u32) -> Option<Expr> {
7761 match s {
7762 Stmt::ExprStmt(Expr::Assign {
7763 target,
7764 op: AssignOp::Plain,
7765 value,
7766 }) if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v) => {
7767 Some((**value).clone())
7768 }
7769 Stmt::Block(xs) if xs.len() == 1 => single_assign_to_v(&xs[0], v),
7770 _ => None,
7771 }
7772}
7773
7774pub(crate) fn flatten_top_blocks(stmts: &mut Vec<Stmt>) {
7796 fn rec(s: Stmt, out: &mut Vec<Stmt>) {
7797 match s {
7798 Stmt::Block(inner) => {
7799 for x in inner {
7800 rec(x, out);
7801 }
7802 }
7803 other => out.push(other),
7804 }
7805 }
7806 if !stmts.iter().any(|s| matches!(s, Stmt::Block(_))) {
7807 return;
7808 }
7809 let mut out: Vec<Stmt> = Vec::with_capacity(stmts.len());
7810 for s in std::mem::take(stmts) {
7811 rec(s, &mut out);
7812 }
7813 *stmts = out;
7814}
7815
7816fn fold_default_arg_bridge(stmts: &mut Vec<Stmt>) -> bool {
7817 flatten_top_blocks(stmts);
7818 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
7819 return false;
7820 };
7821 if pos == 0 {
7822 return false;
7823 }
7824 let Stmt::ExprStmt(Expr::Method { args, .. }) = &stmts[pos] else {
7825 return false;
7826 };
7827 let mut arg_vars: Vec<u32> = Vec::new();
7828 let mut reads = 0usize;
7829 for a in args {
7830 let mut c = a.clone();
7831 deep_rewrite_reads(&mut c, &mut |x| {
7832 if let Expr::Local { var, .. } = x {
7833 reads += 1;
7834 if !arg_vars.contains(var) {
7835 arg_vars.push(*var);
7836 }
7837 }
7838 });
7839 }
7840 if arg_vars.is_empty() || reads != arg_vars.len() {
7841 return false; }
7843 for &v in &arg_vars {
7846 if stmts_read_var(&stmts[pos + 1..], v) != 0
7847 || stmts_count_writes(&stmts[pos + 1..], v) != 0
7848 {
7849 return false;
7850 }
7851 }
7852 let prefix_vars = prefix_written_vars(&stmts[..pos]);
7853 let mut repl: Vec<(u32, Expr)> = Vec::new();
7854 let mut drop: Vec<usize> = Vec::new();
7855 for &v in &arg_vars {
7856 let mut base: Option<(usize, Expr)> = None;
7858 let mut decl: Option<usize> = None;
7859 let mut condf: Option<(usize, Expr, Option<Expr>, Option<Expr>)> = None;
7860 let mut ok = true;
7861 for (i, s) in stmts[..pos].iter().enumerate() {
7862 match s {
7863 Stmt::LocalDef { var, init, .. } if *var == v => match init {
7864 None => {
7865 if decl.is_some() {
7866 ok = false;
7867 break;
7868 }
7869 decl = Some(i);
7870 }
7871 Some(e) => {
7872 if base.is_some() {
7873 ok = false;
7874 break;
7875 }
7876 base = Some((i, e.clone()));
7877 }
7878 },
7879 Stmt::ExprStmt(Expr::Assign {
7880 target,
7881 op: AssignOp::Plain,
7882 value,
7883 }) if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v) => {
7884 if base.is_some() {
7885 ok = false;
7886 break;
7887 }
7888 base = Some((i, (**value).clone()));
7889 }
7890 Stmt::If {
7891 cond,
7892 then_stmt,
7893 else_stmt,
7894 } => {
7895 let t = single_assign_to_v(then_stmt, v);
7896 let f = else_stmt.as_ref().and_then(|b| single_assign_to_v(b, v));
7897 let then_touches = t.is_some()
7898 || stmts_read_var(std::slice::from_ref(&**then_stmt), v) != 0
7899 || stmts_count_writes(std::slice::from_ref(&**then_stmt), v) != 0;
7900 let else_touches = else_stmt.as_ref().is_some_and(|b| {
7901 f.is_some()
7902 || stmts_read_var(std::slice::from_ref(b), v) != 0
7903 || stmts_count_writes(std::slice::from_ref(b), v) != 0
7904 });
7905 if !then_touches && !else_touches {
7906 continue; }
7908 if condf.is_some()
7913 || base.is_none()
7914 || (then_touches && t.is_none())
7915 || (else_touches && f.is_none())
7916 {
7917 ok = false;
7918 break;
7919 }
7920 let cond_stmt = Stmt::ExprStmt(cond.clone());
7921 if stmts_read_var(std::slice::from_ref(&cond_stmt), v) != 0 {
7922 ok = false;
7923 break;
7924 }
7925 condf = Some((i, cond.clone(), t, f));
7926 }
7927 _ => {}
7928 }
7929 }
7930 if !ok {
7931 continue;
7932 }
7933 let Some((bi, be)) = base else { continue };
7934 let want_writes = 1
7937 + condf
7938 .as_ref()
7939 .map(|(_, _, t, f)| t.is_some() as usize + f.is_some() as usize)
7940 .unwrap_or(0);
7941 if stmts_count_writes(&stmts[..pos], v) != want_writes
7942 || stmts_read_var(&stmts[..pos], v) != 0
7943 {
7944 continue;
7945 }
7946 let rep = match condf {
7947 Some((ci, c, t, f)) => {
7948 drop.push(ci);
7949 match (t, f) {
7950 (Some(t), Some(f)) => Expr::Cond {
7951 c: Box::new(c),
7952 t: Box::new(t),
7953 f: Box::new(f),
7954 },
7955 (Some(t), None) => Expr::Cond {
7956 c: Box::new(c),
7957 t: Box::new(t),
7958 f: Box::new(be.clone()),
7959 },
7960 (None, Some(f)) => Expr::Cond {
7961 c: Box::new(c),
7962 t: Box::new(be.clone()),
7963 f: Box::new(f),
7964 },
7965 (None, None) => unreachable!("condf requires a branch assign"),
7966 }
7967 }
7968 None => be.clone(),
7969 };
7970 if !pre_this_safe(&rep, &prefix_vars) {
7971 continue;
7972 }
7973 drop.push(bi);
7974 if let Some(d) = decl {
7975 drop.push(d);
7976 }
7977 repl.push((v, rep));
7978 }
7979 if repl.is_empty() {
7980 return false;
7981 }
7982 if let Stmt::ExprStmt(Expr::Method { args, .. }) = &mut stmts[pos] {
7983 for a in args.iter_mut() {
7984 deep_rewrite_reads(a, &mut |x| {
7985 if let Expr::Local { var, .. } = x {
7986 if let Some((_, e)) = repl.iter().find(|(vv, _)| vv == var) {
7987 *x = e.clone();
7988 }
7989 }
7990 });
7991 }
7992 }
7993 drop.sort_unstable();
7994 drop.dedup();
7995 for i in drop.into_iter().rev() {
7996 stmts.remove(i);
7997 }
7998 true
7999}
8000
8001pub fn rewrite_kotlin_facades(body: &mut Stmt, pool: &crate::DexPool) {
8009 let map = pool.kotlin_facade_map();
8010 if map.is_empty() {
8011 return;
8012 }
8013 walk_stmt_exprs(body, &mut |e| {
8014 match e {
8015 Expr::Method {
8016 cls,
8017 name,
8018 desc,
8019 is_static,
8020 ..
8021 } if *is_static => {
8022 if let Some((base, cover)) = map.get(cls.as_ref()) {
8023 let hit = match cover {
8024 None => true,
8025 Some(c) => c.0.contains(&(name.to_string(), desc.to_string())),
8026 };
8027 if hit {
8028 *cls = std::sync::Arc::from(base.as_str());
8029 }
8030 }
8031 }
8032 Expr::Field {
8033 cls,
8034 name,
8035 is_static,
8036 ..
8037 } if *is_static => {
8038 if let Some((base, cover)) = map.get(cls.as_ref()) {
8039 let hit = match cover {
8040 None => true,
8041 Some(c) => c.1.contains(name.as_ref()),
8042 };
8043 if hit {
8044 *cls = std::sync::Arc::from(base.as_str());
8045 }
8046 }
8047 }
8048 _ => {}
8049 }
8050 });
8051}
8052
8053pub fn fold_enum_default_arg_bridge(body: &mut Stmt) {
8061 let Stmt::Block(stmts) = body else { return };
8062 fold_default_arg_bridge(stmts);
8063}
8064
8065pub fn fix_ctor_delegation_arg_defs(body: &mut Stmt) {
8066 let Stmt::Block(stmts) = body else { return };
8067 fold_default_arg_bridge(stmts);
8070 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
8074 return;
8075 };
8076 if pos == 0 {
8077 return;
8078 }
8079 let mut arg_vars: Vec<u32> = Vec::new();
8082 let mut total_reads = 0usize;
8083 {
8084 let Stmt::ExprStmt(Expr::Method { args, .. }) = &stmts[pos] else {
8085 return;
8086 };
8087 for a in args {
8088 let mut c = a.clone();
8089 deep_rewrite_reads(&mut c, &mut |x| {
8090 if let Expr::Local { var: v, .. } = x {
8091 total_reads += 1;
8092 if !arg_vars.contains(v) {
8093 arg_vars.push(*v);
8094 }
8095 }
8096 });
8097 }
8098 }
8099 if arg_vars.is_empty() || total_reads != arg_vars.len() {
8100 return;
8101 }
8102 let prefix_defs: Vec<u32> = stmts[..pos]
8105 .iter()
8106 .filter_map(|s| match s {
8107 Stmt::LocalDef { var, init: Some(_), .. } => Some(*var),
8108 _ => None,
8109 })
8110 .collect();
8111 let mut plan: Vec<(u32, usize, Option<usize>)> = Vec::new();
8116 for v in arg_vars {
8117 let defs: Vec<usize> = stmts[..pos]
8118 .iter()
8119 .enumerate()
8120 .filter(|(i, _)| {
8121 matches!(&stmts[*i], Stmt::LocalDef { var, init: Some(_), .. } if *var == v)
8122 })
8123 .map(|(i, _)| i)
8124 .collect();
8125 if defs.len() > 1 {
8126 return; }
8128 let Some(&q) = defs.first() else {
8129 continue; };
8131 let init = match &stmts[q] {
8132 Stmt::LocalDef { init: Some(e), .. } => e.clone(),
8133 _ => return,
8134 };
8135 if stmts_read_var(&stmts[q + 1..pos], v) != 0 {
8136 return; }
8138 let mut init_vars: Vec<u32> = Vec::new();
8141 deep_rewrite_reads(&mut { init.clone() }, &mut |x| {
8142 if let Expr::Local { var: vv, .. } = x {
8143 if !init_vars.contains(vv) {
8144 init_vars.push(*vv);
8145 }
8146 }
8147 });
8148 if init_vars.iter().any(|iv| prefix_defs.contains(iv)) {
8149 return;
8150 }
8151 match stmts[pos + 1..]
8152 .iter()
8153 .position(|s| {
8154 matches!(s, Stmt::ExprStmt(Expr::Assign { target, .. })
8155 if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v))
8156 }) {
8157 Some(w) => {
8158 if stmts_read_var(&stmts[pos + 1..pos + 1 + w], v) != 0 {
8160 return;
8161 }
8162 plan.push((v, q, Some(pos + 1 + w)));
8163 }
8164 None => {
8165 if stmts_read_var(&stmts[pos + 1..], v) != 0 {
8167 return;
8168 }
8169 plan.push((v, q, None));
8170 }
8171 }
8172 }
8173 if plan.is_empty() {
8174 return;
8175 }
8176 let values: Vec<(u32, Expr)> = plan
8178 .iter()
8179 .map(|(_v, q, _)| match &stmts[*q] {
8180 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e.clone()),
8181 _ => unreachable!("plan entries carry a LocalDef with init"),
8182 })
8183 .collect();
8184 if let Stmt::ExprStmt(Expr::Method { args, .. }) = &mut stmts[pos] {
8185 for a in args {
8186 deep_rewrite_reads(a, &mut |x| {
8187 if let Expr::Local { var: v, .. } = x {
8188 if let Some((_, e)) = values.iter().find(|(vv, _)| vv == v) {
8189 *x = e.clone();
8190 }
8191 }
8192 });
8193 }
8194 }
8195 for (v, _, w) in &plan {
8199 if let Some(w) = w {
8200 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = &mut stmts[*w] {
8201 if matches!(&**target, Expr::Local { var: vv, .. } if vv == v) {
8202 let taken = std::mem::replace(&mut **value, Expr::Const(ConstVal::Null));
8203 stmts[*w] = Stmt::LocalDef {
8204 var: *v,
8205 init: Some(taken),
8206 is_final: false,
8207 force_type: false,
8208 };
8209 }
8210 }
8211 }
8212 }
8213 let mut order: Vec<usize> = plan.iter().map(|(_, q, _)| *q).collect();
8214 order.sort_unstable();
8215 order.reverse();
8216 for q in order {
8217 stmts.remove(q);
8218 }
8219}
8220
8221pub fn fix_bool_returns(vt: &VarTable, body: &mut Stmt) {
8232 walk_mut_deep(body, &mut |st| {
8233 if let Stmt::Return(Some(e)) = st {
8234 let numeric = match &*e {
8235 Expr::Local { var, ty } => {
8236 let t = if (*var as usize) < vt.vars.len() {
8237 vt.var(*var).ty.erased()
8238 } else {
8239 ty.erased()
8240 };
8241 matches!(
8242 t,
8243 JavaType::Int
8244 | JavaType::Long
8245 | JavaType::Short
8246 | JavaType::Byte
8247 | JavaType::Char
8248 )
8249 }
8250 other => matches!(
8251 other.type_ref().erased(),
8252 JavaType::Int | JavaType::Long | JavaType::Short | JavaType::Byte
8253 | JavaType::Char
8254 ),
8255 };
8256 if numeric {
8257 let taken = e.clone();
8258 *e = Expr::Bin {
8259 op: BinOp::Ne,
8260 l: Box::new(taken),
8261 r: Box::new(Expr::Const(ConstVal::Int(0))),
8262 ty: Some(TypeRef::J(JavaType::Boolean)),
8263 };
8264 }
8265 }
8266 });
8267}
8268
8269pub fn fix_int_returns(vt: &VarTable, body: &mut Stmt) {
8270 walk_mut_deep(body, &mut |st| {
8271 if let Stmt::Return(Some(e)) = st {
8272 let is_bool = match e {
8273 Expr::Local { var, .. } => {
8274 (*var as usize) < vt.vars.len()
8275 && vt.vars[*var as usize].ty.erased() == JavaType::Boolean
8276 }
8277 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
8278 _ => false,
8279 };
8280 if is_bool {
8281 *e = Expr::Cond {
8282 c: Box::new(e.clone()),
8283 t: Box::new(Expr::Const(ConstVal::Int(1))),
8284 f: Box::new(Expr::Const(ConstVal::Int(0))),
8285 };
8286 }
8287 }
8288 });
8289}
8290
8291pub fn split_generations(vt: &mut VarTable, body: &mut Stmt, pool: &DexPool) {
8307 let mut counter: u32 = 0;
8308 let mut gen: std::collections::HashMap<u32, u32> = std::collections::HashMap::default();
8309 split_walk_stmt(body, vt, &mut gen, &mut counter, pool);
8310}
8311
8312fn split_needed(vt: &VarTable, target: u32, value: &Expr, pool: &DexPool) -> bool {
8314 let n = vt.vars.len();
8315 if target as usize >= n {
8316 return false;
8317 }
8318 let declared = vt.vars[target as usize].ty.erased();
8319 let vt_is_ref = |t: &JavaType| matches!(t, JavaType::Object(_) | JavaType::Array(_));
8320 fn side_bool(e: &Expr, vt: &VarTable) -> bool {
8329 match e {
8330 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
8331 matches!(vt.var(*var).ty.erased(), JavaType::Boolean)
8332 }
8333 Expr::Const(ConstVal::Int(0 | 1)) => true,
8334 Expr::Bin { op: BinOp::Or | BinOp::And | BinOp::Xor, l, r, .. } => {
8335 side_bool(l, vt) && side_bool(r, vt)
8336 }
8337 other => matches!(other.type_ref().erased(), JavaType::Boolean),
8338 }
8339 }
8340 let value_ty = match value {
8341 Expr::Local { var, ty } if (*var as usize) < vt.vars.len() => vt.var(*var).ty.erased(),
8342 Expr::Bin {
8343 op: BinOp::Or | BinOp::And | BinOp::Xor,
8344 l,
8345 r,
8346 ..
8347 } if side_bool(l, vt) && side_bool(r, vt) => JavaType::Boolean,
8348 other => other.type_ref().erased(),
8349 };
8350 if vt_is_ref(&declared) && vt_is_ref(&value_ty) {
8351 let obj = JavaType::Object("java/lang/Object".into());
8361 if declared == value_ty || declared == obj || value_ty == obj {
8362 return false;
8363 }
8364 if let (JavaType::Object(v), JavaType::Object(d)) = (&value_ty, &declared) {
8377 if matches!(value, Expr::New { .. })
8378 && (pool.get(v).is_none() || pool.get(d).is_none())
8379 {
8380 return false;
8381 }
8382 }
8383 true
8384 } else {
8385 if vt_is_ref(&declared) != vt_is_ref(&value_ty) {
8389 return true;
8390 }
8391 let d_bool = matches!(declared, JavaType::Boolean);
8399 let v_bool = matches!(value_ty, JavaType::Boolean);
8400 let zero_one = matches!(value, Expr::Const(ConstVal::Int(0 | 1)));
8401 d_bool != v_bool && !zero_one
8402 }
8403}
8404
8405fn rewrite_gen_reads(e: &mut Expr, gen: &std::collections::HashMap<u32, u32>, vt: &VarTable) {
8406 deep_rewrite(e, &mut |x| {
8407 if let Expr::Local { var, ty } = x {
8408 if let Some(&to) = gen.get(var) {
8409 if to != *var {
8410 *var = to;
8411 *ty = vt.var(to).ty.clone();
8412 }
8413 }
8414 }
8415 });
8416}
8417
8418fn do_split(
8419 vt: &mut VarTable,
8420 target: &mut Expr,
8421 old: u32,
8422 value_ty: &JavaType,
8423 gen: &mut std::collections::HashMap<u32, u32>,
8424 counter: &mut u32,
8425) {
8426 *counter += 1;
8427 let info = &vt.vars[old as usize];
8428 let id = vt.vars.len() as u32;
8429 let name = format!("{}_g{}", info.name, counter);
8430 let slot = info.slot;
8431 let is_param = false;
8439 vt.vars.push(jdc_core::var::VarInfo {
8440 id,
8441 slot,
8442 name,
8443 ty: TypeRef::J(value_ty.clone()),
8444 is_param,
8445 range_start: 0,
8446 range_end: u16::MAX,
8447 synthetic_name: true,
8448 });
8449 while vt.by_slot.len() <= slot as usize {
8450 vt.by_slot.push(Vec::new());
8451 }
8452 vt.by_slot[slot as usize].push((0, u16::MAX, id));
8453 gen.insert(old, id);
8454 if let Expr::Local { var, ty, .. } = target {
8455 *var = id;
8456 *ty = TypeRef::J(value_ty.clone());
8457 }
8458}
8459
8460fn rename_gen_back(s: &mut Stmt, from: u32, to: u32, vt: &VarTable) {
8472 if from == to || (to as usize) >= vt.vars.len() {
8473 return;
8474 }
8475 let to_ty = vt.vars[to as usize].ty.clone();
8476 walk_mut_deep(s, &mut |st| {
8477 if let Stmt::LocalDef { var, .. } = st {
8478 if *var == from {
8479 *var = to;
8480 }
8481 }
8482 });
8483 rewrite_exprs(s, &mut |e| {
8484 deep_rewrite(e, &mut |x| {
8485 if let Expr::Local { var, ty } = x {
8486 if *var == from {
8487 *var = to;
8488 *ty = to_ty.clone();
8489 }
8490 }
8491 });
8492 });
8493}
8494
8495fn drop_gen_slot(vt: &mut VarTable, dead: u32) {
8499 if (dead as usize) >= vt.vars.len() {
8500 return;
8501 }
8502 let slot = vt.vars[dead as usize].slot as usize;
8503 if let Some(seg) = vt.by_slot.get_mut(slot) {
8504 seg.retain(|&(_, _, id)| id != dead);
8505 }
8506}
8507
8508fn undo_type_safe(vt: &VarTable, k: u32, v: u32) -> bool {
8517 if (k as usize) >= vt.vars.len() || (v as usize) >= vt.vars.len() {
8518 return false;
8519 }
8520 let kt = vt.vars[k as usize].ty.erased();
8521 let vt_ty = vt.vars[v as usize].ty.erased();
8522 if kt == vt_ty {
8523 return true;
8524 }
8525 let is_prim = |t: &JavaType| {
8526 matches!(
8527 t,
8528 JavaType::Int
8529 | JavaType::Long
8530 | JavaType::Short
8531 | JavaType::Byte
8532 | JavaType::Char
8533 | JavaType::Float
8534 | JavaType::Double
8535 | JavaType::Boolean
8536 )
8537 };
8538 is_prim(&kt) && is_prim(&vt_ty)
8539}
8540
8541fn split_walk_stmt(
8542 st: &mut Stmt,
8543 vt: &mut VarTable,
8544 gen: &mut std::collections::HashMap<u32, u32>,
8545 counter: &mut u32,
8546 pool: &DexPool,
8547) {
8548 match st {
8549 Stmt::Block(v) => {
8550 for x in v.iter_mut() {
8551 split_walk_stmt(x, vt, gen, counter, pool);
8552 }
8553 }
8554 Stmt::ExprStmt(e) => {
8555 rewrite_gen_reads(e, gen, vt);
8556 if let Expr::Assign {
8557 target,
8558 value,
8559 op: AssignOp::Plain,
8560 ..
8561 } = e
8562 {
8563 if let Expr::Local { var, .. } = &**target {
8564 let old = *var;
8565 if split_needed(vt, old, value, pool) {
8566 let value_ty = value.type_ref().erased();
8567 do_split(vt, target, old, &value_ty, gen, counter);
8568 }
8569 }
8570 }
8571 }
8572 Stmt::LocalDef { var, init, .. } => {
8573 if let Some(e) = init {
8574 rewrite_gen_reads(e, gen, vt);
8575 if split_needed(vt, *var, e, pool) {
8576 let value_ty = e.type_ref().erased();
8580 let old = *var;
8581 let mut fake = Expr::Local {
8582 var: old,
8583 ty: vt.vars[old as usize].ty.clone(),
8584 };
8585 do_split(vt, &mut fake, old, &value_ty, gen, counter);
8586 if let Expr::Local { var: nv, .. } = fake {
8587 *var = nv;
8588 }
8589 }
8590 }
8591 }
8592 Stmt::If {
8593 cond,
8594 then_stmt,
8595 else_stmt,
8596 ..
8597 } => {
8598 rewrite_gen_reads(cond, gen, vt);
8599 let then_floor = vt.vars.len() as u32;
8600 let mut g_then = gen.clone();
8601 split_walk_stmt(then_stmt, vt, &mut g_then, counter, pool);
8602 let else_floor = vt.vars.len() as u32;
8603 let mut g_else = gen.clone();
8604 if let Some(e) = else_stmt {
8605 split_walk_stmt(e, vt, &mut g_else, counter, pool);
8606 }
8607 let mut undo_then: Vec<(u32, u32)> = g_then
8624 .iter()
8625 .filter(|(&k, &v)| {
8626 v >= then_floor
8627 && v < else_floor
8628 && g_else.get(&k).copied() == gen.get(&k).copied()
8629 && undo_type_safe(vt, k, v)
8630 && stmts_read_var(std::slice::from_ref(&**then_stmt), v) == 0
8631 })
8632 .map(|(&k, &v)| (v, k))
8633 .collect();
8634 undo_then.sort_unstable_by_key(|&(v, _)| std::cmp::Reverse(v));
8635 for &(v, k) in &undo_then {
8636 rename_gen_back(then_stmt, v, k, vt);
8637 drop_gen_slot(vt, v);
8638 }
8639 if let Some(e) = else_stmt.as_deref_mut() {
8640 let mut undo_else: Vec<(u32, u32)> = g_else
8641 .iter()
8642 .filter(|(&k, &v)| {
8643 v >= else_floor
8644 && g_then.get(&k).copied() == gen.get(&k).copied()
8645 && undo_type_safe(vt, k, v)
8646 && stmts_read_var(std::slice::from_ref(&*e), v) == 0
8647 })
8648 .map(|(&k, &v)| (v, k))
8649 .collect();
8650 undo_else.sort_unstable_by_key(|&(v, _)| std::cmp::Reverse(v));
8651 for &(v, k) in &undo_else {
8652 rename_gen_back(e, v, k, vt);
8653 drop_gen_slot(vt, v);
8654 }
8655 }
8656 gen.retain(|k, v| g_then.get(k) == Some(v) && g_else.get(k) == Some(v));
8659 }
8660 Stmt::While { cond, body, .. } | Stmt::DoWhile { cond, body, .. } => {
8661 let entry = gen.clone();
8665 for _ in 0..3 {
8666 rewrite_gen_reads(cond, gen, vt);
8667 split_walk_stmt(body, vt, gen, counter, pool);
8668 }
8669 *gen = entry;
8672 }
8673 Stmt::For { init, body, .. } => {
8674 for x in init.iter_mut() {
8675 split_walk_stmt(x, vt, gen, counter, pool);
8676 }
8677 let entry = gen.clone();
8678 for _ in 0..3 {
8679 split_walk_stmt(body, vt, gen, counter, pool);
8680 }
8681 *gen = entry;
8682 }
8683 Stmt::ForEach { body, .. } => {
8684 let entry = gen.clone();
8685 for _ in 0..3 {
8686 split_walk_stmt(body, vt, gen, counter, pool);
8687 }
8688 *gen = entry;
8689 }
8690 Stmt::Switch { cases, default, .. } => {
8691 let entry = gen.clone();
8692 for c in cases.iter_mut() {
8693 let mut g_case = entry.clone();
8694 for x in c.body.iter_mut() {
8695 split_walk_stmt(x, vt, &mut g_case, counter, pool);
8696 }
8697 gen.retain(|k, v| g_case.get(k) == Some(v));
8698 }
8699 if let Some(d) = default {
8700 let mut g_def = entry.clone();
8701 split_walk_stmt(d, vt, &mut g_def, counter, pool);
8702 gen.retain(|k, v| g_def.get(k) == Some(v));
8703 }
8704 }
8705 Stmt::Try {
8706 body,
8707 catches,
8708 finally,
8709 } => {
8710 let entry = gen.clone();
8711 let mut g_try = entry.clone();
8712 split_walk_stmt(body, vt, &mut g_try, counter, pool);
8713 let mut g_all = g_try.clone();
8714 for c in catches.iter_mut() {
8715 let mut g_c = entry.clone();
8718 split_walk_stmt(&mut c.body, vt, &mut g_c, counter, pool);
8719 g_all.retain(|k, v| g_c.get(k) == Some(v));
8720 }
8721 *gen = g_all;
8722 if let Some(f) = finally {
8723 split_walk_stmt(f, vt, gen, counter, pool);
8724 }
8725 }
8726 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => {
8727 split_walk_stmt(body, vt, gen, counter, pool);
8728 }
8729 Stmt::Return(Some(e)) | Stmt::Throw(e) => {
8730 rewrite_gen_reads(e, gen, vt);
8731 }
8732 _ => {}
8733 }
8734}
8735
8736static FW_EXC_SUPERS: &[(&str, &str)] = &[
8752 ("java/lang/Exception", "java/lang/Throwable"),
8754 ("java/lang/RuntimeException", "java/lang/Exception"),
8755 ("java/lang/IllegalArgumentException", "java/lang/RuntimeException"),
8756 ("java/lang/IllegalStateException", "java/lang/RuntimeException"),
8757 ("java/lang/NullPointerException", "java/lang/RuntimeException"),
8758 ("java/lang/ArithmeticException", "java/lang/RuntimeException"),
8759 ("java/lang/ClassCastException", "java/lang/RuntimeException"),
8760 ("java/lang/IndexOutOfBoundsException", "java/lang/RuntimeException"),
8761 ("java/lang/ArrayIndexOutOfBoundsException", "java/lang/IndexOutOfBoundsException"),
8762 ("java/lang/StringIndexOutOfBoundsException", "java/lang/IndexOutOfBoundsException"),
8763 ("java/lang/NumberFormatException", "java/lang/IllegalArgumentException"),
8764 ("java/lang/UnsupportedOperationException", "java/lang/RuntimeException"),
8765 ("java/lang/SecurityException", "java/lang/RuntimeException"),
8766 ("java/lang/NegativeArraySizeException", "java/lang/RuntimeException"),
8767 ("java/lang/EnumConstantNotPresentException", "java/lang/RuntimeException"),
8768 ("java/lang/TypeNotPresentException", "java/lang/RuntimeException"),
8769 ("java/lang/CloneNotSupportedException", "java/lang/Exception"),
8770 ("java/lang/InterruptedException", "java/lang/Exception"),
8771 ("java/lang/ReflectiveOperationException", "java/lang/Exception"),
8772 ("java/lang/ClassNotFoundException", "java/lang/ReflectiveOperationException"),
8773 ("java/lang/NoSuchFieldException", "java/lang/ReflectiveOperationException"),
8774 ("java/lang/NoSuchMethodException", "java/lang/ReflectiveOperationException"),
8775 ("java/lang/InstantiationException", "java/lang/ReflectiveOperationException"),
8776 ("java/lang/IllegalAccessException", "java/lang/ReflectiveOperationException"),
8777 ("java/lang/reflect/InvocationTargetException", "java/lang/ReflectiveOperationException"),
8778 ("java/lang/reflect/UndeclaredThrowableException", "java/lang/RuntimeException"),
8779 ("java/lang/invoke/WrongMethodTypeException", "java/lang/RuntimeException"),
8780 ("java/lang/annotation/IncompleteAnnotationException", "java/lang/RuntimeException"),
8781 ("java/lang/Error", "java/lang/Throwable"),
8783 ("java/lang/AssertionError", "java/lang/Error"),
8784 ("java/lang/VirtualMachineError", "java/lang/Error"),
8785 ("java/lang/OutOfMemoryError", "java/lang/VirtualMachineError"),
8786 ("java/lang/StackOverflowError", "java/lang/VirtualMachineError"),
8787 ("java/lang/LinkageError", "java/lang/Error"),
8788 ("java/lang/IncompatibleClassChangeError", "java/lang/LinkageError"),
8789 ("java/lang/NoSuchMethodError", "java/lang/IncompatibleClassChangeError"),
8790 ("java/lang/NoSuchFieldError", "java/lang/IncompatibleClassChangeError"),
8791 ("java/lang/NoClassDefFoundError", "java/lang/LinkageError"),
8792 ("java/lang/ExceptionInInitializerError", "java/lang/LinkageError"),
8793 ("java/lang/UnsatisfiedLinkError", "java/lang/LinkageError"),
8794 ("java/lang/VerifyError", "java/lang/LinkageError"),
8795 ("java/lang/ClassFormatError", "java/lang/LinkageError"),
8796 ("java/lang/BootstrapMethodError", "java/lang/LinkageError"),
8797 ("java/util/ConcurrentModificationException", "java/lang/RuntimeException"),
8799 ("java/util/NoSuchElementException", "java/lang/RuntimeException"),
8800 ("java/util/MissingResourceException", "java/lang/RuntimeException"),
8801 ("java/util/EmptyStackException", "java/lang/RuntimeException"),
8802 ("java/util/TooManyListenersException", "java/lang/Exception"),
8803 ("java/util/ServiceConfigurationError", "java/lang/Error"),
8804 ("java/util/concurrent/ExecutionException", "java/lang/Exception"),
8805 ("java/util/concurrent/TimeoutException", "java/lang/Exception"),
8806 ("java/util/concurrent/BrokenBarrierException", "java/lang/Exception"),
8807 ("java/util/concurrent/RejectedExecutionException", "java/lang/RuntimeException"),
8808 ("java/util/concurrent/CompletionException", "java/lang/RuntimeException"),
8809 ("java/util/concurrent/CancellationException", "java/lang/IllegalStateException"),
8810 ("java/io/IOException", "java/lang/Exception"),
8812 ("java/io/FileNotFoundException", "java/io/IOException"),
8813 ("java/io/InterruptedIOException", "java/io/IOException"),
8814 ("java/io/UnsupportedEncodingException", "java/io/IOException"),
8815 ("java/io/UTFDataFormatException", "java/io/IOException"),
8816 ("java/io/EOFException", "java/io/IOException"),
8817 ("java/io/CharConversionException", "java/io/IOException"),
8818 ("java/io/ObjectStreamException", "java/io/IOException"),
8819 ("java/io/InvalidObjectException", "java/io/ObjectStreamException"),
8820 ("java/io/NotSerializableException", "java/io/ObjectStreamException"),
8821 ("java/net/SocketException", "java/io/IOException"),
8822 ("java/net/ConnectException", "java/net/SocketException"),
8823 ("java/net/BindException", "java/net/SocketException"),
8824 ("java/net/NoRouteToHostException", "java/net/SocketException"),
8825 ("java/net/ProtocolException", "java/io/IOException"),
8826 ("java/net/UnknownHostException", "java/io/IOException"),
8827 ("java/net/MalformedURLException", "java/io/IOException"),
8828 ("java/net/HttpRetryException", "java/io/IOException"),
8829 ("java/net/SocketTimeoutException", "java/io/InterruptedIOException"),
8830 ("java/net/URISyntaxException", "java/lang/Exception"),
8831 ("java/text/ParseException", "java/lang/Exception"),
8833 ("java/security/GeneralSecurityException", "java/lang/Exception"),
8834 ("java/security/NoSuchAlgorithmException", "java/security/GeneralSecurityException"),
8835 ("java/security/NoSuchProviderException", "java/security/GeneralSecurityException"),
8836 ("java/security/InvalidKeyException", "java/security/GeneralSecurityException"),
8837 ("java/security/InvalidAlgorithmParameterException", "java/security/GeneralSecurityException"),
8838 ("java/security/SignatureException", "java/security/GeneralSecurityException"),
8839 ("java/security/UnrecoverableKeyException", "java/security/GeneralSecurityException"),
8840 ("java/security/KeyStoreException", "java/security/GeneralSecurityException"),
8841 ("java/security/cert/CertificateException", "java/security/GeneralSecurityException"),
8842 ("java/security/spec/InvalidKeySpecException", "java/security/GeneralSecurityException"),
8843 ("javax/crypto/BadPaddingException", "java/security/GeneralSecurityException"),
8844 ("javax/crypto/IllegalBlockSizeException", "java/security/GeneralSecurityException"),
8845 ("javax/crypto/NoSuchPaddingException", "java/security/GeneralSecurityException"),
8846 ("javax/crypto/ShortBufferException", "java/security/GeneralSecurityException"),
8847 ("javax/net/ssl/SSLException", "java/io/IOException"),
8848 ("org/xml/sax/SAXException", "java/lang/Exception"),
8849 ("org/xml/sax/SAXParseException", "org/xml/sax/SAXException"),
8850 ("org/json/JSONException", "java/lang/RuntimeException"),
8851 ("org/xmlpull/v1/XmlPullParserException", "java/lang/Exception"),
8852 ("java/util/IllegalFormatException", "java/lang/IllegalArgumentException"),
8853 ("java/util/MissingFormatArgumentException", "java/util/IllegalFormatException"),
8854 ("java/util/UnknownFormatConversionException", "java/util/IllegalFormatException"),
8855 ("android/util/AndroidRuntimeException", "java/lang/RuntimeException"),
8857 ("android/util/AndroidException", "java/lang/Exception"),
8858 ("android/content/ActivityNotFoundException", "java/lang/RuntimeException"),
8859 ("android/content/ComponentNotFoundException", "java/lang/RuntimeException"),
8860 ("android/content/pm/PackageManager$NameNotFoundException", "java/lang/Exception"),
8861 ("android/content/res/Resources$NotFoundException", "java/lang/RuntimeException"),
8862 ("android/database/SQLException", "java/lang/RuntimeException"),
8863 ("android/database/sqlite/SQLiteException", "android/database/SQLException"),
8864 ("android/database/CursorIndexOutOfBoundsException", "java/lang/IndexOutOfBoundsException"),
8865 ("android/os/NetworkOnMainThreadException", "java/lang/RuntimeException"),
8866 ("android/system/ErrnoException", "java/lang/Exception"),
8867 ("android/provider/Settings$SettingNotFoundException", "android/util/AndroidException"),
8868 ("android/net/UriParseException", "java/lang/RuntimeException"),
8869 ("android/view/InflateException", "java/lang/RuntimeException"),
8870 ("android/accounts/AuthenticatorException", "java/lang/Exception"),
8871 ("android/accounts/OperationCanceledException", "android/accounts/AuthenticatorException"),
8872 ("android/hardware/camera2/CameraAccessException", "java/lang/Exception"),
8873 ("android/opengl/GLException", "java/lang/RuntimeException"),
8874 ("android/renderscript/RSRuntimeException", "java/lang/RuntimeException"),
8875];
8876
8877fn fw_super(c: &str) -> Option<&'static str> {
8878 FW_EXC_SUPERS
8879 .iter()
8880 .find(|(k, _)| *k == c)
8881 .map(|(_, v)| *v)
8882}
8883
8884fn exc_subtype(sub: &str, sup: &str, pool: &DexPool) -> bool {
8890 if sub == sup {
8891 return true;
8892 }
8893 if pool.is_subtype(sub, sup) {
8894 return true;
8895 }
8896 let mut cur = sub;
8897 for _ in 0..24 {
8898 let Some(next) = fw_super(cur) else { break };
8899 if next == sup {
8900 return true;
8901 }
8902 cur = next;
8903 }
8904 false
8905}
8906
8907pub fn dedupe_multicatch(s: &mut Stmt, pool: &DexPool) {
8913 dedupe_multicatch_walk(s, pool);
8914}
8915
8916fn dedupe_multicatch_walk(s: &mut Stmt, pool: &DexPool) {
8917 match s {
8918 Stmt::Try {
8919 body,
8920 catches,
8921 finally,
8922 }
8923 | Stmt::TryWithResources {
8924 body,
8925 catches,
8926 finally,
8927 ..
8928 } => {
8929 dedupe_multicatch_walk(body, pool);
8930 for c in catches.iter_mut() {
8931 if c.exc.len() > 1 {
8932 let mut keep: Vec<std::sync::Arc<str>> = Vec::with_capacity(c.exc.len());
8933 for e in c.exc.iter() {
8934 let shadowed = c.exc.iter().any(|o| {
8935 o.as_ref() != e.as_ref() && exc_subtype(e, o, pool)
8936 });
8937 if !shadowed && !keep.iter().any(|k| k.as_ref() == e.as_ref()) {
8938 keep.push(e.clone());
8939 }
8940 }
8941 c.exc = keep;
8942 }
8943 dedupe_multicatch_walk(&mut c.body, pool);
8944 }
8945 if let Some(f) = finally {
8946 dedupe_multicatch_walk(f, pool);
8947 }
8948 }
8949 Stmt::Block(v) => {
8950 for x in v.iter_mut() {
8951 dedupe_multicatch_walk(x, pool);
8952 }
8953 }
8954 Stmt::If {
8955 then_stmt,
8956 else_stmt,
8957 ..
8958 } => {
8959 dedupe_multicatch_walk(then_stmt, pool);
8960 if let Some(e) = else_stmt {
8961 dedupe_multicatch_walk(e, pool);
8962 }
8963 }
8964 Stmt::While { body, .. }
8965 | Stmt::DoWhile { body, .. }
8966 | Stmt::For { body, .. }
8967 | Stmt::ForEach { body, .. }
8968 | Stmt::Labeled { body, .. }
8969 | Stmt::Synchronized { body, .. } => dedupe_multicatch_walk(body, pool),
8970 Stmt::Switch { cases, default, .. } => {
8971 for c in cases.iter_mut() {
8972 for x in c.body.iter_mut() {
8973 dedupe_multicatch_walk(x, pool);
8974 }
8975 }
8976 if let Some(d) = default {
8977 dedupe_multicatch_walk(d, pool);
8978 }
8979 }
8980 _ => {}
8981 }
8982}
8983
8984pub fn resolve_dangling_gotos(s: &mut Stmt) {
8996 let mut labels: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
8997 let probe = s.clone();
8998 walk_all(&probe, &mut |st| {
8999 if let Stmt::Label(id) = st {
9000 labels.insert(*id);
9001 }
9002 });
9003 resolve_gotos_walk(s, &labels, 0);
9004}
9005
9006fn resolve_gotos_walk(s: &mut Stmt, labels: &jdc_core::FxHashSet<u32>, loop_depth: u32) {
9007 let depth = match s {
9008 Stmt::While { .. } | Stmt::DoWhile { .. } | Stmt::For { .. } | Stmt::ForEach { .. } => {
9009 loop_depth + 1
9010 }
9011 _ => loop_depth,
9012 };
9013 match s {
9014 Stmt::Goto(id) => {
9015 if labels.contains(id) {
9016 return; }
9018 if depth > 0 {
9019 *s = Stmt::Break(None);
9020 } else {
9021 *s = Stmt::Comment(format!(
9022 "$DDC: unresolved jump to block {} (irreducible control flow)",
9023 id
9024 ));
9025 }
9026 }
9027 Stmt::Block(v) => {
9028 let mut i = 0usize;
9029 while i < v.len() {
9030 let dangling_here =
9035 depth == 0 && matches!(&v[i], Stmt::Goto(id) if !labels.contains(id));
9036 resolve_gotos_walk(&mut v[i], labels, depth);
9037 if dangling_here {
9038 v.truncate(i + 1);
9039 break;
9040 }
9041 i += 1;
9042 }
9043 }
9044 Stmt::If { then_stmt, else_stmt, .. } => {
9045 resolve_gotos_walk(then_stmt, labels, depth);
9046 if let Some(e) = else_stmt {
9047 resolve_gotos_walk(e, labels, depth);
9048 }
9049 }
9050 Stmt::While { body, .. }
9051 | Stmt::DoWhile { body, .. }
9052 | Stmt::For { body, .. }
9053 | Stmt::ForEach { body, .. }
9054 | Stmt::Labeled { body, .. }
9055 | Stmt::Synchronized { body, .. } => resolve_gotos_walk(body, labels, depth),
9056 Stmt::Try {
9057 body,
9058 catches,
9059 finally,
9060 } => {
9061 resolve_gotos_walk(body, labels, depth);
9062 for c in catches.iter_mut() {
9063 resolve_gotos_walk(&mut c.body, labels, depth);
9064 }
9065 if let Some(f) = finally {
9066 resolve_gotos_walk(f, labels, depth);
9067 }
9068 }
9069 Stmt::Switch { cases, default, .. } => {
9070 for c in cases.iter_mut() {
9071 for x in c.body.iter_mut() {
9072 resolve_gotos_walk(x, labels, depth);
9073 }
9074 }
9075 if let Some(d) = default {
9076 resolve_gotos_walk(d, labels, depth);
9077 }
9078 }
9079 _ => {}
9080 }
9081}
9082
9083fn stmts_read_var(stmts: &[Stmt], v: u32) -> usize {
9085 let mut n = 0usize;
9086 for s in stmts {
9087 let mut c = s.clone();
9088 walk_stmt_exprs(&mut c, &mut |e| {
9089 deep_rewrite_reads(e, &mut |x| {
9090 if let Expr::Local { var: vv, .. } = x {
9091 if *vv == v {
9092 n += 1;
9093 }
9094 }
9095 });
9096 });
9097 }
9098 n
9099}
9100
9101pub fn fix_ctor_conditional_super(body: &mut Stmt) {
9102 if !matches!(body, Stmt::Block(_)) {
9105 let inner = std::mem::replace(body, Stmt::Block(Vec::new()));
9106 *body = Stmt::Block(vec![inner]);
9107 }
9108 let Stmt::Block(stmts) = body else { return };
9109 while stmts.len() == 1 {
9113 let Stmt::Block(inner) = &stmts[0] else { break };
9114 *stmts = inner.clone();
9115 }
9116 if stmts.is_empty() {
9117 return;
9118 }
9119 if is_bare_ctor_call(stmts.first().unwrap()) {
9120 dedupe_ctor_delegations(body);
9121 return;
9122 }
9123 if try_linear_super_inline(stmts) {
9124 return;
9125 }
9126 try_merge_branched_super(stmts);
9127}
9128
9129pub fn strip_secondary_delegations(body: &mut Stmt) {
9153 let Stmt::Block(stmts) = body else { return };
9154 if !stmts.first().is_some_and(is_bare_ctor_call) {
9155 return;
9156 }
9157 fn strip_in(s: &mut Stmt) {
9158 if is_bare_ctor_call(s) {
9159 *s = Stmt::Block(Vec::new());
9160 return;
9161 }
9162 match s {
9163 Stmt::Block(v) => v.iter_mut().for_each(strip_in),
9164 Stmt::If {
9165 then_stmt,
9166 else_stmt,
9167 ..
9168 } => {
9169 strip_in(then_stmt);
9170 if let Some(e) = else_stmt {
9171 strip_in(e);
9172 }
9173 }
9174 Stmt::While { body, .. }
9175 | Stmt::DoWhile { body, .. }
9176 | Stmt::ForEach { body, .. }
9177 | Stmt::Synchronized { body, .. }
9178 | Stmt::Labeled { body, .. } => strip_in(body),
9179 Stmt::For { init, body, .. } => {
9180 init.iter_mut().for_each(strip_in);
9181 strip_in(body);
9182 }
9183 Stmt::Switch { cases, default, .. } => {
9184 for c in cases.iter_mut() {
9185 c.body.iter_mut().for_each(strip_in);
9186 }
9187 if let Some(d) = default {
9188 strip_in(d);
9189 }
9190 }
9191 Stmt::Try {
9192 body,
9193 catches,
9194 finally,
9195 }
9196 | Stmt::TryWithResources {
9197 body,
9198 catches,
9199 finally,
9200 ..
9201 } => {
9202 strip_in(body);
9203 for c in catches.iter_mut() {
9204 strip_in(&mut c.body);
9205 }
9206 if let Some(f) = finally {
9207 strip_in(f);
9208 }
9209 }
9210 _ => {}
9211 }
9212 }
9213 for st in stmts.iter_mut().skip(1) {
9214 strip_in(st);
9215 }
9216}
9217
9218pub fn dedupe_ctor_delegations(body: &mut Stmt) {
9219 let Stmt::Block(stmts) = body else { return };
9220 let Some(d0) = stmts.first().and_then(|s| match s {
9221 Stmt::ExprStmt(e) if is_delegation_expr(e) => Some(e.clone()),
9222 _ => None,
9223 }) else {
9224 return;
9225 };
9226 fn rec(s: &mut Stmt, d0: &Expr) {
9227 match s {
9228 Stmt::Block(v) => {
9229 v.retain(|x| {
9230 !matches!(x, Stmt::ExprStmt(e) if is_delegation_expr(e) && same_delegation(e, d0))
9231 });
9232 for x in v.iter_mut() {
9233 rec(x, d0);
9234 }
9235 }
9236 Stmt::If { then_stmt, else_stmt, .. } => {
9237 rec(then_stmt, d0);
9238 if let Some(e) = else_stmt {
9239 rec(e, d0);
9240 }
9241 }
9242 _ => {}
9243 }
9244 }
9245 {
9249 let d0r = &d0;
9250 let mut idx = 0usize;
9251 stmts.retain(|s| {
9252 idx += 1;
9253 idx == 1
9254 || !(matches!(s, Stmt::ExprStmt(e)
9255 if is_delegation_expr(e) && same_delegation(e, d0r)))
9256 });
9257 }
9258 for s in stmts.iter_mut().skip(1) {
9259 rec(s, &d0);
9260 }
9261}
9262
9263fn same_delegation(a: &Expr, b: &Expr) -> bool {
9267 match (a, b) {
9268 (
9269 Expr::Method { name: n1, cls: c1, is_super: s1, args: a1, .. },
9270 Expr::Method { name: n2, cls: c2, is_super: s2, args: a2, .. },
9271 ) => {
9272 &**n1 == "<init>"
9273 && &**n2 == "<init>"
9274 && c1 == c2
9275 && s1 == s2
9276 && (a1 == a2 || (a1.is_empty() && a2.is_empty()))
9277 }
9278 _ => false,
9279 }
9280}
9281
9282fn ctor_call_expr(s: &Stmt) -> Option<&Expr> {
9284 match s {
9285 Stmt::ExprStmt(e) if is_delegation_expr(e) => Some(e),
9286 _ => None,
9287 }
9288}
9289
9290fn same_ctor_kind(a: &Expr, b: &Expr) -> bool {
9292 match (a, b) {
9293 (
9294 Expr::Method { cls: c1, is_super: s1, owner: o1, .. },
9295 Expr::Method { cls: c2, is_super: s2, owner: o2, .. },
9296 ) => c1 == c2 && s1 == s2 && o1 == o2,
9297 _ => false,
9298 }
9299}
9300
9301fn flat_list(s: &Stmt) -> Vec<&Stmt> {
9306 fn rec<'a>(s: &'a Stmt, out: &mut Vec<&'a Stmt>) {
9307 match s {
9308 Stmt::Block(v) => {
9309 for x in v {
9310 rec(x, out);
9311 }
9312 }
9313 other => out.push(other),
9314 }
9315 }
9316 let mut out = Vec::new();
9317 rec(s, &mut out);
9318 out
9319}
9320
9321fn strip_lost_alloc_owners(e: &mut Expr) {
9327 deep_rewrite(e, &mut |x| {
9328 if let Expr::Method { name, is_special: true, owner: Some(o), .. } = x {
9329 if &**name == "<init>" && !matches!(**o, Expr::This) {
9330 **o = Expr::Const(jdc_core::ir::expr::ConstVal::Int(0));
9331 }
9332 }
9333 });
9334}
9335
9336fn count_locals_expr(e: &Expr) -> std::collections::HashMap<u32, usize> {
9339 let mut m = std::collections::HashMap::new();
9340 let mut probe = e.clone();
9341 strip_lost_alloc_owners(&mut probe);
9342 deep_rewrite(&mut probe, &mut |x| {
9343 if let Expr::Local { var: v, .. } = x {
9344 *m.entry(*v).or_insert(0) += 1;
9345 }
9346 });
9347 m
9348}
9349
9350pub(crate) fn count_locals_stmts(ss: &[Stmt]) -> std::collections::HashMap<u32, usize> {
9351 let mut m = std::collections::HashMap::new();
9352 for s in ss {
9353 let mut c = s.clone();
9354 walk_stmt_exprs(&mut c, &mut |e| {
9355 strip_lost_alloc_owners(e);
9356 deep_rewrite(e, &mut |x| {
9357 if let Expr::Local { var: v, .. } = x {
9358 *m.entry(*v).or_insert(0) += 1;
9359 }
9360 });
9361 });
9362 }
9363 m
9364}
9365
9366fn cnt(m: &std::collections::HashMap<u32, usize>, v: u32) -> usize {
9367 m.get(&v).copied().unwrap_or(0)
9368}
9369
9370fn inline_locals(
9374 e: &Expr,
9375 map: &std::collections::HashMap<u32, Expr>,
9376 depth: u32,
9377) -> Option<Expr> {
9378 if depth > 4 {
9379 return None;
9380 }
9381 let mut out = e.clone();
9382 let mut fail = false;
9383 deep_rewrite(&mut out, &mut |x| {
9384 if let Expr::Local { var, .. } = x {
9385 if let Some(def) = map.get(var) {
9386 match inline_locals(def, map, depth + 1) {
9387 Some(r) => *x = r,
9388 None => fail = true,
9389 }
9390 }
9391 }
9392 });
9393 if fail {
9394 None
9395 } else {
9396 Some(out)
9397 }
9398}
9399
9400fn def_of(s: &Stmt) -> Option<(u32, &Expr)> {
9402 match s {
9403 Stmt::LocalDef { var, init: Some(e), .. } => Some((*var, e)),
9404 Stmt::ExprStmt(Expr::Assign {
9405 target,
9406 value,
9407 op: jdc_core::ir::expr::AssignOp::Plain,
9408 ..
9409 }) => match &**target {
9410 Expr::Local { var, .. } => Some((*var, value)),
9411 _ => None,
9412 },
9413 _ => None,
9414 }
9415}
9416
9417fn try_linear_super_inline(stmts: &mut Vec<Stmt>) -> bool {
9419 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
9420 return false;
9421 };
9422 if pos == 0 {
9423 return false; }
9425 if !stmts[..pos]
9427 .iter()
9428 .all(|s| matches!(s, Stmt::LocalDef { .. } | Stmt::ExprStmt(_)))
9429 {
9430 return false;
9431 }
9432 let Some(call) = ctor_call_expr(&stmts[pos]) else {
9433 return false;
9434 };
9435 let Expr::Method { args, .. } = call else {
9436 return false;
9437 };
9438 let mut map: std::collections::HashMap<u32, Expr> =
9440 std::collections::HashMap::new();
9441 let mut def_idx: std::collections::HashMap<u32, usize> =
9442 std::collections::HashMap::new();
9443 for (i, s) in stmts[..pos].iter().enumerate() {
9444 if let Some((v, e)) = def_of(s) {
9445 map.insert(v, e.clone());
9446 def_idx.insert(v, i);
9447 }
9448 }
9449 let mut needed: Vec<u32> = Vec::new();
9451 for a in args {
9452 let mut probe = a.clone();
9453 strip_lost_alloc_owners(&mut probe);
9454 deep_rewrite(&mut probe, &mut |x| {
9455 if let Expr::Local { var, .. } = x {
9456 if map.contains_key(var) && !needed.contains(var) {
9457 needed.push(*var);
9458 }
9459 }
9460 });
9461 }
9462 if needed.is_empty() {
9463 return false; }
9465 let mut consumed: Vec<usize> = Vec::new();
9468 let use_counts = count_locals_stmts(stmts);
9469 for &v in &needed {
9470 let def = &map[&v];
9471 let uses = cnt(&use_counts, v);
9472 if jdc_core::ir::build::has_side_effects(def) && uses != 1 {
9473 return false;
9474 }
9475 if uses == 1 {
9476 consumed.push(def_idx[&v]);
9477 }
9478 }
9479 let mut new_args: Vec<Expr> = Vec::with_capacity(args.len());
9481 for a in args {
9482 match inline_locals(a, &map, 0) {
9483 Some(r) => new_args.push(r),
9484 None => return false,
9485 }
9486 }
9487 let mut call_stmt = stmts.remove(pos);
9490 if let Stmt::ExprStmt(Expr::Method { args: slot, .. }) = &mut call_stmt {
9491 *slot = new_args;
9492 }
9493 let mut rest: Vec<Stmt> = Vec::with_capacity(stmts.len() + 1);
9494 rest.push(call_stmt);
9495 for (i, s) in stmts.drain(..).enumerate() {
9496 let orig = if i >= pos { i + 1 } else { i };
9499 if !consumed.contains(&orig) {
9500 rest.push(s);
9501 }
9502 }
9503 *stmts = rest;
9504 true
9505}
9506
9507fn try_merge_branched_super(stmts: &mut Vec<Stmt>) -> bool {
9509 let if_positions: Vec<usize> = stmts
9513 .iter()
9514 .enumerate()
9515 .filter(|(_, s)| matches!(s, Stmt::If { .. }))
9516 .map(|(i, _)| i)
9517 .collect();
9518 for if_pos in if_positions {
9519 let gate = match &stmts[if_pos] {
9525 Stmt::If { then_stmt, else_stmt, .. } => {
9526 contains_delegation(then_stmt)
9527 || else_stmt.as_ref().is_some_and(|e| contains_delegation(e))
9528 }
9529 _ => false,
9530 };
9531 if gate && merge_at(stmts, if_pos) {
9532 return true;
9533 }
9534 }
9535 false
9536}
9537
9538fn contains_delegation(s: &Stmt) -> bool {
9541 match s {
9542 Stmt::ExprStmt(e) => is_delegation_expr(e),
9543 Stmt::Block(v) => v.iter().any(contains_delegation),
9544 Stmt::If { then_stmt, else_stmt, .. } => {
9545 contains_delegation(then_stmt)
9546 || else_stmt.as_ref().is_some_and(|e| contains_delegation(e))
9547 }
9548 _ => false,
9549 }
9550}
9551
9552
9553
9554fn merge_at(stmts: &mut Vec<Stmt>, if_pos: usize) -> bool {
9567 enum Br {
9572 Call(usize, Vec<(u32, Expr)>, Vec<usize>),
9574 Clean,
9575 Dirty,
9576 }
9577 fn split_branch(list: &[&Stmt]) -> Br {
9578 let call_pos = list.iter().position(|s| ctor_call_expr(s).is_some());
9579 let Some(c) = call_pos else {
9580 return if list.iter().any(|s| contains_delegation(s)) {
9583 Br::Dirty
9584 } else {
9585 Br::Clean
9586 };
9587 };
9588 if list.iter().skip(c + 1).any(|s| ctor_call_expr(s).is_some()) {
9589 return Br::Dirty; }
9591 let mut defs: Vec<(u32, Expr)> = Vec::new();
9592 let mut def_idxs: Vec<usize> = Vec::new();
9593 for (i, s) in list.iter().enumerate().take(c) {
9594 match def_of(s) {
9595 Some((v, e)) => {
9596 defs.push((v, e.clone()));
9597 def_idxs.push(i);
9598 }
9599 None => match s {
9600 Stmt::ExprStmt(_) | Stmt::LocalDef { .. } => {}
9603 _ => return Br::Dirty, },
9605 }
9606 }
9607 Br::Call(c, defs, def_idxs)
9608 }
9609 fn cond_def_of(s: &Stmt, scope: &[Stmt]) -> Option<(u32, Expr)> {
9611 let Stmt::If { cond, then_stmt, else_stmt: Some(e), .. } = s else {
9612 return None;
9613 };
9614 fn one<'x>(b: &'x Stmt, scope: &[Stmt]) -> Option<(u32, &'x Expr)> {
9615 let l = flat_list(b);
9616 if l.len() == 1 {
9617 return def_of(l[0]);
9618 }
9619 if l.len() == 2 {
9628 let (w, ex) = def_of(l[0])?;
9629 let Stmt::ExprStmt(Expr::Assign {
9630 target,
9631 op: AssignOp::Plain,
9632 value,
9633 }) = l[1]
9634 else {
9635 return None;
9636 };
9637 let Expr::Local { var: tv, .. } = &**target else {
9638 return None;
9639 };
9640 let Expr::Local { var: rv, .. } = &**value else {
9641 return None;
9642 };
9643 if *rv != w || *tv == w {
9644 return None;
9645 }
9646 let mut ldef_writes = 0usize;
9650 for st in scope {
9651 walk_all(st, &mut |x| {
9652 if let Stmt::LocalDef {
9653 var,
9654 init: Some(_),
9655 ..
9656 } = x
9657 {
9658 if *var == w {
9659 ldef_writes += 1;
9660 }
9661 }
9662 });
9663 }
9664 if stmts_read_var(scope, w) != 1
9665 || stmts_count_writes(scope, w) + ldef_writes != 1
9666 {
9667 return None;
9668 }
9669 return Some((*tv, ex));
9670 }
9671 None
9672 }
9673 let (v1, e1) = one(then_stmt, scope)?;
9674 let (v2, e2) = one(e, scope)?;
9675 if v1 != v2 {
9676 return None;
9677 }
9678 Some((
9679 v1,
9680 Expr::Cond {
9681 c: Box::new(cond.clone()),
9682 t: Box::new(e1.clone()),
9683 f: Box::new(e2.clone()),
9684 },
9685 ))
9686 }
9687
9688 let mut pmap: std::collections::HashMap<u32, Expr> =
9690 std::collections::HashMap::new();
9691 let mut pidx: std::collections::HashMap<u32, usize> =
9692 std::collections::HashMap::new();
9693 for (i, s) in stmts[..if_pos].iter().enumerate() {
9694 match s {
9695 Stmt::LocalDef { .. } | Stmt::ExprStmt(_) => {
9696 if let Some((v, e)) = def_of(s) {
9697 pmap.insert(v, e.clone());
9698 pidx.insert(v, i);
9699 }
9700 }
9701 Stmt::If { .. } => match cond_def_of(s, stmts) {
9702 Some((v, e)) => {
9703 pmap.insert(v, e);
9704 pidx.insert(v, i);
9705 }
9706 None => return false,
9707 },
9708 _ => return false,
9709 }
9710 }
9711 let (cond, then_v, else_v, had_else) = match &stmts[if_pos] {
9712 Stmt::If { cond, then_stmt, else_stmt, .. } => (
9713 cond.clone(),
9714 then_stmt.as_ref().clone(),
9715 else_stmt.as_ref().map_or_else(|| Stmt::Block(Vec::new()), |e| e.as_ref().clone()),
9716 else_stmt.is_some(),
9717 ),
9718 _ => return false,
9719 };
9720 fn normalize_branch(b: &mut Stmt) {
9726 let slot: &mut Stmt = match b {
9731 Stmt::Block(v) if v.len() == 1 => &mut v[0],
9732 other => other,
9733 };
9734 if !matches!(slot, Stmt::If { .. }) || !contains_delegation(slot) {
9735 return;
9736 }
9737 let taken = std::mem::replace(slot, Stmt::Block(Vec::new()));
9738 let mut tmp = vec![taken];
9739 if merge_at(&mut tmp, 0) {
9740 *slot = Stmt::Block(tmp);
9741 } else {
9742 *slot = tmp.pop().unwrap();
9743 }
9744 }
9745 let mut then_v = then_v;
9746 let mut else_v = else_v;
9747 normalize_branch(&mut then_v);
9748 normalize_branch(&mut else_v);
9749 let then_list = flat_list(&then_v);
9750 let else_list = flat_list(&else_v);
9751
9752 enum Side {
9755 Call {
9756 at: usize,
9757 defs: Vec<(u32, Expr)>,
9758 def_idxs: Vec<usize>,
9759 },
9760 Clean,
9761 }
9762 let (t_side, e_side, two_sided) = match (split_branch(&then_list), split_branch(&else_list)) {
9763 (Br::Call(tc, td, ti), Br::Call(ec, ed, ei)) => (
9764 Side::Call { at: tc, defs: td, def_idxs: ti },
9765 Side::Call { at: ec, defs: ed, def_idxs: ei },
9766 true,
9767 ),
9768 (Br::Call(tc, td, ti), Br::Clean) => (
9769 Side::Call { at: tc, defs: td, def_idxs: ti },
9770 Side::Clean,
9771 false,
9772 ),
9773 (Br::Clean, Br::Call(ec, ed, ei)) => (
9774 Side::Clean,
9775 Side::Call { at: ec, defs: ed, def_idxs: ei },
9776 false,
9777 ),
9778 _ => return false,
9779 };
9780 let (t_call, e_call) = match (&t_side, &e_side) {
9782 (Side::Call { at: ta, .. }, Side::Call { at: ea, .. }) => (
9783 ctor_call_expr(then_list[*ta]),
9784 ctor_call_expr(else_list[*ea]),
9785 ),
9786 (Side::Call { at: ta, .. }, Side::Clean) => {
9787 (ctor_call_expr(then_list[*ta]), None)
9788 }
9789 (Side::Clean, Side::Call { at: ea, .. }) => {
9790 (None, ctor_call_expr(else_list[*ea]))
9791 }
9792 _ => return false,
9793 };
9794 let (call_list, call_at) = match &t_side {
9796 Side::Call { at, .. } => (&then_list, *at),
9797 Side::Clean => match &e_side {
9798 Side::Call { at, .. } => (&else_list, *at),
9799 Side::Clean => return false,
9800 },
9801 };
9802 let (base_args, other_args) = if two_sided {
9803 let (Some(tc), Some(ec)) = (t_call, e_call) else {
9804 return false;
9805 };
9806 if !same_ctor_kind(tc, ec) {
9807 return false;
9808 }
9809 let (Expr::Method { args: a1, .. }, Expr::Method { args: a2, .. }) = (tc, ec) else {
9810 return false;
9811 };
9812 if a1.len() != a2.len() {
9813 return false;
9814 }
9815 (a1, Some(a2))
9816 } else {
9817 let c = t_call.or(e_call);
9818 let Some(c) = c else { return false };
9819 let Expr::Method { args, .. } = c else {
9820 return false;
9821 };
9822 (args, None)
9823 };
9824
9825 let mut tmap: std::collections::HashMap<u32, Expr> = pmap.clone();
9827 let mut emap: std::collections::HashMap<u32, Expr> = pmap.clone();
9828 if let Side::Call { defs, .. } = &t_side {
9829 for (v, e) in defs {
9830 tmap.insert(*v, e.clone());
9831 }
9832 }
9833 if let Side::Call { defs, .. } = &e_side {
9834 for (v, e) in defs {
9835 emap.insert(*v, e.clone());
9836 }
9837 }
9838 let Some(cond_i) = inline_locals(&cond, &pmap, 0) else {
9839 return false;
9840 };
9841 let mut then_args: Vec<Expr> = Vec::with_capacity(base_args.len());
9843 let mut else_args: Vec<Expr> = Vec::new();
9844 if two_sided {
9845 for a in base_args {
9846 match inline_locals(a, &tmap, 0) {
9847 Some(r) => then_args.push(r),
9848 None => return false,
9849 }
9850 }
9851 for a in other_args.unwrap() {
9852 match inline_locals(a, &emap, 0) {
9853 Some(r) => else_args.push(r),
9854 None => return false,
9855 }
9856 }
9857 } else {
9858 let from_then = matches!(&t_side, Side::Call { .. });
9861 for a in base_args {
9862 let r = if from_then {
9863 inline_locals(a, &tmap, 0)
9864 } else {
9865 inline_locals(a, &emap, 0)
9866 };
9867 match r {
9868 Some(r) => then_args.push(r),
9869 None => return false,
9870 }
9871 }
9872 }
9873 let primary_args = base_args;
9874 let mut merged: Vec<Expr> = Vec::with_capacity(then_args.len());
9876 let mut differ = 0usize;
9877 for (j, t) in then_args.iter().enumerate() {
9878 if two_sided {
9879 let e = &else_args[j];
9880 if t == e {
9881 merged.push(t.clone());
9882 } else {
9883 differ += 1;
9884 merged.push(Expr::Cond {
9885 c: Box::new(cond_i.clone()),
9886 t: Box::new(t.clone()),
9887 f: Box::new(e.clone()),
9888 });
9889 }
9890 } else {
9891 merged.push(t.clone());
9892 }
9893 }
9894
9895 let vars_in = |e: &Expr| -> Vec<u32> {
9897 let mut vs = Vec::new();
9898 let mut probe = e.clone();
9899 strip_lost_alloc_owners(&mut probe);
9900 deep_rewrite(&mut probe, &mut |x| {
9901 if let Expr::Local { var, .. } = x {
9902 if !vs.contains(var) {
9903 vs.push(*var);
9904 }
9905 }
9906 });
9907 vs
9908 };
9909 let branch_remainder = |list: &[&Stmt], side: &Side| -> Vec<Stmt> {
9917 match side {
9918 Side::Clean => list.iter().map(|s| (*s).clone()).collect(),
9919 Side::Call { at, def_idxs, .. } => {
9920 list.iter()
9921 .enumerate()
9922 .filter(|(i, _)| *i != *at && !def_idxs.contains(i))
9923 .map(|(_, s)| (*s).clone())
9924 .collect()
9925 }
9926 }
9927 };
9928 let tail_then_v = branch_remainder(&then_list, &t_side);
9929 let tail_else_v = branch_remainder(&else_list, &e_side);
9930 let post = &stmts[if_pos + 1..];
9931 let mut inlined: Vec<u32> = vars_in(&cond);
9932 for a in primary_args.iter().chain(else_args.iter()) {
9933 for v in vars_in(a) {
9934 if !inlined.contains(&v) {
9935 inlined.push(v);
9936 }
9937 }
9938 }
9939 inlined.retain(|v| pmap.contains_key(v) || tmap.contains_key(v) || emap.contains_key(v));
9940 for &v in &inlined {
9942 let in_branch = match (&t_side, &e_side) {
9943 (Side::Call { defs: td, .. }, Side::Call { defs: ed, .. }) => {
9944 td.iter().any(|(d, _)| *d == v) || ed.iter().any(|(d, _)| *d == v)
9945 }
9946 (Side::Call { defs, .. }, Side::Clean)
9947 | (Side::Clean, Side::Call { defs, .. }) => {
9948 defs.iter().any(|(d, _)| *d == v)
9949 }
9950 _ => false,
9951 };
9952 if pmap.contains_key(&v) && in_branch {
9953 return false;
9954 }
9955 }
9956 type Counts = std::collections::HashMap<u32, usize>;
9957 type GuardCounts = (
9958 Counts,
9959 Vec<Counts>,
9960 Vec<Counts>,
9961 Counts,
9962 Counts,
9963 Counts,
9964 Vec<(u32, Counts)>,
9965 );
9966 let (cond_counts, a1_counts, a2_counts, tail1_counts, tail2_counts, post_counts, def_counts): GuardCounts =
9967 if inlined.is_empty() {
9968 Default::default()
9969 } else {
9970 let all_defs: Vec<(u32, &Expr)> = pmap
9971 .iter()
9972 .map(|(k, v)| (*k, v))
9973 .chain(match &t_side {
9974 Side::Call { defs, .. } => defs.iter().map(|(k, v)| (*k, v)).collect(),
9975 Side::Clean => Vec::new(),
9976 })
9977 .chain(match &e_side {
9978 Side::Call { defs, .. } => defs.iter().map(|(k, v)| (*k, v)).collect(),
9979 Side::Clean => Vec::new(),
9980 })
9981 .collect();
9982 (
9983 count_locals_expr(&cond),
9984 primary_args.iter().map(count_locals_expr).collect(),
9985 else_args.iter().map(count_locals_expr).collect(),
9986 count_locals_stmts(&tail_then_v),
9987 count_locals_stmts(&tail_else_v),
9988 count_locals_stmts(post),
9989 all_defs.into_iter().map(|(v, e)| (v, count_locals_expr(e))).collect(),
9990 )
9991 };
9992 for &v in &inlined {
9993 let in_t = matches!(&t_side, Side::Call { defs, .. } if defs.iter().any(|(d, _)| *d == v));
9994 let in_e = matches!(&e_side, Side::Call { defs, .. } if defs.iter().any(|(d, _)| *d == v));
9995 let effectful = [
9996 in_t.then(|| tmap.get(&v)).flatten(),
9997 in_e.then(|| emap.get(&v)).flatten(),
9998 pmap.get(&v),
9999 ]
10000 .into_iter()
10001 .flatten()
10002 .any(jdc_core::ir::build::has_side_effects);
10003 if !effectful {
10004 continue;
10005 }
10006 let u_cond = cnt(&cond_counts, v);
10007 let u_post = cnt(&post_counts, v);
10008 let u_t1 = cnt(&tail1_counts, v);
10009 let u_t2 = cnt(&tail2_counts, v);
10010 let mut u_odef = 0usize;
10011 for (dv, dc) in &def_counts {
10012 if *dv != v && inlined.contains(dv) {
10013 u_odef += cnt(dc, v);
10014 }
10015 }
10016 let mut emit = u_cond * (1 + differ);
10017 let mut uses_args = 0usize;
10018 for j in 0..primary_args.len() {
10019 let c1 = cnt(&a1_counts[j], v);
10020 let c2 = if two_sided && j < a2_counts.len() {
10021 cnt(&a2_counts[j], v)
10022 } else {
10023 0
10024 };
10025 uses_args += c1 + c2;
10026 emit += if two_sided && then_args[j] != else_args[j] {
10027 c1 + c2
10028 } else {
10029 c1.max(c2)
10030 };
10031 }
10032 let ok = if in_t || in_e {
10033 u_cond == 0
10034 && u_post == 0
10035 && u_t1 == 0
10036 && u_t2 == 0
10037 && u_odef == 0
10038 && a1_counts.iter().map(|c| cnt(c, v)).sum::<usize>() <= 1
10039 && a2_counts.iter().map(|c| cnt(c, v)).sum::<usize>() <= 1
10040 && emit <= 2
10041 && (in_t || a1_counts.iter().all(|c| cnt(c, v) == 0))
10042 && (in_e || a2_counts.iter().all(|c| cnt(c, v) == 0))
10043 } else {
10044 u_post == 0
10045 && u_t1 == 0
10046 && u_t2 == 0
10047 && u_odef == 0
10048 && ((u_cond == 1 && uses_args == 0 && emit <= 2)
10049 || (u_cond == 0 && emit <= 1))
10050 };
10051 if !ok {
10052 return false;
10053 }
10054 }
10055
10056 let mut call_stmt = call_list[call_at].clone();
10058 if let Stmt::ExprStmt(Expr::Method { args: slot, .. }) = &mut call_stmt {
10059 *slot = merged;
10060 }
10061 let residual = |v: u32| -> usize {
10062 cnt(&tail1_counts, v) + cnt(&tail2_counts, v) + cnt(&post_counts, v)
10063 };
10064 let mut kept_prelude: Vec<Stmt> = Vec::new();
10065 for (i, s) in stmts[..if_pos].iter().enumerate() {
10066 let consumed = match s {
10067 Stmt::LocalDef { var, .. } => Some(*var),
10068 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
10069 Expr::Local { var, .. } => Some(*var),
10070 _ => None,
10071 },
10072 Stmt::If { .. } => cond_def_of(s, stmts).map(|(v, _)| v),
10073 _ => None,
10074 };
10075 if let Some(v) = consumed {
10076 if inlined.contains(&v) && pidx.get(&v) == Some(&i) && residual(v) == 0 {
10077 continue;
10078 }
10079 }
10080 kept_prelude.push(s.clone());
10081 }
10082 let build_keep = |list: &[&Stmt], side: &Side| -> Vec<Stmt> {
10083 match side {
10084 Side::Clean => list.iter().map(|s| (*s).clone()).collect(),
10085 Side::Call { at, .. } => {
10086 let mut out = Vec::new();
10087 for (i, s) in list.iter().enumerate() {
10088 if i == *at {
10089 continue;
10090 }
10091 if i < *at {
10092 if let Some((v, _)) = def_of(s) {
10093 if inlined.contains(&v) && residual(v) == 0 {
10094 continue;
10095 }
10096 }
10097 }
10098 out.push((*s).clone());
10099 }
10100 out
10101 }
10102 }
10103 };
10104 let then_keep = build_keep(&then_list, &t_side);
10105 let else_keep = build_keep(&else_list, &e_side);
10106 let else_part = if else_keep.is_empty() && !had_else {
10107 None
10108 } else {
10109 Some(Box::new(Stmt::Block(else_keep)))
10110 };
10111 let new_if = Stmt::If {
10112 cond: cond_i,
10113 then_stmt: Box::new(Stmt::Block(then_keep)),
10114 else_stmt: else_part,
10115 };
10116 let if_empty = match &new_if {
10117 Stmt::If { then_stmt, else_stmt, .. } => {
10118 let be = |s: &Stmt| matches!(s, Stmt::Block(v) if v.is_empty());
10119 be(then_stmt) && else_stmt.as_ref().is_none_or(|e| be(e))
10120 }
10121 _ => false,
10122 };
10123 let mut out: Vec<Stmt> = Vec::with_capacity(stmts.len());
10124 out.push(call_stmt);
10125 out.extend(kept_prelude);
10126 if !if_empty {
10127 out.push(new_if);
10128 }
10129 out.extend(stmts[if_pos + 1..].to_vec());
10130 *stmts = out;
10131 true
10132}
10133
10134
10135pub fn strip_enum_ctor_super(body: &mut Stmt) {
10136 let is_enum_super = |s: &Stmt| {
10137 matches!(
10138 s,
10139 Stmt::ExprStmt(Expr::Method { name, is_super: true, .. }) if &**name == "<init>"
10140 )
10141 };
10142 let Stmt::Block(stmts) = body else {
10143 return;
10144 };
10145 stmts.retain(|s| !is_enum_super(s));
10146 for st in stmts.iter_mut() {
10147 if let Stmt::Block(inner) = st {
10148 inner.retain(|s| !is_enum_super(s));
10149 }
10150 }
10151}
10152
10153pub fn ensure_declared(body: &mut Stmt, vt: &VarTable) {
10154 let n = vt.vars.len().max(1);
10158
10159 let mut seen: HashSet<u32> = HashSet::default();
10162 demote_dup_decls(body, &mut seen, vt);
10163
10164 let mut def_block: Vec<u32> = vec![u32::MAX; n];
10172 let mut sizes: Vec<u32> = vec![0];
10173 let mut next_id: u32 = 1;
10174 scope_pass_a(body, 0, &mut next_id, &mut def_block, &mut sizes);
10175 sizes[0] = next_id;
10176 let mut within: Vec<u32> = vec![0; n];
10177 let mut total: Vec<u32> = vec![0; n];
10178 let mut next_b: u32 = 1;
10179 scope_pass_b(body, 0, &mut next_b, &def_block, &sizes, &mut within, &mut total);
10180 let mut hoist: HashSet<u32> = HashSet::default();
10181 for v in 0..n {
10182 if def_block[v] != u32::MAX && def_block[v] != 0 && within[v] < total[v] {
10183 hoist.insert(v as u32);
10184 }
10185 }
10186 if !hoist.is_empty() {
10187 let mut seed: HashSet<u32> = hoist.iter().copied().collect();
10188 demote_dup_decls(body, &mut seed, vt);
10189 }
10190
10191 let mut declared: Vec<bool> = vec![false; n];
10192 let mut is_param: Vec<bool> = vec![false; n];
10193 for v in &vt.vars {
10194 if v.is_param && (v.id as usize) < n {
10195 is_param[v.id as usize] = true;
10196 }
10197 }
10198 let mut is_catch: Vec<bool> = vec![false; n];
10204 walk_all(body, &mut |st| {
10205 if let Stmt::Try { catches, .. } = st {
10206 for c in catches {
10207 if (c.var as usize) < n {
10208 is_catch[c.var as usize] = true;
10209 }
10210 }
10211 }
10212 });
10213 walk_all(body, &mut |st| {
10214 if let Stmt::LocalDef { var, .. } = st {
10215 if (*var as usize) < n {
10216 declared[*var as usize] = true;
10217 }
10218 }
10219 });
10220 let mut used: HashSet<u32> = HashSet::default();
10221 stmt_collect_vars(body, &mut used, true);
10225
10226 let mut needs_decl: Vec<u32> = used
10229 .into_iter()
10230 .filter(|v| {
10231 let i = *v as usize;
10232 i >= n
10233 || ((!declared[i] && !is_param[i] && !is_catch[i]) || hoist.contains(v))
10234 })
10235 .collect();
10236 needs_decl.sort_unstable();
10237 needs_decl.dedup();
10238
10239 if !needs_decl.is_empty() {
10240 let mut decls: Vec<Stmt> = needs_decl
10241 .into_iter()
10242 .map(|v| Stmt::LocalDef {
10243 var: v,
10244 init: None,
10245 is_final: false,
10246 force_type: true,
10247 })
10248 .collect();
10249 decls.reverse();
10250 match body {
10251 Stmt::Block(v) => {
10252 for d in decls {
10253 v.insert(0, d);
10254 }
10255 }
10256 other => {
10257 let inner = std::mem::replace(other, Stmt::Block(vec![]));
10258 *other = Stmt::Block({
10259 let mut vs = decls;
10260 vs.push(inner);
10261 vs
10262 });
10263 }
10264 }
10265 }
10266}
10267
10268fn demote_dup_decls(s: &mut Stmt, seen: &mut HashSet<u32>, vt: &VarTable) {
10269 match s {
10270 Stmt::Block(v) => {
10273 for x in v.iter_mut() {
10274 demote_dup_decls(x, seen, vt);
10275 }
10276 }
10277 Stmt::LocalDef { var, init, .. } => {
10278 if seen.contains(var) {
10279 let ty = vt.var(*var).ty.clone();
10280 if let Some(e) = init.take() {
10281 *s = Stmt::ExprStmt(Expr::Assign {
10282 target: Box::new(Expr::Local { var: *var, ty }),
10283 op: AssignOp::Plain,
10284 value: Box::new(e),
10285 });
10286 } else {
10287 *s = Stmt::Block(vec![]);
10288 }
10289 } else {
10290 seen.insert(*var);
10291 }
10292 }
10293 other => {
10294 walk_mut(other, &mut |x| demote_dup_decls(x, seen, vt));
10295 }
10296 }
10297}
10298
10299#[allow(dead_code)]
10300fn unused(_: &Vec<CaseGroup>, _: &Catch) {}
10301
10302pub fn apply_local_names(vt: &mut VarTable, body: &Stmt) {
10316 use std::collections::{HashMap, HashSet};
10317
10318 let mut by_call: HashMap<u32, Vec<String>> = HashMap::new();
10321 let mut by_intrinsics: HashMap<u32, String> = HashMap::new();
10322
10323 visit_all_exprs(body, &mut |e| {
10324 if let Expr::Method { cls, name, args, .. } = e {
10325 if (cls.as_ref() == "kotlin/jvm/internal/Intrinsics"
10326 || cls.as_ref() == "kotlin/jvm/internal/IntrinsicsKt")
10327 && matches!(name.as_ref(), "checkNotNullParameter" | "checkParameterIsNotNull")
10328 && args.len() == 2
10329 {
10330 if let (Expr::Local { var, .. }, Expr::Const(ConstVal::Str(s))) = (&args[0], &args[1])
10331 {
10332 by_intrinsics
10333 .entry(*var)
10334 .or_insert_with(|| sanitize_name(s).unwrap_or_default());
10335 }
10336 }
10337 }
10338 });
10339
10340 walk_all(body, &mut |st| {
10341 let (var, value) = match st {
10342 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e),
10343 Stmt::ExprStmt(Expr::Assign { target, value, .. })
10344 if matches!(&**target, Expr::Local { .. }) =>
10345 {
10346 let Expr::Local { var, .. } = &**target else { return };
10347 (*var, value.as_ref())
10348 }
10349 _ => return,
10350 };
10351 if let Some(n) = defining_call_name(value) {
10352 by_call.entry(var).or_default().push(n);
10353 }
10354 });
10355
10356 let sanit = |n: &str| crate::classdec::java_ident(n).into_owned();
10367 let mut taken: HashSet<String> = HashSet::default();
10368 for v in vt.vars.iter() {
10373 if v.synthetic_name {
10378 taken.insert(sanit(&v.name));
10379 }
10380 }
10381 for v in vt.vars.iter_mut() {
10382 if v.synthetic_name {
10383 continue;
10384 }
10385 if !taken.insert(sanit(&v.name)) {
10386 let base = sanit(&v.name);
10387 for i in 2.. {
10388 let cand = format!("{base}{i}");
10389 if taken.insert(cand.clone()) {
10390 v.name = cand;
10391 break;
10392 }
10393 }
10394 }
10395 }
10396 let claim = |taken: &mut HashSet<String>, want: &str| -> String {
10397 let w = crate::classdec::java_ident(want).into_owned();
10398 if taken.insert(w.clone()) {
10399 return w;
10400 }
10401 for i in 2.. {
10402 let cand = format!("{w}{i}");
10403 if taken.insert(cand.clone()) {
10404 return cand;
10405 }
10406 }
10407 w
10408 };
10409
10410 for info in vt.vars.iter_mut() {
10411 if !info.synthetic_name {
10412 continue;
10413 }
10414 if let Some(n) = by_intrinsics.get(&info.id) {
10417 if !n.is_empty() {
10418 info.name = claim(&mut taken, n);
10419 continue;
10420 }
10421 }
10422 if let Some(names) = by_call.get(&info.id) {
10424 let unique: HashSet<&String> = names.iter().collect();
10425 if unique.len() == 1 {
10426 let n = unique.into_iter().next().unwrap();
10427 if !is_java_keyword(n) {
10428 info.name = claim(&mut taken, n);
10429 continue;
10430 }
10431 }
10432 }
10433 let want = type_alias(&info.ty).map(str::to_string).or_else(|| simple_type_name(&info.ty));
10436 if let Some(n) = want {
10437 info.name = claim(&mut taken, &n);
10438 }
10439 }
10440
10441 let mut seen: HashSet<String> = HashSet::default();
10446 for v in vt.vars.iter_mut() {
10447 if !seen.insert(sanit(&v.name)) {
10448 let base = sanit(&v.name);
10449 for i in 2.. {
10450 let cand = format!("{base}{i}");
10451 if seen.insert(cand.clone()) {
10452 v.name = cand;
10453 break;
10454 }
10455 }
10456 }
10457 }
10458}
10459
10460fn simple_type_name(ty: &TypeRef) -> Option<String> {
10462 let TypeRef::J(JavaType::Object(n)) = ty else {
10463 return None;
10464 };
10465 let simple = n.rsplit('/').next().unwrap_or(n);
10466 let simple = simple.rsplit('$').next().unwrap_or(simple);
10467 let mut chars = simple.chars();
10470 let mut out = String::with_capacity(simple.len());
10471 let mut lowered = false;
10472 for c in chars.by_ref() {
10473 if c.is_ascii_alphabetic() && !lowered {
10474 out.push(c.to_ascii_lowercase());
10475 lowered = true;
10476 } else {
10477 out.push(c);
10478 }
10479 if lowered {
10480 break;
10481 }
10482 }
10483 out.extend(chars);
10484 sanitize_name(&out)
10485}
10486
10487fn defining_call_name(e: &Expr) -> Option<String> {
10491 match e {
10492 Expr::Method { name, .. } => {
10493 let base = name
10494 .strip_prefix("get")
10495 .or_else(|| name.strip_prefix("is"))
10496 .filter(|rest| rest.chars().next().is_some_and(|c| c.is_ascii_uppercase()))
10497 .unwrap_or(name);
10498 let first = base.chars().next()?;
10499 let mut out = String::with_capacity(base.len());
10500 out.push(first.to_ascii_lowercase());
10501 out.extend(base.chars().skip(1));
10502 sanitize_name(&out)
10503 }
10504 Expr::New { cls, .. } => {
10505 let simple = cls.rsplit('/').next().unwrap_or(cls);
10506 let simple = simple.rsplit('$').next().unwrap_or(simple);
10507 let first = simple.chars().next()?;
10508 let mut out = String::with_capacity(simple.len());
10509 out.push(first.to_ascii_lowercase());
10510 out.extend(simple.chars().skip(1));
10511 sanitize_name(&out)
10512 }
10513 _ => None,
10514 }
10515}
10516
10517fn sanitize_name(s: &str) -> Option<String> {
10519 let mut out = String::with_capacity(s.len());
10520 for c in s.chars() {
10521 if c.is_ascii_alphanumeric() || c == '_' || c == '$' {
10522 out.push(c);
10523 } else {
10524 out.push('_');
10525 }
10526 }
10527 if out.len() < 2
10528 || out.chars().next().is_some_and(|c| c.is_ascii_digit())
10529 || is_java_keyword(&out)
10530 {
10531 return None;
10532 }
10533 Some(out)
10534}
10535
10536fn type_alias(ty: &TypeRef) -> Option<&'static str> {
10539 let TypeRef::J(JavaType::Object(n)) = ty else {
10540 return None;
10541 };
10542 Some(match n.as_ref() {
10543 "java/lang/String" | "kotlin/String" => "str",
10544 "java/lang/Class" => "cls",
10545 "java/lang/Throwable" => "th",
10546 "java/lang/Object" | "kotlin/Any" => "obj",
10547 "java/util/Iterator" | "kotlin/collections/Iterator" => "it",
10548 "java/lang/Boolean" => "bool",
10549 "java/lang/Integer" => "num",
10550 "java/lang/Character" => "ch",
10551 "java/lang/Byte" => "b",
10552 "java/lang/Short" => "sh",
10553 "java/lang/Float" => "f",
10554 "java/lang/Double" => "d",
10555 "java/lang/Long" => "i",
10556 "java/lang/StringBuilder" => "sb",
10557 "java/util/ArrayList" => "list",
10558 "java/util/HashMap" => "map",
10559 "android/content/Context" => "context",
10560 "android/content/Intent" => "intent",
10561 "android/os/Bundle" => "bundle",
10562 "android/view/View" => "view",
10563 "android/graphics/Bitmap" => "bitmap",
10564 "android/view/ViewGroup" => "viewGroup",
10565 _ => return None,
10566 })
10567}
10568
10569pub(crate) fn visit_all_exprs<F: FnMut(&Expr)>(s: &Stmt, f: &mut F) {
10571 walk_all(s, &mut |st| {
10572 let exprs: Vec<&Expr> = match st {
10573 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
10574 let mut v: Vec<&Expr> = vec![value];
10575 if !matches!(&**target, Expr::Local { .. }) {
10576 v.push(target);
10577 }
10578 v
10579 }
10580 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
10581 vec![e]
10582 }
10583 Stmt::Return(Some(e)) => vec![e],
10584 Stmt::LocalDef { init: Some(e), .. } => vec![e],
10585 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
10586 vec![cond]
10587 }
10588 _ => Vec::new(),
10589 };
10590 for e in exprs {
10591 visit_exprs(e, f);
10592 }
10593 });
10594}
10595
10596pub fn platform_constants(body: &mut Stmt) {
10601 if !crate::platform::installed() {
10602 return;
10603 }
10604 rewrite_exprs(body, &mut |e| {
10605 if let Expr::Method { cls, name, desc, args, .. } = e {
10606 for a in args.iter_mut() {
10607 let (value, is_long) = match a {
10608 Expr::Const(ConstVal::Int(v)) => (*v as i64, false),
10609 Expr::Const(ConstVal::Long(v)) => (*v, true),
10610 _ => continue,
10611 };
10612 if let Some(named) =
10613 crate::platform::named_constant(cls, name, &desc_to_string(desc), 0, value)
10614 {
10615 let _ = is_long;
10616 *a = Expr::Raw(named);
10617 }
10618 }
10619 }
10620 });
10621}
10622
10623fn desc_to_string(d: &jdc_core::types::MethodDescriptor) -> String {
10625 let mut out = String::from("(");
10626 for a in &d.args {
10627 type_to_desc(a, &mut out);
10628 }
10629 out.push(')');
10630 type_to_desc(&d.ret, &mut out);
10631 out
10632}
10633
10634fn type_to_desc(t: &jdc_core::types::JavaType, out: &mut String) {
10635 match t {
10636 JavaType::Void => out.push('V'),
10637 JavaType::Boolean => out.push('Z'),
10638 JavaType::Byte => out.push('B'),
10639 JavaType::Char => out.push('C'),
10640 JavaType::Short => out.push('S'),
10641 JavaType::Int => out.push('I'),
10642 JavaType::Float => out.push('F'),
10643 JavaType::Long => out.push('J'),
10644 JavaType::Double => out.push('D'),
10645 JavaType::Object(n) => {
10646 out.push('L');
10647 out.push_str(n);
10648 out.push(';');
10649 }
10650 JavaType::Array(inner) => {
10651 out.push('[');
10652 type_to_desc(inner, out);
10653 }
10654 }
10655}
10656
10657pub fn remove_kotlin_checks(s: &mut Stmt) {
10663 walk_mut_deep(s, &mut |st| {
10664 let Stmt::Block(v) = st else { return };
10665 v.retain(|x| !is_kotlin_check(x));
10666 });
10667}
10668
10669fn is_kotlin_check(s: &Stmt) -> bool {
10670 let Stmt::ExprStmt(e) = s else { return false };
10671 let Expr::Method { cls, name, .. } = e else { return false };
10672 (cls.as_ref() == "kotlin/jvm/internal/Intrinsics" || cls.as_ref() == "kotlin/jvm/internal/IntrinsicsKt__Jdk7Kt")
10673 && matches!(
10674 name.as_ref(),
10675 "checkNotNullParameter"
10676 | "checkParameterIsNotNull"
10677 | "checkExpressionValueIsNotNull"
10678 | "checkNotNullExpressionValue"
10679 | "checkReturnedValueIsNotNull"
10680 | "checkFieldIsNotNull"
10681 | "checkNotNull"
10682 )
10683}
10684
10685fn inline_ref_ok(pool: &DexPool, host: &str, target: &str, member: Option<(&str, bool)>) -> bool {
10705 let hpkg = match host.rfind('/') {
10706 Some(i) => &host[..i],
10707 None => "",
10708 };
10709 let mut cur = target;
10710 loop {
10711 let tpkg = match cur.rfind('/') {
10712 Some(i) => &cur[..i],
10713 None => "",
10714 };
10715 if tpkg != hpkg {
10716 if let Some(pc) = pool.get(cur) {
10717 if pc.access & crate::access::ACC_PUBLIC == 0 {
10718 return false;
10719 }
10720 if cur == target {
10721 if let Some((mname, is_method)) = member {
10722 let ok = if is_method {
10723 pc.all_methods().any(|m| {
10724 &*m.name == mname
10725 && m.access & crate::access::ACC_PUBLIC != 0
10726 })
10727 } else {
10728 pc.static_fields
10729 .iter()
10730 .chain(pc.instance_fields.iter())
10731 .any(|f| {
10732 f.name == mname
10733 && f.access & crate::access::ACC_PUBLIC != 0
10734 })
10735 };
10736 if !ok
10741 && (if is_method {
10742 pc.all_methods().any(|m| &*m.name == mname)
10743 } else {
10744 pc.static_fields
10745 .iter()
10746 .chain(pc.instance_fields.iter())
10747 .any(|f| f.name == mname)
10748 })
10749 {
10750 return false;
10751 }
10752 }
10753 }
10754 }
10755 }
10756 match cur.rfind('$') {
10757 Some(i) if i > 0 => cur = &cur[..i],
10758 _ => break,
10759 }
10760 }
10761 true
10762}
10763
10764pub fn inline_accessors(s: &mut Stmt, pool: &DexPool, host: &str) {
10765 rewrite_exprs(s, &mut |e| {
10766 let Expr::Method { cls, name, desc, args, is_static, .. } = e else { return };
10767 if !*is_static || args.is_empty() {
10768 return;
10769 }
10770 let Some(target) = pool.get(cls) else { return };
10771 let desc_s = desc_to_string(desc);
10772 let Some(m) = target.find_method(name, &desc_s) else { return };
10773 if !(m.is_static() && m.access & access::ACC_SYNTHETIC != 0) {
10774 return;
10775 }
10776 let Some(dex) = pool.dex(m.dex_idx) else { return };
10777 let code = match pool.accessor_code(m.dex_idx, m.code_off) {
10782 Some(bytes) => ddc_dex::CodeItem::parse(&bytes, 0),
10783 None => dex.code_at(m.code_off),
10784 };
10785 let Some(code) = code else { return };
10786 let insns: Vec<&Insn> =
10787 code.insns.iter().filter(|i| !matches!(i.kind, InsnKind::Nop)).collect();
10788 let core: Vec<&Insn> = insns
10790 .iter()
10791 .copied()
10792 .filter(|i| !is_const_only_trace(&i.kind, &insns))
10793 .collect();
10794 match accessor_shape(&dex, &core, &code) {
10795 Some(Shape::Identity(arg_i)) => {
10796 if let Some(a) = args.get(arg_i) {
10797 *e = a.clone();
10798 }
10799 }
10800 Some(Shape::FieldRead { arg, cls: field_cls, field, field_ty }) => {
10801 if !inline_ref_ok(pool, host, &field_cls, Some((&field, false))) {
10802 return;
10803 }
10804 if let Some(owner) = args.get(arg).cloned().map(Box::new) {
10805 let ty = TypeRef::J(desc_type(&field_ty));
10806 let name = jdc_core::rename::field_display(
10812 &field_cls,
10813 &field,
10814 &field_ty,
10815 )
10816 .map(std::sync::Arc::from)
10817 .unwrap_or_else(|| field.into());
10818 *e = Expr::Field {
10819 owner: Some(owner),
10820 cls: field_cls.into(),
10821 name,
10822 ty,
10823 is_static: false,
10824 };
10825 }
10826 }
10827 Some(Shape::Forward { cls: tcls, name: tname, desc: tdesc, instance }) => {
10828 if !inline_ref_ok(pool, host, &tcls, Some((&tname, true))) {
10829 return;
10830 }
10831 let disp = jdc_core::rename::field_display(
10835 &tcls,
10836 &tname,
10837 &tdesc.to_string(),
10838 )
10839 .map(std::sync::Arc::from)
10840 .unwrap_or_else(|| tname.into());
10841 *cls = tcls.into();
10842 *name = disp;
10843 *desc = std::sync::Arc::new(tdesc);
10844 if instance && !args.is_empty() {
10845 let recv = args.remove(0);
10847 if let Expr::Method { owner, .. } = e {
10848 *owner = Some(Box::new(recv));
10849 }
10850 }
10851 }
10852 None => {}
10853 }
10854 });
10855}
10856
10857enum Shape {
10859 Identity(usize),
10861 FieldRead { arg: usize, cls: String, field: String, field_ty: String },
10863 Forward { cls: String, name: String, desc: MethodDescriptor, instance: bool },
10866}
10867
10868fn field_of(dex: &ddc_dex::DexFile, field_idx: u32) -> Option<(String, String, String)> {
10870 let f = dex.field(field_idx);
10871 Some((
10872 dex.class_name(f.class_idx),
10875 dex.string(f.name_idx).to_string(),
10876 dex.type_name(f.type_idx).to_string(),
10878 ))
10879}
10880
10881fn param_index(code: &ddc_dex::CodeItem, reg: u16) -> Option<usize> {
10884 let rs = code.registers_size as usize;
10885 let ins = code.ins_size as usize;
10886 let r = reg as usize;
10887 let first = rs.checked_sub(ins)?;
10888 (r >= first && r < rs).then(|| r - first)
10889}
10890
10891fn accessor_shape(
10892 dex: &ddc_dex::DexFile,
10893 core: &[&Insn],
10894 code: &ddc_dex::CodeItem,
10895) -> Option<Shape> {
10896 let core: &[&Insn] = match core.split_first() {
10899 Some((first, rest)) if matches!(first.kind, InsnKind::Const { .. }) => rest,
10900 _ => core,
10901 };
10902 let core: &[&Insn] = match core.split_last() {
10903 Some((last, rest)) if matches!(last.kind, InsnKind::Const { .. }) => rest,
10904 _ => core,
10905 };
10906 match core {
10907 [Insn { kind: InsnKind::Return { src }, .. }] => {
10909 let idx = param_index(code, *src)?;
10910 Some(Shape::Identity(idx))
10911 }
10912 [
10914 Insn { kind: InsnKind::IGet { dst, obj, field_idx }, .. },
10915 Insn { kind: InsnKind::Return { src }, .. },
10916 ] if dst == src => {
10917 let arg = param_index(code, *obj)?;
10918 let (cls, field, field_ty) = field_of(dex, *field_idx)?;
10919 Some(Shape::FieldRead { arg, cls, field, field_ty })
10920 }
10921 [Insn { kind: InsnKind::Invoke { method_idx, regs, kind, .. }, .. }, rest @ ..] if rest.len() <= 2 => {
10923 if !regs.iter().all(|r| param_index(code, *r).is_some()) || regs.is_empty() {
10925 return None;
10926 }
10927 if rest.len() == 2 {
10930 let ok = matches!(rest[0].kind, InsnKind::MoveResult { .. })
10931 && matches!(rest[1].kind, InsnKind::Return { .. } | InsnKind::ReturnVoid);
10932 if !ok {
10933 return None;
10934 }
10935 }
10936 let mid = dex.method(*method_idx);
10937 let cls = dex.class_name(mid.class_idx);
10940 let name = dex.string(mid.name_idx).to_string();
10941 let mut s = String::from("(");
10942 for t in dex.proto_params(mid.proto_idx) {
10943 s.push_str(dex.type_name(*t));
10944 }
10945 s.push(')');
10946 s.push_str(dex.type_name(dex.proto(mid.proto_idx).return_type_idx));
10947 let desc = parse_method_descriptor(&s)?;
10948 Some(Shape::Forward { cls, name, desc, instance: !matches!(kind, InvokeKind::Static) })
10949 }
10950 _ => None,
10951 }
10952}
10953
10954
10955fn is_const_only_trace(kind: &InsnKind, insns: &[&Insn]) -> bool {
10961 let InsnKind::Invoke { kind, regs, .. } = kind else { return false };
10962 if !matches!(kind, InvokeKind::Static) || regs.is_empty() {
10963 return false;
10964 }
10965 regs.iter().all(|r| const_loaded(*r, insns, 0))
10966}
10967
10968fn const_loaded(r: u16, insns: &[&Insn], depth: u8) -> bool {
10971 if depth > 3 {
10972 return false;
10973 }
10974 insns.iter().any(|i| match &i.kind {
10975 InsnKind::Const { dst, .. } => *dst == r,
10976 InsnKind::Move { dst, src } => *dst == r && const_loaded(*src, insns, depth + 1),
10977 _ => false,
10978 })
10979}