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 ins: &[Insn],
293) -> std::sync::Arc<jdc_core::FxHashSet<(u32, u16)>> {
294 use jdc_core::FxHashMap as HashMap;
295 let mut events: HashMap<u16, Vec<(u32, bool)>> = HashMap::default();
297 for i in ins {
298 for r in src_regs(&i.kind) {
299 events.entry(r).or_default().push((i.pc, true));
300 }
301 for r in dst_regs(&i.kind) {
302 events.entry(r).or_default().push((i.pc, false));
303 }
304 }
305 let mut out = jdc_core::FxHashSet::default();
306 for (reg, evs) in events {
307 let mut dedup: Vec<(u32, bool)> = Vec::with_capacity(evs.len());
308 for (pc, is_read) in evs {
309 if dedup.last() != Some(&(pc, is_read)) {
310 dedup.push((pc, is_read));
311 }
312 }
313 for (i, &(pc, is_read)) in dedup.iter().enumerate() {
314 if !is_read {
315 continue;
316 }
317 let mut final_ = true;
320 if let Some(&(_, is_read2)) = dedup.get(i + 1) {
321 if is_read2 {
322 final_ = false;
323 }
324 }
325 if final_ {
326 out.insert((pc, reg));
327 }
328 }
329 }
330 std::sync::Arc::new(out)
331}
332
333pub struct Lifter<'a> {
334 pub env: &'a MethodEnv<'a>,
335 pub vt: &'a mut VarTable,
336 block_id: usize,
338 stable: &'a mut jdc_core::FxHashMap<(usize, u16, u64), u32>,
345 regs: Vec<Reg>,
346 write_pc: Vec<u32>,
347 stmts: Vec<Stmt>,
348 pending_call: Option<Expr>,
349 code_units: u32,
350 cur_pc: u32,
353 final_read: std::sync::Arc<jdc_core::FxHashSet<(u32, u16)>>,
360 mflags: &'a mut MethodFlags,
364 force_mat: jdc_core::FxHashSet<u32>,
371}
372
373impl<'a> Lifter<'a> {
374 pub fn new(
375 env: &'a MethodEnv<'a>,
376 vt: &'a mut VarTable,
377 in_regs: Vec<Reg>,
378 block_id: usize,
379 stable: &'a mut jdc_core::FxHashMap<(usize, u16, u64), u32>,
380 mflags: &'a mut MethodFlags,
381 ) -> Lifter<'a> {
382 let n = env.code.registers_size as usize;
383 let n = n.max(in_regs.len()).max(1);
384 let mut regs = in_regs;
385 regs.resize(n, Reg::Undef);
386 Lifter {
387 env,
388 vt,
389 regs,
390 write_pc: vec![0; n],
391 stmts: Vec::new(),
392 block_id,
393 stable,
394 pending_call: None,
395 code_units: env.code_units,
396 cur_pc: 0,
397 final_read: std::sync::Arc::new(jdc_core::FxHashSet::default()),
398 mflags,
399 force_mat: jdc_core::FxHashSet::default(),
400 }
401 }
402
403 fn ty_key(ty: &TypeRef) -> u64 {
411 use std::hash::{Hash, Hasher};
412 fn jt_key(j: &JavaType, h: &mut jdc_core::fx::FxHasher) {
413 match j {
414 JavaType::Void => 1u64.hash(h),
415 JavaType::Boolean => 2u64.hash(h),
416 JavaType::Byte => 3u64.hash(h),
417 JavaType::Char => 4u64.hash(h),
418 JavaType::Short => 5u64.hash(h),
419 JavaType::Int => 6u64.hash(h),
420 JavaType::Float => 7u64.hash(h),
421 JavaType::Long => 8u64.hash(h),
422 JavaType::Double => 9u64.hash(h),
423 JavaType::Object(n) => {
424 10u64.hash(h);
425 n.hash(h);
426 }
427 JavaType::Array(inner) => {
428 11u64.hash(h);
429 jt_key(inner, h);
430 }
431 }
432 }
433 let mut h = jdc_core::fx::FxHasher::default();
434 match ty {
435 TypeRef::J(j) => jt_key(j, &mut h),
436 TypeRef::G(_) => 99u64.hash(&mut h),
437 }
438 h.finish()
439 }
440
441 fn fresh_var(&mut self, slot: u16, ty: TypeRef) -> u32 {
442 let key = (self.block_id, slot, Self::ty_key(&ty));
452 if let Some(&id) = self.stable.get(&key) {
453 return id;
454 }
455 if let Some(name) = self.debug_name(slot, &ty) {
460 let tkey = Self::ty_key(&ty);
466 if let Some(id) = self
467 .vt
468 .vars
469 .iter()
470 .find(|v| {
471 v.slot == slot
472 && !v.synthetic_name
473 && v.name == name
474 && Self::ty_key(&v.ty) == tkey
475 })
476 .map(|v| v.id)
477 {
478 self.stable.insert(key, id);
479 return id;
480 }
481 let id = self.vt.vars.len() as u32;
482 self.stable.insert(key, id);
483 self.push_var(id, slot, name, ty, false);
484 return id;
485 }
486 let id = self.vt.vars.len() as u32;
487 let name = format!("v{}", id);
488 self.stable.insert(key, id);
489 self.push_var(id, slot, name, ty, true);
490 id
491 }
492
493 fn push_var(&mut self, id: u32, slot: u16, name: String, ty: TypeRef, synthetic: bool) {
494 self.vt.vars.push(VarInfo {
495 id,
496 slot,
497 name,
498 ty,
499 is_param: false,
500 range_start: 0,
501 range_end: u16::MAX,
502 synthetic_name: synthetic,
503 });
504 while self.vt.by_slot.len() <= slot as usize {
505 self.vt.by_slot.push(Vec::new());
506 }
507 self.vt.by_slot[slot as usize].push((0, u16::MAX, id));
508 }
509
510 fn debug_name(&self, slot: u16, ty: &TypeRef) -> Option<String> {
515 let locals = &self.env.debug_locals;
516 if locals.is_empty() {
517 return None;
518 }
519 let want = ty.erased();
520 let covers = |pc: u32| {
521 locals.iter().find(|l| {
522 l.reg == slot
523 && pc >= l.start
524 && pc < l.end
525 && l.ty.as_deref().map(|d| crate::desc_type(d) == want).unwrap_or(true)
530 })
531 };
532 let hit = covers(self.cur_pc).or_else(|| {
537 let wpc = *self.write_pc.get(slot as usize)?;
538 if wpc == 0 {
539 None
540 } else {
541 covers(wpc)
542 }
543 })?;
544 let name = crate::classdec::java_ident(&hit.name);
545 if name.is_empty() {
546 return None;
547 }
548 Some(name.into_owned())
549 }
550
551 fn local_expr(&self, v: u32) -> Expr {
552 Expr::Local {
553 var: v,
554 ty: self.vt.var(v).ty.clone(),
555 }
556 }
557
558 fn folded_new_aliases(&self, r: u16) -> Vec<usize> {
565 let Some(w) = self.write_pc.get(r as usize).copied() else {
566 return Vec::new();
567 };
568 (0..self.regs.len())
569 .filter(|&rr| {
570 rr != r as usize
571 && self.write_pc.get(rr).copied() == Some(w)
572 && matches!(self.regs[rr], Reg::Pending(Expr::New { raw: false, .. }))
573 })
574 .collect()
575 }
576
577 #[track_caller]
581 fn read_nest(&mut self, r: u16) -> Expr {
582 match self.regs.get(r as usize).cloned().unwrap_or(Reg::Undef) {
583 Reg::Undef | Reg::WideHi => {
584 let ty = TypeRef::J(JavaType::Int);
588 let v = self.fresh_var(r, ty);
589 if (r as usize) < self.regs.len() {
590 self.regs[r as usize] = Reg::Live(v);
591 }
592 self.local_expr(v)
593 }
594 Reg::Live(v) => self.local_expr(v),
595 Reg::Pending(e) => {
596 let alias_needs_share = matches!(&e, Expr::New { raw: false, .. })
622 && self.folded_new_aliases(r).iter().any(|&rr| {
623 self.final_read
624 .iter()
625 .any(|&(pc2, r2)| r2 == rr as u16 && pc2 >= self.cur_pc)
626 });
627 let alloc = alias_needs_share
628 || matches!(&e, Expr::NewArray { .. })
629 || (matches!(&e, Expr::New { .. })
630 && !self.final_read.contains(&(self.cur_pc, r)));
631 if alloc {
632 let v = self.materialize(r);
633 self.local_expr(v)
634 } else {
635 if matches!(&e, Expr::NewArray { .. })
646 || matches!(&e, Expr::New { raw: false, .. })
647 {
648 let aliases = if matches!(&e, Expr::New { raw: false, .. }) {
649 self.folded_new_aliases(r)
650 } else {
651 Vec::new()
652 };
653 if let Some(slot) = self.regs.get_mut(r as usize) {
654 *slot = Reg::Undef;
655 }
656 for rr in aliases {
660 self.regs[rr] = Reg::Undef;
661 }
662 }
663 e
664 }
665 }
666 Reg::PendingCall(_) => {
667 let v = self.materialize(r);
668 self.local_expr(v)
669 }
670 }
671 }
672
673 fn read_term(&mut self, r: u16) -> Expr {
677 match self.regs.get(r as usize).cloned().unwrap_or(Reg::Undef) {
678 Reg::PendingCall(e) | Reg::Pending(e) => {
679 let aliases = if matches!(&e, Expr::New { raw: false, .. }) {
683 self.folded_new_aliases(r)
684 } else {
685 Vec::new()
686 };
687 if (r as usize) < self.regs.len() {
688 self.regs[r as usize] = Reg::Undef;
689 }
690 for rr in aliases {
691 self.regs[rr] = Reg::Undef;
692 }
693 self.pending_call = None;
694 e
695 }
696 _ => self.read_nest(r),
697 }
698 }
699
700 fn write(&mut self, r: u16, e: Expr, pc: u32, wide: bool) {
701 if r as usize >= self.regs.len() {
702 return;
703 }
704 self.write_pc[r as usize] = pc;
705 if self.force_mat.contains(&pc) {
709 let e = value_of_cmp(&e);
710 let v = self.materialize_value(r, e);
711 self.regs[r as usize] = Reg::Live(v);
712 if wide {
713 self.mark_wide_hi(r + 1, pc);
714 }
715 return;
716 }
717 if expr_size(&e) > 32 {
722 self.write_pc[r as usize] = pc;
723 let v = self.materialize_value(r, e);
724 if (r as usize) < self.regs.len() {
725 self.regs[r as usize] = Reg::Live(v);
726 }
727 if wide {
728 self.mark_wide_hi(r + 1, pc);
729 }
730 return;
731 }
732 self.regs[r as usize] = Reg::Pending(e);
733 if wide {
734 self.mark_wide_hi(r + 1, pc);
735 }
736 }
737
738 fn materialize_value(&mut self, r: u16, e: Expr) -> u32 {
741 let e = value_of_cmp(&e);
742 let ty = e.type_ref();
743 let v = self.fresh_var(r, ty);
747 self.stmts.push(Stmt::LocalDef {
748 var: v,
749 init: Some(e),
750 is_final: false,
751 force_type: true,
752 });
753 v
754 }
755
756 fn write_call_result(&mut self, r: u16, e: Expr, pc: u32) {
759 if r as usize >= self.regs.len() {
760 return;
761 }
762 self.write_pc[r as usize] = pc;
763 self.regs[r as usize] = Reg::PendingCall(e);
764 }
765
766 fn mark_wide_hi(&mut self, r: u16, pc: u32) {
767 if (r as usize) < self.regs.len() {
768 self.regs[r as usize] = Reg::WideHi;
769 self.write_pc[r as usize] = pc;
770 }
771 }
772
773 fn materialize_views_mentioning(
780 &mut self,
781 cls: &std::sync::Arc<str>,
782 name: &std::sync::Arc<str>,
783 is_static: bool,
784 ) {
785 let mut stale: Vec<u16> = Vec::new();
786 for (i, r) in self.regs.iter().enumerate() {
787 match r {
788 Reg::Pending(e) | Reg::PendingCall(e)
789 if expr_mentions_field(e, cls, name, is_static) =>
790 {
791 stale.push(i as u16);
792 }
793 _ => {}
794 }
795 }
796 for r in stale {
797 self.materialize(r);
798 }
799 }
800
801 fn materialize(&mut self, r: u16) -> u32 {
803 let cur = std::mem::replace(
807 self.regs.get_mut(r as usize).unwrap_or(&mut Reg::Undef),
808 Reg::Undef,
809 );
810 let e = match cur {
811 Reg::Pending(e) | Reg::PendingCall(e) => e,
812 Reg::Live(v) => {
813 if (r as usize) < self.regs.len() {
814 self.regs[r as usize] = Reg::Live(v);
815 }
816 return v;
817 }
818 _ => {
821 let v = self.fresh_var(r, TypeRef::J(JavaType::Object("java/lang/Object".into())));
822 self.stmts.push(Stmt::LocalDef {
823 var: v,
824 init: None,
825 is_final: false,
826 force_type: false,
827 });
828 if (r as usize) < self.regs.len() {
829 self.regs[r as usize] = Reg::Live(v);
830 }
831 return v;
832 }
833 };
834 let e = value_of_cmp_owned(e);
836 let ty = e.type_ref();
837 let v = self.fresh_var(r, ty);
841 self.stmts.push(Stmt::LocalDef {
842 var: v,
843 init: Some(e),
844 is_final: false,
845 force_type: true,
846 });
847 if (r as usize) < self.regs.len() {
848 self.regs[r as usize] = Reg::Live(v);
849 }
850 for rr in self.folded_new_aliases(r) {
854 self.regs[rr] = Reg::Live(v);
855 }
856 v
857 }
858
859 fn drop_pending_call(&mut self) {
861 if let Some(e) = self.pending_call.take() {
862 let e = value_of_cmp(&e);
863 if has_side_effects(&e) {
864 self.stmts.push(Stmt::ExprStmt(e));
865 }
866 }
867 }
868
869 fn owner_expr(&self, obj: Expr, cls: &str) -> Option<Box<Expr>> {
872 if cls == self.env.class_name && matches!(obj, Expr::This) {
873 return None;
874 }
875 Some(Box::new(obj))
876 }
877
878 fn arith_op(op: ArithOp) -> BinOp {
879 match op {
880 ArithOp::Add => BinOp::Add,
881 ArithOp::Sub => BinOp::Sub,
882 ArithOp::Mul => BinOp::Mul,
883 ArithOp::Div => BinOp::Div,
884 ArithOp::Rem => BinOp::Rem,
885 ArithOp::And => BinOp::And,
886 ArithOp::Or => BinOp::Or,
887 ArithOp::Xor => BinOp::Xor,
888 ArithOp::Shl => BinOp::Shl,
889 ArithOp::Shr => BinOp::Shr,
890 ArithOp::Ushr => BinOp::Ushr,
891 }
892 }
893
894 fn cmp_op(op: CmpOp) -> BinOp {
895 match op {
896 CmpOp::Eq => BinOp::Eq,
897 CmpOp::Ne => BinOp::Ne,
898 CmpOp::Lt => BinOp::Lt,
899 CmpOp::Ge => BinOp::Ge,
900 CmpOp::Gt => BinOp::Gt,
901 CmpOp::Le => BinOp::Le,
902 }
903 }
904
905 fn cmp_sentinel(kind: CmpKind, l: Expr, r: Expr) -> Expr {
906 let name = match kind {
907 CmpKind::CmplF => "\0cmpl-float",
908 CmpKind::CmpgF => "\0cmpg-float",
909 CmpKind::CmplD => "\0cmpl-double",
910 CmpKind::CmpgD => "\0cmpg-double",
911 CmpKind::CmpJ => "\0cmp-long",
912 };
913 Expr::Invokedynamic {
914 name: name.into(),
915 desc: MethodDescriptor {
916 args: vec![],
917 ret: JavaType::Int,
918 },
919 args: vec![l, r],
920 bsm_text: String::new(),
921 bsm_static_args: vec![],
922 }
923 }
924
925 pub fn build_block(
930 mut self,
931 ins: &[Insn],
932 handler_types: &[Option<std::sync::Arc<str>>],
933 final_read: std::sync::Arc<jdc_core::FxHashSet<(u32, u16)>>,
934 ) -> BResult<(BlockResult, OutState)> {
935 let payloads = &self.env.code.payloads;
936 self.final_read = final_read;
946 {
952 let mut field_writes: jdc_core::FxHashMap<
953 (std::sync::Arc<str>, std::sync::Arc<str>, bool),
954 Vec<u32>,
955 > = jdc_core::FxHashMap::default();
956 for ins in ins {
957 match &ins.kind {
958 InsnKind::IPut { field_idx, .. } | InsnKind::SPut { field_idx, .. } => {
959 let (cls, name, _) = self.env.field_ref(*field_idx);
960 let is_static = matches!(ins.kind, InsnKind::SPut { .. });
961 field_writes
962 .entry((cls, name, is_static))
963 .or_default()
964 .push(ins.pc);
965 }
966 _ => {}
967 }
968 }
969 if !field_writes.is_empty() {
970 for ins in ins {
971 let (_dst, field_idx, is_static) = match &ins.kind {
972 InsnKind::IGet { dst, field_idx, .. } => (*dst, *field_idx, false),
973 InsnKind::SGet { dst, field_idx } => (*dst, *field_idx, true),
974 _ => continue,
975 };
976 let (cls, name, _) = self.env.field_ref(field_idx);
977 let killed = field_writes
978 .get(&(cls, name, is_static))
979 .is_some_and(|ws| ws.iter().any(|w| *w > ins.pc));
980 if killed {
981 self.force_mat.insert(ins.pc);
982 }
983 }
984 }
985 }
986 for ins in ins {
987 self.cur_pc = ins.pc;
988 match &ins.kind {
989 InsnKind::Nop => {}
990 InsnKind::Unknown => {
991 return Err(format!(
992 "unsupported opcode {:#04x} ({}) at pc {}",
993 ins.op,
994 ddc_dex::insn::op_name(ins.op),
995 ins.pc
996 ));
997 }
998 InsnKind::Move { dst, src } => {
999 let src_state = self.regs.get(*src as usize).cloned().unwrap_or(Reg::Undef);
1010 if matches!(src_state, Reg::PendingCall(_))
1011 || matches!(&src_state, Reg::Pending(e)
1012 if has_side_effects(e) && !matches!(e, Expr::New { raw: true, .. }))
1013 {
1014 self.materialize(*src);
1015 }
1016 let st = self.regs.get(*src as usize).cloned().unwrap_or(Reg::Undef);
1017 let wide = reg_is_wide(&st, self.vt);
1018 if (*dst as usize) < self.regs.len() {
1019 self.regs[*dst as usize] = st;
1020 self.write_pc[*dst as usize] =
1021 self.write_pc.get(*src as usize).copied().unwrap_or(0);
1022 }
1023 if wide {
1024 self.mark_wide_hi(*dst + 1, ins.pc);
1025 }
1026 }
1027 InsnKind::MoveResult { dst } => {
1028 if let Some(e) = self.pending_call.take() {
1029 let wide =
1030 matches!(e.type_ref().erased(), JavaType::Long | JavaType::Double);
1031 self.write_call_result(*dst, e, ins.pc);
1032 if wide {
1033 self.mark_wide_hi(*dst + 1, ins.pc);
1034 }
1035 } else {
1036 let v = self.fresh_var(*dst, TypeRef::J(JavaType::Int));
1038 if (*dst as usize) < self.regs.len() {
1039 self.regs[*dst as usize] = Reg::Live(v);
1040 }
1041 }
1042 }
1043 InsnKind::MoveException { dst } => {
1044 let ty = handler_types
1045 .iter()
1046 .flatten()
1047 .next()
1048 .map(|t| TypeRef::J(JavaType::Object(t.clone())))
1049 .unwrap_or_else(|| {
1050 TypeRef::J(JavaType::Object("java/lang/Throwable".into()))
1051 });
1052 let v = self.fresh_var(*dst, ty);
1053 self.stmts.push(Stmt::LocalDef {
1054 var: v,
1055 init: None,
1056 is_final: false,
1057 force_type: true,
1058 });
1059 if (*dst as usize) < self.regs.len() {
1060 self.regs[*dst as usize] = Reg::Live(v);
1061 }
1062 }
1063 InsnKind::ReturnVoid
1064 | InsnKind::Return { .. }
1065 | InsnKind::Throw { .. }
1066 | InsnKind::Goto { .. }
1067 | InsnKind::PackedSwitch { .. }
1068 | InsnKind::SparseSwitch { .. }
1069 | InsnKind::If { .. } => {}
1070 InsnKind::Const { dst, val, wide } => {
1071 let e = if *wide {
1072 Expr::Const(ConstVal::Long(*val))
1073 } else {
1074 Expr::Const(ConstVal::Int(*val as i32))
1075 };
1076 self.write(*dst, e, ins.pc, *wide);
1077 }
1078 InsnKind::ConstString { dst, str_idx } => {
1079 let s = std::sync::Arc::from(self.env.dex.string(*str_idx));
1080 self.write(*dst, Expr::Const(ConstVal::Str(s)), ins.pc, false);
1081 }
1082 InsnKind::ConstClass { dst, type_idx } => {
1083 let ty = TypeRef::J(self.env.java_type(*type_idx));
1084 self.write(*dst, Expr::Const(ConstVal::ClassLit(ty)), ins.pc, false);
1085 }
1086 InsnKind::MonitorEnter { reg } => {
1087 let e = self.read_nest(*reg);
1088 *self.mflags = self.mflags.with_monitor();
1089 self.stmts.push(Stmt::MonitorEnter(e));
1090 }
1091 InsnKind::MonitorExit { reg } => {
1092 let e = self.read_nest(*reg);
1093 *self.mflags = self.mflags.with_monitor();
1094 self.stmts.push(Stmt::MonitorExit(e));
1095 }
1096 InsnKind::CheckCast { reg, type_idx } => {
1097 let e = self.read_nest(*reg);
1098 let ty = TypeRef::J(self.env.java_type(*type_idx));
1099 self.write(*reg, Expr::Cast { ty, e: Box::new(e) }, ins.pc, false);
1100 }
1101 InsnKind::InstanceOf { dst, src, type_idx } => {
1102 let e = self.read_nest(*src);
1103 let ty = TypeRef::J(self.env.java_type(*type_idx));
1104 self.write(*dst, Expr::InstanceOf { e: Box::new(e), ty }, ins.pc, false);
1105 }
1106 InsnKind::ArrayLength { dst, src } => {
1107 let e = self.read_nest(*src);
1108 let length = Expr::Field {
1109 owner: Some(Box::new(e)),
1110 cls: std::sync::Arc::from(""),
1111 name: "length".into(),
1112 ty: TypeRef::J(JavaType::Int),
1113 is_static: false,
1114 };
1115 self.write(*dst, length, ins.pc, false);
1116 }
1117 InsnKind::NewInstance { dst, type_idx } => {
1118 let cls = self.env.type_name_arc(*type_idx);
1119 let ty = TypeRef::J(JavaType::Object(cls.clone()));
1120 self.write(
1121 *dst,
1122 Expr::New {
1123 cls,
1124 ty,
1125 args: vec![],
1126 arg_tys: Vec::new(),
1127 raw: true,
1128 },
1129 ins.pc,
1130 false,
1131 );
1132 }
1133 InsnKind::NewArray {
1134 dst,
1135 size,
1136 type_idx,
1137 } => {
1138 let elem_desc = self.env.type_name(*type_idx);
1139 let elem = crate::desc_type(elem_desc.trim_start_matches('['));
1141 let n_brackets = elem_desc.chars().take_while(|&c| c == '[').count();
1152 let trailing_dims = n_brackets.saturating_sub(1).min(u8::MAX as usize) as u8;
1153 let dims = self.read_nest(*size);
1154 self.write(
1155 *dst,
1156 Expr::NewArray {
1157 elem: TypeRef::J(elem),
1158 dims: vec![dims],
1159 trailing_dims,
1160 init: None,
1161 },
1162 ins.pc,
1163 false,
1164 );
1165 }
1166 InsnKind::FilledNewArray { regs, type_idx } => {
1167 self.drop_pending_call();
1168 let elem_desc = self.env.type_name(*type_idx);
1169 let base = elem_desc.trim_start_matches('[');
1170 let depth = elem_desc.len() - base.len();
1180 let elem = crate::desc_type(base);
1181 let wide = depth == 1 && elem.is_wide();
1188 let mut args: Vec<Expr> = Vec::with_capacity(regs.len());
1189 let mut it = regs.iter();
1190 while let Some(&r) = it.next() {
1191 args.push(self.read_nest(r));
1192 if wide {
1193 it.next();
1194 }
1195 }
1196 self.pending_call = Some(Expr::NewArray {
1197 elem: TypeRef::J(elem),
1198 dims: vec![],
1199 trailing_dims: depth.saturating_sub(1).min(255) as u8,
1200 init: Some(args),
1201 });
1202 }
1203 InsnKind::FillArrayData { reg, payload_pc } => {
1204 if let Some(Payload::ArrayData {
1205 elem_width,
1206 size,
1207 data,
1208 }) = payloads.get(payload_pc)
1209 {
1210 let elems = payload_consts(*elem_width, *size, data);
1211 let cur = self.regs.get(*reg as usize).cloned().unwrap_or(Reg::Undef);
1212 if let Reg::Pending(Expr::NewArray { elem, init, .. }) = cur {
1213 if init.is_none() {
1214 self.write(
1215 *reg,
1216 Expr::NewArray {
1217 elem,
1218 dims: vec![Expr::Const(ConstVal::Int(*size as i32))],
1219 trailing_dims: 0,
1220 init: Some(elems),
1221 },
1222 ins.pc,
1223 false,
1224 );
1225 continue;
1226 }
1227 }
1228 let arr = self.read_nest(*reg);
1230 for (i, e) in elems.into_iter().enumerate() {
1231 let idx = Expr::Const(ConstVal::Int(i as i32));
1232 let target = Expr::ArrayIndex {
1233 array: Box::new(arr.clone()),
1234 index: Box::new(idx),
1235 };
1236 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
1237 target: Box::new(target),
1238 op: AssignOp::Plain,
1239 value: Box::new(e),
1240 }));
1241 }
1242 }
1243 }
1244 InsnKind::Cmp { dst, a, b, kind } => {
1245 let l = self.read_nest(*a);
1246 let r = self.read_nest(*b);
1247 *self.mflags = self.mflags.with_cmp();
1248 self.write(*dst, Self::cmp_sentinel(*kind, l, r), ins.pc, false);
1249 }
1250 InsnKind::AGet {
1251 dst, array, index, ..
1252 } => {
1253 let a = self.read_nest(*array);
1254 let i = self.read_nest(*index);
1255 self.write(
1256 *dst,
1257 Expr::ArrayIndex {
1258 array: Box::new(a),
1259 index: Box::new(i),
1260 },
1261 ins.pc,
1262 false,
1263 );
1264 }
1265 InsnKind::APut {
1266 value,
1267 array,
1268 index,
1269 ..
1270 } => {
1271 self.drop_pending_call();
1272 let v = self.read_nest(*value);
1273 let a = self.read_nest(*array);
1274 let i = self.read_nest(*index);
1275 let target = Expr::ArrayIndex {
1276 array: Box::new(a),
1277 index: Box::new(i),
1278 };
1279 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
1280 target: Box::new(target),
1281 op: AssignOp::Plain,
1282 value: Box::new(v),
1283 }));
1284 }
1285 InsnKind::IGet {
1286 dst,
1287 obj,
1288 field_idx,
1289 } => {
1290 self.drop_pending_call();
1291 let (cls, name, ty) = self.env.field_ref(*field_idx);
1292 let owner = self.read_nest(*obj);
1293 let owner_opt = self.owner_expr(owner, &cls);
1294 self.write(
1295 *dst,
1296 Expr::Field {
1297 owner: owner_opt,
1298 cls,
1299 name,
1300 ty: TypeRef::J(ty),
1301 is_static: false,
1302 },
1303 ins.pc,
1304 false,
1305 );
1306 }
1307 InsnKind::IPut {
1308 value,
1309 obj,
1310 field_idx,
1311 } => {
1312 self.drop_pending_call();
1313 let (cls, name, ty) = self.env.field_ref(*field_idx);
1314 self.materialize_views_mentioning(&cls, &name, false);
1320 let v = null_in_obj_ctx(self.read_nest(*value), &ty);
1321 let owner = self.read_nest(*obj);
1322 let owner_opt = self.owner_expr(owner, &cls);
1323 let target = Expr::Field {
1324 owner: owner_opt,
1325 cls,
1326 name,
1327 ty: TypeRef::J(ty),
1328 is_static: false,
1329 };
1330 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
1331 target: Box::new(target),
1332 op: AssignOp::Plain,
1333 value: Box::new(v),
1334 }));
1335 }
1336 InsnKind::SGet { dst, field_idx } => {
1337 self.drop_pending_call();
1338 let (cls, name, ty) = self.env.field_ref(*field_idx);
1339 self.write(
1340 *dst,
1341 Expr::Field {
1342 owner: None,
1343 cls,
1344 name,
1345 ty: TypeRef::J(ty),
1346 is_static: true,
1347 },
1348 ins.pc,
1349 false,
1350 );
1351 }
1352 InsnKind::SPut { value, field_idx } => {
1353 self.drop_pending_call();
1354 let (cls, name, ty) = self.env.field_ref(*field_idx);
1355 self.materialize_views_mentioning(&cls, &name, true);
1356 let v = null_in_obj_ctx(self.read_nest(*value), &ty);
1357 let target = Expr::Field {
1358 owner: None,
1359 cls,
1360 name,
1361 ty: TypeRef::J(ty),
1362 is_static: true,
1363 };
1364 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
1365 target: Box::new(target),
1366 op: AssignOp::Plain,
1367 value: Box::new(v),
1368 }));
1369 }
1370 InsnKind::Invoke {
1371 kind,
1372 regs,
1373 method_idx,
1374 } => {
1375 self.drop_pending_call();
1376 self.do_invoke(*kind, regs, *method_idx, ins.pc)?;
1377 }
1378 InsnKind::InvokeCustom {
1379 call_site_idx,
1380 regs,
1381 } => {
1382 self.drop_pending_call();
1383 let e = self.build_invoke_custom(*call_site_idx, regs, ins.pc);
1384 self.pending_call = Some(e);
1385 }
1386 InsnKind::Un {
1387 dst,
1388 src,
1389 op,
1390 from: _,
1391 to,
1392 } => {
1393 let e = self.read_nest(*src);
1394 let ne = match op {
1395 ddc_dex::insn::UnArith::Neg => Expr::Un {
1396 op: UnOp::Neg,
1397 e: Box::new(e),
1398 },
1399 ddc_dex::insn::UnArith::Not => Expr::Un {
1400 op: UnOp::BitNot,
1401 e: Box::new(e),
1402 },
1403 ddc_dex::insn::UnArith::Conv => Expr::Cast {
1404 ty: TypeRef::J(primitive_type(*to)),
1405 e: Box::new(e),
1406 },
1407 };
1408 let wide = matches!(*to, 'J' | 'D');
1409 self.write(*dst, ne, ins.pc, wide);
1410 }
1411 InsnKind::Bin { op, dst, a, b, ty } => {
1412 let l = self.read_nest(*a);
1413 let r = self.read_nest(*b);
1414 let jt = primitive_type(*ty);
1415 let e = Expr::Bin {
1416 op: Self::arith_op(*op),
1417 l: Box::new(l),
1418 r: Box::new(r),
1419 ty: Some(TypeRef::J(jt)),
1420 };
1421 let wide = matches!(*ty, 'J' | 'D');
1422 self.write(*dst, e, ins.pc, wide);
1423 }
1424 InsnKind::BinLit {
1425 op,
1426 dst,
1427 a,
1428 lit,
1429 rsub,
1430 } => {
1431 let av = self.read_nest(*a);
1432 let e = if *rsub {
1433 Expr::Bin {
1434 op: BinOp::Sub,
1435 l: Box::new(Expr::Const(ConstVal::Int(*lit))),
1436 r: Box::new(av),
1437 ty: Some(TypeRef::J(JavaType::Int)),
1438 }
1439 } else {
1440 Expr::Bin {
1441 op: Self::arith_op(*op),
1442 l: Box::new(av),
1443 r: Box::new(Expr::Const(ConstVal::Int(*lit))),
1444 ty: Some(TypeRef::J(JavaType::Int)),
1445 }
1446 };
1447 self.write(*dst, e, ins.pc, false);
1448 }
1449 InsnKind::ConstMethodHandle { dst, handle_idx } => {
1450 self.drop_pending_call();
1451 let e = self.method_handle_expr(*handle_idx);
1452 self.write(*dst, e, ins.pc, false);
1453 }
1454 InsnKind::ConstMethodType { dst, proto_idx } => {
1455 self.drop_pending_call();
1456 let proto = self.env.dex.proto(*proto_idx);
1457 let desc = format!(
1458 "({}){}",
1459 self.env
1460 .dex
1461 .proto_params(*proto_idx)
1462 .iter()
1463 .map(|&t| self.env.dex.type_name(t))
1464 .collect::<Vec<_>>()
1465 .join(""),
1466 self.env.dex.type_name(proto.return_type_idx)
1467 );
1468 self.write(*dst, Expr::Raw(desc), ins.pc, false);
1469 }
1470 }
1471 }
1472
1473 let term = self.block_term(ins);
1475
1476 self.drop_pending_call();
1480 self.materialize_impure_at_exit();
1481
1482 let out = OutState {
1486 regs: std::mem::take(&mut self.regs),
1487 write_pc: std::mem::take(&mut self.write_pc),
1488 };
1489 Ok((
1490 BlockResult {
1491 stmts: std::mem::take(&mut self.stmts),
1492 out_stack: Vec::new(),
1493 term,
1494 },
1495 out,
1496 ))
1497 }
1498
1499 fn do_invoke(
1500 &mut self,
1501 kind: InvokeKind,
1502 regs: &[u16],
1503 method_idx: u32,
1504 pc: u32,
1505 ) -> BResult<()> {
1506 let (cls, name, md) = self.env.method_ref(method_idx);
1507 if cls.as_ref() == "java/lang/StringBuilder" || cls.as_ref() == "java/lang/StringBuffer" {
1508 *self.mflags = self.mflags.with_sb();
1509 }
1510 let is_static = matches!(kind, InvokeKind::Static);
1511 let receiver_reg = if is_static {
1512 None
1513 } else {
1514 regs.first().copied()
1515 };
1516 let arg_regs: Vec<u16> = if is_static {
1517 regs.to_vec()
1518 } else {
1519 regs.iter().skip(1).copied().collect()
1520 };
1521 let mut args: Vec<Expr> = Vec::with_capacity(md.args.len());
1522 let mut ri = 0usize;
1530 for at in md.args.iter() {
1531 let r = arg_regs.get(ri).copied().unwrap_or(0);
1532 args.push(null_in_obj_ctx(self.read_nest(r), at));
1533 ri += if at.is_wide() { 2 } else { 1 };
1534 }
1535 if name.as_ref() == "<init>" && matches!(kind, InvokeKind::Direct) {
1541 let recv_reg = receiver_reg.unwrap_or(0);
1542 let recv_state = self
1543 .regs
1544 .get(recv_reg as usize)
1545 .cloned()
1546 .unwrap_or(Reg::Undef);
1547 if let Reg::Pending(Expr::New {
1548 raw: true, cls: nc, ..
1549 }) = &recv_state
1550 {
1551 if nc == &cls || self.env.pool.is_subtype(nc.as_ref(), cls.as_ref()) {
1562 let folded = Expr::New {
1563 cls: nc.clone(),
1564 ty: TypeRef::J(JavaType::Object(nc.clone())),
1565 arg_tys: md.args.clone(),
1566 args,
1567 raw: false,
1568 };
1569 let origin = self.write_pc.get(recv_reg as usize).copied();
1578 let aliases: Vec<usize> = origin
1579 .map(|w| {
1580 (0..self.regs.len())
1581 .filter(|&r| {
1582 r != recv_reg as usize
1583 && self.write_pc.get(r).copied() == Some(w)
1584 && matches!(
1585 &self.regs[r],
1586 Reg::Pending(Expr::New { raw: true, cls: c, .. })
1587 if c == nc
1588 )
1589 })
1590 .collect()
1591 })
1592 .unwrap_or_default();
1593 self.write(recv_reg, folded.clone(), pc, false);
1594 match self.regs.get(recv_reg as usize) {
1599 Some(Reg::Live(v)) => {
1600 let v = *v;
1601 for r in aliases {
1602 self.regs[r] = Reg::Live(v);
1603 }
1604 }
1605 _ => {
1606 for r in aliases {
1607 self.regs[r] = Reg::Pending(folded.clone());
1608 self.write_pc[r] = pc;
1609 }
1610 }
1611 }
1612 return Ok(());
1613 }
1614 }
1615 let recv_expr = receiver_reg.map(|r| self.read_nest(r));
1616 let owner_expr_v = recv_expr.unwrap_or(Expr::This);
1617 let owner_expr_v = if !self.env.is_static
1624 && matches!(&owner_expr_v, Expr::Local { var: 0, .. })
1625 {
1626 Expr::This
1627 } else {
1628 owner_expr_v
1629 };
1630 let is_super = cls.as_ref() != self.env.class_name.as_str();
1631 let owner = if matches!(owner_expr_v, Expr::This) && is_super {
1632 None
1633 } else {
1634 Some(Box::new(owner_expr_v))
1635 };
1636 let call = Expr::Method {
1637 owner,
1638 cls,
1639 name: "<init>".into(),
1640 desc: md,
1641 args,
1642 is_static: false,
1643 is_interface: false,
1644 is_special: true,
1645 is_super,
1646 is_dynamic: false,
1647 type_args: vec![],
1648 };
1649 self.stmts.push(Stmt::ExprStmt(call));
1650 return Ok(());
1651 }
1652
1653 let recv_expr = receiver_reg.map(|r| self.read_nest(r));
1654 let is_interface = matches!(kind, InvokeKind::Interface);
1655 let recv_is_this = match &recv_expr {
1669 Some(Expr::This) => true,
1670 Some(Expr::Raw(t)) => t == "\u{3}" || t == "this",
1671 Some(Expr::Local { var, .. }) => {
1672 (*var as usize) < self.vt.vars.len() && self.vt.vars[*var as usize].name == "this"
1673 }
1674 _ => false,
1675 };
1676 let is_super = match kind {
1677 InvokeKind::Super | InvokeKind::Direct => {
1678 recv_is_this && cls.as_ref() != self.env.class_name.as_str()
1679 }
1680 _ => false,
1681 };
1682 let owner: Option<Box<Expr>> = match recv_expr {
1683 None => None,
1684 Some(o) => {
1685 if is_super && matches!(o, Expr::This) {
1686 None
1687 } else {
1688 self.owner_expr(o, &cls)
1689 }
1690 }
1691 };
1692 let is_dynamic = matches!(kind, InvokeKind::Polymorphic);
1693 let call = Expr::Method {
1694 owner,
1695 cls,
1696 name,
1697 desc: md,
1698 args,
1699 is_static,
1700 is_interface,
1701 is_special: matches!(kind, InvokeKind::Direct),
1702 is_super,
1703 is_dynamic,
1704 type_args: vec![],
1705 };
1706 self.pending_call = Some(call);
1707 Ok(())
1708 }
1709
1710 fn method_handle_expr(&mut self, handle_idx: u32) -> Expr {
1713 let Some(h) = self.env.dex.method_handle(handle_idx) else {
1714 return Expr::Raw(format!("methodHandle@{}", handle_idx));
1715 };
1716 if h.is_field {
1717 let f = self.env.dex.field(h.target_id);
1718 let cls = self.env.dex.class_name(f.class_idx);
1719 let name = self.env.dex.string(f.name_idx).to_string();
1720 return Expr::Raw(format!("{}::{}", cls.replace('/', "."), name));
1721 }
1722 let m = self.env.dex.method(h.target_id);
1723 let cls = self.env.dex.class_name(m.class_idx);
1724 let name = self.env.dex.string(m.name_idx).to_string();
1725 Expr::Raw(format!("{}::{}", cls.replace('/', "."), name))
1726 }
1727
1728 fn build_invoke_custom(&mut self, cs_idx: u32, regs: &[u16], pc: u32) -> Expr {
1730 let Some(cs) = self.env.dex.call_site(cs_idx).cloned() else {
1731 return Expr::Invokedynamic {
1732 name: format!("call-site#{}", cs_idx),
1733 desc: jdc_core::types::MethodDescriptor {
1734 args: vec![],
1735 ret: JavaType::Object("java/lang/Object".into()),
1736 },
1737 args: vec![],
1738 bsm_text: "unresolved".into(),
1739 bsm_static_args: vec![],
1740 };
1741 };
1742 let site_name = self.env.dex.string(cs.name_idx).to_string();
1743 let params: Vec<JavaType> = self
1744 .env
1745 .dex
1746 .proto_params(cs.proto_idx)
1747 .iter()
1748 .map(|&t| self.env.java_type(t))
1749 .collect();
1750 let ret = self
1751 .env
1752 .java_type(self.env.dex.proto(cs.proto_idx).return_type_idx);
1753
1754 let (bs_cls, bs_name) = match self.env.dex.method_handle(cs.bootstrap_handle) {
1755 Some(h) if !h.is_field => {
1756 let m = self.env.dex.method(h.target_id);
1757 (
1758 self.env.dex.class_name(m.class_idx),
1759 self.env.dex.string(m.name_idx).to_string(),
1760 )
1761 }
1762 _ => (String::new(), String::new()),
1763 };
1764
1765 let mut args: Vec<Expr> = Vec::with_capacity(params.len().min(regs.len()));
1770 let mut ri = 0usize;
1771 for at in params.iter() {
1772 if let Some(&r) = regs.get(ri) {
1773 args.push(self.read_nest(r));
1774 }
1775 ri += if at.is_wide() { 2 } else { 1 };
1776 }
1777
1778 if bs_name == "makeConcatWithConstants" {
1780 let recipe = cs
1781 .linker_args
1782 .iter()
1783 .find_map(|v| match v {
1784 ddc_dex::annotations::EncodedValue::String(s) => {
1785 Some(self.env.dex.string(*s).to_string())
1786 }
1787 _ => None,
1788 })
1789 .unwrap_or_default();
1790 return concat_from_recipe(&recipe, args);
1791 }
1792
1793 let mut impl_m = None;
1797 let mut instantiated_proto: Option<u32> = None;
1798 if bs_name == "metafactory" || bs_name == "altMetafactory" {
1799 let mut seen_handle = false;
1800 for lv in &cs.linker_args {
1801 match lv {
1802 ddc_dex::annotations::EncodedValue::MethodHandle(hi) => {
1803 if !seen_handle {
1804 seen_handle = true;
1805 if let Some(h) = self.env.dex.method_handle(*hi) {
1806 if !h.is_field {
1807 let m = self.env.dex.method(h.target_id);
1808 impl_m = Some((
1809 self.env.dex.class_name(m.class_idx).replace('/', "."),
1810 self.env.dex.string(m.name_idx).to_string(),
1811 ));
1812 }
1813 }
1814 }
1815 }
1816 ddc_dex::annotations::EncodedValue::MethodType(p) if seen_handle => {
1817 instantiated_proto = Some(*p);
1818 }
1819 _ => {}
1820 }
1821 }
1822 }
1823 let _ = pc;
1824
1825 let mut captures = Vec::new();
1827 for &r in regs.iter().skip(params.len().min(regs.len())) {
1828 captures.push(self.read_nest(r));
1829 }
1830
1831 if let Some((impl_cls, impl_name)) = impl_m {
1832 if captures.is_empty() {
1833 if impl_name.starts_with("lambda$") {
1837 let n_params = match instantiated_proto {
1841 Some(p) => self.env.dex.proto_params(p).len(),
1842 None => params.len(),
1843 };
1844 let pnames: Vec<String> = (0..n_params).map(|i| format!("a{}", i)).collect();
1845 return Expr::Raw(format!(
1846 "({}) -> {}.{}({})",
1847 pnames.join(", "),
1848 impl_cls,
1849 impl_name,
1850 pnames.join(", ")
1851 ));
1852 }
1853 return Expr::Raw(format!("{}::{}", impl_cls, impl_name));
1854 }
1855 let mut call_args = args;
1858 call_args.extend(captures);
1859 return Expr::Invokedynamic {
1860 name: format!("{} -> {}.{}", site_name, impl_cls, impl_name),
1861 desc: jdc_core::types::MethodDescriptor { args: params, ret },
1862 args: call_args,
1863 bsm_text: format!("{}::{}", bs_cls, bs_name),
1864 bsm_static_args: vec![],
1865 };
1866 }
1867
1868 Expr::Invokedynamic {
1869 name: site_name,
1870 desc: jdc_core::types::MethodDescriptor { args: params, ret },
1871 args: captures,
1872 bsm_text: format!("{}::{}", bs_cls, bs_name),
1873 bsm_static_args: vec![],
1874 }
1875 }
1876
1877 fn materialize_impure_at_exit(&mut self) {
1878 let n = self.regs.len();
1879 for r in 0..n {
1880 let st = self.regs[r].clone();
1881 let impure = match &st {
1882 Reg::PendingCall(_) => true,
1883 Reg::Pending(e) => {
1884 has_side_effects(e)
1885 && !matches!(e, Expr::New { raw: true, .. })
1886 && !matches!(e, Expr::NewArray { init: None, .. })
1887 }
1888 _ => false,
1889 };
1890 if impure {
1891 self.materialize(r as u16);
1892 }
1893 }
1894 }
1895
1896 fn block_term(&mut self, ins: &[Insn]) -> Term {
1898 let Some(last) = ins.last() else {
1899 return Term::Goto;
1900 };
1901 match &last.kind {
1902 InsnKind::Goto { .. } => Term::Goto,
1903 InsnKind::ReturnVoid => Term::Return(None),
1904 InsnKind::Return { src } => {
1905 let v = null_in_obj_ctx(self.read_term(*src), &self.env.desc.ret);
1906 Term::Return(Some(v))
1907 }
1908 InsnKind::Throw { reg } => {
1909 let mut v = self.read_term(*reg);
1911 if let Expr::Const(ConstVal::Int(0)) = v {
1912 v = Expr::Const(ConstVal::Null);
1913 }
1914 Term::Throw(v)
1915 }
1916 InsnKind::If { op, a, b, z, .. } => {
1917 let cond = if *z {
1918 let v = self.read_nest(*a);
1919 let (l, r, realop) = jdc_core::ir::build::unfold_cmp(v, Self::cmp_op(*op));
1920 Expr::Bin {
1921 op: realop,
1922 l: Box::new(l),
1923 r: Box::new(r),
1924 ty: Some(TypeRef::J(JavaType::Boolean)),
1925 }
1926 } else {
1927 let l = self.read_nest(*a);
1928 let r = self.read_nest(*b);
1929 Expr::Bin {
1930 op: Self::cmp_op(*op),
1931 l: Box::new(l),
1932 r: Box::new(r),
1933 ty: Some(TypeRef::J(JavaType::Boolean)),
1934 }
1935 };
1936 Term::Cond { cond }
1937 }
1938 InsnKind::PackedSwitch { reg, payload_pc }
1939 | InsnKind::SparseSwitch { reg, payload_pc } => {
1940 let selector = self.read_term(*reg);
1941 let default = Some(last.pc + last.size);
1942 let targets = match self.env.code.payloads.get(payload_pc) {
1943 Some(Payload::Packed { first_key, targets }) => SwitchTargets::Table {
1944 low: *first_key,
1945 targets: targets
1946 .iter()
1947 .map(|t| (last.pc as i64 + *t as i64) as u32)
1948 .collect(),
1949 },
1950 Some(Payload::Sparse { pairs }) => SwitchTargets::Lookup {
1951 pairs: pairs
1952 .iter()
1953 .map(|(k, t)| (*k, (last.pc as i64 + *t as i64) as u32))
1954 .collect(),
1955 },
1956 _ => SwitchTargets::Lookup { pairs: vec![] },
1957 };
1958 Term::Switch {
1959 selector,
1960 targets,
1961 default,
1962 }
1963 }
1964 _ => {
1966 let next = last.pc + last.size;
1967 if next < self.code_units {
1968 Term::Fallthrough
1969 } else {
1970 Term::Goto
1971 }
1972 }
1973 }
1974 }
1975}
1976
1977fn concat_from_recipe(recipe: &str, args: Vec<Expr>) -> Expr {
1981 use jdc_core::ir::expr::ConcatPart;
1982 let mut parts: Vec<ConcatPart> = Vec::new();
1983 let mut arg_i = 0usize;
1984 let mut const_buf = String::new();
1985 for c in recipe.chars() {
1986 match c {
1987 '\u{1}' => {
1988 if !const_buf.is_empty() {
1989 parts.push(ConcatPart::Const(std::mem::take(&mut const_buf)));
1990 }
1991 if arg_i < args.len() {
1992 parts.push(ConcatPart::Str(args[arg_i].clone()));
1993 arg_i += 1;
1994 }
1995 }
1996 '\u{2}' => {
1997 }
2000 c => const_buf.push(c),
2001 }
2002 }
2003 if !const_buf.is_empty() {
2004 parts.push(ConcatPart::Const(const_buf));
2005 }
2006 Expr::StringConcat(parts)
2007}
2008
2009fn expr_size(e: &Expr) -> usize {
2013 let mut n = 0usize;
2014 count_nodes(e, &mut n);
2015 n
2016}
2017
2018fn expr_mentions_field(
2021 e: &Expr,
2022 cls: &std::sync::Arc<str>,
2023 name: &std::sync::Arc<str>,
2024 is_static: bool,
2025) -> bool {
2026 match e {
2027 Expr::Field {
2028 owner,
2029 cls: c,
2030 name: n,
2031 is_static: s,
2032 ..
2033 } => {
2034 (c == cls && n == name && *s == is_static)
2035 || owner
2036 .as_deref()
2037 .is_some_and(|o| expr_mentions_field(o, cls, name, is_static))
2038 }
2039 Expr::Un { e, .. } | Expr::Cast { e, .. } | Expr::InstanceOf { e, .. } => {
2040 expr_mentions_field(e, cls, name, is_static)
2041 }
2042 Expr::Bin { l, r, .. } => {
2043 expr_mentions_field(l, cls, name, is_static)
2044 || expr_mentions_field(r, cls, name, is_static)
2045 }
2046 Expr::Cond { c, t, f } => {
2047 expr_mentions_field(c, cls, name, is_static)
2048 || expr_mentions_field(t, cls, name, is_static)
2049 || expr_mentions_field(f, cls, name, is_static)
2050 }
2051 Expr::Assign { target, value, .. } => {
2052 expr_mentions_field(target, cls, name, is_static)
2053 || expr_mentions_field(value, cls, name, is_static)
2054 }
2055 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => {
2056 expr_mentions_field(e, cls, name, is_static)
2057 }
2058 Expr::Method {
2059 owner, args, ..
2060 } => {
2061 owner
2062 .as_deref()
2063 .is_some_and(|o| expr_mentions_field(o, cls, name, is_static))
2064 || args
2065 .iter()
2066 .any(|a| expr_mentions_field(a, cls, name, is_static))
2067 }
2068 Expr::ArrayIndex { array, index } => {
2069 expr_mentions_field(array, cls, name, is_static)
2070 || expr_mentions_field(index, cls, name, is_static)
2071 }
2072 Expr::New { args, .. } => args
2073 .iter()
2074 .any(|a| expr_mentions_field(a, cls, name, is_static)),
2075 Expr::NewArray { dims, init, .. } => {
2076 dims.iter().any(|d| expr_mentions_field(d, cls, name, is_static))
2077 || init
2078 .as_ref()
2079 .is_some_and(|v| v.iter().any(|x| expr_mentions_field(x, cls, name, is_static)))
2080 }
2081 Expr::NewMultiArray { dims, .. } => {
2082 dims.iter().any(|d| expr_mentions_field(d, cls, name, is_static))
2083 }
2084 Expr::StringConcat(parts) => parts.iter().any(|p| match p {
2085 ConcatPart::Str(e) => expr_mentions_field(e, cls, name, is_static),
2086 ConcatPart::Const(_) => false,
2087 }),
2088 _ => false,
2089 }
2090}
2091
2092fn count_nodes(e: &Expr, n: &mut usize) {
2093 *n += 1;
2094 if *n > 512 {
2095 return; }
2097 match e {
2098 Expr::Un { e, .. } | Expr::Cast { e, .. } | Expr::InstanceOf { e, .. } => count_nodes(e, n),
2099 Expr::Bin { l, r, .. } => {
2100 count_nodes(l, n);
2101 count_nodes(r, n);
2102 }
2103 Expr::Cond { c, t, f } => {
2104 count_nodes(c, n);
2105 count_nodes(t, n);
2106 count_nodes(f, n);
2107 }
2108 Expr::Assign { target, value, .. } => {
2109 count_nodes(target, n);
2110 count_nodes(value, n);
2111 }
2112 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => count_nodes(e, n),
2113 Expr::Field { owner: Some(o), .. } => count_nodes(o, n),
2114 Expr::Method {
2115 owner: Some(o),
2116 args,
2117 ..
2118 } => {
2119 count_nodes(o, n);
2120 for a in args {
2121 count_nodes(a, n);
2122 }
2123 }
2124 Expr::ArrayIndex { array, index } => {
2125 count_nodes(array, n);
2126 count_nodes(index, n);
2127 }
2128 Expr::New { args, .. } => {
2129 for a in args {
2130 count_nodes(a, n);
2131 }
2132 }
2133 Expr::NewArray { dims, init, .. } => {
2134 for d in dims {
2135 count_nodes(d, n);
2136 }
2137 if let Some(v) = init {
2138 for x in v {
2139 count_nodes(x, n);
2140 }
2141 }
2142 }
2143 Expr::NewMultiArray { dims, .. } => {
2144 for d in dims {
2145 count_nodes(d, n);
2146 }
2147 }
2148 Expr::StringConcat(parts) => {
2149 for p in parts {
2150 if let ConcatPart::Str(x) = p {
2151 count_nodes(x, n);
2152 }
2153 }
2154 }
2155 _ => {}
2156 }
2157}
2158
2159fn reg_is_wide(st: &Reg, vt: &VarTable) -> bool {
2160 match st {
2161 Reg::Pending(e) | Reg::PendingCall(e) => e.type_ref().erased().is_wide(),
2162 Reg::Live(v) => vt.var(*v).ty.erased().is_wide(),
2163 Reg::WideHi => true,
2164 Reg::Undef => false,
2165 }
2166}
2167
2168fn null_in_obj_ctx(mut v: Expr, ctx_ty: &JavaType) -> Expr {
2174 if ctx_ty.is_reference() {
2175 if let Expr::Const(ConstVal::Int(0)) = v {
2176 v = Expr::Const(ConstVal::Null);
2177 }
2178 }
2179 v
2180}
2181
2182fn value_of_cmp(e: &Expr) -> Expr {
2184 value_of_cmp_owned(e.clone())
2185}
2186
2187fn value_of_cmp_owned(e: Expr) -> Expr {
2191 if let Expr::Invokedynamic { name, args, .. } = &e {
2194 if name.starts_with('\0') && args.len() == 2 {
2195 let (cls, ty) = match name.as_str() {
2196 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
2197 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
2198 _ => ("java/lang/Double", JavaType::Double),
2199 };
2200 let desc = MethodDescriptor {
2201 args: vec![ty.clone(), ty],
2202 ret: JavaType::Int,
2203 };
2204 return Expr::Method {
2205 owner: None,
2206 cls: cls.into(),
2207 name: "compare".into(),
2208 desc: std::sync::Arc::new(desc),
2209 args: args.clone(),
2210 is_static: true,
2211 is_interface: false,
2212 is_special: false,
2213 is_super: false,
2214 is_dynamic: false,
2215 type_args: vec![],
2216 };
2217 }
2218 }
2219 e
2220}
2221
2222pub fn primitive_type(c: char) -> JavaType {
2223 match c {
2224 'Z' => JavaType::Boolean,
2225 'B' => JavaType::Byte,
2226 'C' => JavaType::Char,
2227 'S' => JavaType::Short,
2228 'F' => JavaType::Float,
2229 'J' => JavaType::Long,
2230 'D' => JavaType::Double,
2231 _ => JavaType::Int,
2232 }
2233}
2234
2235fn payload_consts(elem_width: u16, size: u32, data: &[u8]) -> Vec<Expr> {
2238 let n = (size as usize).min(data.len() / elem_width.max(1) as usize);
2239 let mut out = Vec::with_capacity(n);
2240 for i in 0..n {
2241 let off = i * elem_width as usize;
2242 let e = match elem_width {
2243 1 => Expr::Const(ConstVal::Int(data[off] as i8 as i32)),
2244 2 => {
2245 let v = i16::from_le_bytes([data[off], data[off + 1]]);
2246 Expr::Const(ConstVal::Int(v as i32))
2247 }
2248 4 => {
2249 let mut b = [0u8; 4];
2250 b.copy_from_slice(&data[off..off + 4]);
2251 Expr::Const(ConstVal::Int(i32::from_le_bytes(b)))
2252 }
2253 8 => {
2254 let mut b = [0u8; 8];
2255 b.copy_from_slice(&data[off..off + 8]);
2256 Expr::Const(ConstVal::Long(i64::from_le_bytes(b)))
2257 }
2258 _ => Expr::Const(ConstVal::Int(0)),
2259 };
2260 out.push(e);
2261 }
2262 out
2263}
2264
2265pub fn entry_regs(vt: &mut VarTable, env: &MethodEnv, param_names: &[Option<String>]) -> Vec<Reg> {
2267 let code = env.code;
2268 let n = code.registers_size as usize;
2269 let mut regs = vec![Reg::Undef; n.max(1)];
2270 let ins = code.ins_size.min(code.registers_size) as usize;
2271 let mut reg = n.saturating_sub(ins);
2272 let mut pidx = 0;
2273 if !env.is_static && reg < n {
2274 let id = vt.vars.len() as u32;
2276 vt.vars.push(VarInfo {
2277 id,
2278 slot: reg as u16,
2279 name: "this".into(),
2280 ty: TypeRef::J(JavaType::Object(env.class_name.as_str().into())),
2281 is_param: true,
2282 range_start: 0,
2283 range_end: u16::MAX,
2284 synthetic_name: false,
2285 });
2286 while vt.by_slot.len() <= reg {
2287 vt.by_slot.push(Vec::new());
2288 }
2289 vt.by_slot[reg].push((0, u16::MAX, id));
2290 regs[reg] = Reg::Live(id);
2291 reg += 1;
2292 pidx += 1;
2293 }
2294 for (i, arg) in env.desc.args.iter().enumerate() {
2295 if reg >= n {
2296 break;
2297 }
2298 let dbg = param_names.get(i).and_then(|o| o.clone());
2299 let named = dbg.is_some();
2300 let name = dbg
2305 .map(|d| crate::classdec::java_ident(&d).into_owned())
2306 .unwrap_or_else(|| format!("p{}", pidx));
2307 let id = vt.vars.len() as u32;
2308 vt.vars.push(VarInfo {
2309 id,
2310 slot: reg as u16,
2311 name,
2312 ty: TypeRef::J(arg.clone()),
2313 is_param: true,
2314 range_start: 0,
2315 range_end: u16::MAX,
2316 synthetic_name: !named,
2317 });
2318 while vt.by_slot.len() <= reg {
2319 vt.by_slot.push(Vec::new());
2320 }
2321 vt.by_slot[reg].push((0, u16::MAX, id));
2322 regs[reg] = Reg::Live(id);
2323 reg += if arg.is_wide() { 2 } else { 1 };
2324 pidx += 1;
2325 }
2326 regs
2327}