1use ddc_dex::insn::{ArithOp, CmpKind, CmpOp, Insn, InsnKind, InvokeKind, Payload};
21use ddc_dex::{CodeItem, DexFile};
22use jdc_core::ir::build::{has_side_effects, BlockResult, SwitchTargets, Term};
23use jdc_core::ir::expr::{AssignOp, BinOp, ConcatPart, ConstVal, Expr, TypeRef, UnOp};
24use jdc_core::ir::stmt::Stmt;
25use jdc_core::types::{JavaType, MethodDescriptor};
26use jdc_core::var::{VarInfo, VarTable};
27
28use crate::DexPool;
29
30pub type BResult<T> = Result<T, String>;
31
32#[derive(Clone, Copy, Default, PartialEq)]
36pub struct MethodFlags(u8);
37
38impl MethodFlags {
39 const SB: u8 = 1;
40 const MONITOR: u8 = 2;
41 const CMP: u8 = 4;
42 pub fn none() -> MethodFlags {
43 MethodFlags(0)
44 }
45 pub fn with_sb(self) -> MethodFlags {
46 MethodFlags(self.0 | Self::SB)
47 }
48 pub fn with_monitor(self) -> MethodFlags {
49 MethodFlags(self.0 | Self::MONITOR)
50 }
51 pub fn with_cmp(self) -> MethodFlags {
52 MethodFlags(self.0 | Self::CMP)
53 }
54 pub fn has_sb(self) -> bool {
55 self.0 & Self::SB != 0
56 }
57 pub fn has_monitor(self) -> bool {
58 self.0 & Self::MONITOR != 0
59 }
60 pub fn has_cmp(self) -> bool {
61 self.0 & Self::CMP != 0
62 }
63 pub fn merge(self, o: MethodFlags) -> MethodFlags {
64 MethodFlags(self.0 | o.0)
65 }
66}
67
68pub struct MethodEnv<'a> {
70 pub pool: &'a DexPool,
71 pub di: usize,
73 pub dex: &'a DexFile,
74 pub code: &'a CodeItem,
75 pub class_name: String,
76 pub method_name: std::sync::Arc<str>,
77 pub desc: MethodDescriptor,
78 pub debug_locals: Vec<ddc_dex::DebugLocal>,
81 pub is_static: bool,
82 pub code_units: u32,
86}
87
88impl<'a> MethodEnv<'a> {
89 pub fn type_name(&self, idx: u32) -> String {
90 self.dex.type_name(idx).to_string()
91 }
92 pub fn java_type(&self, idx: u32) -> JavaType {
93 (*self.pool.type_java(self.di, idx)).clone()
95 }
96 pub fn type_name_arc(&self, idx: u32) -> std::sync::Arc<str> {
97 self.pool.type_name_arc(self.di, idx)
98 }
99 pub fn field_ref(
100 &self,
101 idx: u32,
102 ) -> (
103 std::sync::Arc<str>,
104 std::sync::Arc<str>,
105 JavaType,
106 ) {
107 let f = self.dex.field(idx);
108 let owner = std::sync::Arc::from(self.dex.class_name(f.class_idx).as_str());
109 let raw = self.dex.string(f.name_idx);
110 let name = if jdc_core::rename::owner_has_renames(&owner) {
115 jdc_core::rename::field_display(&owner, raw, self.dex.type_name(f.type_idx))
116 .map(std::sync::Arc::from)
117 .unwrap_or_else(|| std::sync::Arc::from(raw))
118 } else {
119 std::sync::Arc::from(raw)
120 };
121 (owner, name, self.java_type(f.type_idx))
122 }
123 pub fn method_ref(
124 &self,
125 idx: u32,
126 ) -> (
127 std::sync::Arc<str>,
128 std::sync::Arc<str>,
129 std::sync::Arc<MethodDescriptor>,
130 ) {
131 let m = self.dex.method(idx);
135 let owner = std::sync::Arc::from(self.dex.class_name(m.class_idx).as_str());
136 let desc = self.pool.proto_desc_parsed(self.di, m.proto_idx);
137 let name = if jdc_core::rename::owner_has_renames(&owner) {
142 let raw = self.dex.string(m.name_idx);
143 jdc_core::rename::field_display(&owner, raw, &desc.to_string())
144 .map(std::sync::Arc::from)
145 .unwrap_or_else(|| std::sync::Arc::from(raw))
146 } else {
147 std::sync::Arc::from(self.dex.string(m.name_idx))
148 };
149 (owner, name, desc)
150 }
151}
152
153#[derive(Debug, Clone, PartialEq)]
155pub enum Reg {
156 Undef,
157 Live(u32),
158 Pending(Expr),
159 PendingCall(Expr),
160 WideHi,
161}
162
163#[derive(Debug, Clone)]
165pub struct OutState {
166 pub regs: Vec<Reg>,
167 pub write_pc: Vec<u32>,
170}
171
172pub(crate) fn src_regs(kind: &InsnKind) -> Vec<u16> {
176 let mut out: Vec<u16> = Vec::with_capacity(4);
177 let mut push = |r: u16| out.push(r);
178 match kind {
179 InsnKind::Nop
180 | InsnKind::ReturnVoid
181 | InsnKind::Unknown
182 | InsnKind::NewInstance { .. }
183 | InsnKind::SGet { .. } => {}
184 InsnKind::Move { src, .. }
185 | InsnKind::Un { src, .. }
186 | InsnKind::Return { src }
187 | InsnKind::ArrayLength { src, .. } => push(*src),
188 InsnKind::MonitorEnter { reg }
189 | InsnKind::MonitorExit { reg }
190 | InsnKind::Throw { reg }
191 | InsnKind::CheckCast { reg, .. }
192 | InsnKind::FillArrayData { reg, .. }
193 | InsnKind::PackedSwitch { reg, .. }
194 | InsnKind::SparseSwitch { reg, .. } => push(*reg),
195 InsnKind::MoveResult { .. } | InsnKind::MoveException { .. } => {}
196 InsnKind::Const { .. }
197 | InsnKind::ConstClass { .. }
198 | InsnKind::ConstString { .. }
199 | InsnKind::ConstMethodHandle { .. }
200 | InsnKind::ConstMethodType { .. } => {}
201 InsnKind::InstanceOf { src, .. } => {
202 push(*src);
203 }
204 InsnKind::NewArray { size, .. } => {
205 push(*size);
206 }
207 InsnKind::FilledNewArray { regs, .. } | InsnKind::InvokeCustom { regs, .. } => {
208 for r in regs {
209 push(*r);
210 }
211 }
212 InsnKind::Goto { .. } => {}
213 InsnKind::Cmp { a, b, .. } | InsnKind::Bin { a, b, .. } => {
214 push(*a);
215 push(*b);
216 }
217 InsnKind::BinLit { a, .. } => push(*a),
218 InsnKind::If { a, b, z, .. } => {
219 push(*a);
220 if !*z {
221 push(*b);
222 }
223 }
224 InsnKind::AGet { array, index, .. } => {
225 push(*array);
226 push(*index);
227 }
228 InsnKind::APut {
229 value,
230 array,
231 index,
232 ..
233 } => {
234 push(*value);
235 push(*array);
236 push(*index);
237 }
238 InsnKind::IGet { obj, .. } => {
239 push(*obj);
240 }
241 InsnKind::IPut { value, obj, .. } => {
242 push(*value);
243 push(*obj);
244 }
245 InsnKind::SPut { value, .. } => {
246 push(*value);
247 }
248 InsnKind::Invoke { regs, .. } => {
249 for r in regs {
250 push(*r);
251 }
252 }
253 }
254 out
255}
256
257fn dst_regs(kind: &InsnKind) -> Vec<u16> {
260 let mut out: Vec<u16> = Vec::with_capacity(2);
261 let mut push = |r: u16| out.push(r);
262 match kind {
263 InsnKind::Move { dst, .. }
264 | InsnKind::MoveResult { dst }
265 | InsnKind::MoveException { dst }
266 | InsnKind::Const { dst, .. }
267 | InsnKind::ConstClass { dst, .. }
268 | InsnKind::ConstString { dst, .. }
269 | InsnKind::ConstMethodHandle { dst, .. }
270 | InsnKind::ConstMethodType { dst, .. }
271 | InsnKind::NewInstance { dst, .. }
272 | InsnKind::NewArray { dst, .. }
273 | InsnKind::InstanceOf { dst, .. }
274 | InsnKind::ArrayLength { dst, .. }
275 | InsnKind::Cmp { dst, .. }
276 | InsnKind::AGet { dst, .. }
277 | InsnKind::IGet { dst, .. }
278 | InsnKind::SGet { dst, .. }
279 | InsnKind::Un { dst, .. }
280 | InsnKind::Bin { dst, .. }
281 | InsnKind::BinLit { dst, .. } => push(*dst),
282 _ => {}
283 }
284 out
285}
286
287pub(crate) fn compute_final_reads(
292 cfg: &crate::cfg::DexCfg,
293) -> std::sync::Arc<jdc_core::FxHashSet<(u32, u16)>> {
294 use jdc_core::FxHashMap as HashMap;
295 let order = cfg.rpo_order();
309 let mut events: HashMap<u16, Vec<(u32, bool)>> = HashMap::default();
310 for &bid in &order {
311 for i in cfg.block_ins(&cfg.blocks[bid]) {
312 for r in src_regs(&i.kind) {
313 events.entry(r).or_default().push((i.pc, true));
314 }
315 for r in dst_regs(&i.kind) {
316 events.entry(r).or_default().push((i.pc, false));
317 }
318 }
319 }
320 let mut out = jdc_core::FxHashSet::default();
321 for (reg, evs) in events {
322 let mut dedup: Vec<(u32, bool)> = Vec::with_capacity(evs.len());
323 for (pc, is_read) in evs {
324 if dedup.last() != Some(&(pc, is_read)) {
325 dedup.push((pc, is_read));
326 }
327 }
328 for (i, &(pc, is_read)) in dedup.iter().enumerate() {
329 if !is_read {
330 continue;
331 }
332 let mut final_ = true;
335 if let Some(&(_, is_read2)) = dedup.get(i + 1) {
336 if is_read2 {
337 final_ = false;
338 }
339 }
340 if final_ {
341 out.insert((pc, reg));
342 }
343 }
344 }
345 std::sync::Arc::new(out)
346}
347
348pub struct Lifter<'a> {
349 pub env: &'a MethodEnv<'a>,
350 pub vt: &'a mut VarTable,
351 block_id: usize,
353 stable: &'a mut jdc_core::FxHashMap<(usize, u16, u64), u32>,
360 regs: Vec<Reg>,
361 write_pc: Vec<u32>,
362 stmts: Vec<Stmt>,
363 pending_call: Option<Expr>,
364 code_units: u32,
365 cur_pc: u32,
368 final_read: std::sync::Arc<jdc_core::FxHashSet<(u32, u16)>>,
375 mflags: &'a mut MethodFlags,
379 force_mat: jdc_core::FxHashSet<u32>,
386}
387
388impl<'a> Lifter<'a> {
389 pub fn new(
390 env: &'a MethodEnv<'a>,
391 vt: &'a mut VarTable,
392 in_regs: Vec<Reg>,
393 block_id: usize,
394 stable: &'a mut jdc_core::FxHashMap<(usize, u16, u64), u32>,
395 mflags: &'a mut MethodFlags,
396 ) -> Lifter<'a> {
397 let n = env.code.registers_size as usize;
398 let n = n.max(in_regs.len()).max(1);
399 let mut regs = in_regs;
400 regs.resize(n, Reg::Undef);
401 Lifter {
402 env,
403 vt,
404 regs,
405 write_pc: vec![0; n],
406 stmts: Vec::new(),
407 block_id,
408 stable,
409 pending_call: None,
410 code_units: env.code_units,
411 cur_pc: 0,
412 final_read: std::sync::Arc::new(jdc_core::FxHashSet::default()),
413 mflags,
414 force_mat: jdc_core::FxHashSet::default(),
415 }
416 }
417
418 fn ty_key(ty: &TypeRef) -> u64 {
426 use std::hash::{Hash, Hasher};
427 fn jt_key(j: &JavaType, h: &mut jdc_core::fx::FxHasher) {
428 match j {
429 JavaType::Void => 1u64.hash(h),
430 JavaType::Boolean => 2u64.hash(h),
431 JavaType::Byte => 3u64.hash(h),
432 JavaType::Char => 4u64.hash(h),
433 JavaType::Short => 5u64.hash(h),
434 JavaType::Int => 6u64.hash(h),
435 JavaType::Float => 7u64.hash(h),
436 JavaType::Long => 8u64.hash(h),
437 JavaType::Double => 9u64.hash(h),
438 JavaType::Object(n) => {
439 10u64.hash(h);
440 n.hash(h);
441 }
442 JavaType::Array(inner) => {
443 11u64.hash(h);
444 jt_key(inner, h);
445 }
446 }
447 }
448 let mut h = jdc_core::fx::FxHasher::default();
449 match ty {
450 TypeRef::J(j) => jt_key(j, &mut h),
451 TypeRef::G(_) => 99u64.hash(&mut h),
452 }
453 h.finish()
454 }
455
456 fn fresh_var(&mut self, slot: u16, ty: TypeRef) -> u32 {
457 let key = (self.block_id, slot, Self::ty_key(&ty));
467 if let Some(&id) = self.stable.get(&key) {
468 return id;
469 }
470 if let Some(name) = self.debug_name(slot, &ty) {
475 let tkey = Self::ty_key(&ty);
481 if let Some(id) = self
482 .vt
483 .vars
484 .iter()
485 .find(|v| {
486 v.slot == slot
487 && !v.synthetic_name
488 && v.name == name
489 && Self::ty_key(&v.ty) == tkey
490 })
491 .map(|v| v.id)
492 {
493 self.stable.insert(key, id);
494 return id;
495 }
496 let id = self.vt.vars.len() as u32;
497 self.stable.insert(key, id);
498 self.push_var(id, slot, name, ty, false);
499 return id;
500 }
501 let id = self.vt.vars.len() as u32;
502 let name = format!("v{}", id);
503 self.stable.insert(key, id);
504 self.push_var(id, slot, name, ty, true);
505 id
506 }
507
508 fn push_var(&mut self, id: u32, slot: u16, name: String, ty: TypeRef, synthetic: bool) {
509 self.vt.vars.push(VarInfo {
510 id,
511 slot,
512 name,
513 ty,
514 is_param: false,
515 range_start: 0,
516 range_end: u16::MAX,
517 synthetic_name: synthetic,
518 });
519 while self.vt.by_slot.len() <= slot as usize {
520 self.vt.by_slot.push(Vec::new());
521 }
522 self.vt.by_slot[slot as usize].push((0, u16::MAX, id));
523 }
524
525 fn debug_name(&self, slot: u16, ty: &TypeRef) -> Option<String> {
530 let locals = &self.env.debug_locals;
531 if locals.is_empty() {
532 return None;
533 }
534 let want = ty.erased();
535 let covers = |pc: u32| {
536 locals.iter().find(|l| {
537 l.reg == slot
538 && pc >= l.start
539 && pc < l.end
540 && l.ty.as_deref().map(|d| crate::desc_type(d) == want).unwrap_or(true)
545 })
546 };
547 let hit = covers(self.cur_pc).or_else(|| {
552 let wpc = *self.write_pc.get(slot as usize)?;
553 if wpc == 0 {
554 None
555 } else {
556 covers(wpc)
557 }
558 })?;
559 let name = crate::classdec::java_ident(&hit.name);
560 if name.is_empty() {
561 return None;
562 }
563 Some(name.into_owned())
564 }
565
566 fn local_expr(&self, v: u32) -> Expr {
567 Expr::Local {
568 var: v,
569 ty: self.vt.var(v).ty.clone(),
570 }
571 }
572
573 fn folded_new_aliases(&self, r: u16) -> Vec<usize> {
580 let Some(w) = self.write_pc.get(r as usize).copied() else {
581 return Vec::new();
582 };
583 (0..self.regs.len())
584 .filter(|&rr| {
585 rr != r as usize
586 && self.write_pc.get(rr).copied() == Some(w)
587 && matches!(self.regs[rr], Reg::Pending(Expr::New { raw: false, .. }))
588 })
589 .collect()
590 }
591
592 #[track_caller]
596 fn read_nest(&mut self, r: u16) -> Expr {
597 match self.regs.get(r as usize).cloned().unwrap_or(Reg::Undef) {
598 Reg::Undef | Reg::WideHi => {
599 let ty = TypeRef::J(JavaType::Int);
603 let v = self.fresh_var(r, ty);
604 if (r as usize) < self.regs.len() {
605 self.regs[r as usize] = Reg::Live(v);
606 }
607 self.local_expr(v)
608 }
609 Reg::Live(v) => self.local_expr(v),
610 Reg::Pending(e) => {
611 let alias_needs_share = matches!(&e, Expr::New { raw: false, .. })
637 && self.folded_new_aliases(r).iter().any(|&rr| {
638 self.final_read
639 .iter()
640 .any(|&(pc2, r2)| r2 == rr as u16 && pc2 >= self.cur_pc)
641 });
642 let alloc = alias_needs_share
643 || matches!(&e, Expr::NewArray { .. })
644 || (matches!(&e, Expr::New { .. })
645 && !self.final_read.contains(&(self.cur_pc, r)));
646 if alloc {
647 let v = self.materialize(r);
648 self.local_expr(v)
649 } else {
650 if matches!(&e, Expr::NewArray { .. })
661 || matches!(&e, Expr::New { raw: false, .. })
662 {
663 let aliases = if matches!(&e, Expr::New { raw: false, .. }) {
664 self.folded_new_aliases(r)
665 } else {
666 Vec::new()
667 };
668 if let Some(slot) = self.regs.get_mut(r as usize) {
669 *slot = Reg::Undef;
670 }
671 for rr in aliases {
675 self.regs[rr] = Reg::Undef;
676 }
677 }
678 e
679 }
680 }
681 Reg::PendingCall(_) => {
682 let v = self.materialize(r);
683 self.local_expr(v)
684 }
685 }
686 }
687
688 fn read_term(&mut self, r: u16) -> Expr {
692 match self.regs.get(r as usize).cloned().unwrap_or(Reg::Undef) {
693 Reg::PendingCall(e) | Reg::Pending(e) => {
694 let aliases = if matches!(&e, Expr::New { raw: false, .. }) {
698 self.folded_new_aliases(r)
699 } else {
700 Vec::new()
701 };
702 if (r as usize) < self.regs.len() {
703 self.regs[r as usize] = Reg::Undef;
704 }
705 for rr in aliases {
706 self.regs[rr] = Reg::Undef;
707 }
708 self.pending_call = None;
709 e
710 }
711 _ => self.read_nest(r),
712 }
713 }
714
715 fn write(&mut self, r: u16, e: Expr, pc: u32, wide: bool) {
716 if r as usize >= self.regs.len() {
717 return;
718 }
719 self.write_pc[r as usize] = pc;
720 if self.force_mat.contains(&pc) {
724 let e = value_of_cmp(&e);
725 let v = self.materialize_value(r, e);
726 self.regs[r as usize] = Reg::Live(v);
727 if wide {
728 self.mark_wide_hi(r + 1, pc);
729 }
730 return;
731 }
732 if expr_size(&e) > 32 {
737 self.write_pc[r as usize] = pc;
738 let v = self.materialize_value(r, e);
739 if (r as usize) < self.regs.len() {
740 self.regs[r as usize] = Reg::Live(v);
741 }
742 if wide {
743 self.mark_wide_hi(r + 1, pc);
744 }
745 return;
746 }
747 self.regs[r as usize] = Reg::Pending(e);
748 if wide {
749 self.mark_wide_hi(r + 1, pc);
750 }
751 }
752
753 fn materialize_value(&mut self, r: u16, e: Expr) -> u32 {
756 let e = value_of_cmp(&e);
757 let ty = e.type_ref();
758 let v = self.fresh_var(r, ty);
762 self.stmts.push(Stmt::LocalDef {
763 var: v,
764 init: Some(e),
765 is_final: false,
766 force_type: true,
767 });
768 v
769 }
770
771 fn write_call_result(&mut self, r: u16, e: Expr, pc: u32) {
774 if r as usize >= self.regs.len() {
775 return;
776 }
777 self.write_pc[r as usize] = pc;
778 self.regs[r as usize] = Reg::PendingCall(e);
779 }
780
781 fn mark_wide_hi(&mut self, r: u16, pc: u32) {
782 if (r as usize) < self.regs.len() {
783 self.regs[r as usize] = Reg::WideHi;
784 self.write_pc[r as usize] = pc;
785 }
786 }
787
788 fn materialize_views_mentioning(
795 &mut self,
796 cls: &std::sync::Arc<str>,
797 name: &std::sync::Arc<str>,
798 is_static: bool,
799 ) {
800 let mut stale: Vec<u16> = Vec::new();
801 for (i, r) in self.regs.iter().enumerate() {
802 match r {
803 Reg::Pending(e) | Reg::PendingCall(e)
804 if expr_mentions_field(e, cls, name, is_static) =>
805 {
806 stale.push(i as u16);
807 }
808 _ => {}
809 }
810 }
811 for r in stale {
812 self.materialize(r);
813 }
814 }
815
816 fn materialize(&mut self, r: u16) -> u32 {
818 let cur = std::mem::replace(
822 self.regs.get_mut(r as usize).unwrap_or(&mut Reg::Undef),
823 Reg::Undef,
824 );
825 let e = match cur {
826 Reg::Pending(e) | Reg::PendingCall(e) => e,
827 Reg::Live(v) => {
828 if (r as usize) < self.regs.len() {
829 self.regs[r as usize] = Reg::Live(v);
830 }
831 return v;
832 }
833 _ => {
836 let v = self.fresh_var(r, TypeRef::J(JavaType::Object("java/lang/Object".into())));
837 self.stmts.push(Stmt::LocalDef {
838 var: v,
839 init: None,
840 is_final: false,
841 force_type: false,
842 });
843 if (r as usize) < self.regs.len() {
844 self.regs[r as usize] = Reg::Live(v);
845 }
846 return v;
847 }
848 };
849 let e = value_of_cmp_owned(e);
851 let ty = e.type_ref();
852 let v = self.fresh_var(r, ty);
856 self.stmts.push(Stmt::LocalDef {
857 var: v,
858 init: Some(e),
859 is_final: false,
860 force_type: true,
861 });
862 if (r as usize) < self.regs.len() {
863 self.regs[r as usize] = Reg::Live(v);
864 }
865 for rr in self.folded_new_aliases(r) {
869 self.regs[rr] = Reg::Live(v);
870 }
871 v
872 }
873
874 fn drop_pending_call(&mut self) {
876 if let Some(e) = self.pending_call.take() {
877 let e = value_of_cmp(&e);
878 if has_side_effects(&e) {
879 self.stmts.push(Stmt::ExprStmt(e));
880 }
881 }
882 }
883
884 fn owner_expr(&self, obj: Expr, cls: &str) -> Option<Box<Expr>> {
887 if cls == self.env.class_name && matches!(obj, Expr::This) {
888 return None;
889 }
890 Some(Box::new(obj))
891 }
892
893 fn arith_op(op: ArithOp) -> BinOp {
894 match op {
895 ArithOp::Add => BinOp::Add,
896 ArithOp::Sub => BinOp::Sub,
897 ArithOp::Mul => BinOp::Mul,
898 ArithOp::Div => BinOp::Div,
899 ArithOp::Rem => BinOp::Rem,
900 ArithOp::And => BinOp::And,
901 ArithOp::Or => BinOp::Or,
902 ArithOp::Xor => BinOp::Xor,
903 ArithOp::Shl => BinOp::Shl,
904 ArithOp::Shr => BinOp::Shr,
905 ArithOp::Ushr => BinOp::Ushr,
906 }
907 }
908
909 fn cmp_op(op: CmpOp) -> BinOp {
910 match op {
911 CmpOp::Eq => BinOp::Eq,
912 CmpOp::Ne => BinOp::Ne,
913 CmpOp::Lt => BinOp::Lt,
914 CmpOp::Ge => BinOp::Ge,
915 CmpOp::Gt => BinOp::Gt,
916 CmpOp::Le => BinOp::Le,
917 }
918 }
919
920 fn cmp_sentinel(kind: CmpKind, l: Expr, r: Expr) -> Expr {
921 let name = match kind {
922 CmpKind::CmplF => "\0cmpl-float",
923 CmpKind::CmpgF => "\0cmpg-float",
924 CmpKind::CmplD => "\0cmpl-double",
925 CmpKind::CmpgD => "\0cmpg-double",
926 CmpKind::CmpJ => "\0cmp-long",
927 };
928 Expr::Invokedynamic {
929 name: name.into(),
930 desc: MethodDescriptor {
931 args: vec![],
932 ret: JavaType::Int,
933 },
934 args: vec![l, r],
935 bsm_text: String::new(),
936 bsm_static_args: vec![],
937 }
938 }
939
940 pub fn build_block(
945 mut self,
946 ins: &[Insn],
947 handler_types: &[Option<std::sync::Arc<str>>],
948 final_read: std::sync::Arc<jdc_core::FxHashSet<(u32, u16)>>,
949 ) -> BResult<(BlockResult, OutState)> {
950 let payloads = &self.env.code.payloads;
951 self.final_read = final_read;
961 {
967 let mut field_writes: jdc_core::FxHashMap<
968 (std::sync::Arc<str>, std::sync::Arc<str>, bool),
969 Vec<u32>,
970 > = jdc_core::FxHashMap::default();
971 for ins in ins {
972 match &ins.kind {
973 InsnKind::IPut { field_idx, .. } | InsnKind::SPut { field_idx, .. } => {
974 let (cls, name, _) = self.env.field_ref(*field_idx);
975 let is_static = matches!(ins.kind, InsnKind::SPut { .. });
976 field_writes
977 .entry((cls, name, is_static))
978 .or_default()
979 .push(ins.pc);
980 }
981 _ => {}
982 }
983 }
984 if !field_writes.is_empty() {
985 for ins in ins {
986 let (_dst, field_idx, is_static) = match &ins.kind {
987 InsnKind::IGet { dst, field_idx, .. } => (*dst, *field_idx, false),
988 InsnKind::SGet { dst, field_idx } => (*dst, *field_idx, true),
989 _ => continue,
990 };
991 let (cls, name, _) = self.env.field_ref(field_idx);
992 let killed = field_writes
993 .get(&(cls, name, is_static))
994 .is_some_and(|ws| ws.iter().any(|w| *w > ins.pc));
995 if killed {
996 self.force_mat.insert(ins.pc);
997 }
998 }
999 }
1000 }
1001 for ins in ins {
1002 self.cur_pc = ins.pc;
1003 match &ins.kind {
1004 InsnKind::Nop => {}
1005 InsnKind::Unknown => {
1006 return Err(format!(
1007 "unsupported opcode {:#04x} ({}) at pc {}",
1008 ins.op,
1009 ddc_dex::insn::op_name(ins.op),
1010 ins.pc
1011 ));
1012 }
1013 InsnKind::Move { dst, src } => {
1014 let src_state = self.regs.get(*src as usize).cloned().unwrap_or(Reg::Undef);
1025 if matches!(src_state, Reg::PendingCall(_))
1026 || matches!(&src_state, Reg::Pending(e)
1027 if has_side_effects(e) && !matches!(e, Expr::New { raw: true, .. }))
1028 {
1029 self.materialize(*src);
1030 }
1031 let st = self.regs.get(*src as usize).cloned().unwrap_or(Reg::Undef);
1032 let wide = reg_is_wide(&st, self.vt);
1033 if (*dst as usize) < self.regs.len() {
1034 self.regs[*dst as usize] = st;
1035 self.write_pc[*dst as usize] =
1036 self.write_pc.get(*src as usize).copied().unwrap_or(0);
1037 }
1038 if wide {
1039 self.mark_wide_hi(*dst + 1, ins.pc);
1040 }
1041 }
1042 InsnKind::MoveResult { dst } => {
1043 if let Some(e) = self.pending_call.take() {
1044 let wide =
1045 matches!(e.type_ref().erased(), JavaType::Long | JavaType::Double);
1046 self.write_call_result(*dst, e, ins.pc);
1047 if wide {
1048 self.mark_wide_hi(*dst + 1, ins.pc);
1049 }
1050 } else {
1051 let v = self.fresh_var(*dst, TypeRef::J(JavaType::Int));
1053 if (*dst as usize) < self.regs.len() {
1054 self.regs[*dst as usize] = Reg::Live(v);
1055 }
1056 }
1057 }
1058 InsnKind::MoveException { dst } => {
1059 let ty = handler_types
1060 .iter()
1061 .flatten()
1062 .next()
1063 .map(|t| TypeRef::J(JavaType::Object(t.clone())))
1064 .unwrap_or_else(|| {
1065 TypeRef::J(JavaType::Object("java/lang/Throwable".into()))
1066 });
1067 let v = self.fresh_var(*dst, ty);
1068 self.stmts.push(Stmt::LocalDef {
1069 var: v,
1070 init: None,
1071 is_final: false,
1072 force_type: true,
1073 });
1074 if (*dst as usize) < self.regs.len() {
1075 self.regs[*dst as usize] = Reg::Live(v);
1076 }
1077 }
1078 InsnKind::ReturnVoid
1079 | InsnKind::Return { .. }
1080 | InsnKind::Throw { .. }
1081 | InsnKind::Goto { .. }
1082 | InsnKind::PackedSwitch { .. }
1083 | InsnKind::SparseSwitch { .. }
1084 | InsnKind::If { .. } => {}
1085 InsnKind::Const { dst, val, wide } => {
1086 let e = if *wide {
1087 Expr::Const(ConstVal::Long(*val))
1088 } else {
1089 Expr::Const(ConstVal::Int(*val as i32))
1090 };
1091 self.write(*dst, e, ins.pc, *wide);
1092 }
1093 InsnKind::ConstString { dst, str_idx } => {
1094 let s = std::sync::Arc::from(self.env.dex.string(*str_idx));
1095 self.write(*dst, Expr::Const(ConstVal::Str(s)), ins.pc, false);
1096 }
1097 InsnKind::ConstClass { dst, type_idx } => {
1098 let ty = TypeRef::J(self.env.java_type(*type_idx));
1099 self.write(*dst, Expr::Const(ConstVal::ClassLit(ty)), ins.pc, false);
1100 }
1101 InsnKind::MonitorEnter { reg } => {
1102 let e = self.read_nest(*reg);
1103 *self.mflags = self.mflags.with_monitor();
1104 self.stmts.push(Stmt::MonitorEnter(e));
1105 }
1106 InsnKind::MonitorExit { reg } => {
1107 let e = self.read_nest(*reg);
1108 *self.mflags = self.mflags.with_monitor();
1109 self.stmts.push(Stmt::MonitorExit(e));
1110 }
1111 InsnKind::CheckCast { reg, type_idx } => {
1112 let e = self.read_nest(*reg);
1113 let ty = TypeRef::J(self.env.java_type(*type_idx));
1114 self.write(*reg, Expr::Cast { ty, e: Box::new(e) }, ins.pc, false);
1115 }
1116 InsnKind::InstanceOf { dst, src, type_idx } => {
1117 let e = self.read_nest(*src);
1118 let ty = TypeRef::J(self.env.java_type(*type_idx));
1119 self.write(*dst, Expr::InstanceOf { e: Box::new(e), ty }, ins.pc, false);
1120 }
1121 InsnKind::ArrayLength { dst, src } => {
1122 let e = self.read_nest(*src);
1123 let length = Expr::Field {
1124 owner: Some(Box::new(e)),
1125 cls: std::sync::Arc::from(""),
1126 name: "length".into(),
1127 ty: TypeRef::J(JavaType::Int),
1128 is_static: false,
1129 };
1130 self.write(*dst, length, ins.pc, false);
1131 }
1132 InsnKind::NewInstance { dst, type_idx } => {
1133 let cls = self.env.type_name_arc(*type_idx);
1134 let ty = TypeRef::J(JavaType::Object(cls.clone()));
1135 self.write(
1136 *dst,
1137 Expr::New {
1138 cls,
1139 ty,
1140 args: vec![],
1141 arg_tys: Vec::new(),
1142 raw: true,
1143 },
1144 ins.pc,
1145 false,
1146 );
1147 }
1148 InsnKind::NewArray {
1149 dst,
1150 size,
1151 type_idx,
1152 } => {
1153 let elem_desc = self.env.type_name(*type_idx);
1154 let elem = crate::desc_type(elem_desc.trim_start_matches('['));
1156 let n_brackets = elem_desc.chars().take_while(|&c| c == '[').count();
1167 let trailing_dims = n_brackets.saturating_sub(1).min(u8::MAX as usize) as u8;
1168 let dims = self.read_nest(*size);
1169 self.write(
1170 *dst,
1171 Expr::NewArray {
1172 elem: TypeRef::J(elem),
1173 dims: vec![dims],
1174 trailing_dims,
1175 init: None,
1176 },
1177 ins.pc,
1178 false,
1179 );
1180 }
1181 InsnKind::FilledNewArray { regs, type_idx } => {
1182 self.drop_pending_call();
1183 let elem_desc = self.env.type_name(*type_idx);
1184 let base = elem_desc.trim_start_matches('[');
1185 let depth = elem_desc.len() - base.len();
1195 let elem = crate::desc_type(base);
1196 let wide = depth == 1 && elem.is_wide();
1203 let mut args: Vec<Expr> = Vec::with_capacity(regs.len());
1204 let mut it = regs.iter();
1205 while let Some(&r) = it.next() {
1206 args.push(self.read_nest(r));
1207 if wide {
1208 it.next();
1209 }
1210 }
1211 self.pending_call = Some(Expr::NewArray {
1212 elem: TypeRef::J(elem),
1213 dims: vec![],
1214 trailing_dims: depth.saturating_sub(1).min(255) as u8,
1215 init: Some(args),
1216 });
1217 }
1218 InsnKind::FillArrayData { reg, payload_pc } => {
1219 if let Some(Payload::ArrayData {
1220 elem_width,
1221 size,
1222 data,
1223 }) = payloads.get(payload_pc)
1224 {
1225 let mut elems = payload_consts(*elem_width, *size, data);
1226 let cur = self.regs.get(*reg as usize).cloned().unwrap_or(Reg::Undef);
1227 if let Reg::Pending(Expr::NewArray { elem, init, .. }) = cur {
1228 if init.is_none() {
1229 match elem.erased() {
1233 jdc_core::types::JavaType::Float => {
1234 for e in elems.iter_mut() {
1235 *e = reinterpret_float(e.clone(), false);
1236 }
1237 }
1238 jdc_core::types::JavaType::Double => {
1239 for e in elems.iter_mut() {
1240 *e = reinterpret_float(e.clone(), true);
1241 }
1242 }
1243 _ => {}
1244 }
1245 self.write(
1246 *reg,
1247 Expr::NewArray {
1248 elem,
1249 dims: vec![Expr::Const(ConstVal::Int(*size as i32))],
1250 trailing_dims: 0,
1251 init: Some(elems),
1252 },
1253 ins.pc,
1254 false,
1255 );
1256 continue;
1257 }
1258 }
1259 let arr = self.read_nest(*reg);
1261 for (i, e) in elems.into_iter().enumerate() {
1262 let idx = Expr::Const(ConstVal::Int(i as i32));
1263 let target = Expr::ArrayIndex {
1264 array: Box::new(arr.clone()),
1265 index: Box::new(idx),
1266 };
1267 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
1268 target: Box::new(target),
1269 op: AssignOp::Plain,
1270 value: Box::new(e),
1271 }));
1272 }
1273 }
1274 }
1275 InsnKind::Cmp { dst, a, b, kind } => {
1276 let l = self.read_nest(*a);
1277 let r = self.read_nest(*b);
1278 let (l, r) = match kind {
1280 CmpKind::CmplF | CmpKind::CmpgF => {
1281 (reinterpret_float(l, false), reinterpret_float(r, false))
1282 }
1283 CmpKind::CmplD | CmpKind::CmpgD => {
1284 (reinterpret_float(l, true), reinterpret_float(r, true))
1285 }
1286 _ => (l, r),
1287 };
1288 *self.mflags = self.mflags.with_cmp();
1289 self.write(*dst, Self::cmp_sentinel(*kind, l, r), ins.pc, false);
1290 }
1291 InsnKind::AGet {
1292 dst, array, index, ..
1293 } => {
1294 let a = self.read_nest(*array);
1295 let i = self.read_nest(*index);
1296 self.write(
1297 *dst,
1298 Expr::ArrayIndex {
1299 array: Box::new(a),
1300 index: Box::new(i),
1301 },
1302 ins.pc,
1303 false,
1304 );
1305 }
1306 InsnKind::APut {
1307 value,
1308 array,
1309 index,
1310 ..
1311 } => {
1312 self.drop_pending_call();
1313 let v = self.read_nest(*value);
1314 let a = self.read_nest(*array);
1315 let i = self.read_nest(*index);
1316 let target = Expr::ArrayIndex {
1317 array: Box::new(a),
1318 index: Box::new(i),
1319 };
1320 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
1321 target: Box::new(target),
1322 op: AssignOp::Plain,
1323 value: Box::new(v),
1324 }));
1325 }
1326 InsnKind::IGet {
1327 dst,
1328 obj,
1329 field_idx,
1330 } => {
1331 self.drop_pending_call();
1332 let (cls, name, ty) = self.env.field_ref(*field_idx);
1333 let owner = self.read_nest(*obj);
1334 let owner_opt = self.owner_expr(owner, &cls);
1335 self.write(
1336 *dst,
1337 Expr::Field {
1338 owner: owner_opt,
1339 cls,
1340 name,
1341 ty: TypeRef::J(ty),
1342 is_static: false,
1343 },
1344 ins.pc,
1345 false,
1346 );
1347 }
1348 InsnKind::IPut {
1349 value,
1350 obj,
1351 field_idx,
1352 } => {
1353 self.drop_pending_call();
1354 let (cls, name, ty) = self.env.field_ref(*field_idx);
1355 self.materialize_views_mentioning(&cls, &name, false);
1361 let v = null_in_obj_ctx(self.read_nest(*value), &ty);
1362 let owner = self.read_nest(*obj);
1363 let owner_opt = self.owner_expr(owner, &cls);
1364 let target = Expr::Field {
1365 owner: owner_opt,
1366 cls,
1367 name,
1368 ty: TypeRef::J(ty),
1369 is_static: false,
1370 };
1371 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
1372 target: Box::new(target),
1373 op: AssignOp::Plain,
1374 value: Box::new(v),
1375 }));
1376 }
1377 InsnKind::SGet { dst, field_idx } => {
1378 self.drop_pending_call();
1379 let (cls, name, ty) = self.env.field_ref(*field_idx);
1380 self.write(
1381 *dst,
1382 Expr::Field {
1383 owner: None,
1384 cls,
1385 name,
1386 ty: TypeRef::J(ty),
1387 is_static: true,
1388 },
1389 ins.pc,
1390 false,
1391 );
1392 }
1393 InsnKind::SPut { value, field_idx } => {
1394 self.drop_pending_call();
1395 let (cls, name, ty) = self.env.field_ref(*field_idx);
1396 self.materialize_views_mentioning(&cls, &name, true);
1397 let v = null_in_obj_ctx(self.read_nest(*value), &ty);
1398 let target = Expr::Field {
1399 owner: None,
1400 cls,
1401 name,
1402 ty: TypeRef::J(ty),
1403 is_static: true,
1404 };
1405 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
1406 target: Box::new(target),
1407 op: AssignOp::Plain,
1408 value: Box::new(v),
1409 }));
1410 }
1411 InsnKind::Invoke {
1412 kind,
1413 regs,
1414 method_idx,
1415 } => {
1416 self.drop_pending_call();
1417 self.do_invoke(*kind, regs, *method_idx, ins.pc)?;
1418 }
1419 InsnKind::InvokeCustom {
1420 call_site_idx,
1421 regs,
1422 } => {
1423 self.drop_pending_call();
1424 let e = self.build_invoke_custom(*call_site_idx, regs, ins.pc);
1425 self.pending_call = Some(e);
1426 }
1427 InsnKind::Un {
1428 dst,
1429 src,
1430 op,
1431 from: _,
1432 to,
1433 } => {
1434 let e = self.read_nest(*src);
1435 let ne = match op {
1436 ddc_dex::insn::UnArith::Neg => Expr::Un {
1437 op: UnOp::Neg,
1438 e: Box::new(e),
1439 },
1440 ddc_dex::insn::UnArith::Not => Expr::Un {
1441 op: UnOp::BitNot,
1442 e: Box::new(e),
1443 },
1444 ddc_dex::insn::UnArith::Conv => Expr::Cast {
1445 ty: TypeRef::J(primitive_type(*to)),
1446 e: Box::new(e),
1447 },
1448 };
1449 let wide = matches!(*to, 'J' | 'D');
1450 self.write(*dst, ne, ins.pc, wide);
1451 }
1452 InsnKind::Bin { op, dst, a, b, ty } => {
1453 let l = self.read_nest(*a);
1454 let r = self.read_nest(*b);
1455 let (l, r) = match *ty {
1457 'F' => (reinterpret_float(l, false), reinterpret_float(r, false)),
1458 'D' => (reinterpret_float(l, true), reinterpret_float(r, true)),
1459 _ => (l, r),
1460 };
1461 let jt = primitive_type(*ty);
1462 let e = Expr::Bin {
1463 op: Self::arith_op(*op),
1464 l: Box::new(l),
1465 r: Box::new(r),
1466 ty: Some(TypeRef::J(jt)),
1467 };
1468 let wide = matches!(*ty, 'J' | 'D');
1469 self.write(*dst, e, ins.pc, wide);
1470 }
1471 InsnKind::BinLit {
1472 op,
1473 dst,
1474 a,
1475 lit,
1476 rsub,
1477 } => {
1478 let av = self.read_nest(*a);
1479 let e = if *rsub {
1480 Expr::Bin {
1481 op: BinOp::Sub,
1482 l: Box::new(Expr::Const(ConstVal::Int(*lit))),
1483 r: Box::new(av),
1484 ty: Some(TypeRef::J(JavaType::Int)),
1485 }
1486 } else {
1487 Expr::Bin {
1488 op: Self::arith_op(*op),
1489 l: Box::new(av),
1490 r: Box::new(Expr::Const(ConstVal::Int(*lit))),
1491 ty: Some(TypeRef::J(JavaType::Int)),
1492 }
1493 };
1494 self.write(*dst, e, ins.pc, false);
1495 }
1496 InsnKind::ConstMethodHandle { dst, handle_idx } => {
1497 self.drop_pending_call();
1498 let e = self.method_handle_expr(*handle_idx);
1499 self.write(*dst, e, ins.pc, false);
1500 }
1501 InsnKind::ConstMethodType { dst, proto_idx } => {
1502 self.drop_pending_call();
1503 let proto = self.env.dex.proto(*proto_idx);
1504 let desc = format!(
1505 "({}){}",
1506 self.env
1507 .dex
1508 .proto_params(*proto_idx)
1509 .iter()
1510 .map(|&t| self.env.dex.type_name(t))
1511 .collect::<Vec<_>>()
1512 .join(""),
1513 self.env.dex.type_name(proto.return_type_idx)
1514 );
1515 self.write(*dst, Expr::Raw(desc), ins.pc, false);
1516 }
1517 }
1518 }
1519
1520 let term = self.block_term(ins);
1522
1523 self.drop_pending_call();
1527 self.materialize_impure_at_exit();
1528
1529 let out = OutState {
1533 regs: std::mem::take(&mut self.regs),
1534 write_pc: std::mem::take(&mut self.write_pc),
1535 };
1536 Ok((
1537 BlockResult {
1538 stmts: std::mem::take(&mut self.stmts),
1539 out_stack: Vec::new(),
1540 term,
1541 },
1542 out,
1543 ))
1544 }
1545
1546 fn do_invoke(
1547 &mut self,
1548 kind: InvokeKind,
1549 regs: &[u16],
1550 method_idx: u32,
1551 pc: u32,
1552 ) -> BResult<()> {
1553 let (cls, name, md) = self.env.method_ref(method_idx);
1554 if cls.as_ref() == "java/lang/StringBuilder" || cls.as_ref() == "java/lang/StringBuffer" {
1555 *self.mflags = self.mflags.with_sb();
1556 }
1557 let is_static = matches!(kind, InvokeKind::Static);
1558 let receiver_reg = if is_static {
1559 None
1560 } else {
1561 regs.first().copied()
1562 };
1563 let arg_regs: Vec<u16> = if is_static {
1564 regs.to_vec()
1565 } else {
1566 regs.iter().skip(1).copied().collect()
1567 };
1568 let mut args: Vec<Expr> = Vec::with_capacity(md.args.len());
1569 let mut ri = 0usize;
1577 for at in md.args.iter() {
1578 let r = arg_regs.get(ri).copied().unwrap_or(0);
1579 args.push(null_in_obj_ctx(self.read_nest(r), at));
1580 ri += if at.is_wide() { 2 } else { 1 };
1581 }
1582 if name.as_ref() == "<init>" && matches!(kind, InvokeKind::Direct) {
1588 let recv_reg = receiver_reg.unwrap_or(0);
1589 let recv_state = self
1590 .regs
1591 .get(recv_reg as usize)
1592 .cloned()
1593 .unwrap_or(Reg::Undef);
1594 if let Reg::Pending(Expr::New {
1595 raw: true, cls: nc, ..
1596 }) = &recv_state
1597 {
1598 if nc == &cls || self.env.pool.is_subtype(nc.as_ref(), cls.as_ref()) {
1609 let folded = Expr::New {
1610 cls: nc.clone(),
1611 ty: TypeRef::J(JavaType::Object(nc.clone())),
1612 arg_tys: md.args.clone(),
1613 args,
1614 raw: false,
1615 };
1616 let origin = self.write_pc.get(recv_reg as usize).copied();
1625 let aliases: Vec<usize> = origin
1626 .map(|w| {
1627 (0..self.regs.len())
1628 .filter(|&r| {
1629 r != recv_reg as usize
1630 && self.write_pc.get(r).copied() == Some(w)
1631 && matches!(
1632 &self.regs[r],
1633 Reg::Pending(Expr::New { raw: true, cls: c, .. })
1634 if c == nc
1635 )
1636 })
1637 .collect()
1638 })
1639 .unwrap_or_default();
1640 self.write(recv_reg, folded.clone(), pc, false);
1641 match self.regs.get(recv_reg as usize) {
1646 Some(Reg::Live(v)) => {
1647 let v = *v;
1648 for r in aliases {
1649 self.regs[r] = Reg::Live(v);
1650 }
1651 }
1652 _ => {
1653 for r in aliases {
1654 self.regs[r] = Reg::Pending(folded.clone());
1655 self.write_pc[r] = pc;
1656 }
1657 }
1658 }
1659 return Ok(());
1660 }
1661 }
1662 let recv_expr = receiver_reg.map(|r| self.read_nest(r));
1663 let owner_expr_v = recv_expr.unwrap_or(Expr::This);
1664 let owner_expr_v = if !self.env.is_static
1671 && matches!(&owner_expr_v, Expr::Local { var: 0, .. })
1672 {
1673 Expr::This
1674 } else {
1675 owner_expr_v
1676 };
1677 let is_super = cls.as_ref() != self.env.class_name.as_str();
1678 let owner = if matches!(owner_expr_v, Expr::This) && is_super {
1679 None
1680 } else {
1681 Some(Box::new(owner_expr_v))
1682 };
1683 let call = Expr::Method {
1684 owner,
1685 cls,
1686 name: "<init>".into(),
1687 desc: md,
1688 args,
1689 is_static: false,
1690 is_interface: false,
1691 is_special: true,
1692 is_super,
1693 is_dynamic: false,
1694 type_args: vec![],
1695 };
1696 self.stmts.push(Stmt::ExprStmt(call));
1697 return Ok(());
1698 }
1699
1700 let recv_expr = receiver_reg.map(|r| self.read_nest(r));
1701 let is_interface = matches!(kind, InvokeKind::Interface);
1702 let recv_is_this = match &recv_expr {
1716 Some(Expr::This) => true,
1717 Some(Expr::Raw(t)) => t == "\u{3}" || t == "this",
1718 Some(Expr::Local { var, .. }) => {
1719 (*var as usize) < self.vt.vars.len() && self.vt.vars[*var as usize].name == "this"
1720 }
1721 _ => false,
1722 };
1723 let is_super = match kind {
1724 InvokeKind::Super | InvokeKind::Direct => {
1725 recv_is_this && cls.as_ref() != self.env.class_name.as_str()
1726 }
1727 _ => false,
1728 };
1729 let owner: Option<Box<Expr>> = match recv_expr {
1730 None => None,
1731 Some(o) => {
1732 if is_super && matches!(o, Expr::This) {
1733 None
1734 } else {
1735 self.owner_expr(o, &cls)
1736 }
1737 }
1738 };
1739 let is_dynamic = matches!(kind, InvokeKind::Polymorphic);
1740 let call = Expr::Method {
1741 owner,
1742 cls,
1743 name,
1744 desc: md,
1745 args,
1746 is_static,
1747 is_interface,
1748 is_special: matches!(kind, InvokeKind::Direct),
1749 is_super,
1750 is_dynamic,
1751 type_args: vec![],
1752 };
1753 self.pending_call = Some(call);
1754 Ok(())
1755 }
1756
1757 fn method_handle_expr(&mut self, handle_idx: u32) -> Expr {
1760 let Some(h) = self.env.dex.method_handle(handle_idx) else {
1761 return Expr::Raw(format!("methodHandle@{}", handle_idx));
1762 };
1763 if h.is_field {
1764 let f = self.env.dex.field(h.target_id);
1765 let cls = self.env.dex.class_name(f.class_idx);
1766 let name = self.env.dex.string(f.name_idx).to_string();
1767 return Expr::Raw(format!("{}::{}", cls.replace('/', "."), name));
1768 }
1769 let m = self.env.dex.method(h.target_id);
1770 let cls = self.env.dex.class_name(m.class_idx);
1771 let name = self.env.dex.string(m.name_idx).to_string();
1772 Expr::Raw(format!("{}::{}", cls.replace('/', "."), name))
1773 }
1774
1775 fn build_invoke_custom(&mut self, cs_idx: u32, regs: &[u16], pc: u32) -> Expr {
1777 let Some(cs) = self.env.dex.call_site(cs_idx).cloned() else {
1778 return Expr::Invokedynamic {
1779 name: format!("call-site#{}", cs_idx),
1780 desc: jdc_core::types::MethodDescriptor {
1781 args: vec![],
1782 ret: JavaType::Object("java/lang/Object".into()),
1783 },
1784 args: vec![],
1785 bsm_text: "unresolved".into(),
1786 bsm_static_args: vec![],
1787 };
1788 };
1789 let site_name = self.env.dex.string(cs.name_idx).to_string();
1790 let params: Vec<JavaType> = self
1791 .env
1792 .dex
1793 .proto_params(cs.proto_idx)
1794 .iter()
1795 .map(|&t| self.env.java_type(t))
1796 .collect();
1797 let ret = self
1798 .env
1799 .java_type(self.env.dex.proto(cs.proto_idx).return_type_idx);
1800
1801 let (bs_cls, bs_name) = match self.env.dex.method_handle(cs.bootstrap_handle) {
1802 Some(h) if !h.is_field => {
1803 let m = self.env.dex.method(h.target_id);
1804 (
1805 self.env.dex.class_name(m.class_idx),
1806 self.env.dex.string(m.name_idx).to_string(),
1807 )
1808 }
1809 _ => (String::new(), String::new()),
1810 };
1811
1812 let mut args: Vec<Expr> = Vec::with_capacity(params.len().min(regs.len()));
1817 let mut ri = 0usize;
1818 for at in params.iter() {
1819 if let Some(&r) = regs.get(ri) {
1820 args.push(self.read_nest(r));
1821 }
1822 ri += if at.is_wide() { 2 } else { 1 };
1823 }
1824
1825 if bs_name == "makeConcatWithConstants" {
1827 let recipe = cs
1828 .linker_args
1829 .iter()
1830 .find_map(|v| match v {
1831 ddc_dex::annotations::EncodedValue::String(s) => {
1832 Some(self.env.dex.string(*s).to_string())
1833 }
1834 _ => None,
1835 })
1836 .unwrap_or_default();
1837 return concat_from_recipe(&recipe, args);
1838 }
1839
1840 let mut impl_m = None;
1844 let mut instantiated_proto: Option<u32> = None;
1845 if bs_name == "metafactory" || bs_name == "altMetafactory" {
1846 let mut seen_handle = false;
1847 for lv in &cs.linker_args {
1848 match lv {
1849 ddc_dex::annotations::EncodedValue::MethodHandle(hi) => {
1850 if !seen_handle {
1851 seen_handle = true;
1852 if let Some(h) = self.env.dex.method_handle(*hi) {
1853 if !h.is_field {
1854 let m = self.env.dex.method(h.target_id);
1855 impl_m = Some((
1856 self.env.dex.class_name(m.class_idx).replace('/', "."),
1857 self.env.dex.string(m.name_idx).to_string(),
1858 ));
1859 }
1860 }
1861 }
1862 }
1863 ddc_dex::annotations::EncodedValue::MethodType(p) if seen_handle => {
1864 instantiated_proto = Some(*p);
1865 }
1866 _ => {}
1867 }
1868 }
1869 }
1870 let _ = pc;
1871
1872 let mut captures = Vec::new();
1874 for &r in regs.iter().skip(params.len().min(regs.len())) {
1875 captures.push(self.read_nest(r));
1876 }
1877
1878 if let Some((impl_cls, impl_name)) = impl_m {
1879 if captures.is_empty() {
1880 if impl_name.starts_with("lambda$") {
1884 let n_params = match instantiated_proto {
1888 Some(p) => self.env.dex.proto_params(p).len(),
1889 None => params.len(),
1890 };
1891 let pnames: Vec<String> = (0..n_params).map(|i| format!("a{}", i)).collect();
1892 return Expr::Raw(format!(
1893 "({}) -> {}.{}({})",
1894 pnames.join(", "),
1895 impl_cls,
1896 impl_name,
1897 pnames.join(", ")
1898 ));
1899 }
1900 return Expr::Raw(format!("{}::{}", impl_cls, impl_name));
1901 }
1902 let mut call_args = args;
1905 call_args.extend(captures);
1906 return Expr::Invokedynamic {
1907 name: format!("{} -> {}.{}", site_name, impl_cls, impl_name),
1908 desc: jdc_core::types::MethodDescriptor { args: params, ret },
1909 args: call_args,
1910 bsm_text: format!("{}::{}", bs_cls, bs_name),
1911 bsm_static_args: vec![],
1912 };
1913 }
1914
1915 Expr::Invokedynamic {
1916 name: site_name,
1917 desc: jdc_core::types::MethodDescriptor { args: params, ret },
1918 args: captures,
1919 bsm_text: format!("{}::{}", bs_cls, bs_name),
1920 bsm_static_args: vec![],
1921 }
1922 }
1923
1924 fn materialize_impure_at_exit(&mut self) {
1925 let n = self.regs.len();
1926 for r in 0..n {
1927 let st = self.regs[r].clone();
1928 let impure = match &st {
1929 Reg::PendingCall(_) => true,
1930 Reg::Pending(e) => {
1931 has_side_effects(e)
1932 && !matches!(e, Expr::New { raw: true, .. })
1933 && !matches!(e, Expr::NewArray { init: None, .. })
1934 }
1935 _ => false,
1936 };
1937 if impure {
1938 self.materialize(r as u16);
1939 }
1940 }
1941 }
1942
1943 fn block_term(&mut self, ins: &[Insn]) -> Term {
1945 let Some(last) = ins.last() else {
1946 return Term::Goto;
1947 };
1948 match &last.kind {
1949 InsnKind::Goto { .. } => Term::Goto,
1950 InsnKind::ReturnVoid => Term::Return(None),
1951 InsnKind::Return { src } => {
1952 let v = null_in_obj_ctx(self.read_term(*src), &self.env.desc.ret);
1953 Term::Return(Some(v))
1954 }
1955 InsnKind::Throw { reg } => {
1956 let mut v = self.read_term(*reg);
1958 if let Expr::Const(ConstVal::Int(0)) = v {
1959 v = Expr::Const(ConstVal::Null);
1960 }
1961 Term::Throw(v)
1962 }
1963 InsnKind::If { op, a, b, z, .. } => {
1964 let cond = if *z {
1965 let v = self.read_nest(*a);
1966 let (l, r, realop) = jdc_core::ir::build::unfold_cmp(v, Self::cmp_op(*op));
1967 Expr::Bin {
1968 op: realop,
1969 l: Box::new(l),
1970 r: Box::new(r),
1971 ty: Some(TypeRef::J(JavaType::Boolean)),
1972 }
1973 } else {
1974 let l = self.read_nest(*a);
1975 let r = self.read_nest(*b);
1976 Expr::Bin {
1977 op: Self::cmp_op(*op),
1978 l: Box::new(l),
1979 r: Box::new(r),
1980 ty: Some(TypeRef::J(JavaType::Boolean)),
1981 }
1982 };
1983 Term::Cond { cond }
1984 }
1985 InsnKind::PackedSwitch { reg, payload_pc }
1986 | InsnKind::SparseSwitch { reg, payload_pc } => {
1987 let selector = self.read_term(*reg);
1988 let default = Some(last.pc + last.size);
1989 let targets = match self.env.code.payloads.get(payload_pc) {
1990 Some(Payload::Packed { first_key, targets }) => SwitchTargets::Table {
1991 low: *first_key,
1992 targets: targets
1993 .iter()
1994 .map(|t| (last.pc as i64 + *t as i64) as u32)
1995 .collect(),
1996 },
1997 Some(Payload::Sparse { pairs }) => SwitchTargets::Lookup {
1998 pairs: pairs
1999 .iter()
2000 .map(|(k, t)| (*k, (last.pc as i64 + *t as i64) as u32))
2001 .collect(),
2002 },
2003 _ => SwitchTargets::Lookup { pairs: vec![] },
2004 };
2005 Term::Switch {
2006 selector,
2007 targets,
2008 default,
2009 }
2010 }
2011 _ => {
2013 let next = last.pc + last.size;
2014 if next < self.code_units {
2015 Term::Fallthrough
2016 } else {
2017 Term::Goto
2018 }
2019 }
2020 }
2021 }
2022}
2023
2024fn concat_from_recipe(recipe: &str, args: Vec<Expr>) -> Expr {
2028 use jdc_core::ir::expr::ConcatPart;
2029 let mut parts: Vec<ConcatPart> = Vec::new();
2030 let mut arg_i = 0usize;
2031 let mut const_buf = String::new();
2032 for c in recipe.chars() {
2033 match c {
2034 '\u{1}' => {
2035 if !const_buf.is_empty() {
2036 parts.push(ConcatPart::Const(std::mem::take(&mut const_buf)));
2037 }
2038 if arg_i < args.len() {
2039 parts.push(ConcatPart::Str(args[arg_i].clone()));
2040 arg_i += 1;
2041 }
2042 }
2043 '\u{2}' => {
2044 }
2047 c => const_buf.push(c),
2048 }
2049 }
2050 if !const_buf.is_empty() {
2051 parts.push(ConcatPart::Const(const_buf));
2052 }
2053 Expr::StringConcat(parts)
2054}
2055
2056fn expr_size(e: &Expr) -> usize {
2060 let mut n = 0usize;
2061 count_nodes(e, &mut n);
2062 n
2063}
2064
2065fn expr_mentions_field(
2068 e: &Expr,
2069 cls: &std::sync::Arc<str>,
2070 name: &std::sync::Arc<str>,
2071 is_static: bool,
2072) -> bool {
2073 match e {
2074 Expr::Field {
2075 owner,
2076 cls: c,
2077 name: n,
2078 is_static: s,
2079 ..
2080 } => {
2081 (c == cls && n == name && *s == is_static)
2082 || owner
2083 .as_deref()
2084 .is_some_and(|o| expr_mentions_field(o, cls, name, is_static))
2085 }
2086 Expr::Un { e, .. } | Expr::Cast { e, .. } | Expr::InstanceOf { e, .. } => {
2087 expr_mentions_field(e, cls, name, is_static)
2088 }
2089 Expr::Bin { l, r, .. } => {
2090 expr_mentions_field(l, cls, name, is_static)
2091 || expr_mentions_field(r, cls, name, is_static)
2092 }
2093 Expr::Cond { c, t, f } => {
2094 expr_mentions_field(c, cls, name, is_static)
2095 || expr_mentions_field(t, cls, name, is_static)
2096 || expr_mentions_field(f, cls, name, is_static)
2097 }
2098 Expr::Assign { target, value, .. } => {
2099 expr_mentions_field(target, cls, name, is_static)
2100 || expr_mentions_field(value, cls, name, is_static)
2101 }
2102 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => {
2103 expr_mentions_field(e, cls, name, is_static)
2104 }
2105 Expr::Method {
2106 owner, args, ..
2107 } => {
2108 owner
2109 .as_deref()
2110 .is_some_and(|o| expr_mentions_field(o, cls, name, is_static))
2111 || args
2112 .iter()
2113 .any(|a| expr_mentions_field(a, cls, name, is_static))
2114 }
2115 Expr::ArrayIndex { array, index } => {
2116 expr_mentions_field(array, cls, name, is_static)
2117 || expr_mentions_field(index, cls, name, is_static)
2118 }
2119 Expr::New { args, .. } => args
2120 .iter()
2121 .any(|a| expr_mentions_field(a, cls, name, is_static)),
2122 Expr::NewArray { dims, init, .. } => {
2123 dims.iter().any(|d| expr_mentions_field(d, cls, name, is_static))
2124 || init
2125 .as_ref()
2126 .is_some_and(|v| v.iter().any(|x| expr_mentions_field(x, cls, name, is_static)))
2127 }
2128 Expr::NewMultiArray { dims, .. } => {
2129 dims.iter().any(|d| expr_mentions_field(d, cls, name, is_static))
2130 }
2131 Expr::StringConcat(parts) => parts.iter().any(|p| match p {
2132 ConcatPart::Str(e) => expr_mentions_field(e, cls, name, is_static),
2133 ConcatPart::Const(_) => false,
2134 }),
2135 _ => false,
2136 }
2137}
2138
2139fn count_nodes(e: &Expr, n: &mut usize) {
2140 *n += 1;
2141 if *n > 512 {
2142 return; }
2144 match e {
2145 Expr::Un { e, .. } | Expr::Cast { e, .. } | Expr::InstanceOf { e, .. } => count_nodes(e, n),
2146 Expr::Bin { l, r, .. } => {
2147 count_nodes(l, n);
2148 count_nodes(r, n);
2149 }
2150 Expr::Cond { c, t, f } => {
2151 count_nodes(c, n);
2152 count_nodes(t, n);
2153 count_nodes(f, n);
2154 }
2155 Expr::Assign { target, value, .. } => {
2156 count_nodes(target, n);
2157 count_nodes(value, n);
2158 }
2159 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => count_nodes(e, n),
2160 Expr::Field { owner: Some(o), .. } => count_nodes(o, n),
2161 Expr::Method {
2162 owner: Some(o),
2163 args,
2164 ..
2165 } => {
2166 count_nodes(o, n);
2167 for a in args {
2168 count_nodes(a, n);
2169 }
2170 }
2171 Expr::ArrayIndex { array, index } => {
2172 count_nodes(array, n);
2173 count_nodes(index, n);
2174 }
2175 Expr::New { args, .. } => {
2176 for a in args {
2177 count_nodes(a, n);
2178 }
2179 }
2180 Expr::NewArray { dims, init, .. } => {
2181 for d in dims {
2182 count_nodes(d, n);
2183 }
2184 if let Some(v) = init {
2185 for x in v {
2186 count_nodes(x, n);
2187 }
2188 }
2189 }
2190 Expr::NewMultiArray { dims, .. } => {
2191 for d in dims {
2192 count_nodes(d, n);
2193 }
2194 }
2195 Expr::StringConcat(parts) => {
2196 for p in parts {
2197 if let ConcatPart::Str(x) = p {
2198 count_nodes(x, n);
2199 }
2200 }
2201 }
2202 _ => {}
2203 }
2204}
2205
2206fn reg_is_wide(st: &Reg, vt: &VarTable) -> bool {
2207 match st {
2208 Reg::Pending(e) | Reg::PendingCall(e) => e.type_ref().erased().is_wide(),
2209 Reg::Live(v) => vt.var(*v).ty.erased().is_wide(),
2210 Reg::WideHi => true,
2211 Reg::Undef => false,
2212 }
2213}
2214
2215fn null_in_obj_ctx(mut v: Expr, ctx_ty: &JavaType) -> Expr {
2221 if ctx_ty.is_reference() {
2222 if let Expr::Const(ConstVal::Int(0)) = v {
2223 v = Expr::Const(ConstVal::Null);
2224 }
2225 }
2226 v
2227}
2228
2229fn reinterpret_float(e: Expr, wide: bool) -> Expr {
2236 match (&e, wide) {
2237 (Expr::Const(ConstVal::Int(i)), false) => {
2238 Expr::Const(ConstVal::Float(f32::from_bits(*i as u32)))
2239 }
2240 (Expr::Const(ConstVal::Long(l)), true) => {
2241 Expr::Const(ConstVal::Double(f64::from_bits(*l as u64)))
2242 }
2243 (Expr::Const(ConstVal::Int(i)), true) => {
2244 Expr::Const(ConstVal::Double(f64::from_bits(*i as u64)))
2245 }
2246 _ => e,
2247 }
2248}
2249
2250fn value_of_cmp(e: &Expr) -> Expr {
2251 value_of_cmp_owned(e.clone())
2252}
2253
2254fn value_of_cmp_owned(e: Expr) -> Expr {
2258 if let Expr::Invokedynamic { name, args, .. } = &e {
2261 if name.starts_with('\0') && args.len() == 2 {
2262 let (cls, ty) = match name.as_str() {
2263 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
2264 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
2265 _ => ("java/lang/Double", JavaType::Double),
2266 };
2267 let desc = MethodDescriptor {
2268 args: vec![ty.clone(), ty],
2269 ret: JavaType::Int,
2270 };
2271 return Expr::Method {
2272 owner: None,
2273 cls: cls.into(),
2274 name: "compare".into(),
2275 desc: std::sync::Arc::new(desc),
2276 args: args.clone(),
2277 is_static: true,
2278 is_interface: false,
2279 is_special: false,
2280 is_super: false,
2281 is_dynamic: false,
2282 type_args: vec![],
2283 };
2284 }
2285 }
2286 e
2287}
2288
2289pub fn primitive_type(c: char) -> JavaType {
2290 match c {
2291 'Z' => JavaType::Boolean,
2292 'B' => JavaType::Byte,
2293 'C' => JavaType::Char,
2294 'S' => JavaType::Short,
2295 'F' => JavaType::Float,
2296 'J' => JavaType::Long,
2297 'D' => JavaType::Double,
2298 _ => JavaType::Int,
2299 }
2300}
2301
2302fn payload_consts(elem_width: u16, size: u32, data: &[u8]) -> Vec<Expr> {
2305 let n = (size as usize).min(data.len() / elem_width.max(1) as usize);
2306 let mut out = Vec::with_capacity(n);
2307 for i in 0..n {
2308 let off = i * elem_width as usize;
2309 let e = match elem_width {
2310 1 => Expr::Const(ConstVal::Int(data[off] as i8 as i32)),
2311 2 => {
2312 let v = i16::from_le_bytes([data[off], data[off + 1]]);
2313 Expr::Const(ConstVal::Int(v as i32))
2314 }
2315 4 => {
2316 let mut b = [0u8; 4];
2317 b.copy_from_slice(&data[off..off + 4]);
2318 Expr::Const(ConstVal::Int(i32::from_le_bytes(b)))
2319 }
2320 8 => {
2321 let mut b = [0u8; 8];
2322 b.copy_from_slice(&data[off..off + 8]);
2323 Expr::Const(ConstVal::Long(i64::from_le_bytes(b)))
2324 }
2325 _ => Expr::Const(ConstVal::Int(0)),
2326 };
2327 out.push(e);
2328 }
2329 out
2330}
2331
2332pub fn entry_regs(vt: &mut VarTable, env: &MethodEnv, param_names: &[Option<String>]) -> Vec<Reg> {
2334 let code = env.code;
2335 let n = code.registers_size as usize;
2336 let mut regs = vec![Reg::Undef; n.max(1)];
2337 let ins = code.ins_size.min(code.registers_size) as usize;
2338 let mut reg = n.saturating_sub(ins);
2339 let mut pidx = 0;
2340 if !env.is_static && reg < n {
2341 let id = vt.vars.len() as u32;
2343 vt.vars.push(VarInfo {
2344 id,
2345 slot: reg as u16,
2346 name: "this".into(),
2347 ty: TypeRef::J(JavaType::Object(env.class_name.as_str().into())),
2348 is_param: true,
2349 range_start: 0,
2350 range_end: u16::MAX,
2351 synthetic_name: false,
2352 });
2353 while vt.by_slot.len() <= reg {
2354 vt.by_slot.push(Vec::new());
2355 }
2356 vt.by_slot[reg].push((0, u16::MAX, id));
2357 regs[reg] = Reg::Live(id);
2358 reg += 1;
2359 pidx += 1;
2360 }
2361 for (i, arg) in env.desc.args.iter().enumerate() {
2362 if reg >= n {
2363 break;
2364 }
2365 let dbg = param_names.get(i).and_then(|o| o.clone());
2366 let named = dbg.is_some();
2367 let name = dbg
2372 .map(|d| crate::classdec::java_ident(&d).into_owned())
2373 .unwrap_or_else(|| format!("p{}", pidx));
2374 let id = vt.vars.len() as u32;
2375 vt.vars.push(VarInfo {
2376 id,
2377 slot: reg as u16,
2378 name,
2379 ty: TypeRef::J(arg.clone()),
2380 is_param: true,
2381 range_start: 0,
2382 range_end: u16::MAX,
2383 synthetic_name: !named,
2384 });
2385 while vt.by_slot.len() <= reg {
2386 vt.by_slot.push(Vec::new());
2387 }
2388 vt.by_slot[reg].push((0, u16::MAX, id));
2389 regs[reg] = Reg::Live(id);
2390 reg += if arg.is_wide() { 2 } else { 1 };
2391 pidx += 1;
2392 }
2393 regs
2394}