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_)
3666 }
3667 _ => false,
3668 };
3669 let null_obj =
3683 matches!(a, Expr::Const(ConstVal::Null)) && matches!(f, JavaType::Object(_));
3684 let framework_owner = pool.get(cls.as_ref()).is_none();
3685 let fw_null_ambiguous = framework_owner
3686 && matches!(
3687 (cls.as_ref(), name.as_ref()),
3688 ("java/lang/StringBuilder", "append")
3689 | ("java/lang/StringBuffer", "append")
3690 | ("java/io/PrintStream", "println")
3691 | ("java/io/PrintStream", "print")
3692 | ("java/io/PrintWriter", "println")
3693 | ("java/io/PrintWriter", "print")
3694 | ("java/lang/String", "valueOf")
3695 );
3696 let fw_subtype_pinnable = framework_owner
3707 && subtype_arg
3708 && !matches!(f, JavaType::Object(fn_) if fn_.as_ref() == "java/lang/Object")
3709 && matches!(
3710 (cls.as_ref(), name.as_ref()),
3711 ("android/content/Intent", "putExtra")
3712 | ("java/util/concurrent/ScheduledExecutorService", "schedule")
3713 | ("java/util/concurrent/ExecutorService", "submit")
3714 | ("java/util/concurrent/ExecutorService", "invokeAll")
3715 );
3716 if fw_subtype_pinnable
3717 || (subtype_arg
3718 && *competes.get_or_insert_with(|| {
3719 overload_competes(cls, name, &desc.args)
3720 }))
3721 || (null_obj
3722 && (fw_null_ambiguous
3723 || *competes.get_or_insert_with(|| {
3724 overload_competes(cls, name, &desc.args)
3725 })))
3726 {
3727 let old =
3728 std::mem::replace(a, Expr::Const(ConstVal::Null));
3729 *a = Expr::Cast {
3730 ty: TypeRef::J(f.clone()),
3731 e: Box::new(old),
3732 };
3733 continue;
3734 }
3735 if !is_bool(&val_ty(a, vt)) && !is_num(&val_ty(a, vt)) {
3736 continue;
3737 }
3738 if !is_bool(f) && !is_num(f) {
3739 continue;
3740 }
3741 if let Some(b) = bridge(&val_ty(a, vt), f, a) {
3742 *a = b;
3743 }
3744 }
3745 }
3746 Expr::New { cls, args, arg_tys, .. } => {
3747 let formals_owned = if arg_tys.len() == args.len() && !arg_tys.is_empty() {
3748 Some(arg_tys.clone())
3749 } else {
3750 ctor_formals(cls, args.len())
3751 };
3752 if let Some(formals) = formals_owned {
3753 for (a, f) in args.iter_mut().zip(formals.iter()) {
3754 if null_arg_cast(a, f, pool) {
3755 continue;
3756 }
3757 if matches!(a, Expr::Cast { .. }) {
3758 continue;
3759 }
3760 if !is_bool(f) && !is_num(f) {
3761 continue;
3762 }
3763 if let Some(b) = bridge(&val_ty(a, vt), f, a) {
3764 *a = b;
3765 }
3766 }
3767 }
3768 }
3769 _ => {}
3770 });
3771 });
3772}
3773
3774pub fn deshadow_locals(vt: &mut VarTable, pool: &crate::DexPool, class_name: &str, body: &Stmt) {
3786 let segs = pool.root_pkg_segs();
3787 let obscured = crate::classdec::obscured_simples_snapshot();
3791 let self_pkg = class_name.rfind('/').map(|i| &class_name[..i]).unwrap_or("");
3798 let mut same_pkg_simples: jdc_core::FxHashSet<String> = jdc_core::FxHashSet::default();
3799 visit_all_exprs(body, &mut |x| {
3800 let cls = match x {
3801 Expr::Field { cls, is_static: true, .. } => Some(cls.as_ref()),
3802 Expr::Method { cls, is_static: true, .. } => Some(cls.as_ref()),
3803 _ => None,
3804 };
3805 if let Some(c) = cls {
3806 let pkg = c.rfind('/').map(|i| &c[..i]).unwrap_or("");
3807 if pkg == self_pkg {
3808 let simple = &c[c.rfind('/').map(|i| i + 1).unwrap_or(0)..];
3809 let head = simple.split('$').next().unwrap_or(simple);
3810 if !head.is_empty() {
3811 same_pkg_simples.insert(head.to_string());
3812 }
3813 }
3814 }
3815 });
3816 let hit = |name: &str| {
3817 segs.contains(name) || obscured.contains(name) || same_pkg_simples.contains(name)
3818 };
3819 if (segs.is_empty() && obscured.is_empty() && same_pkg_simples.is_empty())
3820 || !vt.vars.iter().any(|v| hit(&v.name))
3821 {
3822 return;
3823 }
3824 let mut taken: jdc_core::FxHashSet<String> =
3825 vt.vars.iter().map(|v| v.name.clone()).collect();
3826 for v in vt.vars.iter_mut() {
3827 if !hit(&v.name) {
3828 continue;
3829 }
3830 let mut k = 0u32;
3831 let cand = loop {
3832 k += 1;
3833 let c = if k == 1 {
3834 format!("{}x", v.name)
3835 } else {
3836 format!("{}x{}", v.name, k)
3837 };
3838 if !taken.contains(&c) {
3839 break c;
3840 }
3841 };
3842 taken.insert(cand.clone());
3843 v.name = cand;
3844 }
3845}
3846
3847pub fn fix_ctor_this_aliases(body: &mut Stmt, vt: &VarTable) {
3873 let Some(this_var) = vt
3874 .vars
3875 .iter()
3876 .find(|v| v.is_param && v.name == "this")
3877 .map(|v| v.id)
3878 else {
3879 return;
3880 };
3881 let mut analysis = VarAnalysis {
3882 assigns: Vec::new(),
3883 reads: Vec::new(),
3884 values: Vec::new(),
3885 };
3886 analyze_vars(body, &mut analysis);
3887 let n = vt.vars.len().max(analysis.assigns.len());
3888 let mut alias = vec![false; n];
3889 for _ in 0..4 {
3890 let mut changed = false;
3891 for v in 0..n as u32 {
3892 if alias[v as usize] {
3893 continue;
3894 }
3895 if analysis.assigns.get(v as usize).copied().unwrap_or(0) != 1 {
3896 continue;
3897 }
3898 let Some(Expr::Local { var: src, .. }) = analysis.values.get(v as usize).and_then(|o| o.as_ref()) else {
3899 continue;
3900 };
3901 if *src == this_var
3902 || (*src < n as u32 && alias[*src as usize])
3903 {
3904 alias[v as usize] = true;
3905 changed = true;
3906 }
3907 }
3908 if !changed {
3909 break;
3910 }
3911 }
3912 if !alias.iter().any(|&b| b) {
3913 return;
3914 }
3915 walk_stmt_exprs(body, &mut |e| {
3916 deep_rewrite_reads(e, &mut |x| {
3917 if let Expr::Local { var, .. } = x {
3918 if (*var as usize) < n && alias[*var as usize] {
3919 *x = Expr::This;
3920 }
3921 }
3922 });
3923 });
3924}
3925
3926pub fn fix_this0_owners(body: &mut Stmt, class_name: &str) {
3927 walk_stmt_exprs(body, &mut |e| {
3928 deep_rewrite(e, &mut |x| {
3929 if let Expr::Field { owner: Some(_), cls, name, .. } = x {
3930 if name.as_ref() == "this$0" && cls.as_ref() == class_name {
3931 if let Expr::Field { owner, .. } = x {
3932 *owner = None;
3933 }
3934 }
3935 }
3936 });
3937 });
3938}
3939
3940pub fn rescue_primitive_receivers(body: &mut Stmt, vt: &VarTable, pool: &crate::DexPool) {
3941 fn prim(t: &JavaType) -> bool {
3942 match t {
3943 JavaType::Int
3944 | JavaType::Long
3945 | JavaType::Short
3946 | JavaType::Byte
3947 | JavaType::Char
3948 | JavaType::Float
3949 | JavaType::Double
3950 | JavaType::Boolean => true,
3951 JavaType::Object(n) => matches!(
3955 n.as_ref(),
3956 "java/lang/Boolean"
3957 | "java/lang/String"
3958 | "java/lang/Integer"
3959 | "java/lang/Long"
3960 | "java/lang/Short"
3961 | "java/lang/Byte"
3962 | "java/lang/Character"
3963 | "java/lang/Float"
3964 | "java/lang/Double"
3965 ),
3966 _ => false,
3967 }
3968 }
3969 walk_stmt_exprs(body, &mut |e| {
3970 deep_rewrite(e, &mut |x| {
3971 let recv_var = {
3973 let o: &Expr = match &*x {
3974 Expr::Method { owner: Some(o), .. } => o,
3975 Expr::Field { owner: Some(o), is_static: false, .. } => o,
3976 _ => return,
3977 };
3978 let Expr::Local { var, ty } = o else {
3979 return;
3980 };
3981 let vt_ty = if (*var as usize) < vt.vars.len() {
3982 vt.var(*var).ty.erased()
3983 } else {
3984 ty.erased()
3985 };
3986 if !prim(&vt_ty) {
3987 return;
3988 }
3989 *var
3990 };
3991 let cls: std::sync::Arc<str> = match &*x {
3992 Expr::Method { cls, .. } | Expr::Field { cls, .. } => cls.clone(),
3993 _ => return,
3994 };
3995 let mut cands: Vec<u32> = Vec::new();
3997 for v in &vt.vars {
3998 if let JavaType::Object(n) = v.ty.erased() {
3999 if n.as_ref() == cls.as_ref()
4000 || pool.is_subtype(n.as_ref(), cls.as_ref())
4001 {
4002 cands.push(v.id);
4003 }
4004 }
4005 }
4006 let id = if cands.len() == 1 {
4007 cands[0]
4008 } else if cands.len() > 1 {
4009 let slot = if (recv_var as usize) < vt.vars.len() {
4014 Some(vt.var(recv_var).slot)
4015 } else {
4016 None
4017 };
4018 let mut hits: Vec<u32> = match slot {
4019 Some(sl) => cands
4020 .iter()
4021 .copied()
4022 .filter(|&c| {
4023 c != recv_var
4024 && (c as usize) < vt.vars.len()
4025 && vt.var(c).slot == sl
4026 })
4027 .collect(),
4028 None => Vec::new(),
4029 };
4030 if hits.is_empty() {
4031 hits = cands
4034 .iter()
4035 .copied()
4036 .filter(|&c| c != recv_var)
4037 .collect();
4038 }
4039 hits.sort_unstable();
4040 hits.dedup();
4041 if hits.is_empty() {
4042 return;
4043 }
4044 *hits.last().unwrap()
4045 } else {
4046 if cands.is_empty() {
4053 return;
4054 }
4055 let slot = if (recv_var as usize) < vt.vars.len() {
4056 vt.var(recv_var).slot
4057 } else {
4058 return;
4059 };
4060 let mut hits: Vec<u32> = cands
4061 .into_iter()
4062 .filter(|&c| {
4063 c != recv_var
4064 && (c as usize) < vt.vars.len()
4065 && vt.var(c).slot == slot
4066 })
4067 .collect();
4068 hits.sort_unstable();
4069 hits.dedup();
4070 if hits.len() != 1 {
4071 return;
4072 }
4073 hits[0]
4074 };
4075 let o: &mut Box<Expr> = match x {
4077 Expr::Method { owner: Some(o), .. } => o,
4078 Expr::Field { owner: Some(o), is_static: false, .. } => o,
4079 _ => return,
4080 };
4081 if let Expr::Local { var, ty } = &mut **o {
4082 if *var == recv_var {
4083 *var = id;
4084 *ty = vt.vars[id as usize].ty.clone();
4085 }
4086 }
4087 });
4088 });
4089
4090}
4091
4092pub fn rescue_arg_return_swaps(
4098 body: &mut Stmt,
4099 vt: &VarTable,
4100 pool: &crate::DexPool,
4101 ret_ty: &JavaType,
4102) {
4103 let mut top_ids: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
4111 for v in &vt.vars {
4112 if v.is_param {
4113 top_ids.insert(v.id);
4114 }
4115 }
4116 if let Stmt::Block(stmts) = body {
4117 for st in stmts.iter() {
4118 if let Stmt::LocalDef { var, .. } = st {
4119 top_ids.insert(*var);
4120 }
4121 }
4122 }
4123 let ret_wants_ref = match ret_ty {
4124 JavaType::Object(n) => n.as_ref() != "java/lang/Object",
4125 JavaType::Array(_) => true,
4126 _ => false,
4127 };
4128 let try_swap = |a: &mut Expr, want: &JavaType| {
4129 let Expr::Local { var, ty } = &mut *a else {
4130 return;
4131 };
4132 if (*var as usize) >= vt.vars.len() {
4133 return;
4134 }
4135 let avt = vt.var(*var).ty.erased();
4136 let avt_ref = matches!(avt, JavaType::Object(_) | JavaType::Array(_));
4137 let want_arr = matches!(want, JavaType::Array(_));
4138 if avt_ref && !(avt == JavaType::Object("java/lang/Object".into())) {
4141 return;
4142 }
4143 if avt_ref && !matches!(want, JavaType::Object(_)) {
4144 return;
4145 }
4146 let slot = vt.var(*var).slot;
4147 let mut hits: Vec<u32> = Vec::new();
4148 for v in &vt.vars {
4149 if v.slot != slot || !top_ids.contains(&v.id) || v.id == *var {
4150 continue;
4151 }
4152 let t = v.ty.erased();
4153 let ok = match (&t, want) {
4154 (JavaType::Object(n), JavaType::Object(w)) => {
4155 n.as_ref() == w.as_ref()
4156 || pool.is_subtype(n.as_ref(), w.as_ref())
4157 }
4158 (JavaType::Array(_), JavaType::Array(_)) => want_arr,
4159 _ => false,
4160 };
4161 if ok {
4162 hits.push(v.id);
4163 }
4164 }
4165 hits.sort_unstable();
4166 hits.dedup();
4167 if hits.is_empty() {
4168 return;
4169 }
4170 let id = *hits.last().unwrap();
4176 *var = id;
4177 *ty = vt.vars[id as usize].ty.clone();
4178 };
4179 walk_stmt_exprs(body, &mut |e| {
4180 deep_rewrite(e, &mut |x| {
4181 match x {
4182 Expr::Method { desc, args, is_dynamic: false, .. } => {
4183 for (i, a) in args.iter_mut().enumerate() {
4184 if let Some(f) = desc.args.get(i) {
4185 if matches!(f, JavaType::Object(n) if n.as_ref() != "java/lang/Object")
4186 || matches!(f, JavaType::Array(_))
4187 {
4188 try_swap(a, f);
4189 }
4190 }
4191 }
4192 }
4193 Expr::New { arg_tys, args, .. } => {
4194 for (i, a) in args.iter_mut().enumerate() {
4195 if let Some(f) = arg_tys.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 _ => {}
4205 }
4206 });
4207 });
4208 if ret_wants_ref {
4209 walk_mut_deep(body, &mut |st| {
4210 if let Stmt::Return(Some(e)) = st {
4211 try_swap(e, ret_ty);
4212 }
4213 });
4214 }
4215}
4216
4217pub fn fix_ref_null_assigns(vt: &VarTable, body: &mut Stmt) {
4232 let is_ref_var = |var: u32| -> bool {
4233 (var as usize) < vt.vars.len()
4234 && matches!(
4235 vt.vars[var as usize].ty.erased(),
4236 JavaType::Object(_) | JavaType::Array(_)
4237 )
4238 };
4239 walk_mut_deep(body, &mut |st| match st {
4240 Stmt::ExprStmt(Expr::Assign {
4241 target,
4242 value,
4243 op: AssignOp::Plain,
4244 ..
4245 }) => {
4246 if let Expr::Local { var, .. } = &**target {
4247 if is_ref_var(*var) && matches!(&**value, Expr::Const(ConstVal::Int(0))) {
4248 **value = Expr::Const(ConstVal::Null);
4249 }
4250 }
4251 }
4252 Stmt::LocalDef {
4253 var,
4254 init: Some(e),
4255 ..
4256 } => {
4257 if is_ref_var(*var) && matches!(e, Expr::Const(ConstVal::Int(0))) {
4258 *e = Expr::Const(ConstVal::Null);
4259 }
4260 }
4261 _ => {}
4262 });
4263}
4264
4265pub fn fix_ref_array_null_consts(body: &mut Stmt) {
4266 walk_stmt_exprs(body, &mut |e| {
4267 deep_rewrite(e, &mut |x| {
4268 if let Expr::NewArray { elem, init: Some(list), .. } = x {
4269 if matches!(elem.erased(), JavaType::Object(_) | JavaType::Array(_)) {
4270 for slot in list.iter_mut() {
4271 if matches!(&*slot, Expr::Const(ConstVal::Int(0))) {
4272 *slot = Expr::Const(ConstVal::Null);
4273 }
4274 }
4275 }
4276 }
4277 });
4278 });
4279}
4280
4281pub fn fix_null_sentinels(body: &mut Stmt, vt: &VarTable, ret: &JavaType) {
4320 #[derive(Clone, Copy, PartialEq)]
4321 enum WK {
4322 Zero,
4323 Copy(u32),
4324 Other,
4325 }
4326 fn is_ref_jt(t: &JavaType) -> bool {
4327 matches!(t, JavaType::Object(_) | JavaType::Array(_))
4328 }
4329 fn cls_write(e: &Expr) -> WK {
4330 match e {
4331 Expr::Const(ConstVal::Int(0)) => WK::Zero,
4332 Expr::Local { var, .. } => WK::Copy(*var),
4333 _ => WK::Other,
4334 }
4335 }
4336 fn expr_writes(e: &Expr, writes: &mut std::collections::HashMap<u32, Vec<WK>>) {
4337 visit_exprs(e, &mut |x| match x {
4338 Expr::Assign { target, op, value } => {
4339 if let Expr::Local { var, .. } = &**target {
4340 let k = if matches!(op, AssignOp::Plain) {
4341 cls_write(value)
4342 } else {
4343 WK::Other
4344 };
4345 writes.entry(*var).or_default().push(k);
4346 }
4347 }
4348 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
4349 if let Expr::Local { var, .. } = &**inner {
4350 writes.entry(*var).or_default().push(WK::Other);
4351 }
4352 }
4353 _ => {}
4354 });
4355 }
4356 fn collect_writes(
4357 s: &Stmt,
4358 writes: &mut std::collections::HashMap<u32, Vec<WK>>,
4359 has_zero: &mut bool,
4360 ) {
4361 fn note(e: &Expr, has_zero: &mut bool, writes: &mut std::collections::HashMap<u32, Vec<WK>>) {
4362 visit_exprs(e, &mut |x| {
4363 if let Expr::Const(ConstVal::Int(0)) = x {
4364 *has_zero = true;
4365 }
4366 });
4367 expr_writes(e, writes);
4368 }
4369 match s {
4370 Stmt::Block(v) => {
4371 for x in v {
4372 collect_writes(x, writes, has_zero);
4373 }
4374 }
4375 Stmt::LocalDef { var, init, .. } => {
4376 if let Some(e) = init {
4377 if matches!(e, Expr::Const(ConstVal::Int(0))) {
4378 *has_zero = true;
4379 }
4380 writes.entry(*var).or_default().push(cls_write(e));
4381 }
4382 }
4383 Stmt::ExprStmt(e) => note(e, has_zero, writes),
4384 Stmt::Return(e) => {
4385 if let Some(x) = e {
4386 note(x, has_zero, writes);
4387 }
4388 }
4389 Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
4390 note(e, has_zero, writes);
4391 }
4392 Stmt::TernaryValue { e } => note(e, has_zero, writes),
4393 Stmt::If { cond, then_stmt, else_stmt } => {
4394 note(cond, has_zero, writes);
4395 collect_writes(then_stmt, writes, has_zero);
4396 if let Some(b) = else_stmt {
4397 collect_writes(b, writes, has_zero);
4398 }
4399 }
4400 Stmt::While { cond, body } | Stmt::DoWhile { body, cond } => {
4401 note(cond, has_zero, writes);
4402 collect_writes(body, writes, has_zero);
4403 }
4404 Stmt::For { init, cond, update, body } => {
4405 for x in init {
4406 collect_writes(x, writes, has_zero);
4407 }
4408 if let Some(c) = cond {
4409 note(c, has_zero, writes);
4410 }
4411 for u in update {
4412 note(u, has_zero, writes);
4413 }
4414 collect_writes(body, writes, has_zero);
4415 }
4416 Stmt::ForEach { var, iterable, body, .. } => {
4417 writes.entry(*var).or_default().push(WK::Other);
4418 note(iterable, has_zero, writes);
4419 collect_writes(body, writes, has_zero);
4420 }
4421 Stmt::Switch { selector, cases, default, .. } => {
4422 note(selector, has_zero, writes);
4423 for c in cases {
4424 if let Some(g) = &c.guard {
4425 note(g, has_zero, writes);
4426 }
4427 for x in &c.body {
4428 collect_writes(x, writes, has_zero);
4429 }
4430 }
4431 if let Some(d) = default {
4432 collect_writes(d, writes, has_zero);
4433 }
4434 }
4435 Stmt::Try { body, catches, finally } => {
4436 collect_writes(body, writes, has_zero);
4437 for c in catches {
4438 writes.entry(c.var).or_default().push(WK::Other);
4439 collect_writes(&c.body, writes, has_zero);
4440 }
4441 if let Some(f) = finally {
4442 collect_writes(f, writes, has_zero);
4443 }
4444 }
4445 Stmt::TryWithResources { resources, body, catches, finally, .. } => {
4446 for r in resources {
4447 collect_writes(r, writes, has_zero);
4448 }
4449 collect_writes(body, writes, has_zero);
4450 for c in catches {
4451 writes.entry(c.var).or_default().push(WK::Other);
4452 collect_writes(&c.body, writes, has_zero);
4453 }
4454 if let Some(f) = finally {
4455 collect_writes(f, writes, has_zero);
4456 }
4457 }
4458 Stmt::Assert { cond, msg } => {
4459 note(cond, has_zero, writes);
4460 if let Some(m) = msg {
4461 note(m, has_zero, writes);
4462 }
4463 }
4464 Stmt::Synchronized { lock, body } => {
4465 note(lock, has_zero, writes);
4466 collect_writes(body, writes, has_zero);
4467 }
4468 Stmt::Labeled { body, .. } => collect_writes(body, writes, has_zero),
4469 _ => {}
4470 }
4471 }
4472
4473 let mut writes: std::collections::HashMap<u32, Vec<WK>> =
4474 std::collections::HashMap::new();
4475 let mut has_zero = false;
4476 collect_writes(body, &mut writes, &mut has_zero);
4477
4478 let mut eligible: HashSet<u32> = HashSet::default();
4480 let int_typed = |v: u32| {
4481 (v as usize) < vt.vars.len() && vt.vars[v as usize].ty.erased() == JavaType::Int
4482 };
4483 loop {
4484 let mut changed = false;
4485 for (&v, ws) in writes.iter() {
4486 if eligible.contains(&v) || ws.is_empty() || !int_typed(v) {
4487 continue;
4488 }
4489 if ws.iter().all(|k| match k {
4490 WK::Zero => true,
4491 WK::Copy(u) => *u != v && eligible.contains(u),
4492 WK::Other => false,
4493 }) {
4494 eligible.insert(v);
4495 changed = true;
4496 }
4497 }
4498 if !changed {
4499 break;
4500 }
4501 }
4502 if eligible.is_empty() && !has_zero {
4503 return; }
4505
4506 #[derive(Clone, Copy, PartialEq)]
4507 enum Mode {
4508 Scan,
4509 Rewrite,
4510 }
4511 struct St<'a> {
4512 mode: Mode,
4513 vt: &'a VarTable,
4514 eligible: &'a HashSet<u32>,
4515 ref_read: HashSet<u32>,
4516 dead: HashSet<u32>,
4517 sentinels: HashSet<u32>,
4518 n_lit: usize,
4519 n_var: usize,
4520 }
4521 fn arg_ctx(t: &JavaType) -> Option<JavaType> {
4525 match t {
4526 JavaType::Object(n) if n.as_ref() == "java/lang/Object" => None,
4527 t if is_ref_jt(t) => Some(t.clone()),
4528 _ => None,
4529 }
4530 }
4531 fn walk_expr(e: &mut Expr, ctx: Option<JavaType>, st: &mut St) {
4532 walk_expr_c(e, ctx, false, st);
4533 }
4534 fn walk_expr_c(e: &mut Expr, ctx: Option<JavaType>, cast_arg: bool, st: &mut St) {
4540 let slot_ref = ctx.as_ref().is_some_and(is_ref_jt);
4541 fn mk_null(ctx: &Option<JavaType>, cast_arg: bool) -> Expr {
4542 match (cast_arg, ctx) {
4543 (true, Some(t)) => Expr::Cast {
4544 ty: TypeRef::J(t.clone()),
4545 e: Box::new(Expr::Const(ConstVal::Null)),
4546 },
4547 _ => Expr::Const(ConstVal::Null),
4548 }
4549 }
4550 match e {
4551 Expr::Const(ConstVal::Int(0)) if slot_ref => {
4552 if st.mode == Mode::Rewrite {
4553 *e = mk_null(&ctx, cast_arg);
4554 st.n_lit += 1;
4555 }
4556 return;
4557 }
4558 Expr::Local { var, .. } => {
4559 if st.eligible.contains(var) {
4560 if slot_ref {
4561 st.ref_read.insert(*var);
4562 } else {
4563 st.dead.insert(*var);
4564 }
4565 }
4566 if st.mode == Mode::Rewrite && slot_ref && st.sentinels.contains(var) {
4567 *e = mk_null(&ctx, cast_arg);
4568 st.n_var += 1;
4569 }
4570 return;
4571 }
4572 _ => {}
4573 }
4574 match e {
4575 Expr::Method { owner, desc, args, .. } => {
4576 if let Some(o) = owner {
4577 walk_expr(o, None, st);
4578 }
4579 for (i, a) in args.iter_mut().enumerate() {
4580 let c = desc.args.get(i).and_then(arg_ctx);
4581 walk_expr_c(a, c, true, st);
4582 }
4583 }
4584 Expr::Invokedynamic { desc, args, .. } => {
4585 for (i, a) in args.iter_mut().enumerate() {
4586 let c = desc.args.get(i).and_then(arg_ctx);
4587 walk_expr_c(a, c, true, st);
4588 }
4589 }
4590 Expr::New { args, arg_tys, .. } => {
4591 for (i, a) in args.iter_mut().enumerate() {
4592 let c = arg_tys.get(i).and_then(arg_ctx);
4593 walk_expr_c(a, c, true, st);
4594 }
4595 }
4596 Expr::AnonNew { args, .. } => {
4597 for a in args.iter_mut() {
4598 walk_expr(a, None, st);
4599 }
4600 }
4601 Expr::Assign { target, value, .. } => {
4602 if let Expr::Local { var: u, .. } = &**target {
4606 if st.eligible.contains(u) {
4607 return;
4608 }
4609 }
4610 let vctx = match &**target {
4611 Expr::Field { ty, .. } => {
4612 let t = ty.erased();
4613 is_ref_jt(&t).then_some(t)
4614 }
4615 Expr::ArrayIndex { array, .. } => match array.type_ref().erased() {
4616 JavaType::Array(inner) if is_ref_jt(&inner) => Some(*inner),
4617 _ => None,
4618 },
4619 _ => None,
4620 };
4621 walk_expr(target, None, st);
4622 walk_expr(value, vctx, st);
4623 }
4624 Expr::Cast { ty, e: inner } => {
4625 let t = ty.erased();
4626 let c = is_ref_jt(&t).then_some(t);
4627 walk_expr(inner, c, st);
4628 }
4629 Expr::InstanceOf { e: inner, .. } => {
4630 walk_expr(inner, Some(JavaType::Object("java/lang/Object".into())), st);
4631 }
4632 Expr::Cond { c, t, f } => {
4633 walk_expr(c, None, st);
4634 let tc = ctx.clone();
4635 walk_expr(t, tc, st);
4636 walk_expr(f, ctx, st);
4637 }
4638 Expr::NewArray { elem, dims, init, .. } => {
4639 for d in dims.iter_mut() {
4640 walk_expr(d, None, st);
4641 }
4642 if let Some(list) = init {
4643 let et = elem.erased();
4644 let c = is_ref_jt(&et).then_some(et);
4645 for slot in list.iter_mut() {
4646 walk_expr(slot, c.clone(), st);
4647 }
4648 }
4649 }
4650 Expr::NewMultiArray { dims, .. } => {
4651 for d in dims.iter_mut() {
4652 walk_expr(d, None, st);
4653 }
4654 }
4655 Expr::ArrayIndex { array, index } => {
4656 walk_expr(array, None, st);
4657 walk_expr(index, None, st);
4658 }
4659 Expr::Field { owner, .. } => {
4660 if let Some(o) = owner {
4661 walk_expr(o, None, st);
4662 }
4663 }
4664 Expr::Bin { l, r, .. } => {
4665 walk_expr(l, None, st);
4666 walk_expr(r, None, st);
4667 }
4668 Expr::Un { e: inner, .. } => walk_expr(inner, None, st),
4669 Expr::PreIncDec { e: inner, .. } | Expr::PostIncDec { e: inner, .. } => {
4670 walk_expr(inner, None, st);
4671 }
4672 Expr::StringConcat(parts) => {
4673 for p in parts.iter_mut() {
4674 if let ConcatPart::Str(x) = p {
4675 walk_expr(x, None, st);
4676 }
4677 }
4678 }
4679 Expr::Lambda(l) => {
4680 for c in l.captures.iter_mut() {
4681 walk_expr(c, None, st);
4682 }
4683 }
4684 _ => {}
4685 }
4686 }
4687 fn walk_stmt(s: &mut Stmt, st: &mut St, ret: &JavaType) {
4688 match s {
4689 Stmt::Block(v) => {
4690 for x in v.iter_mut() {
4691 walk_stmt(x, st, ret);
4692 }
4693 }
4694 Stmt::ExprStmt(e) => walk_expr(e, None, st),
4695 Stmt::LocalDef { var, init, .. } => {
4696 if let Some(e) = init {
4697 let ctx = if (*var as usize) < st.vt.vars.len() {
4698 let t = st.vt.vars[*var as usize].ty.erased();
4699 is_ref_jt(&t).then_some(t)
4700 } else {
4701 None
4702 };
4703 walk_expr(e, ctx, st);
4704 }
4705 }
4706 Stmt::Return(e) => {
4707 if let Some(x) = e {
4708 let c = is_ref_jt(ret).then(|| ret.clone());
4709 walk_expr(x, c, st);
4710 }
4711 }
4712 Stmt::Throw(e) => {
4713 walk_expr(e, Some(JavaType::Object("java/lang/Throwable".into())), st);
4714 }
4715 Stmt::If { cond, then_stmt, else_stmt } => {
4716 walk_expr(cond, None, st);
4717 walk_stmt(then_stmt, st, ret);
4718 if let Some(b) = else_stmt {
4719 walk_stmt(b, st, ret);
4720 }
4721 }
4722 Stmt::While { cond, body } => {
4723 walk_expr(cond, None, st);
4724 walk_stmt(body, st, ret);
4725 }
4726 Stmt::DoWhile { body, cond } => {
4727 walk_stmt(body, st, ret);
4728 walk_expr(cond, None, st);
4729 }
4730 Stmt::For { init, cond, update, body } => {
4731 for x in init.iter_mut() {
4732 walk_stmt(x, st, ret);
4733 }
4734 if let Some(c) = cond {
4735 walk_expr(c, None, st);
4736 }
4737 for u in update.iter_mut() {
4738 walk_expr(u, None, st);
4739 }
4740 walk_stmt(body, st, ret);
4741 }
4742 Stmt::ForEach { iterable, body, .. } => {
4743 walk_expr(iterable, None, st);
4744 walk_stmt(body, st, ret);
4745 }
4746 Stmt::Switch { selector, cases, default, .. } => {
4747 walk_expr(selector, None, st);
4748 for c in cases.iter_mut() {
4749 if let Some(g) = &mut c.guard {
4750 walk_expr(g, None, st);
4751 }
4752 for x in c.body.iter_mut() {
4753 walk_stmt(x, st, ret);
4754 }
4755 }
4756 if let Some(d) = default {
4757 walk_stmt(d, st, ret);
4758 }
4759 }
4760 Stmt::Try { body, catches, finally } => {
4761 walk_stmt(body, st, ret);
4762 for c in catches.iter_mut() {
4763 walk_stmt(&mut c.body, st, ret);
4764 }
4765 if let Some(f) = finally {
4766 walk_stmt(f, st, ret);
4767 }
4768 }
4769 Stmt::TryWithResources { resources, body, catches, finally, .. } => {
4770 for r in resources.iter_mut() {
4771 walk_stmt(r, st, ret);
4772 }
4773 walk_stmt(body, st, ret);
4774 for c in catches.iter_mut() {
4775 walk_stmt(&mut c.body, st, ret);
4776 }
4777 if let Some(f) = finally {
4778 walk_stmt(f, st, ret);
4779 }
4780 }
4781 Stmt::Assert { cond, msg } => {
4782 walk_expr(cond, None, st);
4783 if let Some(m) = msg {
4784 walk_expr(m, None, st);
4785 }
4786 }
4787 Stmt::Synchronized { lock, body } => {
4788 walk_expr(lock, Some(JavaType::Object("java/lang/Object".into())), st);
4789 walk_stmt(body, st, ret);
4790 }
4791 Stmt::TernaryValue { e } => walk_expr(e, None, st),
4792 Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
4793 walk_expr(e, Some(JavaType::Object("java/lang/Object".into())), st);
4794 }
4795 Stmt::Labeled { body, .. } => walk_stmt(body, st, ret),
4796 _ => {}
4797 }
4798 }
4799
4800 let mut st = St {
4801 mode: Mode::Scan,
4802 vt,
4803 eligible: &eligible,
4804 ref_read: HashSet::default(),
4805 dead: HashSet::default(),
4806 sentinels: HashSet::default(),
4807 n_lit: 0,
4808 n_var: 0,
4809 };
4810 if !eligible.is_empty() {
4811 walk_stmt(body, &mut st, ret);
4812 st.sentinels = eligible
4813 .iter()
4814 .copied()
4815 .filter(|v| st.ref_read.contains(v) && !st.dead.contains(v))
4816 .collect();
4817 }
4818 if st.sentinels.is_empty() && !has_zero {
4819 return;
4820 }
4821 st.mode = Mode::Rewrite;
4822 walk_stmt(body, &mut st, ret);
4823 if std::env::var_os("DDC_SENT").is_some() {
4824 eprintln!(
4825 "[sent] elig={} confirmed={} lit={} var={}",
4826 eligible.len(),
4827 st.sentinels.len(),
4828 st.n_lit,
4829 st.n_var
4830 );
4831 }
4832}
4833
4834pub fn fix_int_operand_bridges(body: &mut Stmt, vt: &VarTable) {
4842 fn side_bool(e: &Expr, vt: &VarTable) -> bool {
4843 matches!(
4844 match e {
4845 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4846 other => other.type_ref().erased(),
4847 },
4848 JavaType::Boolean
4849 )
4850 }
4851 fn side_int(e: &Expr, vt: &VarTable) -> bool {
4852 matches!(
4853 match e {
4854 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
4855 other => other.type_ref().erased(),
4856 },
4857 JavaType::Int | JavaType::Short | JavaType::Byte | JavaType::Char | JavaType::Long
4858 )
4859 }
4860 fn wrap(e: Box<Expr>) -> Box<Expr> {
4861 Box::new(Expr::Cond {
4862 c: e,
4863 t: Box::new(Expr::Const(ConstVal::Int(1))),
4864 f: Box::new(Expr::Const(ConstVal::Int(0))),
4865 })
4866 }
4867 walk_stmt_exprs(body, &mut |e| {
4868 deep_rewrite(e, &mut |x| {
4869 if let Expr::Assign { target, value, op: AssignOp::Plain } = x {
4876 let tgt_ty: Option<JavaType> = match &**target {
4877 Expr::Field { ty, .. } => Some(ty.erased()),
4878 Expr::ArrayIndex { array, .. } => {
4879 match array.type_ref().erased() {
4880 JavaType::Array(el) => Some(el.as_ref().clone()),
4881 _ => None,
4882 }
4883 }
4884 Expr::Local { var, .. } => {
4889 if (*var as usize) < vt.vars.len() {
4890 Some(vt.var(*var).ty.erased())
4891 } else {
4892 None
4893 }
4894 }
4895 _ => None,
4896 };
4897 if let Some(t) = tgt_ty {
4898 let tgt_int = matches!(
4899 t,
4900 JavaType::Int
4901 | JavaType::Short
4902 | JavaType::Byte
4903 | JavaType::Char
4904 | JavaType::Long
4905 );
4906 if tgt_int && side_bool(value, vt) && !side_int(value, vt) {
4907 let taken = std::mem::replace(
4908 value,
4909 Box::new(Expr::Const(ConstVal::Null)),
4910 );
4911 let mut wrapped = wrap(taken);
4912 if !matches!(t, JavaType::Int | JavaType::Long) {
4913 wrapped = Box::new(Expr::Cast {
4914 ty: TypeRef::J(t.clone()),
4915 e: wrapped,
4916 });
4917 }
4918 *value = wrapped;
4919 } else if matches!(t, JavaType::Boolean)
4920 && side_int(value, vt)
4921 && !side_bool(value, vt)
4922 {
4923 let taken = std::mem::replace(
4924 value,
4925 Box::new(Expr::Const(ConstVal::Null)),
4926 );
4927 **value = Expr::Bin {
4928 op: BinOp::Ne,
4929 l: taken,
4930 r: Box::new(Expr::Const(ConstVal::Int(0))),
4931 ty: Some(TypeRef::J(JavaType::Boolean)),
4932 };
4933 }
4934 }
4935 }
4936 if let Expr::Cast { ty, e } = x {
4942 let t = ty.erased();
4943 let t_num = matches!(
4944 t,
4945 JavaType::Int
4946 | JavaType::Short
4947 | JavaType::Byte
4948 | JavaType::Char
4949 | JavaType::Long
4950 | JavaType::Float
4951 | JavaType::Double
4952 );
4953 if t_num && side_bool(e, vt) && !side_int(e, vt) {
4954 let taken = std::mem::replace(e, Box::new(Expr::Const(ConstVal::Null)));
4955 **e = Expr::Cond {
4956 c: taken,
4957 t: Box::new(Expr::Const(ConstVal::Int(1))),
4958 f: Box::new(Expr::Const(ConstVal::Int(0))),
4959 };
4960 } else if matches!(t, JavaType::Boolean)
4961 && side_int(e, vt)
4962 && !side_bool(e, vt)
4963 {
4964 let taken = std::mem::replace(e, Box::new(Expr::Const(ConstVal::Null)));
4965 *x = Expr::Bin {
4966 op: BinOp::Ne,
4967 l: taken,
4968 r: Box::new(Expr::Const(ConstVal::Int(0))),
4969 ty: Some(TypeRef::J(JavaType::Boolean)),
4970 };
4971 }
4972 }
4973 if let Expr::Bin { op, l, r, .. } = x {
4979 if matches!(op, BinOp::Eq | BinOp::Ne) {
4980 if matches!(&**r, Expr::Const(ConstVal::Null))
4981 && side_int(l, vt)
4982 && !side_bool(l, vt)
4983 {
4984 **r = Expr::Const(ConstVal::Int(0));
4985 } else if matches!(&**l, Expr::Const(ConstVal::Null))
4986 && side_int(r, vt)
4987 && !side_bool(r, vt)
4988 {
4989 **l = Expr::Const(ConstVal::Int(0));
4990 }
4991 }
4992 }
4993 if let Expr::ArrayIndex { index, .. } = x {
4997 if side_bool(index, vt) && !side_int(index, vt) {
4998 let taken =
4999 std::mem::replace(index, Box::new(Expr::Const(ConstVal::Null)));
5000 *index = wrap(taken);
5001 }
5002 }
5003 if let Expr::NewArray { dims, .. } = x {
5009 for d in dims.iter_mut() {
5010 if side_bool(d, vt) && !side_int(d, vt) {
5011 let taken =
5012 std::mem::replace(d, Expr::Const(ConstVal::Null));
5013 *d = *wrap(Box::new(taken));
5014 }
5015 }
5016 }
5017 if let Expr::Bin { op, l, r, .. } = x {
5018 let int_kind = matches!(
5019 op,
5020 BinOp::Or
5021 | BinOp::And
5022 | BinOp::Xor
5023 | BinOp::Add
5024 | BinOp::Sub
5025 | BinOp::Mul
5026 | BinOp::Div
5027 | BinOp::Rem
5028 | BinOp::Shl
5029 | BinOp::Shr
5030 | BinOp::Ushr
5031 | BinOp::Eq
5032 | BinOp::Ne
5033 | BinOp::Lt
5034 | BinOp::Ge
5035 | BinOp::Gt
5036 | BinOp::Le
5037 );
5038 if !int_kind {
5039 return;
5040 }
5041 if side_bool(l, vt) && side_int(r, vt) && !side_bool(r, vt) {
5044 let taken = std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
5045 *l = wrap(taken);
5046 } else if side_bool(r, vt) && side_int(l, vt) && !side_bool(l, vt) {
5047 let taken = std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
5048 *r = wrap(taken);
5049 }
5050 }
5051 });
5052 });
5053}
5054
5055pub fn fold_value_diamonds(body: &mut Stmt) {
5093 let Stmt::Block(stmts) = body else { return };
5094 fold_diamonds_in(stmts);
5095}
5096
5097fn diamond_types_ok(a: &Expr, b: &Expr) -> bool {
5098 fn num(t: &JavaType) -> bool {
5099 matches!(
5100 t,
5101 JavaType::Byte
5102 | JavaType::Short
5103 | JavaType::Int
5104 | JavaType::Char
5105 | JavaType::Long
5106 | JavaType::Float
5107 | JavaType::Double
5108 )
5109 }
5110 let ta = a.type_ref().erased();
5111 let tb = b.type_ref().erased();
5112 ta == tb || (num(&ta) && num(&tb))
5113}
5114
5115fn single_local_assign(st: &Stmt) -> Option<(u32, Expr, Expr)> {
5123 let mut inner = st;
5124 while let Stmt::Block(v) = inner {
5125 if v.len() != 1 {
5126 return None;
5127 }
5128 inner = &v[0];
5129 }
5130 match inner {
5131 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain }) => {
5132 match &**target {
5133 Expr::Local { var, .. } => Some((*var, *target.clone(), *value.clone())),
5134 _ => None,
5135 }
5136 }
5137 _ => None,
5138 }
5139}
5140
5141fn mk_ternary_assign(tgt: Expr, c: Expr, a: Expr, b: Expr) -> Stmt {
5142 Stmt::ExprStmt(Expr::Assign {
5143 target: Box::new(tgt),
5144 op: AssignOp::Plain,
5145 value: Box::new(Expr::Cond {
5146 c: Box::new(c),
5147 t: Box::new(a),
5148 f: Box::new(b),
5149 }),
5150 })
5151}
5152
5153fn fold_diamonds_in(stmts: &mut Vec<Stmt>) {
5154 let mut i = 0usize;
5155 while i < stmts.len() {
5156 match &mut stmts[i] {
5157 Stmt::Block(v) => fold_diamonds_in(v),
5158 Stmt::If { then_stmt, else_stmt, .. } => {
5159 if let Stmt::Block(v) = &mut **then_stmt {
5160 fold_diamonds_in(v);
5161 }
5162 if let Some(e) = else_stmt {
5163 if let Stmt::Block(v) = &mut **e {
5164 fold_diamonds_in(v);
5165 }
5166 }
5167 }
5168 _ => {}
5169 }
5170 let shape1 = if let Stmt::If { cond, then_stmt, else_stmt: Some(el), .. } = &stmts[i] {
5172 match (single_local_assign(then_stmt), single_local_assign(el)) {
5173 (Some((v1, tgt, a)), Some((v2, _, b)))
5174 if v1 == v2 && diamond_types_ok(&a, &b) =>
5175 {
5176 Some(mk_ternary_assign(tgt, cond.clone(), a, b))
5177 }
5178 _ => None,
5179 }
5180 } else {
5181 None
5182 };
5183 if let Some(f) = shape1 {
5184 stmts[i] = f;
5185 i += 1;
5186 continue;
5187 }
5188 if i + 1 < stmts.len() {
5190 let shape2 = if let Stmt::If { cond, then_stmt, else_stmt: None, .. } = &stmts[i + 1] {
5191 match (single_local_assign(&stmts[i]), single_local_assign(then_stmt)) {
5192 (Some((v1, tgt, b)), Some((v2, _, a)))
5193 if v1 == v2 && diamond_types_ok(&a, &b) =>
5194 {
5195 Some(mk_ternary_assign(tgt, cond.clone(), a, b))
5196 }
5197 _ => None,
5198 }
5199 } else {
5200 None
5201 };
5202 if let Some(f) = shape2 {
5203 stmts[i] = f;
5204 stmts.remove(i + 1);
5205 continue; }
5207 }
5208 i += 1;
5209 }
5210}
5211
5212pub fn fold_bool_value_diamonds(body: &mut Stmt, vt: &VarTable) {
5213 fn is_bool_var(v: u32, vt: &VarTable) -> bool {
5214 (v as usize) < vt.vars.len()
5215 && matches!(vt.vars[v as usize].ty.erased(), JavaType::Boolean)
5216 }
5217 fn bool_const(e: &Expr) -> Option<bool> {
5218 fn cint(e: &Expr) -> Option<i32> {
5219 match e {
5220 Expr::Const(ConstVal::Int(n)) => Some(*n),
5221 _ => None,
5222 }
5223 }
5224 match e {
5225 Expr::Const(ConstVal::Int(0)) => Some(false),
5226 Expr::Const(ConstVal::Int(1)) => Some(true),
5227 Expr::Bin { op: op @ (BinOp::Eq | BinOp::Ne), l, r, .. } => {
5231 let (a, b) = (cint(l)?, cint(r)?);
5232 let eq = a == b;
5233 Some(if matches!(op, BinOp::Eq) { eq } else { !eq })
5234 }
5235 _ => None,
5236 }
5237 }
5238 fn reads_var(e: &Expr, v: u32) -> bool {
5239 let mut hit = false;
5240 visit_exprs(e, &mut |x| {
5241 if let Expr::Local { var, .. } = x {
5242 if *var == v {
5243 hit = true;
5244 }
5245 }
5246 });
5247 hit
5248 }
5249 fn bool_valued(e: &Expr, vt: &VarTable) -> bool {
5253 fn side(e: &Expr, vt: &VarTable) -> bool {
5254 bool_const(e).is_some() || bool_valued(e, vt)
5255 }
5256 match e {
5257 Expr::Local { var, .. } => is_bool_var(*var, vt),
5258 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
5259 Expr::InstanceOf { .. } => true,
5260 Expr::Un { op: UnOp::Not, e: x, .. } => bool_valued(x, vt) || bool_const(x).is_some(),
5261 Expr::Bin { op, l, r, .. } => match op {
5262 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le => true,
5263 BinOp::LogAnd | BinOp::LogOr => {
5266 side(l, vt) && side(r, vt)
5267 }
5268 _ => false,
5269 },
5270 Expr::Cond { t, f, .. } => side(t, vt) && side(f, vt),
5271 _ => false,
5272 }
5273 }
5274 fn lone_assign(
5283 b: &Stmt,
5284 v: u32,
5285 vt: &VarTable,
5286 an: &VarAnalysis,
5287 consumed: &mut HashSet<u32>,
5288 ) -> Option<Expr> {
5289 let l = flat_list(b);
5290 if l.len() == 2 {
5291 let (u, e): (u32, &Expr) = match l[0] {
5292 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e),
5293 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
5294 match &**target {
5295 Expr::Local { var, .. } => (*var, &**value),
5296 _ => return None,
5297 }
5298 }
5299 _ => return None,
5300 };
5301 if u == v || consumed.contains(&u) {
5302 return None;
5303 }
5304 if an.assigns.get(u as usize).copied().unwrap_or(0) != 1
5311 || an.reads.get(u as usize).copied().unwrap_or(0) != 1
5312 {
5313 return None;
5314 }
5315 let (tv, rv) = match l[1] {
5316 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
5317 match (&**target, &**value) {
5318 (Expr::Local { var: t, .. }, Expr::Local { var: r, .. }) => (*t, *r),
5319 _ => return None,
5320 }
5321 }
5322 Stmt::LocalDef { var, init: Some(Expr::Local { var: r, .. }), .. } => (*var, *r),
5323 _ => return None,
5324 };
5325 if tv != v || rv != u {
5326 return None;
5327 }
5328 if reads_var(e, v) || !(bool_valued(e, vt) || bool_const(e).is_some()) {
5329 return None;
5330 }
5331 consumed.insert(u);
5332 return Some(e.clone());
5333 }
5334 if l.len() != 1 {
5335 return None;
5336 }
5337 let (target, value) = match l[0] {
5338 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => (target, value),
5339 Stmt::LocalDef { var, init: Some(value), .. } if *var == v => {
5340 if reads_var(value, v) || !(bool_valued(value, vt) || bool_const(value).is_some()) {
5341 return None;
5342 }
5343 return Some(value.clone());
5344 }
5345 _ => return None,
5346 };
5347 let Expr::Local { var: tv, .. } = &**target else { return None };
5348 if *tv != v || reads_var(value, v) {
5349 return None;
5350 }
5351 if !(bool_valued(value, vt) || bool_const(value).is_some()) {
5352 return None;
5353 }
5354 Some((**value).clone())
5355 }
5356 fn not(e: Expr) -> Expr {
5357 Expr::Un { op: UnOp::Not, e: Box::new(e) }
5358 }
5359 fn simplify_bool(e: Expr) -> Expr {
5364 match e {
5365 Expr::Un { op: UnOp::Not, e: inner, .. } => match *inner {
5366 Expr::Bin { op: BinOp::Ne, l, r, ty } => {
5371 simplify_bool(Expr::Bin { op: BinOp::Eq, l, r, ty })
5372 }
5373 Expr::Bin { op: BinOp::Eq, l, r, ty } => {
5374 simplify_bool(Expr::Bin { op: BinOp::Ne, l, r, ty })
5375 }
5376 other => Expr::Un { op: UnOp::Not, e: Box::new(simplify_bool(other)) },
5377 },
5378 Expr::Bin { op: op @ (BinOp::Eq | BinOp::Ne), l, r, ty } => {
5379 fn cond01(e: &Expr) -> Option<Expr> {
5381 if let Expr::Cond { c, t, f } = e {
5382 if matches!(&**t, Expr::Const(ConstVal::Int(1)))
5383 && matches!(&**f, Expr::Const(ConstVal::Int(0)))
5384 {
5385 return Some((**c).clone());
5386 }
5387 }
5388 None
5389 }
5390 let (lc, rc) = (cond01(&l), cond01(&r));
5391 match (lc, rc) {
5392 (Some(c), None) => match (&*r, op) {
5393 (Expr::Const(ConstVal::Int(1)), BinOp::Eq) => c,
5394 (Expr::Const(ConstVal::Int(1)), BinOp::Ne) => not(c),
5395 (Expr::Const(ConstVal::Int(0)), BinOp::Eq) => not(c),
5396 (Expr::Const(ConstVal::Int(0)), BinOp::Ne) => c,
5397 _ => Expr::Bin { op, l: Box::new(c), r, ty },
5398 },
5399 (None, Some(c)) => match (&*l, op) {
5400 (Expr::Const(ConstVal::Int(1)), BinOp::Eq) => c,
5401 (Expr::Const(ConstVal::Int(1)), BinOp::Ne) => not(c),
5402 (Expr::Const(ConstVal::Int(0)), BinOp::Eq) => not(c),
5403 (Expr::Const(ConstVal::Int(0)), BinOp::Ne) => c,
5404 _ => Expr::Bin { op, l, r: Box::new(c), ty },
5405 },
5406 _ => Expr::Bin { op, l, r, ty },
5407 }
5408 }
5409 other => other,
5410 }
5411 }
5412 fn bin(op: BinOp, l: Expr, r: Expr) -> Expr {
5413 Expr::Bin { op, l: Box::new(l), r: Box::new(r), ty: None }
5414 }
5415
5416 for _round in 0..4 {
5417 let mut changed = false;
5418 let mut analysis = VarAnalysis {
5419 assigns: Vec::new(),
5420 reads: Vec::new(),
5421 values: Vec::new(),
5422 };
5423 analyze_vars(body, &mut analysis);
5424 let mut consumed: HashSet<u32> = HashSet::default();
5425 walk_mut_deep(body, &mut |st| {
5426 let Stmt::Block(list) = st else { return };
5427 let mut i = 0usize;
5428 while i + 1 < list.len() {
5429 let folded = try_at(list, i, vt, &is_bool_var, &bool_const, &reads_var, &lone_assign, &analysis, &mut consumed);
5430 match folded {
5431 Some(expr) => {
5432 let is_def = matches!(&list[i], Stmt::LocalDef { .. });
5433 let var = match &list[i] {
5434 Stmt::LocalDef { var, .. } => *var,
5435 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
5436 match &**target { Expr::Local { var, .. } => *var, _ => unreachable!() }
5437 }
5438 _ => unreachable!(),
5439 };
5440 let (is_final, force_type) = match &list[i] {
5441 Stmt::LocalDef { is_final, force_type, .. } => (*is_final, *force_type),
5442 _ => (false, false),
5443 };
5444 list[i] = if is_def {
5445 Stmt::LocalDef { var, init: Some(expr), is_final, force_type }
5446 } else {
5447 Stmt::ExprStmt(Expr::Assign {
5448 target: Box::new(Expr::Local {
5449 var,
5450 ty: TypeRef::J(JavaType::Boolean),
5451 }),
5452 op: AssignOp::Plain,
5453 value: Box::new(expr),
5454 })
5455 };
5456 list.remove(i + 1);
5457 changed = true;
5458 }
5461 None => i += 1,
5462 }
5463 }
5464 });
5465 if !changed {
5466 break;
5467 }
5468 }
5469
5470 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
5471 fn try_at(
5472 list: &[Stmt],
5473 i: usize,
5474 vt: &VarTable,
5475 is_bool_var: &dyn Fn(u32, &VarTable) -> bool,
5476 bool_const: &dyn Fn(&Expr) -> Option<bool>,
5477 reads_var: &dyn Fn(&Expr, u32) -> bool,
5478 lone_assign: &dyn Fn(&Stmt, u32, &VarTable, &VarAnalysis, &mut HashSet<u32>) -> Option<Expr>,
5479 an: &VarAnalysis,
5480 consumed: &mut HashSet<u32>,
5481 ) -> Option<Expr> {
5482 let (v, base) = match &list[i] {
5484 Stmt::ExprStmt(Expr::Assign { target, op: AssignOp::Plain, value }) => {
5485 match &**target {
5486 Expr::Local { var, .. } => {
5487 let b = bool_const(value)?;
5490 if !is_bool_var(*var, vt) { return None; }
5491 (*var, b)
5492 }
5493 _ => return None,
5494 }
5495 }
5496 Stmt::LocalDef { var, init: Some(e), .. } => {
5497 let b = bool_const(e)?;
5498 if !is_bool_var(*var, vt) { return None; }
5499 (*var, b)
5500 }
5501 _ => return None,
5502 };
5503 let Stmt::If { cond, then_stmt, else_stmt } = &list[i + 1] else {
5504 return None;
5505 };
5506 if reads_var(cond, v) {
5507 return None;
5508 }
5509 let mut touches_v = false;
5510 visit_exprs(cond, &mut |x| {
5511 if let Expr::Assign { target, .. } = x {
5512 if let Expr::Local { var, .. } = &**target {
5513 if *var == v { touches_v = true; }
5514 }
5515 }
5516 });
5517 if touches_v {
5518 return None;
5519 }
5520 let (cond_n, then_arm, else_arm): (Expr, &Stmt, Option<&Stmt>) =
5523 if flat_list(then_stmt).is_empty() && else_stmt.is_some() {
5524 (not(cond.clone()), else_stmt.as_ref().unwrap(), None)
5525 } else {
5526 (cond.clone(), then_stmt.as_ref(), else_stmt.as_deref())
5527 };
5528 let e1 = lone_assign(then_arm, v, vt, an, consumed)?;
5529 let e2 = match else_arm {
5530 None => None,
5531 Some(b) => Some(lone_assign(b, v, vt, an, consumed)?),
5532 };
5533 let e1c = bool_const(&e1);
5534 let c = simplify_bool(cond_n);
5535 Some(match e2 {
5536 None => {
5537 if !base {
5538 match e1c {
5539 Some(true) => c,
5540 Some(false) => return None, None => bin(BinOp::LogAnd, c, e1),
5542 }
5543 } else {
5544 match e1c {
5545 Some(false) => not(c),
5546 Some(true) => return None,
5547 None => bin(BinOp::LogOr, not(c), e1),
5548 }
5549 }
5550 }
5551 Some(e2) => {
5552 if e1 == e2 {
5554 return None; }
5556 Expr::Cond { c: Box::new(c), t: Box::new(e1), f: Box::new(e2) }
5557 }
5558 })
5559 }
5560}
5561
5562pub fn idiom_compounds(body: &mut Stmt, vt: &VarTable) {
5563 walk_mut_deep(body, &mut |st| {
5564 let Stmt::ExprStmt(e) = st else { return };
5565 let Expr::Assign { target, op: AssignOp::Plain, value } = &mut *e else {
5566 return;
5567 };
5568 enum Tgt {
5571 Loc(u32),
5572 Fld,
5573 }
5574 let tgt = match &**target {
5575 Expr::Local { var, .. } => Tgt::Loc(*var),
5576 Expr::Field { owner, .. }
5577 if owner.is_none()
5578 || matches!(&**owner.as_ref().unwrap(), Expr::This) =>
5579 {
5580 Tgt::Fld
5581 }
5582 _ => return,
5583 };
5584 let Expr::Bin { op, l, r, .. } = &mut **value else {
5585 return;
5586 };
5587 match (&tgt, &**l) {
5589 (Tgt::Loc(v), Expr::Local { var, .. }) if *var == *v => {}
5590 (Tgt::Fld, Expr::Field { cls: c2, name: n2, .. }) => {
5591 let Expr::Field { cls: c1, name: n1, .. } = &**target else {
5592 return;
5593 };
5594 if c1 != c2 || n1 != n2 {
5595 return;
5596 }
5597 }
5598 _ => return,
5599 }
5600 let one = match &**r {
5602 Expr::Const(ConstVal::Int(1)) => Some(1i64),
5603 Expr::Const(ConstVal::Int(-1)) => Some(-1),
5604 Expr::Const(ConstVal::Long(1)) => Some(1),
5605 Expr::Const(ConstVal::Long(-1)) => Some(-1),
5606 _ => None,
5607 };
5608 if *op == BinOp::Add || *op == BinOp::Sub {
5609 if let Some(mut d) = one {
5610 if *op == BinOp::Sub {
5611 d = -d;
5612 }
5613 let tgt_ty = match &tgt {
5614 Tgt::Loc(v) => vt.var(*v).ty.erased(),
5615 Tgt::Fld => {
5616 let Expr::Field { ty, .. } = &**target else {
5617 return;
5618 };
5619 ty.erased()
5620 }
5621 };
5622 let numeric = matches!(
5623 tgt_ty,
5624 JavaType::Int
5625 | JavaType::Long
5626 | JavaType::Short
5627 | JavaType::Byte
5628 | JavaType::Char
5629 | JavaType::Float
5630 | JavaType::Double
5631 );
5632 if numeric {
5633 let wide = matches!(tgt_ty, JavaType::Long);
5634 let inner = match &tgt {
5635 Tgt::Loc(v) => Expr::Local {
5636 var: *v,
5637 ty: vt.var(*v).ty.clone(),
5638 },
5639 Tgt::Fld => (**target).clone(),
5640 };
5641 *e = Expr::PostIncDec {
5642 e: Box::new(inner),
5643 delta: d,
5644 wide,
5645 };
5646 return;
5647 }
5648 }
5649 }
5650 let cop = match op {
5651 BinOp::Add => AssignOp::Add,
5652 BinOp::Sub => AssignOp::Sub,
5653 BinOp::Mul => AssignOp::Mul,
5654 BinOp::Div => AssignOp::Div,
5655 BinOp::Rem => AssignOp::Rem,
5656 BinOp::Shl => AssignOp::Shl,
5657 BinOp::Shr => AssignOp::Shr,
5658 BinOp::Ushr => AssignOp::Ushr,
5659 BinOp::And => AssignOp::And,
5660 BinOp::Or => AssignOp::Or,
5661 BinOp::Xor => AssignOp::Xor,
5662 _ => return,
5663 };
5664 let rhs = std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
5665 let new_target = match &tgt {
5666 Tgt::Loc(v) => Box::new(Expr::Local {
5667 var: *v,
5668 ty: vt.var(*v).ty.clone(),
5669 }),
5670 Tgt::Fld => target.clone(),
5671 };
5672 *e = Expr::Assign {
5673 target: new_target,
5674 op: cop,
5675 value: rhs,
5676 };
5677 });
5678}
5679
5680pub fn fix_bool_xor(body: &mut Stmt, vt: &VarTable, ret_bool: bool) {
5681 fn is_one(e: &Expr) -> bool {
5682 matches!(e, Expr::Const(ConstVal::Int(1)))
5683 }
5684 fn bty(e: &Expr, vt: &VarTable) -> bool {
5685 matches!(
5686 match e {
5687 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
5688 other => other.type_ref().erased(),
5689 },
5690 JavaType::Boolean
5691 )
5692 }
5693 fn inv_of_xor(x: &Expr, vt: &VarTable) -> Option<Expr> {
5698 if let Expr::Bin { op: BinOp::Xor, l, r, .. } = x {
5699 let (int_side, one_side) = if is_one(r) {
5700 (l, r)
5701 } else if is_one(l) {
5702 (r, l)
5703 } else {
5704 return None;
5705 };
5706 let _ = one_side;
5707 if !bty(int_side, vt) {
5708 return Some(Expr::Bin {
5709 op: BinOp::Eq,
5710 l: int_side.clone(),
5711 r: Box::new(Expr::Const(ConstVal::Int(0))),
5712 ty: Some(TypeRef::J(JavaType::Boolean)),
5713 });
5714 }
5715 }
5716 None
5717 }
5718 let tgt_bool = |target: &Expr, vt: &VarTable| -> bool {
5719 match target {
5720 Expr::Local { var, .. } => matches!(vt.var(*var).ty.erased(), JavaType::Boolean),
5721 Expr::Field { ty, .. } => matches!(ty.erased(), JavaType::Boolean),
5722 _ => false,
5723 }
5724 };
5725 walk_mut_deep(body, &mut |st| match st {
5726 Stmt::Return(Some(e)) if ret_bool => {
5727 if let Some(r) = inv_of_xor(e, vt) {
5728 *e = r;
5729 }
5730 }
5731 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain, .. }) => {
5732 if tgt_bool(target, vt) {
5733 if let Some(r) = inv_of_xor(value, vt) {
5734 **value = r;
5735 }
5736 }
5737 }
5738 Stmt::LocalDef { var, init: Some(value), .. }
5739 if matches!(vt.var(*var).ty.erased(), JavaType::Boolean) =>
5740 {
5741 if let Some(r) = inv_of_xor(value, vt) {
5742 *value = r;
5743 }
5744 }
5745 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
5746 if let Some(r) = inv_of_xor(cond, vt) {
5747 *cond = r;
5748 }
5749 }
5750 _ => {}
5751 });
5752 fn ity(e: &Expr, vt: &VarTable) -> bool {
5753 matches!(
5754 match e {
5755 Expr::Local { var, .. } => vt.var(*var).ty.erased(),
5756 other => other.type_ref().erased(),
5757 },
5758 JavaType::Int | JavaType::Short | JavaType::Byte | JavaType::Char
5759 )
5760 }
5761 fn mixed_rewrite(e: &mut Expr, vt: &VarTable) {
5770 deep_rewrite(e, &mut |x| {
5771 if let Expr::Bin { op, l, r, ty } = x {
5772 match op {
5773 BinOp::Xor => {
5774 if is_one(r) && bty(l, vt) {
5775 let taken =
5776 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
5777 *x = Expr::Un { op: UnOp::Not, e: taken };
5778 } else if is_one(l) && bty(r, vt) {
5779 let taken =
5780 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
5781 *x = Expr::Un { op: UnOp::Not, e: taken };
5782 }
5783 }
5784 BinOp::Or | BinOp::And => {
5785 if bty(l, vt) && ity(r, vt) {
5786 let taken =
5787 std::mem::replace(r, Box::new(Expr::Const(ConstVal::Null)));
5788 **r = Expr::Bin {
5789 op: BinOp::Ne,
5790 l: taken,
5791 r: Box::new(Expr::Const(ConstVal::Int(0))),
5792 ty: Some(TypeRef::J(JavaType::Boolean)),
5793 };
5794 *ty = Some(TypeRef::J(JavaType::Boolean));
5795 } else if ity(l, vt) && bty(r, vt) {
5796 let taken =
5797 std::mem::replace(l, Box::new(Expr::Const(ConstVal::Null)));
5798 **l = Expr::Bin {
5799 op: BinOp::Ne,
5800 l: taken,
5801 r: Box::new(Expr::Const(ConstVal::Int(0))),
5802 ty: Some(TypeRef::J(JavaType::Boolean)),
5803 };
5804 *ty = Some(TypeRef::J(JavaType::Boolean));
5805 }
5806 }
5807 _ => {}
5808 }
5809 }
5810 });
5811 }
5812 walk_mut_deep(body, &mut |st| match st {
5815 Stmt::Return(Some(e)) if ret_bool => mixed_rewrite(e, vt),
5816 Stmt::ExprStmt(Expr::Assign { target, value, op: AssignOp::Plain, .. }) => {
5817 if tgt_bool(target, vt) {
5818 mixed_rewrite(value, vt);
5819 }
5820 }
5821 Stmt::LocalDef { var, init: Some(e), .. }
5822 if matches!(vt.var(*var).ty.erased(), JavaType::Boolean) =>
5823 {
5824 mixed_rewrite(e, vt);
5825 }
5826 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
5827 mixed_rewrite(cond, vt);
5828 }
5829 _ => {}
5830 });
5831 walk_stmt_exprs(body, &mut |e| {
5834 deep_rewrite(e, &mut |x| {
5835 if let Expr::Cond { c, .. } = x {
5836 mixed_rewrite(c, vt);
5837 }
5838 });
5839 });
5840}
5841
5842pub fn booleanize(vt: &mut VarTable, body: &mut Stmt, ret_bool: bool) -> usize {
5848 let n0 = vt.vars.len();
5855 let mut derefed = vec![false; n0];
5856 mark_derefs(body, &mut derefed);
5857 let mut total = 0usize;
5858 for _ in 0..4 {
5859 let n = booleanize_round(vt, body, ret_bool, &derefed);
5860 if n == 0 {
5861 break;
5862 }
5863 total += n;
5864 }
5865 total
5866}
5867
5868fn mark_deref_expr(e: &Expr, derefed: &mut [bool]) {
5872 match e {
5873 Expr::Field { owner: Some(o), is_static: false, .. }
5874 | Expr::Method { owner: Some(o), .. } => {
5875 if let Expr::Local { var, .. } = &**o {
5876 if (*var as usize) < derefed.len() {
5877 derefed[*var as usize] = true;
5878 }
5879 }
5880 }
5881 Expr::ArrayIndex { array, .. } => {
5882 if let Expr::Local { var, .. } = &**array {
5883 if (*var as usize) < derefed.len() {
5884 derefed[*var as usize] = true;
5885 }
5886 }
5887 }
5888 _ => {}
5889 }
5890 for_each_child(e, &mut |c| mark_deref_expr(c, derefed));
5891}
5892
5893fn mark_derefs(body: &Stmt, derefed: &mut [bool]) {
5894 walk_all(body, &mut |st| {
5895 match st {
5896 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
5897 mark_deref_expr(e, derefed);
5898 }
5899 Stmt::Return(Some(e)) => mark_deref_expr(e, derefed),
5900 Stmt::LocalDef { init: Some(e), .. } => mark_deref_expr(e, derefed),
5901 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
5902 mark_deref_expr(cond, derefed);
5903 }
5904 Stmt::For { init, .. } => {
5905 for x in init {
5906 mark_derefs(x, derefed);
5907 }
5908 }
5909 Stmt::Switch { selector, .. } => mark_deref_expr(selector, derefed),
5910 Stmt::ForEach { iterable, .. } => mark_deref_expr(iterable, derefed),
5911 _ => {}
5912 }
5913 });
5914}
5915
5916fn booleanize_round(
5917 vt: &mut VarTable,
5918 body: &mut Stmt,
5919 ret_bool: bool,
5920 derefed: &[bool],
5921) -> usize {
5922 let n = vt.vars.len();
5923 let mut in_cond = vec![false; n];
5924
5925 walk_all(body, &mut |st| {
5928 let cond: Option<&Expr> = match st {
5929 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
5930 Some(cond)
5931 }
5932 _ => None,
5933 };
5934 if let Some(c) = cond {
5935 if let Expr::Bin {
5936 op: BinOp::Eq | BinOp::Ne,
5937 l,
5938 r,
5939 ..
5940 } = c
5941 {
5942 for (x, other) in [(l, r), (r, l)] {
5943 if let (Expr::Local { var, .. }, Expr::Const(ConstVal::Int(0))) =
5944 (&**x, &**other)
5945 {
5946 if (*var as usize) < n {
5947 in_cond[*var as usize] = true;
5948 }
5949 }
5950 }
5951 }
5952 }
5953 if ret_bool {
5958 if let Stmt::Return(Some(Expr::Local { var, .. })) = st {
5959 if (*var as usize) < n {
5960 in_cond[*var as usize] = true;
5961 }
5962 }
5963 }
5964 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = st {
5971 if let Expr::Field { ty, .. } = &**target {
5972 if ty.erased() == JavaType::Boolean {
5973 if let Expr::Local { var, .. } = &**value {
5974 if (*var as usize) < n {
5975 in_cond[*var as usize] = true;
5976 }
5977 }
5978 }
5979 }
5980 }
5981 let val: Option<&Expr> = match st {
5982 Stmt::ExprStmt(Expr::Assign { value, .. }) => Some(value),
5983 Stmt::ExprStmt(e) => Some(e),
5986 Stmt::LocalDef { init: Some(e), .. } => Some(e),
5987 Stmt::Return(Some(e)) => Some(e),
5988 _ => None,
5989 };
5990 if let Some(v) = val {
5991 visit_exprs(v, &mut |x| {
5992 if let Expr::Method { desc, args, .. } = x {
5998 for (i, a) in args.iter().enumerate() {
5999 if desc.args.get(i) == Some(&JavaType::Boolean) {
6000 if let Expr::Local { var, .. } = a {
6001 if (*var as usize) < n {
6002 in_cond[*var as usize] = true;
6003 }
6004 }
6005 }
6006 }
6007 }
6008 if let Expr::Bin { op, l, r, .. } = x {
6009 if matches!(
6010 op,
6011 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
6012 ) {
6013 for side in [l, r] {
6014 if let Expr::Local { var, .. } = &**side {
6015 if (*var as usize) < n {
6016 in_cond[*var as usize] = true;
6017 }
6018 }
6019 }
6020 }
6021 if matches!(op, BinOp::Or | BinOp::And) {
6027 let l_bool = l.type_ref().erased() == JavaType::Boolean;
6028 let r_bool = r.type_ref().erased() == JavaType::Boolean;
6029 if l_bool != r_bool {
6030 let side = if l_bool { r } else { l };
6031 if let Expr::Local { var, .. } = &**side {
6032 if (*var as usize) < n {
6033 in_cond[*var as usize] = true;
6034 }
6035 }
6036 }
6037 }
6038 }
6039 });
6040 }
6041 });
6042
6043 let mut assigned_any = vec![false; n];
6050 let mut all_bool = vec![true; n];
6051 let mut edges: Vec<(usize, usize)> = Vec::new();
6052 walk_all(body, &mut |st| {
6053 let (var, value) = match st {
6054 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => match &**target {
6055 Expr::Local { var, .. } => (var, &**value),
6056 _ => return,
6057 },
6058 Stmt::LocalDef {
6059 var, init: Some(e), ..
6060 } => (var, e),
6061 _ => return,
6062 };
6063 let i = *var as usize;
6064 if i < n {
6065 assigned_any[i] = true;
6066 if !is_boolean_valued(value, vt) {
6067 all_bool[i] = false;
6068 }
6069 if let Expr::Local { var: src, .. } = value {
6075 if (*src as usize) < n {
6076 edges.push((i, *src as usize));
6077 }
6078 }
6079 }
6080 });
6081 loop {
6086 let mut changed = false;
6087 for &(tgt, src) in &edges {
6088 if in_cond[tgt] && !in_cond[src] && all_bool[src] {
6089 in_cond[src] = true;
6090 changed = true;
6091 }
6092 if in_cond[src] && !in_cond[tgt] && all_bool[tgt] {
6098 in_cond[tgt] = true;
6099 changed = true;
6100 }
6101 }
6102 if !changed {
6103 break;
6104 }
6105 }
6106 let reads = count_locals_stmts(std::slice::from_ref(body));
6107 let mut boolean_vars: HashSet<u32> = HashSet::default();
6108 for i in 0..n {
6109 if vt.vars[i].is_param {
6110 continue;
6111 }
6112 if derefed[i] {
6113 continue;
6114 }
6115 if !in_cond[i] && !(reads.get(&(i as u32)).copied().unwrap_or(0) == 0) {
6116 continue;
6117 }
6118 if assigned_any[i] && all_bool[i] {
6119 boolean_vars.insert(i as u32);
6120 }
6121 }
6122 if boolean_vars.is_empty() {
6123 return 0;
6124 }
6125 for v in &boolean_vars {
6126 vt.vars[*v as usize].ty = TypeRef::J(JavaType::Boolean);
6127 }
6128 let types: Vec<&TypeRef> = vt.vars.iter().map(|v| &v.ty).collect();
6129 rewrite_exprs(body, &mut |e| {
6130 deep_rewrite(e, &mut |x| {
6131 if let Expr::Local { var, ty } = x {
6132 if let Some(want) = types.get(*var as usize) {
6133 if ty != *want {
6134 *ty = (*want).clone();
6135 }
6136 }
6137 }
6138 });
6139 });
6140
6141 fold_bool_conditions(body, &boolean_vars);
6143 boolean_vars.len()
6144}
6145
6146fn is_boolean_valued(e: &Expr, vt: &VarTable) -> bool {
6147 match e {
6148 Expr::Const(ConstVal::Int(0)) | Expr::Const(ConstVal::Int(1)) => true,
6149 Expr::Bin { op: BinOp::Or | BinOp::And, l, r, .. } => {
6156 is_boolean_valued(l, vt) || is_boolean_valued(r, vt)
6157 }
6158 Expr::Bin {
6166 op: BinOp::Xor,
6167 l,
6168 r,
6169 ..
6170 } => is_boolean_valued(l, vt) && is_boolean_valued(r, vt),
6171 Expr::Bin { op, .. } => matches!(
6172 op,
6173 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
6174 ),
6175 Expr::InstanceOf { .. } => true,
6176 Expr::Local { var, ty } => {
6182 let t = if (*var as usize) < vt.vars.len() {
6183 vt.var(*var).ty.erased()
6184 } else {
6185 ty.erased()
6186 };
6187 t == JavaType::Boolean
6188 }
6189 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
6190 Expr::Cond { t, f, .. } => is_boolean_valued(t, vt) && is_boolean_valued(f, vt),
6191 Expr::Un { op: UnOp::Not, .. } => true,
6192 Expr::Field { ty, .. } => ty.erased() == JavaType::Boolean,
6196 _ => false,
6197 }
6198}
6199
6200fn fold_bool_conditions(s: &mut Stmt, bv: &HashSet<u32>) {
6201 let fold = |e: &mut Expr| {
6202 deep_rewrite(e, &mut |x| {
6203 if let Expr::Bin { op, l, r, .. } = x {
6204 let (var_side, const_side) = match (&**l, &**r) {
6205 (Expr::Local { .. }, Expr::Const(ConstVal::Int(_))) => (l, 0),
6206 (Expr::Const(ConstVal::Int(_)), Expr::Local { .. }) => (r, 1),
6207 _ => return,
6208 };
6209 let var = match &**var_side {
6210 Expr::Local { var, .. } => *var,
6211 _ => return,
6212 };
6213 if !bv.contains(&var) {
6214 return;
6215 }
6216 let _ = const_side;
6217 match op {
6218 BinOp::Ne => {
6219 let inner = var_side.clone();
6220 *x = *inner;
6221 }
6222 BinOp::Eq => {
6223 let inner = var_side.clone();
6224 *x = Expr::Un {
6225 op: UnOp::Not,
6226 e: Box::new(*inner),
6227 };
6228 }
6229 _ => {}
6230 }
6231 }
6232 });
6233 };
6234 match s {
6235 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => fold(cond),
6236 _ => {}
6237 }
6238 walk_mut_deep(s, &mut |st| {
6239 let cond = match st {
6240 Stmt::If { cond, .. } => Some(cond),
6241 Stmt::While { cond, .. } => Some(cond),
6242 Stmt::DoWhile { cond, .. } => Some(cond),
6243 _ => None,
6244 };
6245 if let Some(c) = cond {
6246 fold(c);
6247 }
6248 });
6249}
6250
6251pub fn flip_const_compares(s: &mut Stmt) {
6253 rewrite_exprs(s, &mut |e| {
6254 deep_rewrite(e, &mut |x| {
6255 if let Expr::Bin { op, l, r, .. } = x {
6256 if matches!(
6257 op,
6258 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Ge | BinOp::Gt | BinOp::Le
6259 ) {
6260 let l_const = matches!(&**l, Expr::Const(_));
6261 let r_var = matches!(&**r, Expr::Local { .. });
6262 if l_const && r_var {
6263 std::mem::swap(l, r);
6264 }
6265 }
6266 }
6267 });
6268 });
6269}
6270
6271pub fn null_compares(vt: &VarTable, body: &mut Stmt) {
6273 let obj_vars: HashSet<u32> = vt
6274 .vars
6275 .iter()
6276 .filter(|v| v.ty.erased().is_reference())
6277 .map(|v| v.id)
6278 .collect();
6279 rewrite_exprs(body, &mut |e| {
6280 deep_rewrite(e, &mut |x| {
6281 null_side_rewrite(x, &|v| obj_vars.contains(&v));
6282 });
6283 });
6284}
6285
6286pub fn drop_dead_locals(body: &mut Stmt) {
6295 loop {
6296 let mut reads: Vec<usize> = Vec::new();
6297 count_reads(body, &mut reads);
6298 let mut dropped = 0usize;
6299 walk_mut_deep(body, &mut |st| match st {
6300 Stmt::Block(items) => prune_dead_items(items, &reads, &mut dropped),
6301 Stmt::Switch { cases, .. } => {
6311 for c in cases {
6312 prune_dead_items(&mut c.body, &reads, &mut dropped);
6313 }
6314 }
6315 Stmt::For { init, .. } => prune_dead_items(init, &reads, &mut dropped),
6316 _ => {}
6317 });
6318 if dropped == 0 {
6319 break;
6320 }
6321 }
6322}
6323
6324fn prune_dead_items(items: &mut Vec<Stmt>, reads: &[usize], dropped: &mut usize) {
6329 let dead = |v: u32| reads.get(v as usize).copied().unwrap_or(0) == 0;
6330 items.retain_mut(|x| match x {
6331 Stmt::ExprStmt(Expr::Assign { target, value, op, .. }) => {
6332 let dead_target = matches!(&**target, Expr::Local { var, .. } if dead(*var));
6333 if dead_target && matches!(*op, AssignOp::Plain) {
6334 *dropped += 1;
6335 if has_side_effects(value) {
6336 let e = std::mem::replace(value, Box::new(Expr::This));
6337 *x = Stmt::ExprStmt(*e);
6338 true
6339 } else {
6340 false
6341 }
6342 } else {
6343 true
6344 }
6345 }
6346 Stmt::LocalDef { var, init, .. } if dead(*var) => {
6347 *dropped += 1;
6348 if let Some(e) = init.take() {
6349 if has_side_effects(&e) {
6350 *x = Stmt::ExprStmt(e);
6351 return true;
6352 }
6353 }
6354 false
6355 }
6356 _ => true,
6357 });
6358}
6359
6360fn for_each_child_stmt<'a>(st: &'a Stmt, f: &mut impl FnMut(&'a Stmt)) {
6363 match st {
6364 Stmt::Block(v) => {
6365 for x in v {
6366 f(x);
6367 }
6368 }
6369 Stmt::If {
6370 then_stmt,
6371 else_stmt,
6372 ..
6373 } => {
6374 f(then_stmt);
6375 if let Some(e) = else_stmt {
6376 f(e);
6377 }
6378 }
6379 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => f(body),
6380 Stmt::For { init, body, .. } => {
6381 for x in init {
6382 f(x);
6383 }
6384 f(body);
6385 }
6386 Stmt::ForEach { body, .. } => f(body),
6387 Stmt::Switch { cases, default, .. } => {
6388 for c in cases {
6389 for x in &c.body {
6390 f(x);
6391 }
6392 }
6393 if let Some(d) = default {
6394 f(d);
6395 }
6396 }
6397 Stmt::Try {
6398 body,
6399 catches,
6400 finally,
6401 } => {
6402 f(body);
6403 for c in catches {
6404 f(&c.body);
6405 }
6406 if let Some(fl) = finally {
6407 f(fl);
6408 }
6409 }
6410 Stmt::TryWithResources {
6411 resources,
6412 body,
6413 catches,
6414 finally,
6415 } => {
6416 for r in resources {
6417 f(r);
6418 }
6419 f(body);
6420 for c in catches {
6421 f(&c.body);
6422 }
6423 if let Some(fl) = finally {
6424 f(fl);
6425 }
6426 }
6427 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => f(body),
6428 _ => {}
6429 }
6430}
6431
6432fn stmt_shallow_vars<F: FnMut(u32)>(st: &Stmt, out: &mut F) {
6437 fn ex<F: FnMut(u32)>(e: &Expr, out: &mut F) {
6438 visit_exprs(e, &mut |x| {
6439 if let Expr::Local { var, .. } = x {
6440 out(*var);
6441 }
6442 });
6443 }
6444 match st {
6445 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
6446 ex(e, out)
6447 }
6448 Stmt::TernaryValue { e } => ex(e, out),
6449 Stmt::Return(Some(e)) => ex(e, out),
6450 Stmt::LocalDef { var, init, .. } => {
6451 out(*var);
6452 if let Some(e) = init {
6453 ex(e, out);
6454 }
6455 }
6456 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
6457 ex(cond, out)
6458 }
6459 Stmt::For { cond, update, .. } => {
6460 if let Some(c) = cond {
6461 ex(c, out);
6462 }
6463 for u in update {
6464 ex(u, out);
6465 }
6466 }
6467 Stmt::ForEach { iterable, .. } => ex(iterable, out),
6468 Stmt::Switch { selector, cases, .. } => {
6469 ex(selector, out);
6470 for cg in cases {
6471 if let Some(g) = &cg.guard {
6472 ex(g, out);
6473 }
6474 }
6475 }
6476 Stmt::Assert { cond, msg } => {
6477 ex(cond, out);
6478 if let Some(m) = msg {
6479 ex(m, out);
6480 }
6481 }
6482 Stmt::Synchronized { lock, .. } => ex(lock, out),
6483 _ => {}
6484 }
6485}
6486
6487fn scope_pass_a(st: &Stmt, cur: u32, next: &mut u32, def_block: &mut [u32], sizes: &mut Vec<u32>) {
6490 if let Stmt::LocalDef { var, .. } = st {
6491 let i = *var as usize;
6492 if i < def_block.len() && def_block[i] == u32::MAX {
6493 def_block[i] = cur;
6494 }
6495 }
6496 for_each_child_stmt(st, &mut |c| {
6497 if matches!(c, Stmt::Block(_)) {
6498 let id = *next;
6499 *next += 1;
6500 sizes.push(0);
6501 scope_pass_a(c, id, next, def_block, sizes);
6502 sizes[id as usize] = *next - id;
6503 } else {
6504 scope_pass_a(c, cur, next, def_block, sizes);
6505 }
6506 });
6507}
6508
6509fn scope_pass_b(
6512 st: &Stmt,
6513 cur: u32,
6514 next: &mut u32,
6515 def_block: &[u32],
6516 sizes: &[u32],
6517 within: &mut [u32],
6518 total: &mut [u32],
6519) {
6520 stmt_shallow_vars(st, &mut |v| {
6521 let i = v as usize;
6522 if i < total.len() {
6523 total[i] += 1;
6524 let d = def_block[i];
6525 if d != u32::MAX && d <= cur && cur < d + sizes[d as usize] {
6526 within[i] += 1;
6527 }
6528 }
6529 });
6530 for_each_child_stmt(st, &mut |c| {
6531 if matches!(c, Stmt::Block(_)) {
6532 let id = *next;
6533 *next += 1;
6534 scope_pass_b(c, id, next, def_block, sizes, within, total);
6535 } else {
6536 scope_pass_b(c, cur, next, def_block, sizes, within, total);
6537 }
6538 });
6539}
6540
6541pub fn strip_enum_const_inits(s: &mut Stmt, class: &crate::PoolClass) {
6550 let const_fields: jdc_core::FxHashSet<&str> = class
6551 .static_fields
6552 .iter()
6553 .filter(|f| f.access & crate::access::ACC_ENUM != 0)
6554 .map(|f| f.name.as_str())
6555 .collect();
6556 if const_fields.is_empty() {
6557 return;
6558 }
6559 if let Stmt::Block(stmts) = s {
6560 stmts.retain(|st| {
6561 !matches!(
6562 st,
6563 Stmt::ExprStmt(Expr::Assign { target, value, .. })
6564 if matches!(&**target,
6565 Expr::Field { cls, name, is_static: true, .. }
6566 if cls.as_ref() == class.name
6567 && const_fields.contains(&**name))
6568 && matches!(&**value, Expr::New { cls: ncls, .. } if ncls.as_ref() == class.name)
6569 )
6570 });
6571 }
6572}
6573
6574fn contains_this_access(e: &Expr) -> bool {
6583 let mut hit = false;
6584 visit_exprs(e, &mut |x| {
6585 if hit {
6586 return;
6587 }
6588 match x {
6589 Expr::This => hit = true,
6590 Expr::Field { owner, is_static: false, .. } => {
6591 if owner.is_none() || matches!(&**owner.as_ref().unwrap(), Expr::This) {
6592 hit = true;
6593 }
6594 }
6595 Expr::Method { owner, is_static: false, .. } => {
6596 if owner.is_none() || matches!(&**owner.as_ref().unwrap(), Expr::This) {
6597 hit = true;
6598 }
6599 }
6600 _ => {}
6601 }
6602 });
6603 hit
6604}
6605
6606fn var_occurs_in(stmts: &[Stmt], var: u32) -> bool {
6621 stmts.iter().any(|st| {
6622 let mut c = st.clone();
6623 let mut hit = false;
6624 walk_stmt_exprs(&mut c, &mut |e| {
6625 if !hit {
6626 deep_rewrite(e, &mut |x| {
6627 if let Expr::Local { var: v, .. } = x {
6628 if *v == var {
6629 hit = true;
6630 }
6631 }
6632 });
6633 }
6634 });
6635 hit
6636 })
6637}
6638
6639pub struct CtorHelper {
6649 pub name: String,
6650 pub ret: JavaType,
6651 pub params: Vec<(JavaType, String)>,
6652 pub body: Stmt,
6653 pub vt: VarTable,
6654}
6655
6656fn ctor_helpers() -> &'static std::sync::Mutex<std::collections::HashMap<String, Vec<CtorHelper>>> {
6657 static H: std::sync::OnceLock<std::sync::Mutex<std::collections::HashMap<String, Vec<CtorHelper>>>> =
6658 std::sync::OnceLock::new();
6659 H.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::default()))
6660}
6661
6662fn helper_name(class: &str, desc: &str) -> String {
6665 let mut h: u64 = 0xcbf29ce484222325;
6666 for b in class.as_bytes().iter().chain(b"::").chain(desc.as_bytes()) {
6667 h ^= *b as u64;
6668 h = h.wrapping_mul(0x100000001b3);
6669 }
6670 format!("resolve${:x}", (h >> 16) & 0xffff_ffff)
6671}
6672
6673pub fn take_ctor_helpers(class: &str) -> Vec<CtorHelper> {
6676 let mut m = ctor_helpers().lock().unwrap_or_else(|e| e.into_inner());
6677 let mut v = m.remove(class).unwrap_or_default();
6678 v.sort_by(|a, b| a.name.cmp(&b.name));
6679 v
6680}
6681
6682fn strip_delegations_deep(stmts: &mut Vec<Stmt>) {
6683 stmts.retain(|s| !is_bare_ctor_call(s));
6684 for s in stmts.iter_mut() {
6685 strip_delegation_one(s);
6686 }
6687}
6688
6689fn strip_delegation_one(st: &mut Stmt) {
6690 if is_bare_ctor_call(st) {
6691 *st = Stmt::Block(Vec::new());
6692 return;
6693 }
6694 match st {
6695 Stmt::Block(v) => strip_delegations_deep(v),
6696 Stmt::If { then_stmt, else_stmt, .. } => {
6697 strip_delegation_one(then_stmt);
6698 if let Some(e) = else_stmt {
6699 strip_delegation_one(e);
6700 }
6701 }
6702 Stmt::While { body, .. }
6703 | Stmt::DoWhile { body, .. }
6704 | Stmt::ForEach { body, .. }
6705 | Stmt::Synchronized { body, .. }
6706 | Stmt::Labeled { body, .. } => strip_delegation_one(body),
6707 Stmt::For { init, body, .. } => {
6708 strip_delegations_deep(init);
6709 strip_delegation_one(body);
6710 }
6711 Stmt::Switch { cases, default, .. } => {
6712 for c in cases.iter_mut() {
6713 strip_delegations_deep(&mut c.body);
6714 }
6715 if let Some(d) = default {
6716 strip_delegation_one(d);
6717 }
6718 }
6719 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
6720 strip_delegation_one(body);
6721 for c in catches.iter_mut() {
6722 strip_delegation_one(&mut c.body);
6723 }
6724 if let Some(f) = finally {
6725 strip_delegation_one(f);
6726 }
6727 }
6728 _ => {}
6729 }
6730}
6731
6732#[allow(clippy::ptr_arg)]
6733fn rewrite_bare_returns(stmts: &mut Vec<Stmt>, val: &Expr) {
6734 for s in stmts.iter_mut() {
6735 rewrite_bare_returns_one(s, val);
6736 }
6737}
6738
6739fn rewrite_bare_returns_one(st: &mut Stmt, val: &Expr) {
6740 match st {
6741 Stmt::Return(None) => *st = Stmt::Return(Some(val.clone())),
6742 Stmt::Block(v) => rewrite_bare_returns(v, val),
6743 Stmt::If { then_stmt, else_stmt, .. } => {
6744 rewrite_bare_returns_one(then_stmt, val);
6745 if let Some(e) = else_stmt {
6746 rewrite_bare_returns_one(e, val);
6747 }
6748 }
6749 Stmt::While { body, .. }
6750 | Stmt::DoWhile { body, .. }
6751 | Stmt::ForEach { body, .. }
6752 | Stmt::Synchronized { body, .. }
6753 | Stmt::Labeled { body, .. } => rewrite_bare_returns_one(body, val),
6754 Stmt::For { init, body, .. } => {
6755 rewrite_bare_returns(init, val);
6756 rewrite_bare_returns_one(body, val);
6757 }
6758 Stmt::Switch { cases, default, .. } => {
6759 for c in cases.iter_mut() {
6760 rewrite_bare_returns(&mut c.body, val);
6761 }
6762 if let Some(d) = default {
6763 rewrite_bare_returns_one(d, val);
6764 }
6765 }
6766 Stmt::Try { body, catches, finally } | Stmt::TryWithResources { body, catches, finally, .. } => {
6767 rewrite_bare_returns_one(body, val);
6768 for c in catches.iter_mut() {
6769 rewrite_bare_returns_one(&mut c.body, val);
6770 }
6771 if let Some(f) = finally {
6772 rewrite_bare_returns_one(f, val);
6773 }
6774 }
6775 _ => {}
6776 }
6777}
6778
6779fn collect_def_vars(stmts: &[Stmt], out: &mut jdc_core::FxHashSet<u32>) {
6780 for st in stmts {
6781 match st {
6782 Stmt::LocalDef { var, .. } => {
6783 out.insert(*var);
6784 }
6785 Stmt::ExprStmt(Expr::Assign { target, .. }) => {
6786 if let Expr::Local { var, .. } = &**target {
6787 out.insert(*var);
6788 }
6789 }
6790 Stmt::Block(v) => collect_def_vars(v, out),
6791 Stmt::If { then_stmt, else_stmt, .. } => {
6792 collect_def_vars(std::slice::from_ref(then_stmt.as_ref()), out);
6793 if let Some(e) = else_stmt {
6794 collect_def_vars(std::slice::from_ref(e.as_ref()), out);
6795 }
6796 }
6797 Stmt::While { body, .. }
6798 | Stmt::DoWhile { body, .. }
6799 | Stmt::Synchronized { body, .. }
6800 | Stmt::Labeled { body, .. } => {
6801 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
6802 }
6803 Stmt::ForEach { var, body, .. } => {
6804 out.insert(*var);
6805 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
6806 }
6807 Stmt::For { init, body, .. } => {
6808 collect_def_vars(init, out);
6809 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
6810 }
6811 Stmt::Switch { cases, default, .. } => {
6812 for c in cases {
6813 collect_def_vars(&c.body, out);
6814 }
6815 if let Some(d) = default {
6816 collect_def_vars(std::slice::from_ref(d.as_ref()), out);
6817 }
6818 }
6819 Stmt::Try { body, catches, finally }
6820 | Stmt::TryWithResources { body, catches, finally, .. } => {
6821 collect_def_vars(std::slice::from_ref(body.as_ref()), out);
6822 for c in catches {
6823 out.insert(c.var);
6824 collect_def_vars(std::slice::from_ref(c.body.as_ref()), out);
6825 }
6826 if let Some(f) = finally {
6827 collect_def_vars(std::slice::from_ref(f.as_ref()), out);
6828 }
6829 }
6830 _ => {}
6831 }
6832 }
6833}
6834
6835fn extraction_shape_ok(stmts: &[Stmt]) -> bool {
6838 stmts.iter().all(|st| match st {
6839 Stmt::Block(v) => extraction_shape_ok(v),
6840 Stmt::LocalDef { .. }
6841 | Stmt::ExprStmt(_)
6842 | Stmt::Return(_)
6843 | Stmt::Throw(_)
6844 | Stmt::Assert { .. }
6845 | Stmt::Break(_)
6846 | Stmt::Continue(_) => true,
6847 Stmt::If { then_stmt, else_stmt, .. } => {
6848 ok_one(then_stmt)
6849 && else_stmt.as_deref().map(ok_one).unwrap_or(true)
6850 }
6851 Stmt::While { body, .. }
6852 | Stmt::DoWhile { body, .. }
6853 | Stmt::ForEach { body, .. }
6854 | Stmt::Synchronized { body, .. } => ok_one(body),
6855 Stmt::Labeled { body, .. } => ok_one(body),
6856 Stmt::For { init, body, .. } => extraction_shape_ok(init) && ok_one(body),
6857 Stmt::Switch { cases, default, .. } => {
6858 cases.iter().all(|c| extraction_shape_ok(&c.body))
6859 && default.as_deref().map(ok_one).unwrap_or(true)
6860 }
6861 Stmt::Try { body, catches, finally }
6862 | Stmt::TryWithResources { body, catches, finally, .. } => {
6863 ok_one(body)
6864 && catches.iter().all(|c| ok_one(&c.body))
6865 && finally.as_deref().map(ok_one).unwrap_or(true)
6866 }
6867 _ => false,
6870 })
6871}
6872
6873fn ok_one(st: &Stmt) -> bool {
6874 extraction_shape_ok(std::slice::from_ref(st))
6875}
6876
6877fn static_safe(stmts: &[Stmt]) -> bool {
6878 let mut ok = true;
6879 for st in stmts {
6880 let mut c = st.clone();
6881 walk_stmt_exprs(&mut c, &mut |e| {
6882 deep_rewrite(e, &mut |x| {
6883 match x {
6884 Expr::This => ok = false,
6885 Expr::Field { owner: None, is_static: false, .. } => ok = false,
6887 Expr::Method { owner: None, is_static: false, .. } => ok = false,
6888 _ => {}
6889 }
6890 });
6891 });
6892 }
6893 ok
6894}
6895
6896pub fn extract_branched_delegation_helper(
6917 body: &mut Stmt,
6918 vt: &VarTable,
6919 class: &crate::PoolClass,
6920 desc_str: &str,
6921) {
6922 if class.is_enum() || class.nesting.enclosing_class.is_some() {
6923 return;
6924 }
6925 let Stmt::Block(stmts) = body else { return };
6926 let mut dels: Vec<Expr> = Vec::new();
6927 for st in stmts.iter() {
6928 let mut c = st.clone();
6929 walk_stmt_exprs(&mut c, &mut |e| {
6930 if is_delegation_expr(e) {
6931 dels.push(e.clone());
6932 }
6933 });
6934 }
6935 if dels.is_empty() {
6936 return;
6937 }
6938 if dels.len() == 1 && stmts.first().is_some_and(is_bare_ctor_call) {
6939 return; }
6941 if !dels.iter().all(|d| *d == dels[0]) {
6942 return;
6943 }
6944 let template = dels[0].clone();
6945 let Expr::Method { args: t_args, .. } = &template else {
6946 return;
6947 };
6948 let mut carrier: Option<u32> = None;
6950 let mut reads = 0usize;
6951 let mut multi = false;
6952 for a in t_args.iter() {
6953 let c = a.clone();
6954 visit_exprs(&c, &mut |x| {
6955 if let Expr::Local { var, .. } = x {
6956 if (*var as usize) < vt.vars.len() && !vt.vars[*var as usize].is_param {
6957 reads += 1;
6958 match carrier {
6959 Some(v) if v == *var => {}
6960 Some(_) => multi = true,
6961 None => carrier = Some(*var),
6962 }
6963 }
6964 }
6965 });
6966 }
6967 let Some(cv) = carrier else { return };
6968 if multi || reads != 1 {
6969 return;
6970 }
6971 let Some(l) = stmts.iter().rposition(is_bare_ctor_call) else {
6972 return;
6973 };
6974 if l == 0 {
6975 return;
6976 }
6977 let mut extracted: Vec<Stmt> = stmts[..l].to_vec();
6978 if !extraction_shape_ok(&extracted) {
6979 return;
6980 }
6981 strip_delegations_deep(&mut extracted);
6982 let cv_local = Expr::Local {
6983 var: cv,
6984 ty: vt.var(cv).ty.clone(),
6985 };
6986 rewrite_bare_returns(&mut extracted, &cv_local);
6987 extracted.push(Stmt::Return(Some(cv_local)));
6988 if !static_safe(&extracted) {
6989 return;
6990 }
6991 let mut defs: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
6992 collect_def_vars(&extracted, &mut defs);
6993 if defs.iter().any(|v| var_occurs_in(&stmts[l + 1..], *v)) {
6994 return;
6995 }
6996 let params: Vec<(u32, JavaType, String)> = vt
6997 .vars
6998 .iter()
6999 .filter(|v| v.is_param && v.name != "this")
7000 .map(|v| (v.id, v.ty.erased(), v.name.clone()))
7001 .collect();
7002 let ret = vt.var(cv).ty.erased();
7003 let name = helper_name(&class.name, desc_str);
7004 let call = Expr::Method {
7005 owner: None,
7006 cls: std::sync::Arc::from(class.name.as_str()),
7007 name: std::sync::Arc::from(name.as_str()),
7008 desc: std::sync::Arc::new(jdc_core::types::MethodDescriptor {
7009 ret: ret.clone(),
7010 args: params.iter().map(|p| p.1.clone()).collect(),
7011 }),
7012 args: params
7013 .iter()
7014 .map(|(id, ty, _)| Expr::Local {
7015 var: *id,
7016 ty: TypeRef::J(ty.clone()),
7017 })
7018 .collect(),
7019 is_static: true,
7020 is_interface: false,
7021 is_special: false,
7022 is_super: false,
7023 is_dynamic: false,
7024 type_args: Vec::new(),
7025 };
7026 let mut new_del = template.clone();
7027 deep_rewrite(&mut new_del, &mut |x| {
7028 if let Expr::Local { var, .. } = x {
7029 if *var == cv {
7030 *x = call.clone();
7031 }
7032 }
7033 });
7034 {
7035 let mut m = ctor_helpers().lock().unwrap_or_else(|e| e.into_inner());
7036 let v = m.entry(class.name.clone()).or_default();
7037 if !v.iter().any(|h| h.name == name) {
7038 let mut hbody = Stmt::Block(extracted);
7042 invert_empty_thens(&mut hbody);
7043 v.push(CtorHelper {
7044 name,
7045 ret,
7046 params: params.into_iter().map(|(_, ty, nm)| (ty, nm)).collect(),
7047 body: hbody,
7048 vt: vt.clone(),
7049 });
7050 } else {
7051 return; }
7053 }
7054 let tail: Vec<Stmt> = stmts[l + 1..].to_vec();
7055 *stmts = std::iter::once(Stmt::ExprStmt(new_del)).chain(tail).collect();
7056}
7057
7058fn inline_ctor_arg_defs(
7059 call: &mut Stmt,
7060 prefix: &[&Stmt],
7061 vt: &VarTable,
7062 consumed: &mut Vec<(u32, usize)>,
7063) -> bool {
7064 let Stmt::ExprStmt(cexpr) = call else {
7065 return false;
7066 };
7067 fn subst(e: &mut Expr, v: u32, val: &Option<Expr>) -> bool {
7070 let mut ok = true;
7071 deep_rewrite(e, &mut |x| {
7072 if let Expr::Local { var, .. } = x {
7073 if *var == v {
7074 match val {
7075 Some(e2) => *x = e2.clone(),
7076 None => ok = false,
7077 }
7078 }
7079 }
7080 });
7081 ok
7082 }
7083 let mut seen: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
7084 for _ in 0..32 {
7085 let mut need: Option<u32> = None;
7086 visit_exprs(cexpr, &mut |x| {
7087 if need.is_none() {
7088 if let Expr::Local { var, .. } = x {
7089 if (*var as usize) < vt.vars.len() && !vt.vars[*var as usize].is_param {
7090 need = Some(*var);
7091 }
7092 }
7093 }
7094 });
7095 let Some(v) = need else { break };
7096 if !seen.insert(v) {
7097 return false; }
7099 let mut val: Option<Expr> = None;
7111 let mut ok = true;
7112 for (si, st) in prefix.iter().enumerate() {
7113 match st {
7114 Stmt::LocalDef { var, init: None, .. } if *var == v => {}
7118 Stmt::LocalDef { var, init, .. } if *var == v => {
7119 let Some(e) = init else { ok = false; break };
7120 let mut e2 = (*e).clone();
7121 if !subst(&mut e2, v, &val) {
7122 ok = false;
7123 break;
7124 }
7125 val = Some(e2);
7126 consumed.push((v, si));
7127 }
7128 Stmt::ExprStmt(Expr::Assign { target, op, .. })
7129 if matches!(&**target, Expr::Local { var: tv, .. } if *tv == v) =>
7130 {
7131 if !matches!(op, AssignOp::Plain) {
7132 ok = false; break;
7134 }
7135 let Stmt::ExprStmt(Expr::Assign { value, .. }) = st else {
7136 unreachable!()
7137 };
7138 let mut e2 = value.as_ref().clone();
7139 if !subst(&mut e2, v, &val) {
7140 ok = false;
7141 break;
7142 }
7143 val = Some(e2);
7144 consumed.push((v, si));
7145 }
7146 _ => {
7147 let mut touched = false;
7148 visit_all_exprs(st, &mut |x| {
7149 if let Expr::Local { var, .. } = x {
7150 if *var == v {
7151 touched = true;
7152 }
7153 }
7154 });
7155 if touched {
7156 ok = false;
7157 break;
7158 }
7159 }
7160 }
7161 }
7162 if !ok {
7163 return false;
7164 }
7165 let Some(de) = val else { return false };
7166 if contains_this_access(&de) {
7167 return false;
7168 }
7169 let mut de_nodes = 0usize;
7171 visit_exprs(&de, &mut |_| de_nodes += 1);
7172 if de_nodes > 256 {
7173 return false;
7174 }
7175 deep_rewrite(cexpr, &mut |x| {
7176 if let Expr::Local { var, .. } = x {
7177 if *var == v {
7178 *x = de.clone();
7179 }
7180 }
7181 });
7182 let mut nodes = 0usize;
7187 visit_exprs(cexpr, &mut |_| nodes += 1);
7188 if nodes > 2_048 {
7189 return false;
7190 }
7191 }
7192 true
7193}
7194
7195fn fold_sb_chain(stmts: &mut Vec<Stmt>, pos: usize) -> usize {
7208 let mut sbv: Option<u32> = None;
7210 let mut def_i: Option<usize> = None;
7211 let mut def_init: Option<Expr> = None;
7212 for (i, st) in stmts[..pos].iter().enumerate() {
7213 let cand: Option<(u32, &Expr)> = match st {
7214 Stmt::LocalDef {
7215 var,
7216 init: Some(e),
7217 ..
7218 } => Some((*var, e)),
7219 Stmt::ExprStmt(Expr::Assign {
7220 target,
7221 value,
7222 op: AssignOp::Plain,
7223 ..
7224 }) => match &**target {
7225 Expr::Local { var, .. } => Some((*var, value.as_ref())),
7226 _ => None,
7227 },
7228 _ => None,
7229 };
7230 if let Some((v, init)) = cand {
7231 if let Expr::New { cls, .. } = init {
7232 if cls.as_ref() == "java/lang/StringBuilder" {
7233 sbv = Some(v);
7234 def_i = Some(i);
7235 def_init = Some(init.clone());
7236 break;
7237 }
7238 }
7239 }
7240 }
7241 let (Some(sbv), Some(def_i), Some(init)) = (sbv, def_i, def_init) else {
7242 return pos;
7243 };
7244 if contains_this_access(&init) {
7245 return pos;
7246 }
7247 let mut appends: Vec<usize> = Vec::new();
7249 for (i, st) in stmts[..pos].iter().enumerate() {
7250 if let Stmt::ExprStmt(Expr::Method {
7251 owner: Some(o),
7252 cls,
7253 name,
7254 ..
7255 }) = st
7256 {
7257 if matches!(&**o, Expr::Local { var, .. } if *var == sbv)
7258 && cls.as_ref() == "java/lang/StringBuilder"
7259 && &**name == "append"
7260 {
7261 appends.push(i);
7262 }
7263 }
7264 }
7265 let mut post_read = false;
7267 for st in stmts.iter().skip(pos + 1) {
7268 visit_all_exprs(st, &mut |e| {
7269 if let Expr::Local { var, .. } = e {
7270 if *var == sbv {
7271 post_read = true;
7272 }
7273 }
7274 });
7275 }
7276 if post_read {
7277 return pos;
7278 }
7279 for (i, st) in stmts[..pos].iter().enumerate() {
7282 if i == def_i || appends.contains(&i) {
7283 if let Stmt::ExprStmt(Expr::Method { args, .. }) = st {
7285 if args.iter().any(contains_this_access) {
7286 return pos;
7287 }
7288 }
7289 continue;
7290 }
7291 let mut leak = false;
7292 visit_all_exprs(st, &mut |e| {
7293 if let Expr::Local { var, .. } = e {
7294 if *var == sbv {
7295 leak = true;
7296 }
7297 }
7298 });
7299 if leak {
7300 return pos;
7301 }
7302 }
7303 let mut local_reads = 0usize;
7305 let mut tostring_reads = 0usize;
7306 visit_all_exprs(&stmts[pos], &mut |e| match e {
7307 Expr::Local { var, .. } if *var == sbv => local_reads += 1,
7308 Expr::Method {
7309 owner: Some(o),
7310 name,
7311 args,
7312 ..
7313 } if args.is_empty()
7314 && &**name == "toString"
7315 && matches!(&**o, Expr::Local { var, .. } if *var == sbv) =>
7316 {
7317 tostring_reads += 1;
7318 }
7319 _ => {}
7320 });
7321 if local_reads != 1 || tostring_reads != 1 {
7322 return pos;
7323 }
7324 let mut chain = init;
7326 for ai in &appends {
7327 if let Stmt::ExprStmt(Expr::Method {
7328 owner: _,
7329 cls,
7330 name,
7331 desc,
7332 args,
7333 is_static,
7334 is_interface,
7335 is_special,
7336 is_super,
7337 is_dynamic,
7338 type_args,
7339 ..
7340 }) = &stmts[*ai]
7341 {
7342 chain = Expr::Method {
7343 owner: Some(Box::new(chain)),
7344 cls: cls.clone(),
7345 name: name.clone(),
7346 desc: desc.clone(),
7347 args: args.clone(),
7348 is_static: *is_static,
7349 is_interface: *is_interface,
7350 is_special: *is_special,
7351 is_super: *is_super,
7352 is_dynamic: *is_dynamic,
7353 type_args: type_args.clone(),
7354 };
7355 }
7356 }
7357 let mut replaced = false;
7358 walk_stmt_exprs(&mut stmts[pos], &mut |e| {
7359 deep_rewrite(e, &mut |x| {
7360 if let Expr::Method {
7361 owner: Some(o),
7362 name,
7363 args,
7364 ..
7365 } = x
7366 {
7367 if args.is_empty()
7368 && &**name == "toString"
7369 && matches!(&**o, Expr::Local { var, .. } if *var == sbv)
7370 {
7371 **o = chain.clone();
7372 replaced = true;
7373 }
7374 }
7375 });
7376 });
7377 if !replaced {
7378 return pos; }
7380 let mut removed: Vec<usize> = appends.clone();
7382 removed.push(def_i);
7383 removed.sort_unstable();
7384 removed.dedup();
7385 for i in removed.iter().rev() {
7386 stmts.remove(*i);
7387 }
7388 pos - removed.len()
7389}
7390
7391pub fn fix_ctor_super_first(body: &mut Stmt, vt: &VarTable) {
7392 let Stmt::Block(stmts) = body else { return };
7393 if stmts.is_empty() || is_bare_ctor_call(stmts.first().unwrap()) {
7394 return;
7395 }
7396 if let Some(pos0) = stmts.iter().position(is_bare_ctor_call) {
7398 let pos = fold_sb_chain(stmts, pos0);
7399 let mut call = stmts[pos].clone();
7400 let prefix: Vec<&Stmt> = stmts[..pos].iter().collect();
7401 let mut consumed: Vec<(u32, usize)> = Vec::new();
7402 if !inline_ctor_arg_defs(&mut call, &prefix, vt, &mut consumed) {
7403 return;
7404 }
7405 stmts.remove(pos);
7406 let mut drop_idx: Vec<usize> = consumed
7413 .iter()
7414 .filter(|(v, _)| !var_occurs_in(&stmts[pos..], *v))
7415 .map(|(_, k)| *k)
7416 .collect();
7417 drop_idx.sort_unstable();
7418 drop_idx.dedup();
7419 for k in drop_idx.iter().rev() {
7420 stmts.remove(*k);
7421 }
7422 stmts.insert(0, call);
7423 return;
7424 }
7425 let mut found: Option<(usize, usize)> = None;
7435 for (i, st) in stmts.iter().enumerate() {
7436 if let Stmt::Block(inner) = st {
7437 if let Some(p) = inner.iter().position(is_bare_ctor_call) {
7438 if inner[..p]
7439 .iter()
7440 .all(|s| matches!(s, Stmt::LocalDef { .. } | Stmt::ExprStmt(_)))
7441 {
7442 found = Some((i, p));
7443 break;
7444 }
7445 }
7446 }
7447 }
7448 if let Some((i, p)) = found {
7449 let mut call = match &stmts[i] {
7450 Stmt::Block(inner) => inner[p].clone(),
7451 _ => unreachable!(),
7452 };
7453 let mut prefix: Vec<&Stmt> = stmts[..i].iter().collect();
7454 if let Stmt::Block(inner) = &stmts[i] {
7455 prefix.extend(inner[..p].iter());
7456 }
7457 let mut consumed: Vec<(u32, usize)> = Vec::new();
7458 if !inline_ctor_arg_defs(&mut call, &prefix, vt, &mut consumed) {
7459 return;
7460 }
7461 let mut ks: Vec<usize> = Vec::new();
7468 {
7469 let inner_tail: &[Stmt] = match &stmts[i] {
7470 Stmt::Block(inner) if p < inner.len() => &inner[p + 1..],
7471 _ => &[],
7472 };
7473 for (v, k) in &consumed {
7474 if !var_occurs_in(inner_tail, *v) && !var_occurs_in(&stmts[i + 1..], *v) {
7475 ks.push(*k);
7476 }
7477 }
7478 }
7479 ks.sort_unstable();
7480 ks.dedup();
7481 if let Stmt::Block(inner) = &mut stmts[i] {
7482 inner.remove(p);
7483 for k in ks.iter().rev().filter(|k| **k >= i) {
7484 inner.remove(k - i);
7485 }
7486 }
7487 for k in ks.iter().rev().filter(|k| **k < i) {
7488 stmts.remove(*k);
7489 }
7490 stmts.insert(0, call);
7491 }
7492}
7493
7494fn is_delegation_expr(e: &Expr) -> bool {
7503 matches!(
7504 e,
7505 Expr::Method { name, is_special: true, owner, .. }
7506 if &**name == "<init>"
7507 && (owner.is_none() || matches!(owner.as_deref(), Some(Expr::This)))
7508 )
7509}
7510
7511pub(crate) fn is_bare_ctor_call(s: &Stmt) -> bool {
7512 matches!(s, Stmt::ExprStmt(e) if is_delegation_expr(e))
7513}
7514
7515pub(crate) fn local_is_written(body: &Stmt, var: u32) -> bool {
7519 let mut hit = false;
7520 let mut c = body.clone();
7521 walk_stmt_exprs(&mut c, &mut |e| {
7522 if !hit {
7523 deep_rewrite(e, &mut |x| {
7524 if hit {
7525 return;
7526 }
7527 match x {
7528 Expr::Assign { target, .. }
7529 if matches!(&**target, Expr::Local { var: v, .. } if *v == var) =>
7530 {
7531 hit = true;
7532 }
7533 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. }
7534 if matches!(&**e, Expr::Local { var: v, .. } if *v == var) =>
7535 {
7536 hit = true;
7537 }
7538 _ => {}
7539 }
7540 });
7541 }
7542 });
7543 hit
7544}
7545
7546pub(crate) fn rewrite_inner_ctor_outer_param(
7556 body: &mut Stmt,
7557 param0: u32,
7558 outer_display: &str,
7559 outer_ty: &TypeRef,
7560 eligible: &[String],
7561) {
7562 walk_stmt_exprs(body, &mut |e| {
7566 if let Expr::Method { name, cls, args, .. } = e {
7567 if &**name == "<init>"
7568 && eligible.iter().any(|c| c.as_str() == cls.as_ref())
7569 && args.first().is_some_and(|a| matches!(a, Expr::Local { var: v, .. } if *v == param0))
7570 {
7571 args.remove(0);
7572 }
7573 }
7574 });
7575 let text = format!("{outer_display}.this");
7577 let ty = outer_ty.clone();
7578 walk_stmt_exprs(body, &mut |e| {
7579 deep_rewrite_reads(e, &mut |x| {
7580 if let Expr::Local { var: v, .. } = x {
7581 if *v == param0 {
7582 *x = Expr::RawT(text.clone(), ty.clone());
7583 }
7584 }
7585 });
7586 });
7587}
7588
7589fn stmts_count_writes(stmts: &[Stmt], var: u32) -> usize {
7637 let mut n = 0usize;
7638 for s in stmts {
7639 let mut c = s.clone();
7640 walk_stmt_exprs(&mut c, &mut |e| {
7641 deep_rewrite(e, &mut |x| match x {
7642 Expr::Assign { target, .. }
7643 if matches!(&**target, Expr::Local { var: v, .. } if *v == var) =>
7644 {
7645 n += 1;
7646 }
7647 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. }
7648 if matches!(&**e, Expr::Local { var: v, .. } if *v == var) =>
7649 {
7650 n += 1;
7651 }
7652 _ => {}
7653 });
7654 });
7655 }
7656 n
7657}
7658
7659fn prefix_written_vars(stmts: &[Stmt]) -> jdc_core::FxHashSet<u32> {
7663 let mut set = jdc_core::FxHashSet::default();
7664 for s in stmts {
7665 let mut c = s.clone();
7666 walk_stmt_exprs(&mut c, &mut |e| {
7667 deep_rewrite(e, &mut |x| match x {
7668 Expr::Assign { target, .. } => {
7669 if let Expr::Local { var, .. } = &**target {
7670 set.insert(*var);
7671 }
7672 }
7673 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => {
7674 if let Expr::Local { var, .. } = &**e {
7675 set.insert(*var);
7676 }
7677 }
7678 _ => {}
7679 });
7680 });
7681 if let Stmt::LocalDef { var, .. } = s {
7682 set.insert(*var);
7683 }
7684 }
7685 set
7686}
7687
7688fn pre_this_safe(e: &Expr, prefix_vars: &jdc_core::FxHashSet<u32>) -> bool {
7692 match e {
7693 Expr::Local { var, .. } => !prefix_vars.contains(var),
7694 Expr::Const(_) => true,
7695 Expr::Cast { e, .. } => pre_this_safe(e, prefix_vars),
7696 Expr::Bin { l, r, .. } => {
7697 pre_this_safe(l, prefix_vars) && pre_this_safe(r, prefix_vars)
7698 }
7699 Expr::Cond { c, t, f } => {
7700 pre_this_safe(c, prefix_vars)
7701 && pre_this_safe(t, prefix_vars)
7702 && pre_this_safe(f, prefix_vars)
7703 }
7704 Expr::Field { owner, is_static, .. } => {
7705 *is_static
7706 && owner
7707 .as_ref()
7708 .is_none_or(|o| pre_this_safe(o, prefix_vars))
7709 }
7710 Expr::Method {
7711 owner,
7712 args,
7713 is_static,
7714 ..
7715 } => {
7716 args.iter().all(|a| pre_this_safe(a, prefix_vars))
7717 && match owner {
7718 Some(o) => pre_this_safe(o, prefix_vars),
7719 None => *is_static,
7720 }
7721 }
7722 Expr::New { args, .. } => args.iter().all(|a| pre_this_safe(a, prefix_vars)),
7730 Expr::NewArray { dims, init, .. } => {
7731 dims.iter().all(|d| pre_this_safe(d, prefix_vars))
7732 && init
7733 .as_ref()
7734 .map(|l| l.iter().all(|x| pre_this_safe(x, prefix_vars)))
7735 .unwrap_or(true)
7736 }
7737 Expr::Un { e, .. } | Expr::InstanceOf { e, .. } => pre_this_safe(e, prefix_vars),
7739 Expr::ArrayIndex { array, index } => {
7740 pre_this_safe(array, prefix_vars) && pre_this_safe(index, prefix_vars)
7741 }
7742 _ => false,
7743 }
7744}
7745
7746fn single_assign_to_v(s: &Stmt, v: u32) -> Option<Expr> {
7750 match s {
7751 Stmt::ExprStmt(Expr::Assign {
7752 target,
7753 op: AssignOp::Plain,
7754 value,
7755 }) if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v) => {
7756 Some((**value).clone())
7757 }
7758 Stmt::Block(xs) if xs.len() == 1 => single_assign_to_v(&xs[0], v),
7759 _ => None,
7760 }
7761}
7762
7763pub(crate) fn flatten_top_blocks(stmts: &mut Vec<Stmt>) {
7785 fn rec(s: Stmt, out: &mut Vec<Stmt>) {
7786 match s {
7787 Stmt::Block(inner) => {
7788 for x in inner {
7789 rec(x, out);
7790 }
7791 }
7792 other => out.push(other),
7793 }
7794 }
7795 if !stmts.iter().any(|s| matches!(s, Stmt::Block(_))) {
7796 return;
7797 }
7798 let mut out: Vec<Stmt> = Vec::with_capacity(stmts.len());
7799 for s in std::mem::take(stmts) {
7800 rec(s, &mut out);
7801 }
7802 *stmts = out;
7803}
7804
7805fn fold_default_arg_bridge(stmts: &mut Vec<Stmt>) -> bool {
7806 flatten_top_blocks(stmts);
7807 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
7808 return false;
7809 };
7810 if pos == 0 {
7811 return false;
7812 }
7813 let Stmt::ExprStmt(Expr::Method { args, .. }) = &stmts[pos] else {
7814 return false;
7815 };
7816 let mut arg_vars: Vec<u32> = Vec::new();
7817 let mut reads = 0usize;
7818 for a in args {
7819 let mut c = a.clone();
7820 deep_rewrite_reads(&mut c, &mut |x| {
7821 if let Expr::Local { var, .. } = x {
7822 reads += 1;
7823 if !arg_vars.contains(var) {
7824 arg_vars.push(*var);
7825 }
7826 }
7827 });
7828 }
7829 if arg_vars.is_empty() || reads != arg_vars.len() {
7830 return false; }
7832 for &v in &arg_vars {
7835 if stmts_read_var(&stmts[pos + 1..], v) != 0
7836 || stmts_count_writes(&stmts[pos + 1..], v) != 0
7837 {
7838 return false;
7839 }
7840 }
7841 let prefix_vars = prefix_written_vars(&stmts[..pos]);
7842 let mut repl: Vec<(u32, Expr)> = Vec::new();
7843 let mut drop: Vec<usize> = Vec::new();
7844 for &v in &arg_vars {
7845 let mut base: Option<(usize, Expr)> = None;
7847 let mut decl: Option<usize> = None;
7848 let mut condf: Option<(usize, Expr, Option<Expr>, Option<Expr>)> = None;
7849 let mut ok = true;
7850 for (i, s) in stmts[..pos].iter().enumerate() {
7851 match s {
7852 Stmt::LocalDef { var, init, .. } if *var == v => match init {
7853 None => {
7854 if decl.is_some() {
7855 ok = false;
7856 break;
7857 }
7858 decl = Some(i);
7859 }
7860 Some(e) => {
7861 if base.is_some() {
7862 ok = false;
7863 break;
7864 }
7865 base = Some((i, e.clone()));
7866 }
7867 },
7868 Stmt::ExprStmt(Expr::Assign {
7869 target,
7870 op: AssignOp::Plain,
7871 value,
7872 }) if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v) => {
7873 if base.is_some() {
7874 ok = false;
7875 break;
7876 }
7877 base = Some((i, (**value).clone()));
7878 }
7879 Stmt::If {
7880 cond,
7881 then_stmt,
7882 else_stmt,
7883 } => {
7884 let t = single_assign_to_v(then_stmt, v);
7885 let f = else_stmt.as_ref().and_then(|b| single_assign_to_v(b, v));
7886 let then_touches = t.is_some()
7887 || stmts_read_var(std::slice::from_ref(&**then_stmt), v) != 0
7888 || stmts_count_writes(std::slice::from_ref(&**then_stmt), v) != 0;
7889 let else_touches = else_stmt.as_ref().is_some_and(|b| {
7890 f.is_some()
7891 || stmts_read_var(std::slice::from_ref(b), v) != 0
7892 || stmts_count_writes(std::slice::from_ref(b), v) != 0
7893 });
7894 if !then_touches && !else_touches {
7895 continue; }
7897 if condf.is_some()
7902 || base.is_none()
7903 || (then_touches && t.is_none())
7904 || (else_touches && f.is_none())
7905 {
7906 ok = false;
7907 break;
7908 }
7909 let cond_stmt = Stmt::ExprStmt(cond.clone());
7910 if stmts_read_var(std::slice::from_ref(&cond_stmt), v) != 0 {
7911 ok = false;
7912 break;
7913 }
7914 condf = Some((i, cond.clone(), t, f));
7915 }
7916 _ => {}
7917 }
7918 }
7919 if !ok {
7920 continue;
7921 }
7922 let Some((bi, be)) = base else { continue };
7923 let want_writes = 1
7926 + condf
7927 .as_ref()
7928 .map(|(_, _, t, f)| t.is_some() as usize + f.is_some() as usize)
7929 .unwrap_or(0);
7930 if stmts_count_writes(&stmts[..pos], v) != want_writes
7931 || stmts_read_var(&stmts[..pos], v) != 0
7932 {
7933 continue;
7934 }
7935 let rep = match condf {
7936 Some((ci, c, t, f)) => {
7937 drop.push(ci);
7938 match (t, f) {
7939 (Some(t), Some(f)) => Expr::Cond {
7940 c: Box::new(c),
7941 t: Box::new(t),
7942 f: Box::new(f),
7943 },
7944 (Some(t), None) => Expr::Cond {
7945 c: Box::new(c),
7946 t: Box::new(t),
7947 f: Box::new(be.clone()),
7948 },
7949 (None, Some(f)) => Expr::Cond {
7950 c: Box::new(c),
7951 t: Box::new(be.clone()),
7952 f: Box::new(f),
7953 },
7954 (None, None) => unreachable!("condf requires a branch assign"),
7955 }
7956 }
7957 None => be.clone(),
7958 };
7959 if !pre_this_safe(&rep, &prefix_vars) {
7960 continue;
7961 }
7962 drop.push(bi);
7963 if let Some(d) = decl {
7964 drop.push(d);
7965 }
7966 repl.push((v, rep));
7967 }
7968 if repl.is_empty() {
7969 return false;
7970 }
7971 if let Stmt::ExprStmt(Expr::Method { args, .. }) = &mut stmts[pos] {
7972 for a in args.iter_mut() {
7973 deep_rewrite_reads(a, &mut |x| {
7974 if let Expr::Local { var, .. } = x {
7975 if let Some((_, e)) = repl.iter().find(|(vv, _)| vv == var) {
7976 *x = e.clone();
7977 }
7978 }
7979 });
7980 }
7981 }
7982 drop.sort_unstable();
7983 drop.dedup();
7984 for i in drop.into_iter().rev() {
7985 stmts.remove(i);
7986 }
7987 true
7988}
7989
7990pub fn rewrite_kotlin_facades(body: &mut Stmt, pool: &crate::DexPool) {
7998 let map = pool.kotlin_facade_map();
7999 if map.is_empty() {
8000 return;
8001 }
8002 walk_stmt_exprs(body, &mut |e| {
8003 match e {
8004 Expr::Method {
8005 cls,
8006 name,
8007 desc,
8008 is_static,
8009 ..
8010 } if *is_static => {
8011 if let Some((base, cover)) = map.get(cls.as_ref()) {
8012 let hit = match cover {
8013 None => true,
8014 Some(c) => c.0.contains(&(name.to_string(), desc.to_string())),
8015 };
8016 if hit {
8017 *cls = std::sync::Arc::from(base.as_str());
8018 }
8019 }
8020 }
8021 Expr::Field {
8022 cls,
8023 name,
8024 is_static,
8025 ..
8026 } if *is_static => {
8027 if let Some((base, cover)) = map.get(cls.as_ref()) {
8028 let hit = match cover {
8029 None => true,
8030 Some(c) => c.1.contains(name.as_ref()),
8031 };
8032 if hit {
8033 *cls = std::sync::Arc::from(base.as_str());
8034 }
8035 }
8036 }
8037 _ => {}
8038 }
8039 });
8040}
8041
8042pub fn fold_enum_default_arg_bridge(body: &mut Stmt) {
8050 let Stmt::Block(stmts) = body else { return };
8051 fold_default_arg_bridge(stmts);
8052}
8053
8054pub fn fix_ctor_delegation_arg_defs(body: &mut Stmt) {
8055 let Stmt::Block(stmts) = body else { return };
8056 fold_default_arg_bridge(stmts);
8059 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
8063 return;
8064 };
8065 if pos == 0 {
8066 return;
8067 }
8068 let mut arg_vars: Vec<u32> = Vec::new();
8071 let mut total_reads = 0usize;
8072 {
8073 let Stmt::ExprStmt(Expr::Method { args, .. }) = &stmts[pos] else {
8074 return;
8075 };
8076 for a in args {
8077 let mut c = a.clone();
8078 deep_rewrite_reads(&mut c, &mut |x| {
8079 if let Expr::Local { var: v, .. } = x {
8080 total_reads += 1;
8081 if !arg_vars.contains(v) {
8082 arg_vars.push(*v);
8083 }
8084 }
8085 });
8086 }
8087 }
8088 if arg_vars.is_empty() || total_reads != arg_vars.len() {
8089 return;
8090 }
8091 let prefix_defs: Vec<u32> = stmts[..pos]
8094 .iter()
8095 .filter_map(|s| match s {
8096 Stmt::LocalDef { var, init: Some(_), .. } => Some(*var),
8097 _ => None,
8098 })
8099 .collect();
8100 let mut plan: Vec<(u32, usize, Option<usize>)> = Vec::new();
8105 for v in arg_vars {
8106 let defs: Vec<usize> = stmts[..pos]
8107 .iter()
8108 .enumerate()
8109 .filter(|(i, _)| {
8110 matches!(&stmts[*i], Stmt::LocalDef { var, init: Some(_), .. } if *var == v)
8111 })
8112 .map(|(i, _)| i)
8113 .collect();
8114 if defs.len() > 1 {
8115 return; }
8117 let Some(&q) = defs.first() else {
8118 continue; };
8120 let init = match &stmts[q] {
8121 Stmt::LocalDef { init: Some(e), .. } => e.clone(),
8122 _ => return,
8123 };
8124 if stmts_read_var(&stmts[q + 1..pos], v) != 0 {
8125 return; }
8127 let mut init_vars: Vec<u32> = Vec::new();
8130 deep_rewrite_reads(&mut { init.clone() }, &mut |x| {
8131 if let Expr::Local { var: vv, .. } = x {
8132 if !init_vars.contains(vv) {
8133 init_vars.push(*vv);
8134 }
8135 }
8136 });
8137 if init_vars.iter().any(|iv| prefix_defs.contains(iv)) {
8138 return;
8139 }
8140 match stmts[pos + 1..]
8141 .iter()
8142 .position(|s| {
8143 matches!(s, Stmt::ExprStmt(Expr::Assign { target, .. })
8144 if matches!(&**target, Expr::Local { var: vv, .. } if *vv == v))
8145 }) {
8146 Some(w) => {
8147 if stmts_read_var(&stmts[pos + 1..pos + 1 + w], v) != 0 {
8149 return;
8150 }
8151 plan.push((v, q, Some(pos + 1 + w)));
8152 }
8153 None => {
8154 if stmts_read_var(&stmts[pos + 1..], v) != 0 {
8156 return;
8157 }
8158 plan.push((v, q, None));
8159 }
8160 }
8161 }
8162 if plan.is_empty() {
8163 return;
8164 }
8165 let values: Vec<(u32, Expr)> = plan
8167 .iter()
8168 .map(|(_v, q, _)| match &stmts[*q] {
8169 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e.clone()),
8170 _ => unreachable!("plan entries carry a LocalDef with init"),
8171 })
8172 .collect();
8173 if let Stmt::ExprStmt(Expr::Method { args, .. }) = &mut stmts[pos] {
8174 for a in args {
8175 deep_rewrite_reads(a, &mut |x| {
8176 if let Expr::Local { var: v, .. } = x {
8177 if let Some((_, e)) = values.iter().find(|(vv, _)| vv == v) {
8178 *x = e.clone();
8179 }
8180 }
8181 });
8182 }
8183 }
8184 for (v, _, w) in &plan {
8188 if let Some(w) = w {
8189 if let Stmt::ExprStmt(Expr::Assign { target, value, .. }) = &mut stmts[*w] {
8190 if matches!(&**target, Expr::Local { var: vv, .. } if vv == v) {
8191 let taken = std::mem::replace(&mut **value, Expr::Const(ConstVal::Null));
8192 stmts[*w] = Stmt::LocalDef {
8193 var: *v,
8194 init: Some(taken),
8195 is_final: false,
8196 force_type: false,
8197 };
8198 }
8199 }
8200 }
8201 }
8202 let mut order: Vec<usize> = plan.iter().map(|(_, q, _)| *q).collect();
8203 order.sort_unstable();
8204 order.reverse();
8205 for q in order {
8206 stmts.remove(q);
8207 }
8208}
8209
8210pub fn fix_bool_returns(vt: &VarTable, body: &mut Stmt) {
8221 walk_mut_deep(body, &mut |st| {
8222 if let Stmt::Return(Some(e)) = st {
8223 let numeric = match &*e {
8224 Expr::Local { var, ty } => {
8225 let t = if (*var as usize) < vt.vars.len() {
8226 vt.var(*var).ty.erased()
8227 } else {
8228 ty.erased()
8229 };
8230 matches!(
8231 t,
8232 JavaType::Int
8233 | JavaType::Long
8234 | JavaType::Short
8235 | JavaType::Byte
8236 | JavaType::Char
8237 )
8238 }
8239 other => matches!(
8240 other.type_ref().erased(),
8241 JavaType::Int | JavaType::Long | JavaType::Short | JavaType::Byte
8242 | JavaType::Char
8243 ),
8244 };
8245 if numeric {
8246 let taken = e.clone();
8247 *e = Expr::Bin {
8248 op: BinOp::Ne,
8249 l: Box::new(taken),
8250 r: Box::new(Expr::Const(ConstVal::Int(0))),
8251 ty: Some(TypeRef::J(JavaType::Boolean)),
8252 };
8253 }
8254 }
8255 });
8256}
8257
8258pub fn fix_int_returns(vt: &VarTable, body: &mut Stmt) {
8259 walk_mut_deep(body, &mut |st| {
8260 if let Stmt::Return(Some(e)) = st {
8261 let is_bool = match e {
8262 Expr::Local { var, .. } => {
8263 (*var as usize) < vt.vars.len()
8264 && vt.vars[*var as usize].ty.erased() == JavaType::Boolean
8265 }
8266 Expr::Method { desc, .. } => desc.ret == JavaType::Boolean,
8267 _ => false,
8268 };
8269 if is_bool {
8270 *e = Expr::Cond {
8271 c: Box::new(e.clone()),
8272 t: Box::new(Expr::Const(ConstVal::Int(1))),
8273 f: Box::new(Expr::Const(ConstVal::Int(0))),
8274 };
8275 }
8276 }
8277 });
8278}
8279
8280pub fn split_generations(vt: &mut VarTable, body: &mut Stmt, pool: &DexPool) {
8296 let mut counter: u32 = 0;
8297 let mut gen: std::collections::HashMap<u32, u32> = std::collections::HashMap::default();
8298 split_walk_stmt(body, vt, &mut gen, &mut counter, pool);
8299}
8300
8301fn split_needed(vt: &VarTable, target: u32, value: &Expr, pool: &DexPool) -> bool {
8303 let n = vt.vars.len();
8304 if target as usize >= n {
8305 return false;
8306 }
8307 let declared = vt.vars[target as usize].ty.erased();
8308 let vt_is_ref = |t: &JavaType| matches!(t, JavaType::Object(_) | JavaType::Array(_));
8309 fn side_bool(e: &Expr, vt: &VarTable) -> bool {
8318 match e {
8319 Expr::Local { var, .. } if (*var as usize) < vt.vars.len() => {
8320 matches!(vt.var(*var).ty.erased(), JavaType::Boolean)
8321 }
8322 Expr::Const(ConstVal::Int(0 | 1)) => true,
8323 Expr::Bin { op: BinOp::Or | BinOp::And | BinOp::Xor, l, r, .. } => {
8324 side_bool(l, vt) && side_bool(r, vt)
8325 }
8326 other => matches!(other.type_ref().erased(), JavaType::Boolean),
8327 }
8328 }
8329 let value_ty = match value {
8330 Expr::Local { var, ty } if (*var as usize) < vt.vars.len() => vt.var(*var).ty.erased(),
8331 Expr::Bin {
8332 op: BinOp::Or | BinOp::And | BinOp::Xor,
8333 l,
8334 r,
8335 ..
8336 } if side_bool(l, vt) && side_bool(r, vt) => JavaType::Boolean,
8337 other => other.type_ref().erased(),
8338 };
8339 if vt_is_ref(&declared) && vt_is_ref(&value_ty) {
8340 let obj = JavaType::Object("java/lang/Object".into());
8350 if declared == value_ty || declared == obj || value_ty == obj {
8351 return false;
8352 }
8353 if let (JavaType::Object(v), JavaType::Object(d)) = (&value_ty, &declared) {
8366 if matches!(value, Expr::New { .. })
8367 && (pool.get(v).is_none() || pool.get(d).is_none())
8368 {
8369 return false;
8370 }
8371 }
8372 true
8373 } else {
8374 if vt_is_ref(&declared) != vt_is_ref(&value_ty) {
8378 return true;
8379 }
8380 let d_bool = matches!(declared, JavaType::Boolean);
8388 let v_bool = matches!(value_ty, JavaType::Boolean);
8389 let zero_one = matches!(value, Expr::Const(ConstVal::Int(0 | 1)));
8390 d_bool != v_bool && !zero_one
8391 }
8392}
8393
8394fn rewrite_gen_reads(e: &mut Expr, gen: &std::collections::HashMap<u32, u32>, vt: &VarTable) {
8395 deep_rewrite(e, &mut |x| {
8396 if let Expr::Local { var, ty } = x {
8397 if let Some(&to) = gen.get(var) {
8398 if to != *var {
8399 *var = to;
8400 *ty = vt.var(to).ty.clone();
8401 }
8402 }
8403 }
8404 });
8405}
8406
8407fn do_split(
8408 vt: &mut VarTable,
8409 target: &mut Expr,
8410 old: u32,
8411 value_ty: &JavaType,
8412 gen: &mut std::collections::HashMap<u32, u32>,
8413 counter: &mut u32,
8414) {
8415 *counter += 1;
8416 let info = &vt.vars[old as usize];
8417 let id = vt.vars.len() as u32;
8418 let name = format!("{}_g{}", info.name, counter);
8419 let slot = info.slot;
8420 let is_param = false;
8428 vt.vars.push(jdc_core::var::VarInfo {
8429 id,
8430 slot,
8431 name,
8432 ty: TypeRef::J(value_ty.clone()),
8433 is_param,
8434 range_start: 0,
8435 range_end: u16::MAX,
8436 synthetic_name: true,
8437 });
8438 while vt.by_slot.len() <= slot as usize {
8439 vt.by_slot.push(Vec::new());
8440 }
8441 vt.by_slot[slot as usize].push((0, u16::MAX, id));
8442 gen.insert(old, id);
8443 if let Expr::Local { var, ty, .. } = target {
8444 *var = id;
8445 *ty = TypeRef::J(value_ty.clone());
8446 }
8447}
8448
8449fn rename_gen_back(s: &mut Stmt, from: u32, to: u32, vt: &VarTable) {
8461 if from == to || (to as usize) >= vt.vars.len() {
8462 return;
8463 }
8464 let to_ty = vt.vars[to as usize].ty.clone();
8465 walk_mut_deep(s, &mut |st| {
8466 if let Stmt::LocalDef { var, .. } = st {
8467 if *var == from {
8468 *var = to;
8469 }
8470 }
8471 });
8472 rewrite_exprs(s, &mut |e| {
8473 deep_rewrite(e, &mut |x| {
8474 if let Expr::Local { var, ty } = x {
8475 if *var == from {
8476 *var = to;
8477 *ty = to_ty.clone();
8478 }
8479 }
8480 });
8481 });
8482}
8483
8484fn drop_gen_slot(vt: &mut VarTable, dead: u32) {
8488 if (dead as usize) >= vt.vars.len() {
8489 return;
8490 }
8491 let slot = vt.vars[dead as usize].slot as usize;
8492 if let Some(seg) = vt.by_slot.get_mut(slot) {
8493 seg.retain(|&(_, _, id)| id != dead);
8494 }
8495}
8496
8497fn undo_type_safe(vt: &VarTable, k: u32, v: u32) -> bool {
8506 if (k as usize) >= vt.vars.len() || (v as usize) >= vt.vars.len() {
8507 return false;
8508 }
8509 let kt = vt.vars[k as usize].ty.erased();
8510 let vt_ty = vt.vars[v as usize].ty.erased();
8511 if kt == vt_ty {
8512 return true;
8513 }
8514 let is_prim = |t: &JavaType| {
8515 matches!(
8516 t,
8517 JavaType::Int
8518 | JavaType::Long
8519 | JavaType::Short
8520 | JavaType::Byte
8521 | JavaType::Char
8522 | JavaType::Float
8523 | JavaType::Double
8524 | JavaType::Boolean
8525 )
8526 };
8527 is_prim(&kt) && is_prim(&vt_ty)
8528}
8529
8530fn split_walk_stmt(
8531 st: &mut Stmt,
8532 vt: &mut VarTable,
8533 gen: &mut std::collections::HashMap<u32, u32>,
8534 counter: &mut u32,
8535 pool: &DexPool,
8536) {
8537 match st {
8538 Stmt::Block(v) => {
8539 for x in v.iter_mut() {
8540 split_walk_stmt(x, vt, gen, counter, pool);
8541 }
8542 }
8543 Stmt::ExprStmt(e) => {
8544 rewrite_gen_reads(e, gen, vt);
8545 if let Expr::Assign {
8546 target,
8547 value,
8548 op: AssignOp::Plain,
8549 ..
8550 } = e
8551 {
8552 if let Expr::Local { var, .. } = &**target {
8553 let old = *var;
8554 if split_needed(vt, old, value, pool) {
8555 let value_ty = value.type_ref().erased();
8556 do_split(vt, target, old, &value_ty, gen, counter);
8557 }
8558 }
8559 }
8560 }
8561 Stmt::LocalDef { var, init, .. } => {
8562 if let Some(e) = init {
8563 rewrite_gen_reads(e, gen, vt);
8564 if split_needed(vt, *var, e, pool) {
8565 let value_ty = e.type_ref().erased();
8569 let old = *var;
8570 let mut fake = Expr::Local {
8571 var: old,
8572 ty: vt.vars[old as usize].ty.clone(),
8573 };
8574 do_split(vt, &mut fake, old, &value_ty, gen, counter);
8575 if let Expr::Local { var: nv, .. } = fake {
8576 *var = nv;
8577 }
8578 }
8579 }
8580 }
8581 Stmt::If {
8582 cond,
8583 then_stmt,
8584 else_stmt,
8585 ..
8586 } => {
8587 rewrite_gen_reads(cond, gen, vt);
8588 let then_floor = vt.vars.len() as u32;
8589 let mut g_then = gen.clone();
8590 split_walk_stmt(then_stmt, vt, &mut g_then, counter, pool);
8591 let else_floor = vt.vars.len() as u32;
8592 let mut g_else = gen.clone();
8593 if let Some(e) = else_stmt {
8594 split_walk_stmt(e, vt, &mut g_else, counter, pool);
8595 }
8596 let mut undo_then: Vec<(u32, u32)> = g_then
8613 .iter()
8614 .filter(|(&k, &v)| {
8615 v >= then_floor
8616 && v < else_floor
8617 && g_else.get(&k).copied() == gen.get(&k).copied()
8618 && undo_type_safe(vt, k, v)
8619 && stmts_read_var(std::slice::from_ref(&**then_stmt), v) == 0
8620 })
8621 .map(|(&k, &v)| (v, k))
8622 .collect();
8623 undo_then.sort_unstable_by_key(|&(v, _)| std::cmp::Reverse(v));
8624 for &(v, k) in &undo_then {
8625 rename_gen_back(then_stmt, v, k, vt);
8626 drop_gen_slot(vt, v);
8627 }
8628 if let Some(e) = else_stmt.as_deref_mut() {
8629 let mut undo_else: Vec<(u32, u32)> = g_else
8630 .iter()
8631 .filter(|(&k, &v)| {
8632 v >= else_floor
8633 && g_then.get(&k).copied() == gen.get(&k).copied()
8634 && undo_type_safe(vt, k, v)
8635 && stmts_read_var(std::slice::from_ref(&*e), v) == 0
8636 })
8637 .map(|(&k, &v)| (v, k))
8638 .collect();
8639 undo_else.sort_unstable_by_key(|&(v, _)| std::cmp::Reverse(v));
8640 for &(v, k) in &undo_else {
8641 rename_gen_back(e, v, k, vt);
8642 drop_gen_slot(vt, v);
8643 }
8644 }
8645 gen.retain(|k, v| g_then.get(k) == Some(v) && g_else.get(k) == Some(v));
8648 }
8649 Stmt::While { cond, body, .. } | Stmt::DoWhile { cond, body, .. } => {
8650 let entry = gen.clone();
8654 for _ in 0..3 {
8655 rewrite_gen_reads(cond, gen, vt);
8656 split_walk_stmt(body, vt, gen, counter, pool);
8657 }
8658 *gen = entry;
8661 }
8662 Stmt::For { init, body, .. } => {
8663 for x in init.iter_mut() {
8664 split_walk_stmt(x, vt, gen, counter, pool);
8665 }
8666 let entry = gen.clone();
8667 for _ in 0..3 {
8668 split_walk_stmt(body, vt, gen, counter, pool);
8669 }
8670 *gen = entry;
8671 }
8672 Stmt::ForEach { body, .. } => {
8673 let entry = gen.clone();
8674 for _ in 0..3 {
8675 split_walk_stmt(body, vt, gen, counter, pool);
8676 }
8677 *gen = entry;
8678 }
8679 Stmt::Switch { cases, default, .. } => {
8680 let entry = gen.clone();
8681 for c in cases.iter_mut() {
8682 let mut g_case = entry.clone();
8683 for x in c.body.iter_mut() {
8684 split_walk_stmt(x, vt, &mut g_case, counter, pool);
8685 }
8686 gen.retain(|k, v| g_case.get(k) == Some(v));
8687 }
8688 if let Some(d) = default {
8689 let mut g_def = entry.clone();
8690 split_walk_stmt(d, vt, &mut g_def, counter, pool);
8691 gen.retain(|k, v| g_def.get(k) == Some(v));
8692 }
8693 }
8694 Stmt::Try {
8695 body,
8696 catches,
8697 finally,
8698 } => {
8699 let entry = gen.clone();
8700 let mut g_try = entry.clone();
8701 split_walk_stmt(body, vt, &mut g_try, counter, pool);
8702 let mut g_all = g_try.clone();
8703 for c in catches.iter_mut() {
8704 let mut g_c = entry.clone();
8707 split_walk_stmt(&mut c.body, vt, &mut g_c, counter, pool);
8708 g_all.retain(|k, v| g_c.get(k) == Some(v));
8709 }
8710 *gen = g_all;
8711 if let Some(f) = finally {
8712 split_walk_stmt(f, vt, gen, counter, pool);
8713 }
8714 }
8715 Stmt::Synchronized { body, .. } | Stmt::Labeled { body, .. } => {
8716 split_walk_stmt(body, vt, gen, counter, pool);
8717 }
8718 Stmt::Return(Some(e)) | Stmt::Throw(e) => {
8719 rewrite_gen_reads(e, gen, vt);
8720 }
8721 _ => {}
8722 }
8723}
8724
8725static FW_EXC_SUPERS: &[(&str, &str)] = &[
8741 ("java/lang/Exception", "java/lang/Throwable"),
8743 ("java/lang/RuntimeException", "java/lang/Exception"),
8744 ("java/lang/IllegalArgumentException", "java/lang/RuntimeException"),
8745 ("java/lang/IllegalStateException", "java/lang/RuntimeException"),
8746 ("java/lang/NullPointerException", "java/lang/RuntimeException"),
8747 ("java/lang/ArithmeticException", "java/lang/RuntimeException"),
8748 ("java/lang/ClassCastException", "java/lang/RuntimeException"),
8749 ("java/lang/IndexOutOfBoundsException", "java/lang/RuntimeException"),
8750 ("java/lang/ArrayIndexOutOfBoundsException", "java/lang/IndexOutOfBoundsException"),
8751 ("java/lang/StringIndexOutOfBoundsException", "java/lang/IndexOutOfBoundsException"),
8752 ("java/lang/NumberFormatException", "java/lang/IllegalArgumentException"),
8753 ("java/lang/UnsupportedOperationException", "java/lang/RuntimeException"),
8754 ("java/lang/SecurityException", "java/lang/RuntimeException"),
8755 ("java/lang/NegativeArraySizeException", "java/lang/RuntimeException"),
8756 ("java/lang/EnumConstantNotPresentException", "java/lang/RuntimeException"),
8757 ("java/lang/TypeNotPresentException", "java/lang/RuntimeException"),
8758 ("java/lang/CloneNotSupportedException", "java/lang/Exception"),
8759 ("java/lang/InterruptedException", "java/lang/Exception"),
8760 ("java/lang/ReflectiveOperationException", "java/lang/Exception"),
8761 ("java/lang/ClassNotFoundException", "java/lang/ReflectiveOperationException"),
8762 ("java/lang/NoSuchFieldException", "java/lang/ReflectiveOperationException"),
8763 ("java/lang/NoSuchMethodException", "java/lang/ReflectiveOperationException"),
8764 ("java/lang/InstantiationException", "java/lang/ReflectiveOperationException"),
8765 ("java/lang/IllegalAccessException", "java/lang/ReflectiveOperationException"),
8766 ("java/lang/reflect/InvocationTargetException", "java/lang/ReflectiveOperationException"),
8767 ("java/lang/reflect/UndeclaredThrowableException", "java/lang/RuntimeException"),
8768 ("java/lang/invoke/WrongMethodTypeException", "java/lang/RuntimeException"),
8769 ("java/lang/annotation/IncompleteAnnotationException", "java/lang/RuntimeException"),
8770 ("java/lang/Error", "java/lang/Throwable"),
8772 ("java/lang/AssertionError", "java/lang/Error"),
8773 ("java/lang/VirtualMachineError", "java/lang/Error"),
8774 ("java/lang/OutOfMemoryError", "java/lang/VirtualMachineError"),
8775 ("java/lang/StackOverflowError", "java/lang/VirtualMachineError"),
8776 ("java/lang/LinkageError", "java/lang/Error"),
8777 ("java/lang/IncompatibleClassChangeError", "java/lang/LinkageError"),
8778 ("java/lang/NoSuchMethodError", "java/lang/IncompatibleClassChangeError"),
8779 ("java/lang/NoSuchFieldError", "java/lang/IncompatibleClassChangeError"),
8780 ("java/lang/NoClassDefFoundError", "java/lang/LinkageError"),
8781 ("java/lang/ExceptionInInitializerError", "java/lang/LinkageError"),
8782 ("java/lang/UnsatisfiedLinkError", "java/lang/LinkageError"),
8783 ("java/lang/VerifyError", "java/lang/LinkageError"),
8784 ("java/lang/ClassFormatError", "java/lang/LinkageError"),
8785 ("java/lang/BootstrapMethodError", "java/lang/LinkageError"),
8786 ("java/util/ConcurrentModificationException", "java/lang/RuntimeException"),
8788 ("java/util/NoSuchElementException", "java/lang/RuntimeException"),
8789 ("java/util/MissingResourceException", "java/lang/RuntimeException"),
8790 ("java/util/EmptyStackException", "java/lang/RuntimeException"),
8791 ("java/util/TooManyListenersException", "java/lang/Exception"),
8792 ("java/util/ServiceConfigurationError", "java/lang/Error"),
8793 ("java/util/concurrent/ExecutionException", "java/lang/Exception"),
8794 ("java/util/concurrent/TimeoutException", "java/lang/Exception"),
8795 ("java/util/concurrent/BrokenBarrierException", "java/lang/Exception"),
8796 ("java/util/concurrent/RejectedExecutionException", "java/lang/RuntimeException"),
8797 ("java/util/concurrent/CompletionException", "java/lang/RuntimeException"),
8798 ("java/util/concurrent/CancellationException", "java/lang/IllegalStateException"),
8799 ("java/io/IOException", "java/lang/Exception"),
8801 ("java/io/FileNotFoundException", "java/io/IOException"),
8802 ("java/io/InterruptedIOException", "java/io/IOException"),
8803 ("java/io/UnsupportedEncodingException", "java/io/IOException"),
8804 ("java/io/UTFDataFormatException", "java/io/IOException"),
8805 ("java/io/EOFException", "java/io/IOException"),
8806 ("java/io/CharConversionException", "java/io/IOException"),
8807 ("java/io/ObjectStreamException", "java/io/IOException"),
8808 ("java/io/InvalidObjectException", "java/io/ObjectStreamException"),
8809 ("java/io/NotSerializableException", "java/io/ObjectStreamException"),
8810 ("java/net/SocketException", "java/io/IOException"),
8811 ("java/net/ConnectException", "java/net/SocketException"),
8812 ("java/net/BindException", "java/net/SocketException"),
8813 ("java/net/NoRouteToHostException", "java/net/SocketException"),
8814 ("java/net/ProtocolException", "java/io/IOException"),
8815 ("java/net/UnknownHostException", "java/io/IOException"),
8816 ("java/net/MalformedURLException", "java/io/IOException"),
8817 ("java/net/HttpRetryException", "java/io/IOException"),
8818 ("java/net/SocketTimeoutException", "java/io/InterruptedIOException"),
8819 ("java/net/URISyntaxException", "java/lang/Exception"),
8820 ("java/text/ParseException", "java/lang/Exception"),
8822 ("java/security/GeneralSecurityException", "java/lang/Exception"),
8823 ("java/security/NoSuchAlgorithmException", "java/security/GeneralSecurityException"),
8824 ("java/security/NoSuchProviderException", "java/security/GeneralSecurityException"),
8825 ("java/security/InvalidKeyException", "java/security/GeneralSecurityException"),
8826 ("java/security/InvalidAlgorithmParameterException", "java/security/GeneralSecurityException"),
8827 ("java/security/SignatureException", "java/security/GeneralSecurityException"),
8828 ("java/security/UnrecoverableKeyException", "java/security/GeneralSecurityException"),
8829 ("java/security/KeyStoreException", "java/security/GeneralSecurityException"),
8830 ("java/security/cert/CertificateException", "java/security/GeneralSecurityException"),
8831 ("java/security/spec/InvalidKeySpecException", "java/security/GeneralSecurityException"),
8832 ("javax/crypto/BadPaddingException", "java/security/GeneralSecurityException"),
8833 ("javax/crypto/IllegalBlockSizeException", "java/security/GeneralSecurityException"),
8834 ("javax/crypto/NoSuchPaddingException", "java/security/GeneralSecurityException"),
8835 ("javax/crypto/ShortBufferException", "java/security/GeneralSecurityException"),
8836 ("javax/net/ssl/SSLException", "java/io/IOException"),
8837 ("org/xml/sax/SAXException", "java/lang/Exception"),
8838 ("org/xml/sax/SAXParseException", "org/xml/sax/SAXException"),
8839 ("org/json/JSONException", "java/lang/RuntimeException"),
8840 ("org/xmlpull/v1/XmlPullParserException", "java/lang/Exception"),
8841 ("java/util/IllegalFormatException", "java/lang/IllegalArgumentException"),
8842 ("java/util/MissingFormatArgumentException", "java/util/IllegalFormatException"),
8843 ("java/util/UnknownFormatConversionException", "java/util/IllegalFormatException"),
8844 ("android/util/AndroidRuntimeException", "java/lang/RuntimeException"),
8846 ("android/util/AndroidException", "java/lang/Exception"),
8847 ("android/content/ActivityNotFoundException", "java/lang/RuntimeException"),
8848 ("android/content/ComponentNotFoundException", "java/lang/RuntimeException"),
8849 ("android/content/pm/PackageManager$NameNotFoundException", "java/lang/Exception"),
8850 ("android/content/res/Resources$NotFoundException", "java/lang/RuntimeException"),
8851 ("android/database/SQLException", "java/lang/RuntimeException"),
8852 ("android/database/sqlite/SQLiteException", "android/database/SQLException"),
8853 ("android/database/CursorIndexOutOfBoundsException", "java/lang/IndexOutOfBoundsException"),
8854 ("android/os/NetworkOnMainThreadException", "java/lang/RuntimeException"),
8855 ("android/system/ErrnoException", "java/lang/Exception"),
8856 ("android/provider/Settings$SettingNotFoundException", "android/util/AndroidException"),
8857 ("android/net/UriParseException", "java/lang/RuntimeException"),
8858 ("android/view/InflateException", "java/lang/RuntimeException"),
8859 ("android/accounts/AuthenticatorException", "java/lang/Exception"),
8860 ("android/accounts/OperationCanceledException", "android/accounts/AuthenticatorException"),
8861 ("android/hardware/camera2/CameraAccessException", "java/lang/Exception"),
8862 ("android/opengl/GLException", "java/lang/RuntimeException"),
8863 ("android/renderscript/RSRuntimeException", "java/lang/RuntimeException"),
8864];
8865
8866fn fw_super(c: &str) -> Option<&'static str> {
8867 FW_EXC_SUPERS
8868 .iter()
8869 .find(|(k, _)| *k == c)
8870 .map(|(_, v)| *v)
8871}
8872
8873fn exc_subtype(sub: &str, sup: &str, pool: &DexPool) -> bool {
8879 if sub == sup {
8880 return true;
8881 }
8882 if pool.is_subtype(sub, sup) {
8883 return true;
8884 }
8885 let mut cur = sub;
8886 for _ in 0..24 {
8887 let Some(next) = fw_super(cur) else { break };
8888 if next == sup {
8889 return true;
8890 }
8891 cur = next;
8892 }
8893 false
8894}
8895
8896pub fn dedupe_multicatch(s: &mut Stmt, pool: &DexPool) {
8902 dedupe_multicatch_walk(s, pool);
8903}
8904
8905fn dedupe_multicatch_walk(s: &mut Stmt, pool: &DexPool) {
8906 match s {
8907 Stmt::Try {
8908 body,
8909 catches,
8910 finally,
8911 }
8912 | Stmt::TryWithResources {
8913 body,
8914 catches,
8915 finally,
8916 ..
8917 } => {
8918 dedupe_multicatch_walk(body, pool);
8919 for c in catches.iter_mut() {
8920 if c.exc.len() > 1 {
8921 let mut keep: Vec<std::sync::Arc<str>> = Vec::with_capacity(c.exc.len());
8922 for e in c.exc.iter() {
8923 let shadowed = c.exc.iter().any(|o| {
8924 o.as_ref() != e.as_ref() && exc_subtype(e, o, pool)
8925 });
8926 if !shadowed && !keep.iter().any(|k| k.as_ref() == e.as_ref()) {
8927 keep.push(e.clone());
8928 }
8929 }
8930 c.exc = keep;
8931 }
8932 dedupe_multicatch_walk(&mut c.body, pool);
8933 }
8934 if let Some(f) = finally {
8935 dedupe_multicatch_walk(f, pool);
8936 }
8937 }
8938 Stmt::Block(v) => {
8939 for x in v.iter_mut() {
8940 dedupe_multicatch_walk(x, pool);
8941 }
8942 }
8943 Stmt::If {
8944 then_stmt,
8945 else_stmt,
8946 ..
8947 } => {
8948 dedupe_multicatch_walk(then_stmt, pool);
8949 if let Some(e) = else_stmt {
8950 dedupe_multicatch_walk(e, pool);
8951 }
8952 }
8953 Stmt::While { body, .. }
8954 | Stmt::DoWhile { body, .. }
8955 | Stmt::For { body, .. }
8956 | Stmt::ForEach { body, .. }
8957 | Stmt::Labeled { body, .. }
8958 | Stmt::Synchronized { body, .. } => dedupe_multicatch_walk(body, pool),
8959 Stmt::Switch { cases, default, .. } => {
8960 for c in cases.iter_mut() {
8961 for x in c.body.iter_mut() {
8962 dedupe_multicatch_walk(x, pool);
8963 }
8964 }
8965 if let Some(d) = default {
8966 dedupe_multicatch_walk(d, pool);
8967 }
8968 }
8969 _ => {}
8970 }
8971}
8972
8973pub fn resolve_dangling_gotos(s: &mut Stmt) {
8985 let mut labels: jdc_core::FxHashSet<u32> = jdc_core::FxHashSet::default();
8986 let probe = s.clone();
8987 walk_all(&probe, &mut |st| {
8988 if let Stmt::Label(id) = st {
8989 labels.insert(*id);
8990 }
8991 });
8992 resolve_gotos_walk(s, &labels, 0);
8993}
8994
8995fn resolve_gotos_walk(s: &mut Stmt, labels: &jdc_core::FxHashSet<u32>, loop_depth: u32) {
8996 let depth = match s {
8997 Stmt::While { .. } | Stmt::DoWhile { .. } | Stmt::For { .. } | Stmt::ForEach { .. } => {
8998 loop_depth + 1
8999 }
9000 _ => loop_depth,
9001 };
9002 match s {
9003 Stmt::Goto(id) => {
9004 if labels.contains(id) {
9005 return; }
9007 if depth > 0 {
9008 *s = Stmt::Break(None);
9009 } else {
9010 *s = Stmt::Comment(format!(
9011 "$DDC: unresolved jump to block {} (irreducible control flow)",
9012 id
9013 ));
9014 }
9015 }
9016 Stmt::Block(v) => {
9017 let mut i = 0usize;
9018 while i < v.len() {
9019 let dangling_here =
9024 depth == 0 && matches!(&v[i], Stmt::Goto(id) if !labels.contains(id));
9025 resolve_gotos_walk(&mut v[i], labels, depth);
9026 if dangling_here {
9027 v.truncate(i + 1);
9028 break;
9029 }
9030 i += 1;
9031 }
9032 }
9033 Stmt::If { then_stmt, else_stmt, .. } => {
9034 resolve_gotos_walk(then_stmt, labels, depth);
9035 if let Some(e) = else_stmt {
9036 resolve_gotos_walk(e, labels, depth);
9037 }
9038 }
9039 Stmt::While { body, .. }
9040 | Stmt::DoWhile { body, .. }
9041 | Stmt::For { body, .. }
9042 | Stmt::ForEach { body, .. }
9043 | Stmt::Labeled { body, .. }
9044 | Stmt::Synchronized { body, .. } => resolve_gotos_walk(body, labels, depth),
9045 Stmt::Try {
9046 body,
9047 catches,
9048 finally,
9049 } => {
9050 resolve_gotos_walk(body, labels, depth);
9051 for c in catches.iter_mut() {
9052 resolve_gotos_walk(&mut c.body, labels, depth);
9053 }
9054 if let Some(f) = finally {
9055 resolve_gotos_walk(f, labels, depth);
9056 }
9057 }
9058 Stmt::Switch { cases, default, .. } => {
9059 for c in cases.iter_mut() {
9060 for x in c.body.iter_mut() {
9061 resolve_gotos_walk(x, labels, depth);
9062 }
9063 }
9064 if let Some(d) = default {
9065 resolve_gotos_walk(d, labels, depth);
9066 }
9067 }
9068 _ => {}
9069 }
9070}
9071
9072fn stmts_read_var(stmts: &[Stmt], v: u32) -> usize {
9074 let mut n = 0usize;
9075 for s in stmts {
9076 let mut c = s.clone();
9077 walk_stmt_exprs(&mut c, &mut |e| {
9078 deep_rewrite_reads(e, &mut |x| {
9079 if let Expr::Local { var: vv, .. } = x {
9080 if *vv == v {
9081 n += 1;
9082 }
9083 }
9084 });
9085 });
9086 }
9087 n
9088}
9089
9090pub fn fix_ctor_conditional_super(body: &mut Stmt) {
9091 if !matches!(body, Stmt::Block(_)) {
9094 let inner = std::mem::replace(body, Stmt::Block(Vec::new()));
9095 *body = Stmt::Block(vec![inner]);
9096 }
9097 let Stmt::Block(stmts) = body else { return };
9098 while stmts.len() == 1 {
9102 let Stmt::Block(inner) = &stmts[0] else { break };
9103 *stmts = inner.clone();
9104 }
9105 if stmts.is_empty() {
9106 return;
9107 }
9108 if is_bare_ctor_call(stmts.first().unwrap()) {
9109 dedupe_ctor_delegations(body);
9110 return;
9111 }
9112 if try_linear_super_inline(stmts) {
9113 return;
9114 }
9115 try_merge_branched_super(stmts);
9116}
9117
9118pub fn dedupe_ctor_delegations(body: &mut Stmt) {
9128 let Stmt::Block(stmts) = body else { return };
9129 let Some(d0) = stmts.first().and_then(|s| match s {
9130 Stmt::ExprStmt(e) if is_delegation_expr(e) => Some(e.clone()),
9131 _ => None,
9132 }) else {
9133 return;
9134 };
9135 fn rec(s: &mut Stmt, d0: &Expr) {
9136 match s {
9137 Stmt::Block(v) => {
9138 v.retain(|x| {
9139 !matches!(x, Stmt::ExprStmt(e) if is_delegation_expr(e) && same_delegation(e, d0))
9140 });
9141 for x in v.iter_mut() {
9142 rec(x, d0);
9143 }
9144 }
9145 Stmt::If { then_stmt, else_stmt, .. } => {
9146 rec(then_stmt, d0);
9147 if let Some(e) = else_stmt {
9148 rec(e, d0);
9149 }
9150 }
9151 _ => {}
9152 }
9153 }
9154 {
9158 let d0r = &d0;
9159 let mut idx = 0usize;
9160 stmts.retain(|s| {
9161 idx += 1;
9162 idx == 1
9163 || !(matches!(s, Stmt::ExprStmt(e)
9164 if is_delegation_expr(e) && same_delegation(e, d0r)))
9165 });
9166 }
9167 for s in stmts.iter_mut().skip(1) {
9168 rec(s, &d0);
9169 }
9170}
9171
9172fn same_delegation(a: &Expr, b: &Expr) -> bool {
9176 match (a, b) {
9177 (
9178 Expr::Method { name: n1, cls: c1, is_super: s1, args: a1, .. },
9179 Expr::Method { name: n2, cls: c2, is_super: s2, args: a2, .. },
9180 ) => {
9181 &**n1 == "<init>"
9182 && &**n2 == "<init>"
9183 && c1 == c2
9184 && s1 == s2
9185 && (a1 == a2 || (a1.is_empty() && a2.is_empty()))
9186 }
9187 _ => false,
9188 }
9189}
9190
9191fn ctor_call_expr(s: &Stmt) -> Option<&Expr> {
9193 match s {
9194 Stmt::ExprStmt(e) if is_delegation_expr(e) => Some(e),
9195 _ => None,
9196 }
9197}
9198
9199fn same_ctor_kind(a: &Expr, b: &Expr) -> bool {
9201 match (a, b) {
9202 (
9203 Expr::Method { cls: c1, is_super: s1, owner: o1, .. },
9204 Expr::Method { cls: c2, is_super: s2, owner: o2, .. },
9205 ) => c1 == c2 && s1 == s2 && o1 == o2,
9206 _ => false,
9207 }
9208}
9209
9210fn flat_list(s: &Stmt) -> Vec<&Stmt> {
9215 fn rec<'a>(s: &'a Stmt, out: &mut Vec<&'a Stmt>) {
9216 match s {
9217 Stmt::Block(v) => {
9218 for x in v {
9219 rec(x, out);
9220 }
9221 }
9222 other => out.push(other),
9223 }
9224 }
9225 let mut out = Vec::new();
9226 rec(s, &mut out);
9227 out
9228}
9229
9230fn strip_lost_alloc_owners(e: &mut Expr) {
9236 deep_rewrite(e, &mut |x| {
9237 if let Expr::Method { name, is_special: true, owner: Some(o), .. } = x {
9238 if &**name == "<init>" && !matches!(**o, Expr::This) {
9239 **o = Expr::Const(jdc_core::ir::expr::ConstVal::Int(0));
9240 }
9241 }
9242 });
9243}
9244
9245fn count_locals_expr(e: &Expr) -> std::collections::HashMap<u32, usize> {
9248 let mut m = std::collections::HashMap::new();
9249 let mut probe = e.clone();
9250 strip_lost_alloc_owners(&mut probe);
9251 deep_rewrite(&mut probe, &mut |x| {
9252 if let Expr::Local { var: v, .. } = x {
9253 *m.entry(*v).or_insert(0) += 1;
9254 }
9255 });
9256 m
9257}
9258
9259pub(crate) fn count_locals_stmts(ss: &[Stmt]) -> std::collections::HashMap<u32, usize> {
9260 let mut m = std::collections::HashMap::new();
9261 for s in ss {
9262 let mut c = s.clone();
9263 walk_stmt_exprs(&mut c, &mut |e| {
9264 strip_lost_alloc_owners(e);
9265 deep_rewrite(e, &mut |x| {
9266 if let Expr::Local { var: v, .. } = x {
9267 *m.entry(*v).or_insert(0) += 1;
9268 }
9269 });
9270 });
9271 }
9272 m
9273}
9274
9275fn cnt(m: &std::collections::HashMap<u32, usize>, v: u32) -> usize {
9276 m.get(&v).copied().unwrap_or(0)
9277}
9278
9279fn inline_locals(
9283 e: &Expr,
9284 map: &std::collections::HashMap<u32, Expr>,
9285 depth: u32,
9286) -> Option<Expr> {
9287 if depth > 4 {
9288 return None;
9289 }
9290 let mut out = e.clone();
9291 let mut fail = false;
9292 deep_rewrite(&mut out, &mut |x| {
9293 if let Expr::Local { var, .. } = x {
9294 if let Some(def) = map.get(var) {
9295 match inline_locals(def, map, depth + 1) {
9296 Some(r) => *x = r,
9297 None => fail = true,
9298 }
9299 }
9300 }
9301 });
9302 if fail {
9303 None
9304 } else {
9305 Some(out)
9306 }
9307}
9308
9309fn def_of(s: &Stmt) -> Option<(u32, &Expr)> {
9311 match s {
9312 Stmt::LocalDef { var, init: Some(e), .. } => Some((*var, e)),
9313 Stmt::ExprStmt(Expr::Assign {
9314 target,
9315 value,
9316 op: jdc_core::ir::expr::AssignOp::Plain,
9317 ..
9318 }) => match &**target {
9319 Expr::Local { var, .. } => Some((*var, value)),
9320 _ => None,
9321 },
9322 _ => None,
9323 }
9324}
9325
9326fn try_linear_super_inline(stmts: &mut Vec<Stmt>) -> bool {
9328 let Some(pos) = stmts.iter().position(is_bare_ctor_call) else {
9329 return false;
9330 };
9331 if pos == 0 {
9332 return false; }
9334 if !stmts[..pos]
9336 .iter()
9337 .all(|s| matches!(s, Stmt::LocalDef { .. } | Stmt::ExprStmt(_)))
9338 {
9339 return false;
9340 }
9341 let Some(call) = ctor_call_expr(&stmts[pos]) else {
9342 return false;
9343 };
9344 let Expr::Method { args, .. } = call else {
9345 return false;
9346 };
9347 let mut map: std::collections::HashMap<u32, Expr> =
9349 std::collections::HashMap::new();
9350 let mut def_idx: std::collections::HashMap<u32, usize> =
9351 std::collections::HashMap::new();
9352 for (i, s) in stmts[..pos].iter().enumerate() {
9353 if let Some((v, e)) = def_of(s) {
9354 map.insert(v, e.clone());
9355 def_idx.insert(v, i);
9356 }
9357 }
9358 let mut needed: Vec<u32> = Vec::new();
9360 for a in args {
9361 let mut probe = a.clone();
9362 strip_lost_alloc_owners(&mut probe);
9363 deep_rewrite(&mut probe, &mut |x| {
9364 if let Expr::Local { var, .. } = x {
9365 if map.contains_key(var) && !needed.contains(var) {
9366 needed.push(*var);
9367 }
9368 }
9369 });
9370 }
9371 if needed.is_empty() {
9372 return false; }
9374 let mut consumed: Vec<usize> = Vec::new();
9377 let use_counts = count_locals_stmts(stmts);
9378 for &v in &needed {
9379 let def = &map[&v];
9380 let uses = cnt(&use_counts, v);
9381 if jdc_core::ir::build::has_side_effects(def) && uses != 1 {
9382 return false;
9383 }
9384 if uses == 1 {
9385 consumed.push(def_idx[&v]);
9386 }
9387 }
9388 let mut new_args: Vec<Expr> = Vec::with_capacity(args.len());
9390 for a in args {
9391 match inline_locals(a, &map, 0) {
9392 Some(r) => new_args.push(r),
9393 None => return false,
9394 }
9395 }
9396 let mut call_stmt = stmts.remove(pos);
9399 if let Stmt::ExprStmt(Expr::Method { args: slot, .. }) = &mut call_stmt {
9400 *slot = new_args;
9401 }
9402 let mut rest: Vec<Stmt> = Vec::with_capacity(stmts.len() + 1);
9403 rest.push(call_stmt);
9404 for (i, s) in stmts.drain(..).enumerate() {
9405 let orig = if i >= pos { i + 1 } else { i };
9408 if !consumed.contains(&orig) {
9409 rest.push(s);
9410 }
9411 }
9412 *stmts = rest;
9413 true
9414}
9415
9416fn try_merge_branched_super(stmts: &mut Vec<Stmt>) -> bool {
9418 let if_positions: Vec<usize> = stmts
9422 .iter()
9423 .enumerate()
9424 .filter(|(_, s)| matches!(s, Stmt::If { .. }))
9425 .map(|(i, _)| i)
9426 .collect();
9427 for if_pos in if_positions {
9428 let gate = match &stmts[if_pos] {
9434 Stmt::If { then_stmt, else_stmt, .. } => {
9435 contains_delegation(then_stmt)
9436 || else_stmt.as_ref().is_some_and(|e| contains_delegation(e))
9437 }
9438 _ => false,
9439 };
9440 if gate && merge_at(stmts, if_pos) {
9441 return true;
9442 }
9443 }
9444 false
9445}
9446
9447fn contains_delegation(s: &Stmt) -> bool {
9450 match s {
9451 Stmt::ExprStmt(e) => is_delegation_expr(e),
9452 Stmt::Block(v) => v.iter().any(contains_delegation),
9453 Stmt::If { then_stmt, else_stmt, .. } => {
9454 contains_delegation(then_stmt)
9455 || else_stmt.as_ref().is_some_and(|e| contains_delegation(e))
9456 }
9457 _ => false,
9458 }
9459}
9460
9461
9462
9463fn merge_at(stmts: &mut Vec<Stmt>, if_pos: usize) -> bool {
9476 enum Br {
9481 Call(usize, Vec<(u32, Expr)>, Vec<usize>),
9483 Clean,
9484 Dirty,
9485 }
9486 fn split_branch(list: &[&Stmt]) -> Br {
9487 let call_pos = list.iter().position(|s| ctor_call_expr(s).is_some());
9488 let Some(c) = call_pos else {
9489 return if list.iter().any(|s| contains_delegation(s)) {
9492 Br::Dirty
9493 } else {
9494 Br::Clean
9495 };
9496 };
9497 if list.iter().skip(c + 1).any(|s| ctor_call_expr(s).is_some()) {
9498 return Br::Dirty; }
9500 let mut defs: Vec<(u32, Expr)> = Vec::new();
9501 let mut def_idxs: Vec<usize> = Vec::new();
9502 for (i, s) in list.iter().enumerate().take(c) {
9503 match def_of(s) {
9504 Some((v, e)) => {
9505 defs.push((v, e.clone()));
9506 def_idxs.push(i);
9507 }
9508 None => match s {
9509 Stmt::ExprStmt(_) | Stmt::LocalDef { .. } => {}
9512 _ => return Br::Dirty, },
9514 }
9515 }
9516 Br::Call(c, defs, def_idxs)
9517 }
9518 fn cond_def_of(s: &Stmt, scope: &[Stmt]) -> Option<(u32, Expr)> {
9520 let Stmt::If { cond, then_stmt, else_stmt: Some(e), .. } = s else {
9521 return None;
9522 };
9523 fn one<'x>(b: &'x Stmt, scope: &[Stmt]) -> Option<(u32, &'x Expr)> {
9524 let l = flat_list(b);
9525 if l.len() == 1 {
9526 return def_of(l[0]);
9527 }
9528 if l.len() == 2 {
9537 let (w, ex) = def_of(l[0])?;
9538 let Stmt::ExprStmt(Expr::Assign {
9539 target,
9540 op: AssignOp::Plain,
9541 value,
9542 }) = l[1]
9543 else {
9544 return None;
9545 };
9546 let Expr::Local { var: tv, .. } = &**target else {
9547 return None;
9548 };
9549 let Expr::Local { var: rv, .. } = &**value else {
9550 return None;
9551 };
9552 if *rv != w || *tv == w {
9553 return None;
9554 }
9555 let mut ldef_writes = 0usize;
9559 for st in scope {
9560 walk_all(st, &mut |x| {
9561 if let Stmt::LocalDef {
9562 var,
9563 init: Some(_),
9564 ..
9565 } = x
9566 {
9567 if *var == w {
9568 ldef_writes += 1;
9569 }
9570 }
9571 });
9572 }
9573 if stmts_read_var(scope, w) != 1
9574 || stmts_count_writes(scope, w) + ldef_writes != 1
9575 {
9576 return None;
9577 }
9578 return Some((*tv, ex));
9579 }
9580 None
9581 }
9582 let (v1, e1) = one(then_stmt, scope)?;
9583 let (v2, e2) = one(e, scope)?;
9584 if v1 != v2 {
9585 return None;
9586 }
9587 Some((
9588 v1,
9589 Expr::Cond {
9590 c: Box::new(cond.clone()),
9591 t: Box::new(e1.clone()),
9592 f: Box::new(e2.clone()),
9593 },
9594 ))
9595 }
9596
9597 let mut pmap: std::collections::HashMap<u32, Expr> =
9599 std::collections::HashMap::new();
9600 let mut pidx: std::collections::HashMap<u32, usize> =
9601 std::collections::HashMap::new();
9602 for (i, s) in stmts[..if_pos].iter().enumerate() {
9603 match s {
9604 Stmt::LocalDef { .. } | Stmt::ExprStmt(_) => {
9605 if let Some((v, e)) = def_of(s) {
9606 pmap.insert(v, e.clone());
9607 pidx.insert(v, i);
9608 }
9609 }
9610 Stmt::If { .. } => match cond_def_of(s, stmts) {
9611 Some((v, e)) => {
9612 pmap.insert(v, e);
9613 pidx.insert(v, i);
9614 }
9615 None => return false,
9616 },
9617 _ => return false,
9618 }
9619 }
9620 let (cond, then_v, else_v, had_else) = match &stmts[if_pos] {
9621 Stmt::If { cond, then_stmt, else_stmt, .. } => (
9622 cond.clone(),
9623 then_stmt.as_ref().clone(),
9624 else_stmt.as_ref().map_or_else(|| Stmt::Block(Vec::new()), |e| e.as_ref().clone()),
9625 else_stmt.is_some(),
9626 ),
9627 _ => return false,
9628 };
9629 fn normalize_branch(b: &mut Stmt) {
9635 let slot: &mut Stmt = match b {
9640 Stmt::Block(v) if v.len() == 1 => &mut v[0],
9641 other => other,
9642 };
9643 if !matches!(slot, Stmt::If { .. }) || !contains_delegation(slot) {
9644 return;
9645 }
9646 let taken = std::mem::replace(slot, Stmt::Block(Vec::new()));
9647 let mut tmp = vec![taken];
9648 if merge_at(&mut tmp, 0) {
9649 *slot = Stmt::Block(tmp);
9650 } else {
9651 *slot = tmp.pop().unwrap();
9652 }
9653 }
9654 let mut then_v = then_v;
9655 let mut else_v = else_v;
9656 normalize_branch(&mut then_v);
9657 normalize_branch(&mut else_v);
9658 let then_list = flat_list(&then_v);
9659 let else_list = flat_list(&else_v);
9660
9661 enum Side {
9664 Call {
9665 at: usize,
9666 defs: Vec<(u32, Expr)>,
9667 def_idxs: Vec<usize>,
9668 },
9669 Clean,
9670 }
9671 let (t_side, e_side, two_sided) = match (split_branch(&then_list), split_branch(&else_list)) {
9672 (Br::Call(tc, td, ti), Br::Call(ec, ed, ei)) => (
9673 Side::Call { at: tc, defs: td, def_idxs: ti },
9674 Side::Call { at: ec, defs: ed, def_idxs: ei },
9675 true,
9676 ),
9677 (Br::Call(tc, td, ti), Br::Clean) => (
9678 Side::Call { at: tc, defs: td, def_idxs: ti },
9679 Side::Clean,
9680 false,
9681 ),
9682 (Br::Clean, Br::Call(ec, ed, ei)) => (
9683 Side::Clean,
9684 Side::Call { at: ec, defs: ed, def_idxs: ei },
9685 false,
9686 ),
9687 _ => return false,
9688 };
9689 let (t_call, e_call) = match (&t_side, &e_side) {
9691 (Side::Call { at: ta, .. }, Side::Call { at: ea, .. }) => (
9692 ctor_call_expr(then_list[*ta]),
9693 ctor_call_expr(else_list[*ea]),
9694 ),
9695 (Side::Call { at: ta, .. }, Side::Clean) => {
9696 (ctor_call_expr(then_list[*ta]), None)
9697 }
9698 (Side::Clean, Side::Call { at: ea, .. }) => {
9699 (None, ctor_call_expr(else_list[*ea]))
9700 }
9701 _ => return false,
9702 };
9703 let (call_list, call_at) = match &t_side {
9705 Side::Call { at, .. } => (&then_list, *at),
9706 Side::Clean => match &e_side {
9707 Side::Call { at, .. } => (&else_list, *at),
9708 Side::Clean => return false,
9709 },
9710 };
9711 let (base_args, other_args) = if two_sided {
9712 let (Some(tc), Some(ec)) = (t_call, e_call) else {
9713 return false;
9714 };
9715 if !same_ctor_kind(tc, ec) {
9716 return false;
9717 }
9718 let (Expr::Method { args: a1, .. }, Expr::Method { args: a2, .. }) = (tc, ec) else {
9719 return false;
9720 };
9721 if a1.len() != a2.len() {
9722 return false;
9723 }
9724 (a1, Some(a2))
9725 } else {
9726 let c = t_call.or(e_call);
9727 let Some(c) = c else { return false };
9728 let Expr::Method { args, .. } = c else {
9729 return false;
9730 };
9731 (args, None)
9732 };
9733
9734 let mut tmap: std::collections::HashMap<u32, Expr> = pmap.clone();
9736 let mut emap: std::collections::HashMap<u32, Expr> = pmap.clone();
9737 if let Side::Call { defs, .. } = &t_side {
9738 for (v, e) in defs {
9739 tmap.insert(*v, e.clone());
9740 }
9741 }
9742 if let Side::Call { defs, .. } = &e_side {
9743 for (v, e) in defs {
9744 emap.insert(*v, e.clone());
9745 }
9746 }
9747 let Some(cond_i) = inline_locals(&cond, &pmap, 0) else {
9748 return false;
9749 };
9750 let mut then_args: Vec<Expr> = Vec::with_capacity(base_args.len());
9752 let mut else_args: Vec<Expr> = Vec::new();
9753 if two_sided {
9754 for a in base_args {
9755 match inline_locals(a, &tmap, 0) {
9756 Some(r) => then_args.push(r),
9757 None => return false,
9758 }
9759 }
9760 for a in other_args.unwrap() {
9761 match inline_locals(a, &emap, 0) {
9762 Some(r) => else_args.push(r),
9763 None => return false,
9764 }
9765 }
9766 } else {
9767 let from_then = matches!(&t_side, Side::Call { .. });
9770 for a in base_args {
9771 let r = if from_then {
9772 inline_locals(a, &tmap, 0)
9773 } else {
9774 inline_locals(a, &emap, 0)
9775 };
9776 match r {
9777 Some(r) => then_args.push(r),
9778 None => return false,
9779 }
9780 }
9781 }
9782 let primary_args = base_args;
9783 let mut merged: Vec<Expr> = Vec::with_capacity(then_args.len());
9785 let mut differ = 0usize;
9786 for (j, t) in then_args.iter().enumerate() {
9787 if two_sided {
9788 let e = &else_args[j];
9789 if t == e {
9790 merged.push(t.clone());
9791 } else {
9792 differ += 1;
9793 merged.push(Expr::Cond {
9794 c: Box::new(cond_i.clone()),
9795 t: Box::new(t.clone()),
9796 f: Box::new(e.clone()),
9797 });
9798 }
9799 } else {
9800 merged.push(t.clone());
9801 }
9802 }
9803
9804 let vars_in = |e: &Expr| -> Vec<u32> {
9806 let mut vs = Vec::new();
9807 let mut probe = e.clone();
9808 strip_lost_alloc_owners(&mut probe);
9809 deep_rewrite(&mut probe, &mut |x| {
9810 if let Expr::Local { var, .. } = x {
9811 if !vs.contains(var) {
9812 vs.push(*var);
9813 }
9814 }
9815 });
9816 vs
9817 };
9818 let branch_remainder = |list: &[&Stmt], side: &Side| -> Vec<Stmt> {
9826 match side {
9827 Side::Clean => list.iter().map(|s| (*s).clone()).collect(),
9828 Side::Call { at, def_idxs, .. } => {
9829 list.iter()
9830 .enumerate()
9831 .filter(|(i, _)| *i != *at && !def_idxs.contains(i))
9832 .map(|(_, s)| (*s).clone())
9833 .collect()
9834 }
9835 }
9836 };
9837 let tail_then_v = branch_remainder(&then_list, &t_side);
9838 let tail_else_v = branch_remainder(&else_list, &e_side);
9839 let post = &stmts[if_pos + 1..];
9840 let mut inlined: Vec<u32> = vars_in(&cond);
9841 for a in primary_args.iter().chain(else_args.iter()) {
9842 for v in vars_in(a) {
9843 if !inlined.contains(&v) {
9844 inlined.push(v);
9845 }
9846 }
9847 }
9848 inlined.retain(|v| pmap.contains_key(v) || tmap.contains_key(v) || emap.contains_key(v));
9849 for &v in &inlined {
9851 let in_branch = match (&t_side, &e_side) {
9852 (Side::Call { defs: td, .. }, Side::Call { defs: ed, .. }) => {
9853 td.iter().any(|(d, _)| *d == v) || ed.iter().any(|(d, _)| *d == v)
9854 }
9855 (Side::Call { defs, .. }, Side::Clean)
9856 | (Side::Clean, Side::Call { defs, .. }) => {
9857 defs.iter().any(|(d, _)| *d == v)
9858 }
9859 _ => false,
9860 };
9861 if pmap.contains_key(&v) && in_branch {
9862 return false;
9863 }
9864 }
9865 type Counts = std::collections::HashMap<u32, usize>;
9866 type GuardCounts = (
9867 Counts,
9868 Vec<Counts>,
9869 Vec<Counts>,
9870 Counts,
9871 Counts,
9872 Counts,
9873 Vec<(u32, Counts)>,
9874 );
9875 let (cond_counts, a1_counts, a2_counts, tail1_counts, tail2_counts, post_counts, def_counts): GuardCounts =
9876 if inlined.is_empty() {
9877 Default::default()
9878 } else {
9879 let all_defs: Vec<(u32, &Expr)> = pmap
9880 .iter()
9881 .map(|(k, v)| (*k, v))
9882 .chain(match &t_side {
9883 Side::Call { defs, .. } => defs.iter().map(|(k, v)| (*k, v)).collect(),
9884 Side::Clean => Vec::new(),
9885 })
9886 .chain(match &e_side {
9887 Side::Call { defs, .. } => defs.iter().map(|(k, v)| (*k, v)).collect(),
9888 Side::Clean => Vec::new(),
9889 })
9890 .collect();
9891 (
9892 count_locals_expr(&cond),
9893 primary_args.iter().map(count_locals_expr).collect(),
9894 else_args.iter().map(count_locals_expr).collect(),
9895 count_locals_stmts(&tail_then_v),
9896 count_locals_stmts(&tail_else_v),
9897 count_locals_stmts(post),
9898 all_defs.into_iter().map(|(v, e)| (v, count_locals_expr(e))).collect(),
9899 )
9900 };
9901 for &v in &inlined {
9902 let in_t = matches!(&t_side, Side::Call { defs, .. } if defs.iter().any(|(d, _)| *d == v));
9903 let in_e = matches!(&e_side, Side::Call { defs, .. } if defs.iter().any(|(d, _)| *d == v));
9904 let effectful = [
9905 in_t.then(|| tmap.get(&v)).flatten(),
9906 in_e.then(|| emap.get(&v)).flatten(),
9907 pmap.get(&v),
9908 ]
9909 .into_iter()
9910 .flatten()
9911 .any(jdc_core::ir::build::has_side_effects);
9912 if !effectful {
9913 continue;
9914 }
9915 let u_cond = cnt(&cond_counts, v);
9916 let u_post = cnt(&post_counts, v);
9917 let u_t1 = cnt(&tail1_counts, v);
9918 let u_t2 = cnt(&tail2_counts, v);
9919 let mut u_odef = 0usize;
9920 for (dv, dc) in &def_counts {
9921 if *dv != v && inlined.contains(dv) {
9922 u_odef += cnt(dc, v);
9923 }
9924 }
9925 let mut emit = u_cond * (1 + differ);
9926 let mut uses_args = 0usize;
9927 for j in 0..primary_args.len() {
9928 let c1 = cnt(&a1_counts[j], v);
9929 let c2 = if two_sided && j < a2_counts.len() {
9930 cnt(&a2_counts[j], v)
9931 } else {
9932 0
9933 };
9934 uses_args += c1 + c2;
9935 emit += if two_sided && then_args[j] != else_args[j] {
9936 c1 + c2
9937 } else {
9938 c1.max(c2)
9939 };
9940 }
9941 let ok = if in_t || in_e {
9942 u_cond == 0
9943 && u_post == 0
9944 && u_t1 == 0
9945 && u_t2 == 0
9946 && u_odef == 0
9947 && a1_counts.iter().map(|c| cnt(c, v)).sum::<usize>() <= 1
9948 && a2_counts.iter().map(|c| cnt(c, v)).sum::<usize>() <= 1
9949 && emit <= 2
9950 && (in_t || a1_counts.iter().all(|c| cnt(c, v) == 0))
9951 && (in_e || a2_counts.iter().all(|c| cnt(c, v) == 0))
9952 } else {
9953 u_post == 0
9954 && u_t1 == 0
9955 && u_t2 == 0
9956 && u_odef == 0
9957 && ((u_cond == 1 && uses_args == 0 && emit <= 2)
9958 || (u_cond == 0 && emit <= 1))
9959 };
9960 if !ok {
9961 return false;
9962 }
9963 }
9964
9965 let mut call_stmt = call_list[call_at].clone();
9967 if let Stmt::ExprStmt(Expr::Method { args: slot, .. }) = &mut call_stmt {
9968 *slot = merged;
9969 }
9970 let residual = |v: u32| -> usize {
9971 cnt(&tail1_counts, v) + cnt(&tail2_counts, v) + cnt(&post_counts, v)
9972 };
9973 let mut kept_prelude: Vec<Stmt> = Vec::new();
9974 for (i, s) in stmts[..if_pos].iter().enumerate() {
9975 let consumed = match s {
9976 Stmt::LocalDef { var, .. } => Some(*var),
9977 Stmt::ExprStmt(Expr::Assign { target, .. }) => match &**target {
9978 Expr::Local { var, .. } => Some(*var),
9979 _ => None,
9980 },
9981 Stmt::If { .. } => cond_def_of(s, stmts).map(|(v, _)| v),
9982 _ => None,
9983 };
9984 if let Some(v) = consumed {
9985 if inlined.contains(&v) && pidx.get(&v) == Some(&i) && residual(v) == 0 {
9986 continue;
9987 }
9988 }
9989 kept_prelude.push(s.clone());
9990 }
9991 let build_keep = |list: &[&Stmt], side: &Side| -> Vec<Stmt> {
9992 match side {
9993 Side::Clean => list.iter().map(|s| (*s).clone()).collect(),
9994 Side::Call { at, .. } => {
9995 let mut out = Vec::new();
9996 for (i, s) in list.iter().enumerate() {
9997 if i == *at {
9998 continue;
9999 }
10000 if i < *at {
10001 if let Some((v, _)) = def_of(s) {
10002 if inlined.contains(&v) && residual(v) == 0 {
10003 continue;
10004 }
10005 }
10006 }
10007 out.push((*s).clone());
10008 }
10009 out
10010 }
10011 }
10012 };
10013 let then_keep = build_keep(&then_list, &t_side);
10014 let else_keep = build_keep(&else_list, &e_side);
10015 let else_part = if else_keep.is_empty() && !had_else {
10016 None
10017 } else {
10018 Some(Box::new(Stmt::Block(else_keep)))
10019 };
10020 let new_if = Stmt::If {
10021 cond: cond_i,
10022 then_stmt: Box::new(Stmt::Block(then_keep)),
10023 else_stmt: else_part,
10024 };
10025 let if_empty = match &new_if {
10026 Stmt::If { then_stmt, else_stmt, .. } => {
10027 let be = |s: &Stmt| matches!(s, Stmt::Block(v) if v.is_empty());
10028 be(then_stmt) && else_stmt.as_ref().is_none_or(|e| be(e))
10029 }
10030 _ => false,
10031 };
10032 let mut out: Vec<Stmt> = Vec::with_capacity(stmts.len());
10033 out.push(call_stmt);
10034 out.extend(kept_prelude);
10035 if !if_empty {
10036 out.push(new_if);
10037 }
10038 out.extend(stmts[if_pos + 1..].to_vec());
10039 *stmts = out;
10040 true
10041}
10042
10043
10044pub fn strip_enum_ctor_super(body: &mut Stmt) {
10045 let is_enum_super = |s: &Stmt| {
10046 matches!(
10047 s,
10048 Stmt::ExprStmt(Expr::Method { name, is_super: true, .. }) if &**name == "<init>"
10049 )
10050 };
10051 let Stmt::Block(stmts) = body else {
10052 return;
10053 };
10054 stmts.retain(|s| !is_enum_super(s));
10055 for st in stmts.iter_mut() {
10056 if let Stmt::Block(inner) = st {
10057 inner.retain(|s| !is_enum_super(s));
10058 }
10059 }
10060}
10061
10062pub fn ensure_declared(body: &mut Stmt, vt: &VarTable) {
10063 let n = vt.vars.len().max(1);
10067
10068 let mut seen: HashSet<u32> = HashSet::default();
10071 demote_dup_decls(body, &mut seen, vt);
10072
10073 let mut def_block: Vec<u32> = vec![u32::MAX; n];
10081 let mut sizes: Vec<u32> = vec![0];
10082 let mut next_id: u32 = 1;
10083 scope_pass_a(body, 0, &mut next_id, &mut def_block, &mut sizes);
10084 sizes[0] = next_id;
10085 let mut within: Vec<u32> = vec![0; n];
10086 let mut total: Vec<u32> = vec![0; n];
10087 let mut next_b: u32 = 1;
10088 scope_pass_b(body, 0, &mut next_b, &def_block, &sizes, &mut within, &mut total);
10089 let mut hoist: HashSet<u32> = HashSet::default();
10090 for v in 0..n {
10091 if def_block[v] != u32::MAX && def_block[v] != 0 && within[v] < total[v] {
10092 hoist.insert(v as u32);
10093 }
10094 }
10095 if !hoist.is_empty() {
10096 let mut seed: HashSet<u32> = hoist.iter().copied().collect();
10097 demote_dup_decls(body, &mut seed, vt);
10098 }
10099
10100 let mut declared: Vec<bool> = vec![false; n];
10101 let mut is_param: Vec<bool> = vec![false; n];
10102 for v in &vt.vars {
10103 if v.is_param && (v.id as usize) < n {
10104 is_param[v.id as usize] = true;
10105 }
10106 }
10107 let mut is_catch: Vec<bool> = vec![false; n];
10113 walk_all(body, &mut |st| {
10114 if let Stmt::Try { catches, .. } = st {
10115 for c in catches {
10116 if (c.var as usize) < n {
10117 is_catch[c.var as usize] = true;
10118 }
10119 }
10120 }
10121 });
10122 walk_all(body, &mut |st| {
10123 if let Stmt::LocalDef { var, .. } = st {
10124 if (*var as usize) < n {
10125 declared[*var as usize] = true;
10126 }
10127 }
10128 });
10129 let mut used: HashSet<u32> = HashSet::default();
10130 stmt_collect_vars(body, &mut used, true);
10134
10135 let mut needs_decl: Vec<u32> = used
10138 .into_iter()
10139 .filter(|v| {
10140 let i = *v as usize;
10141 i >= n
10142 || ((!declared[i] && !is_param[i] && !is_catch[i]) || hoist.contains(v))
10143 })
10144 .collect();
10145 needs_decl.sort_unstable();
10146 needs_decl.dedup();
10147
10148 if !needs_decl.is_empty() {
10149 let mut decls: Vec<Stmt> = needs_decl
10150 .into_iter()
10151 .map(|v| Stmt::LocalDef {
10152 var: v,
10153 init: None,
10154 is_final: false,
10155 force_type: true,
10156 })
10157 .collect();
10158 decls.reverse();
10159 match body {
10160 Stmt::Block(v) => {
10161 for d in decls {
10162 v.insert(0, d);
10163 }
10164 }
10165 other => {
10166 let inner = std::mem::replace(other, Stmt::Block(vec![]));
10167 *other = Stmt::Block({
10168 let mut vs = decls;
10169 vs.push(inner);
10170 vs
10171 });
10172 }
10173 }
10174 }
10175}
10176
10177fn demote_dup_decls(s: &mut Stmt, seen: &mut HashSet<u32>, vt: &VarTable) {
10178 match s {
10179 Stmt::Block(v) => {
10182 for x in v.iter_mut() {
10183 demote_dup_decls(x, seen, vt);
10184 }
10185 }
10186 Stmt::LocalDef { var, init, .. } => {
10187 if seen.contains(var) {
10188 let ty = vt.var(*var).ty.clone();
10189 if let Some(e) = init.take() {
10190 *s = Stmt::ExprStmt(Expr::Assign {
10191 target: Box::new(Expr::Local { var: *var, ty }),
10192 op: AssignOp::Plain,
10193 value: Box::new(e),
10194 });
10195 } else {
10196 *s = Stmt::Block(vec![]);
10197 }
10198 } else {
10199 seen.insert(*var);
10200 }
10201 }
10202 other => {
10203 walk_mut(other, &mut |x| demote_dup_decls(x, seen, vt));
10204 }
10205 }
10206}
10207
10208#[allow(dead_code)]
10209fn unused(_: &Vec<CaseGroup>, _: &Catch) {}
10210
10211pub fn apply_local_names(vt: &mut VarTable, body: &Stmt) {
10225 use std::collections::{HashMap, HashSet};
10226
10227 let mut by_call: HashMap<u32, Vec<String>> = HashMap::new();
10230 let mut by_intrinsics: HashMap<u32, String> = HashMap::new();
10231
10232 visit_all_exprs(body, &mut |e| {
10233 if let Expr::Method { cls, name, args, .. } = e {
10234 if (cls.as_ref() == "kotlin/jvm/internal/Intrinsics"
10235 || cls.as_ref() == "kotlin/jvm/internal/IntrinsicsKt")
10236 && matches!(name.as_ref(), "checkNotNullParameter" | "checkParameterIsNotNull")
10237 && args.len() == 2
10238 {
10239 if let (Expr::Local { var, .. }, Expr::Const(ConstVal::Str(s))) = (&args[0], &args[1])
10240 {
10241 by_intrinsics
10242 .entry(*var)
10243 .or_insert_with(|| sanitize_name(s).unwrap_or_default());
10244 }
10245 }
10246 }
10247 });
10248
10249 walk_all(body, &mut |st| {
10250 let (var, value) = match st {
10251 Stmt::LocalDef { var, init: Some(e), .. } => (*var, e),
10252 Stmt::ExprStmt(Expr::Assign { target, value, .. })
10253 if matches!(&**target, Expr::Local { .. }) =>
10254 {
10255 let Expr::Local { var, .. } = &**target else { return };
10256 (*var, value.as_ref())
10257 }
10258 _ => return,
10259 };
10260 if let Some(n) = defining_call_name(value) {
10261 by_call.entry(var).or_default().push(n);
10262 }
10263 });
10264
10265 let sanit = |n: &str| crate::classdec::java_ident(n).into_owned();
10276 let mut taken: HashSet<String> = HashSet::default();
10277 for v in vt.vars.iter() {
10282 if v.synthetic_name {
10287 taken.insert(sanit(&v.name));
10288 }
10289 }
10290 for v in vt.vars.iter_mut() {
10291 if v.synthetic_name {
10292 continue;
10293 }
10294 if !taken.insert(sanit(&v.name)) {
10295 let base = sanit(&v.name);
10296 for i in 2.. {
10297 let cand = format!("{base}{i}");
10298 if taken.insert(cand.clone()) {
10299 v.name = cand;
10300 break;
10301 }
10302 }
10303 }
10304 }
10305 let claim = |taken: &mut HashSet<String>, want: &str| -> String {
10306 let w = crate::classdec::java_ident(want).into_owned();
10307 if taken.insert(w.clone()) {
10308 return w;
10309 }
10310 for i in 2.. {
10311 let cand = format!("{w}{i}");
10312 if taken.insert(cand.clone()) {
10313 return cand;
10314 }
10315 }
10316 w
10317 };
10318
10319 for info in vt.vars.iter_mut() {
10320 if !info.synthetic_name {
10321 continue;
10322 }
10323 if let Some(n) = by_intrinsics.get(&info.id) {
10326 if !n.is_empty() {
10327 info.name = claim(&mut taken, n);
10328 continue;
10329 }
10330 }
10331 if let Some(names) = by_call.get(&info.id) {
10333 let unique: HashSet<&String> = names.iter().collect();
10334 if unique.len() == 1 {
10335 let n = unique.into_iter().next().unwrap();
10336 if !is_java_keyword(n) {
10337 info.name = claim(&mut taken, n);
10338 continue;
10339 }
10340 }
10341 }
10342 let want = type_alias(&info.ty).map(str::to_string).or_else(|| simple_type_name(&info.ty));
10345 if let Some(n) = want {
10346 info.name = claim(&mut taken, &n);
10347 }
10348 }
10349
10350 let mut seen: HashSet<String> = HashSet::default();
10355 for v in vt.vars.iter_mut() {
10356 if !seen.insert(sanit(&v.name)) {
10357 let base = sanit(&v.name);
10358 for i in 2.. {
10359 let cand = format!("{base}{i}");
10360 if seen.insert(cand.clone()) {
10361 v.name = cand;
10362 break;
10363 }
10364 }
10365 }
10366 }
10367}
10368
10369fn simple_type_name(ty: &TypeRef) -> Option<String> {
10371 let TypeRef::J(JavaType::Object(n)) = ty else {
10372 return None;
10373 };
10374 let simple = n.rsplit('/').next().unwrap_or(n);
10375 let simple = simple.rsplit('$').next().unwrap_or(simple);
10376 let mut chars = simple.chars();
10379 let mut out = String::with_capacity(simple.len());
10380 let mut lowered = false;
10381 for c in chars.by_ref() {
10382 if c.is_ascii_alphabetic() && !lowered {
10383 out.push(c.to_ascii_lowercase());
10384 lowered = true;
10385 } else {
10386 out.push(c);
10387 }
10388 if lowered {
10389 break;
10390 }
10391 }
10392 out.extend(chars);
10393 sanitize_name(&out)
10394}
10395
10396fn defining_call_name(e: &Expr) -> Option<String> {
10400 match e {
10401 Expr::Method { name, .. } => {
10402 let base = name
10403 .strip_prefix("get")
10404 .or_else(|| name.strip_prefix("is"))
10405 .filter(|rest| rest.chars().next().is_some_and(|c| c.is_ascii_uppercase()))
10406 .unwrap_or(name);
10407 let first = base.chars().next()?;
10408 let mut out = String::with_capacity(base.len());
10409 out.push(first.to_ascii_lowercase());
10410 out.extend(base.chars().skip(1));
10411 sanitize_name(&out)
10412 }
10413 Expr::New { cls, .. } => {
10414 let simple = cls.rsplit('/').next().unwrap_or(cls);
10415 let simple = simple.rsplit('$').next().unwrap_or(simple);
10416 let first = simple.chars().next()?;
10417 let mut out = String::with_capacity(simple.len());
10418 out.push(first.to_ascii_lowercase());
10419 out.extend(simple.chars().skip(1));
10420 sanitize_name(&out)
10421 }
10422 _ => None,
10423 }
10424}
10425
10426fn sanitize_name(s: &str) -> Option<String> {
10428 let mut out = String::with_capacity(s.len());
10429 for c in s.chars() {
10430 if c.is_ascii_alphanumeric() || c == '_' || c == '$' {
10431 out.push(c);
10432 } else {
10433 out.push('_');
10434 }
10435 }
10436 if out.len() < 2
10437 || out.chars().next().is_some_and(|c| c.is_ascii_digit())
10438 || is_java_keyword(&out)
10439 {
10440 return None;
10441 }
10442 Some(out)
10443}
10444
10445fn type_alias(ty: &TypeRef) -> Option<&'static str> {
10448 let TypeRef::J(JavaType::Object(n)) = ty else {
10449 return None;
10450 };
10451 Some(match n.as_ref() {
10452 "java/lang/String" | "kotlin/String" => "str",
10453 "java/lang/Class" => "cls",
10454 "java/lang/Throwable" => "th",
10455 "java/lang/Object" | "kotlin/Any" => "obj",
10456 "java/util/Iterator" | "kotlin/collections/Iterator" => "it",
10457 "java/lang/Boolean" => "bool",
10458 "java/lang/Integer" => "num",
10459 "java/lang/Character" => "ch",
10460 "java/lang/Byte" => "b",
10461 "java/lang/Short" => "sh",
10462 "java/lang/Float" => "f",
10463 "java/lang/Double" => "d",
10464 "java/lang/Long" => "i",
10465 "java/lang/StringBuilder" => "sb",
10466 "java/util/ArrayList" => "list",
10467 "java/util/HashMap" => "map",
10468 "android/content/Context" => "context",
10469 "android/content/Intent" => "intent",
10470 "android/os/Bundle" => "bundle",
10471 "android/view/View" => "view",
10472 "android/graphics/Bitmap" => "bitmap",
10473 "android/view/ViewGroup" => "viewGroup",
10474 _ => return None,
10475 })
10476}
10477
10478pub(crate) fn visit_all_exprs<F: FnMut(&Expr)>(s: &Stmt, f: &mut F) {
10480 walk_all(s, &mut |st| {
10481 let exprs: Vec<&Expr> = match st {
10482 Stmt::ExprStmt(Expr::Assign { target, value, .. }) => {
10483 let mut v: Vec<&Expr> = vec![value];
10484 if !matches!(&**target, Expr::Local { .. }) {
10485 v.push(target);
10486 }
10487 v
10488 }
10489 Stmt::ExprStmt(e) | Stmt::Throw(e) | Stmt::MonitorEnter(e) | Stmt::MonitorExit(e) => {
10490 vec![e]
10491 }
10492 Stmt::Return(Some(e)) => vec![e],
10493 Stmt::LocalDef { init: Some(e), .. } => vec![e],
10494 Stmt::If { cond, .. } | Stmt::While { cond, .. } | Stmt::DoWhile { cond, .. } => {
10495 vec![cond]
10496 }
10497 _ => Vec::new(),
10498 };
10499 for e in exprs {
10500 visit_exprs(e, f);
10501 }
10502 });
10503}
10504
10505pub fn platform_constants(body: &mut Stmt) {
10510 if !crate::platform::installed() {
10511 return;
10512 }
10513 rewrite_exprs(body, &mut |e| {
10514 if let Expr::Method { cls, name, desc, args, .. } = e {
10515 for a in args.iter_mut() {
10516 let (value, is_long) = match a {
10517 Expr::Const(ConstVal::Int(v)) => (*v as i64, false),
10518 Expr::Const(ConstVal::Long(v)) => (*v, true),
10519 _ => continue,
10520 };
10521 if let Some(named) =
10522 crate::platform::named_constant(cls, name, &desc_to_string(desc), 0, value)
10523 {
10524 let _ = is_long;
10525 *a = Expr::Raw(named);
10526 }
10527 }
10528 }
10529 });
10530}
10531
10532fn desc_to_string(d: &jdc_core::types::MethodDescriptor) -> String {
10534 let mut out = String::from("(");
10535 for a in &d.args {
10536 type_to_desc(a, &mut out);
10537 }
10538 out.push(')');
10539 type_to_desc(&d.ret, &mut out);
10540 out
10541}
10542
10543fn type_to_desc(t: &jdc_core::types::JavaType, out: &mut String) {
10544 match t {
10545 JavaType::Void => out.push('V'),
10546 JavaType::Boolean => out.push('Z'),
10547 JavaType::Byte => out.push('B'),
10548 JavaType::Char => out.push('C'),
10549 JavaType::Short => out.push('S'),
10550 JavaType::Int => out.push('I'),
10551 JavaType::Float => out.push('F'),
10552 JavaType::Long => out.push('J'),
10553 JavaType::Double => out.push('D'),
10554 JavaType::Object(n) => {
10555 out.push('L');
10556 out.push_str(n);
10557 out.push(';');
10558 }
10559 JavaType::Array(inner) => {
10560 out.push('[');
10561 type_to_desc(inner, out);
10562 }
10563 }
10564}
10565
10566pub fn remove_kotlin_checks(s: &mut Stmt) {
10572 walk_mut_deep(s, &mut |st| {
10573 let Stmt::Block(v) = st else { return };
10574 v.retain(|x| !is_kotlin_check(x));
10575 });
10576}
10577
10578fn is_kotlin_check(s: &Stmt) -> bool {
10579 let Stmt::ExprStmt(e) = s else { return false };
10580 let Expr::Method { cls, name, .. } = e else { return false };
10581 (cls.as_ref() == "kotlin/jvm/internal/Intrinsics" || cls.as_ref() == "kotlin/jvm/internal/IntrinsicsKt__Jdk7Kt")
10582 && matches!(
10583 name.as_ref(),
10584 "checkNotNullParameter"
10585 | "checkParameterIsNotNull"
10586 | "checkExpressionValueIsNotNull"
10587 | "checkNotNullExpressionValue"
10588 | "checkReturnedValueIsNotNull"
10589 | "checkFieldIsNotNull"
10590 | "checkNotNull"
10591 )
10592}
10593
10594fn inline_ref_ok(pool: &DexPool, host: &str, target: &str, member: Option<(&str, bool)>) -> bool {
10614 let hpkg = match host.rfind('/') {
10615 Some(i) => &host[..i],
10616 None => "",
10617 };
10618 let mut cur = target;
10619 loop {
10620 let tpkg = match cur.rfind('/') {
10621 Some(i) => &cur[..i],
10622 None => "",
10623 };
10624 if tpkg != hpkg {
10625 if let Some(pc) = pool.get(cur) {
10626 if pc.access & crate::access::ACC_PUBLIC == 0 {
10627 return false;
10628 }
10629 if cur == target {
10630 if let Some((mname, is_method)) = member {
10631 let ok = if is_method {
10632 pc.all_methods().any(|m| {
10633 &*m.name == mname
10634 && m.access & crate::access::ACC_PUBLIC != 0
10635 })
10636 } else {
10637 pc.static_fields
10638 .iter()
10639 .chain(pc.instance_fields.iter())
10640 .any(|f| {
10641 f.name == mname
10642 && f.access & crate::access::ACC_PUBLIC != 0
10643 })
10644 };
10645 if !ok
10650 && (if is_method {
10651 pc.all_methods().any(|m| &*m.name == mname)
10652 } else {
10653 pc.static_fields
10654 .iter()
10655 .chain(pc.instance_fields.iter())
10656 .any(|f| f.name == mname)
10657 })
10658 {
10659 return false;
10660 }
10661 }
10662 }
10663 }
10664 }
10665 match cur.rfind('$') {
10666 Some(i) if i > 0 => cur = &cur[..i],
10667 _ => break,
10668 }
10669 }
10670 true
10671}
10672
10673pub fn inline_accessors(s: &mut Stmt, pool: &DexPool, host: &str) {
10674 rewrite_exprs(s, &mut |e| {
10675 let Expr::Method { cls, name, desc, args, is_static, .. } = e else { return };
10676 if !*is_static || args.is_empty() {
10677 return;
10678 }
10679 let Some(target) = pool.get(cls) else { return };
10680 let desc_s = desc_to_string(desc);
10681 let Some(m) = target.find_method(name, &desc_s) else { return };
10682 if !(m.is_static() && m.access & access::ACC_SYNTHETIC != 0) {
10683 return;
10684 }
10685 let Some(dex) = pool.dex(m.dex_idx) else { return };
10686 let code = match pool.accessor_code(m.dex_idx, m.code_off) {
10691 Some(bytes) => ddc_dex::CodeItem::parse(&bytes, 0),
10692 None => dex.code_at(m.code_off),
10693 };
10694 let Some(code) = code else { return };
10695 let insns: Vec<&Insn> =
10696 code.insns.iter().filter(|i| !matches!(i.kind, InsnKind::Nop)).collect();
10697 let core: Vec<&Insn> = insns
10699 .iter()
10700 .copied()
10701 .filter(|i| !is_const_only_trace(&i.kind, &insns))
10702 .collect();
10703 match accessor_shape(&dex, &core, &code) {
10704 Some(Shape::Identity(arg_i)) => {
10705 if let Some(a) = args.get(arg_i) {
10706 *e = a.clone();
10707 }
10708 }
10709 Some(Shape::FieldRead { arg, cls: field_cls, field, field_ty }) => {
10710 if !inline_ref_ok(pool, host, &field_cls, Some((&field, false))) {
10711 return;
10712 }
10713 if let Some(owner) = args.get(arg).cloned().map(Box::new) {
10714 let ty = TypeRef::J(desc_type(&field_ty));
10715 let name = jdc_core::rename::field_display(
10721 &field_cls,
10722 &field,
10723 &field_ty,
10724 )
10725 .map(std::sync::Arc::from)
10726 .unwrap_or_else(|| field.into());
10727 *e = Expr::Field {
10728 owner: Some(owner),
10729 cls: field_cls.into(),
10730 name,
10731 ty,
10732 is_static: false,
10733 };
10734 }
10735 }
10736 Some(Shape::Forward { cls: tcls, name: tname, desc: tdesc, instance }) => {
10737 if !inline_ref_ok(pool, host, &tcls, Some((&tname, true))) {
10738 return;
10739 }
10740 let disp = jdc_core::rename::field_display(
10744 &tcls,
10745 &tname,
10746 &tdesc.to_string(),
10747 )
10748 .map(std::sync::Arc::from)
10749 .unwrap_or_else(|| tname.into());
10750 *cls = tcls.into();
10751 *name = disp;
10752 *desc = std::sync::Arc::new(tdesc);
10753 if instance && !args.is_empty() {
10754 let recv = args.remove(0);
10756 if let Expr::Method { owner, .. } = e {
10757 *owner = Some(Box::new(recv));
10758 }
10759 }
10760 }
10761 None => {}
10762 }
10763 });
10764}
10765
10766enum Shape {
10768 Identity(usize),
10770 FieldRead { arg: usize, cls: String, field: String, field_ty: String },
10772 Forward { cls: String, name: String, desc: MethodDescriptor, instance: bool },
10775}
10776
10777fn field_of(dex: &ddc_dex::DexFile, field_idx: u32) -> Option<(String, String, String)> {
10779 let f = dex.field(field_idx);
10780 Some((
10781 dex.class_name(f.class_idx),
10784 dex.string(f.name_idx).to_string(),
10785 dex.type_name(f.type_idx).to_string(),
10787 ))
10788}
10789
10790fn param_index(code: &ddc_dex::CodeItem, reg: u16) -> Option<usize> {
10793 let rs = code.registers_size as usize;
10794 let ins = code.ins_size as usize;
10795 let r = reg as usize;
10796 let first = rs.checked_sub(ins)?;
10797 (r >= first && r < rs).then(|| r - first)
10798}
10799
10800fn accessor_shape(
10801 dex: &ddc_dex::DexFile,
10802 core: &[&Insn],
10803 code: &ddc_dex::CodeItem,
10804) -> Option<Shape> {
10805 let core: &[&Insn] = match core.split_first() {
10808 Some((first, rest)) if matches!(first.kind, InsnKind::Const { .. }) => rest,
10809 _ => core,
10810 };
10811 let core: &[&Insn] = match core.split_last() {
10812 Some((last, rest)) if matches!(last.kind, InsnKind::Const { .. }) => rest,
10813 _ => core,
10814 };
10815 match core {
10816 [Insn { kind: InsnKind::Return { src }, .. }] => {
10818 let idx = param_index(code, *src)?;
10819 Some(Shape::Identity(idx))
10820 }
10821 [
10823 Insn { kind: InsnKind::IGet { dst, obj, field_idx }, .. },
10824 Insn { kind: InsnKind::Return { src }, .. },
10825 ] if dst == src => {
10826 let arg = param_index(code, *obj)?;
10827 let (cls, field, field_ty) = field_of(dex, *field_idx)?;
10828 Some(Shape::FieldRead { arg, cls, field, field_ty })
10829 }
10830 [Insn { kind: InsnKind::Invoke { method_idx, regs, kind, .. }, .. }, rest @ ..] if rest.len() <= 2 => {
10832 if !regs.iter().all(|r| param_index(code, *r).is_some()) || regs.is_empty() {
10834 return None;
10835 }
10836 if rest.len() == 2 {
10839 let ok = matches!(rest[0].kind, InsnKind::MoveResult { .. })
10840 && matches!(rest[1].kind, InsnKind::Return { .. } | InsnKind::ReturnVoid);
10841 if !ok {
10842 return None;
10843 }
10844 }
10845 let mid = dex.method(*method_idx);
10846 let cls = dex.class_name(mid.class_idx);
10849 let name = dex.string(mid.name_idx).to_string();
10850 let mut s = String::from("(");
10851 for t in dex.proto_params(mid.proto_idx) {
10852 s.push_str(dex.type_name(*t));
10853 }
10854 s.push(')');
10855 s.push_str(dex.type_name(dex.proto(mid.proto_idx).return_type_idx));
10856 let desc = parse_method_descriptor(&s)?;
10857 Some(Shape::Forward { cls, name, desc, instance: !matches!(kind, InvokeKind::Static) })
10858 }
10859 _ => None,
10860 }
10861}
10862
10863
10864fn is_const_only_trace(kind: &InsnKind, insns: &[&Insn]) -> bool {
10870 let InsnKind::Invoke { kind, regs, .. } = kind else { return false };
10871 if !matches!(kind, InvokeKind::Static) || regs.is_empty() {
10872 return false;
10873 }
10874 regs.iter().all(|r| const_loaded(*r, insns, 0))
10875}
10876
10877fn const_loaded(r: u16, insns: &[&Insn], depth: u8) -> bool {
10880 if depth > 3 {
10881 return false;
10882 }
10883 insns.iter().any(|i| match &i.kind {
10884 InsnKind::Const { dst, .. } => *dst == r,
10885 InsnKind::Move { dst, src } => *dst == r && const_loaded(*src, insns, depth + 1),
10886 _ => false,
10887 })
10888}