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}
365
366impl<'a> Lifter<'a> {
367 pub fn new(
368 env: &'a MethodEnv<'a>,
369 vt: &'a mut VarTable,
370 in_regs: Vec<Reg>,
371 block_id: usize,
372 stable: &'a mut jdc_core::FxHashMap<(usize, u16, u64), u32>,
373 mflags: &'a mut MethodFlags,
374 ) -> Lifter<'a> {
375 let n = env.code.registers_size as usize;
376 let n = n.max(in_regs.len()).max(1);
377 let mut regs = in_regs;
378 regs.resize(n, Reg::Undef);
379 Lifter {
380 env,
381 vt,
382 regs,
383 write_pc: vec![0; n],
384 stmts: Vec::new(),
385 block_id,
386 stable,
387 pending_call: None,
388 code_units: env.code_units,
389 cur_pc: 0,
390 final_read: std::sync::Arc::new(jdc_core::FxHashSet::default()),
391 mflags,
392 }
393 }
394
395 fn ty_key(ty: &TypeRef) -> u64 {
403 use std::hash::{Hash, Hasher};
404 fn jt_key(j: &JavaType, h: &mut jdc_core::fx::FxHasher) {
405 match j {
406 JavaType::Void => 1u64.hash(h),
407 JavaType::Boolean => 2u64.hash(h),
408 JavaType::Byte => 3u64.hash(h),
409 JavaType::Char => 4u64.hash(h),
410 JavaType::Short => 5u64.hash(h),
411 JavaType::Int => 6u64.hash(h),
412 JavaType::Float => 7u64.hash(h),
413 JavaType::Long => 8u64.hash(h),
414 JavaType::Double => 9u64.hash(h),
415 JavaType::Object(n) => {
416 10u64.hash(h);
417 n.hash(h);
418 }
419 JavaType::Array(inner) => {
420 11u64.hash(h);
421 jt_key(inner, h);
422 }
423 }
424 }
425 let mut h = jdc_core::fx::FxHasher::default();
426 match ty {
427 TypeRef::J(j) => jt_key(j, &mut h),
428 TypeRef::G(_) => 99u64.hash(&mut h),
429 }
430 h.finish()
431 }
432
433 fn fresh_var(&mut self, slot: u16, ty: TypeRef) -> u32 {
434 let key = (self.block_id, slot, Self::ty_key(&ty));
444 if let Some(&id) = self.stable.get(&key) {
445 return id;
446 }
447 if let Some(name) = self.debug_name(slot, &ty) {
452 let tkey = Self::ty_key(&ty);
458 if let Some(id) = self
459 .vt
460 .vars
461 .iter()
462 .find(|v| {
463 v.slot == slot
464 && !v.synthetic_name
465 && v.name == name
466 && Self::ty_key(&v.ty) == tkey
467 })
468 .map(|v| v.id)
469 {
470 self.stable.insert(key, id);
471 return id;
472 }
473 let id = self.vt.vars.len() as u32;
474 self.stable.insert(key, id);
475 self.push_var(id, slot, name, ty, false);
476 return id;
477 }
478 let id = self.vt.vars.len() as u32;
479 let name = format!("v{}", id);
480 self.stable.insert(key, id);
481 self.push_var(id, slot, name, ty, true);
482 id
483 }
484
485 fn push_var(&mut self, id: u32, slot: u16, name: String, ty: TypeRef, synthetic: bool) {
486 self.vt.vars.push(VarInfo {
487 id,
488 slot,
489 name,
490 ty,
491 is_param: false,
492 range_start: 0,
493 range_end: u16::MAX,
494 synthetic_name: synthetic,
495 });
496 while self.vt.by_slot.len() <= slot as usize {
497 self.vt.by_slot.push(Vec::new());
498 }
499 self.vt.by_slot[slot as usize].push((0, u16::MAX, id));
500 }
501
502 fn debug_name(&self, slot: u16, ty: &TypeRef) -> Option<String> {
507 let locals = &self.env.debug_locals;
508 if locals.is_empty() {
509 return None;
510 }
511 let want = ty.erased();
512 let covers = |pc: u32| {
513 locals.iter().find(|l| {
514 l.reg == slot
515 && pc >= l.start
516 && pc < l.end
517 && l.ty.as_deref().map(|d| crate::desc_type(d) == want).unwrap_or(true)
522 })
523 };
524 let hit = covers(self.cur_pc).or_else(|| {
529 let wpc = *self.write_pc.get(slot as usize)?;
530 if wpc == 0 {
531 None
532 } else {
533 covers(wpc)
534 }
535 })?;
536 let name = crate::classdec::java_ident(&hit.name);
537 if name.is_empty() {
538 return None;
539 }
540 Some(name.into_owned())
541 }
542
543 fn local_expr(&self, v: u32) -> Expr {
544 Expr::Local {
545 var: v,
546 ty: self.vt.var(v).ty.clone(),
547 }
548 }
549
550 fn folded_new_aliases(&self, r: u16) -> Vec<usize> {
557 let Some(w) = self.write_pc.get(r as usize).copied() else {
558 return Vec::new();
559 };
560 (0..self.regs.len())
561 .filter(|&rr| {
562 rr != r as usize
563 && self.write_pc.get(rr).copied() == Some(w)
564 && matches!(self.regs[rr], Reg::Pending(Expr::New { raw: false, .. }))
565 })
566 .collect()
567 }
568
569 #[track_caller]
573 fn read_nest(&mut self, r: u16) -> Expr {
574 match self.regs.get(r as usize).cloned().unwrap_or(Reg::Undef) {
575 Reg::Undef | Reg::WideHi => {
576 let ty = TypeRef::J(JavaType::Int);
580 let v = self.fresh_var(r, ty);
581 if (r as usize) < self.regs.len() {
582 self.regs[r as usize] = Reg::Live(v);
583 }
584 self.local_expr(v)
585 }
586 Reg::Live(v) => self.local_expr(v),
587 Reg::Pending(e) => {
588 let alias_needs_share = matches!(&e, Expr::New { raw: false, .. })
614 && self.folded_new_aliases(r).iter().any(|&rr| {
615 self.final_read
616 .iter()
617 .any(|&(pc2, r2)| r2 == rr as u16 && pc2 >= self.cur_pc)
618 });
619 let alloc = alias_needs_share
620 || matches!(&e, Expr::NewArray { .. })
621 || (matches!(&e, Expr::New { .. })
622 && !self.final_read.contains(&(self.cur_pc, r)));
623 if alloc {
624 let v = self.materialize(r);
625 self.local_expr(v)
626 } else {
627 if matches!(&e, Expr::NewArray { .. })
638 || matches!(&e, Expr::New { raw: false, .. })
639 {
640 let aliases = if matches!(&e, Expr::New { raw: false, .. }) {
641 self.folded_new_aliases(r)
642 } else {
643 Vec::new()
644 };
645 if let Some(slot) = self.regs.get_mut(r as usize) {
646 *slot = Reg::Undef;
647 }
648 for rr in aliases {
652 self.regs[rr] = Reg::Undef;
653 }
654 }
655 e
656 }
657 }
658 Reg::PendingCall(_) => {
659 let v = self.materialize(r);
660 self.local_expr(v)
661 }
662 }
663 }
664
665 fn read_term(&mut self, r: u16) -> Expr {
669 match self.regs.get(r as usize).cloned().unwrap_or(Reg::Undef) {
670 Reg::PendingCall(e) | Reg::Pending(e) => {
671 let aliases = if matches!(&e, Expr::New { raw: false, .. }) {
675 self.folded_new_aliases(r)
676 } else {
677 Vec::new()
678 };
679 if (r as usize) < self.regs.len() {
680 self.regs[r as usize] = Reg::Undef;
681 }
682 for rr in aliases {
683 self.regs[rr] = Reg::Undef;
684 }
685 self.pending_call = None;
686 e
687 }
688 _ => self.read_nest(r),
689 }
690 }
691
692 fn write(&mut self, r: u16, e: Expr, pc: u32, wide: bool) {
693 if r as usize >= self.regs.len() {
694 return;
695 }
696 self.write_pc[r as usize] = pc;
697 if expr_size(&e) > 32 {
702 self.write_pc[r as usize] = pc;
703 let v = self.materialize_value(r, e);
704 if (r as usize) < self.regs.len() {
705 self.regs[r as usize] = Reg::Live(v);
706 }
707 if wide {
708 self.mark_wide_hi(r + 1, pc);
709 }
710 return;
711 }
712 self.regs[r as usize] = Reg::Pending(e);
713 if wide {
714 self.mark_wide_hi(r + 1, pc);
715 }
716 }
717
718 fn materialize_value(&mut self, r: u16, e: Expr) -> u32 {
721 let e = value_of_cmp(&e);
722 let ty = e.type_ref();
723 let v = self.fresh_var(r, ty);
727 self.stmts.push(Stmt::LocalDef {
728 var: v,
729 init: Some(e),
730 is_final: false,
731 force_type: true,
732 });
733 v
734 }
735
736 fn write_call_result(&mut self, r: u16, e: Expr, pc: u32) {
739 if r as usize >= self.regs.len() {
740 return;
741 }
742 self.write_pc[r as usize] = pc;
743 self.regs[r as usize] = Reg::PendingCall(e);
744 }
745
746 fn mark_wide_hi(&mut self, r: u16, pc: u32) {
747 if (r as usize) < self.regs.len() {
748 self.regs[r as usize] = Reg::WideHi;
749 self.write_pc[r as usize] = pc;
750 }
751 }
752
753 fn materialize(&mut self, r: u16) -> u32 {
755 let cur = self.regs.get(r as usize).cloned().unwrap_or(Reg::Undef);
756 let e = match cur {
757 Reg::Pending(e) | Reg::PendingCall(e) => e,
758 Reg::Live(v) => return v,
759 _ => {
762 let v = self.fresh_var(r, TypeRef::J(JavaType::Object("java/lang/Object".into())));
763 self.stmts.push(Stmt::LocalDef {
764 var: v,
765 init: None,
766 is_final: false,
767 force_type: false,
768 });
769 if (r as usize) < self.regs.len() {
770 self.regs[r as usize] = Reg::Live(v);
771 }
772 return v;
773 }
774 };
775 let e = value_of_cmp(&e);
777 let ty = e.type_ref();
778 let v = self.fresh_var(r, ty);
782 self.stmts.push(Stmt::LocalDef {
783 var: v,
784 init: Some(e),
785 is_final: false,
786 force_type: true,
787 });
788 if (r as usize) < self.regs.len() {
789 self.regs[r as usize] = Reg::Live(v);
790 }
791 for rr in self.folded_new_aliases(r) {
795 self.regs[rr] = Reg::Live(v);
796 }
797 v
798 }
799
800 fn drop_pending_call(&mut self) {
802 if let Some(e) = self.pending_call.take() {
803 let e = value_of_cmp(&e);
804 if has_side_effects(&e) {
805 self.stmts.push(Stmt::ExprStmt(e));
806 }
807 }
808 }
809
810 fn owner_expr(&self, obj: Expr, cls: &str) -> Option<Box<Expr>> {
813 if cls == self.env.class_name && matches!(obj, Expr::This) {
814 return None;
815 }
816 Some(Box::new(obj))
817 }
818
819 fn arith_op(op: ArithOp) -> BinOp {
820 match op {
821 ArithOp::Add => BinOp::Add,
822 ArithOp::Sub => BinOp::Sub,
823 ArithOp::Mul => BinOp::Mul,
824 ArithOp::Div => BinOp::Div,
825 ArithOp::Rem => BinOp::Rem,
826 ArithOp::And => BinOp::And,
827 ArithOp::Or => BinOp::Or,
828 ArithOp::Xor => BinOp::Xor,
829 ArithOp::Shl => BinOp::Shl,
830 ArithOp::Shr => BinOp::Shr,
831 ArithOp::Ushr => BinOp::Ushr,
832 }
833 }
834
835 fn cmp_op(op: CmpOp) -> BinOp {
836 match op {
837 CmpOp::Eq => BinOp::Eq,
838 CmpOp::Ne => BinOp::Ne,
839 CmpOp::Lt => BinOp::Lt,
840 CmpOp::Ge => BinOp::Ge,
841 CmpOp::Gt => BinOp::Gt,
842 CmpOp::Le => BinOp::Le,
843 }
844 }
845
846 fn cmp_sentinel(kind: CmpKind, l: Expr, r: Expr) -> Expr {
847 let name = match kind {
848 CmpKind::CmplF => "\0cmpl-float",
849 CmpKind::CmpgF => "\0cmpg-float",
850 CmpKind::CmplD => "\0cmpl-double",
851 CmpKind::CmpgD => "\0cmpg-double",
852 CmpKind::CmpJ => "\0cmp-long",
853 };
854 Expr::Invokedynamic {
855 name: name.into(),
856 desc: MethodDescriptor {
857 args: vec![],
858 ret: JavaType::Int,
859 },
860 args: vec![l, r],
861 bsm_text: String::new(),
862 bsm_static_args: vec![],
863 }
864 }
865
866 pub fn build_block(
871 mut self,
872 ins: &[Insn],
873 handler_types: &[Option<std::sync::Arc<str>>],
874 final_read: std::sync::Arc<jdc_core::FxHashSet<(u32, u16)>>,
875 ) -> BResult<(BlockResult, OutState)> {
876 let payloads = &self.env.code.payloads;
877 self.final_read = final_read;
887 for ins in ins {
888 self.cur_pc = ins.pc;
889 match &ins.kind {
890 InsnKind::Nop => {}
891 InsnKind::Unknown => {
892 return Err(format!(
893 "unsupported opcode {:#04x} ({}) at pc {}",
894 ins.op,
895 ddc_dex::insn::op_name(ins.op),
896 ins.pc
897 ));
898 }
899 InsnKind::Move { dst, src } => {
900 let src_state = self.regs.get(*src as usize).cloned().unwrap_or(Reg::Undef);
911 if matches!(src_state, Reg::PendingCall(_))
912 || matches!(&src_state, Reg::Pending(e)
913 if has_side_effects(e) && !matches!(e, Expr::New { raw: true, .. }))
914 {
915 self.materialize(*src);
916 }
917 let st = self.regs.get(*src as usize).cloned().unwrap_or(Reg::Undef);
918 let wide = reg_is_wide(&st, self.vt);
919 if (*dst as usize) < self.regs.len() {
920 self.regs[*dst as usize] = st;
921 self.write_pc[*dst as usize] =
922 self.write_pc.get(*src as usize).copied().unwrap_or(0);
923 }
924 if wide {
925 self.mark_wide_hi(*dst + 1, ins.pc);
926 }
927 }
928 InsnKind::MoveResult { dst } => {
929 if let Some(e) = self.pending_call.take() {
930 let wide =
931 matches!(e.type_ref().erased(), JavaType::Long | JavaType::Double);
932 self.write_call_result(*dst, e, ins.pc);
933 if wide {
934 self.mark_wide_hi(*dst + 1, ins.pc);
935 }
936 } else {
937 let v = self.fresh_var(*dst, TypeRef::J(JavaType::Int));
939 if (*dst as usize) < self.regs.len() {
940 self.regs[*dst as usize] = Reg::Live(v);
941 }
942 }
943 }
944 InsnKind::MoveException { dst } => {
945 let ty = handler_types
946 .iter()
947 .flatten()
948 .next()
949 .map(|t| TypeRef::J(JavaType::Object(t.clone())))
950 .unwrap_or_else(|| {
951 TypeRef::J(JavaType::Object("java/lang/Throwable".into()))
952 });
953 let v = self.fresh_var(*dst, ty);
954 self.stmts.push(Stmt::LocalDef {
955 var: v,
956 init: None,
957 is_final: false,
958 force_type: true,
959 });
960 if (*dst as usize) < self.regs.len() {
961 self.regs[*dst as usize] = Reg::Live(v);
962 }
963 }
964 InsnKind::ReturnVoid
965 | InsnKind::Return { .. }
966 | InsnKind::Throw { .. }
967 | InsnKind::Goto { .. }
968 | InsnKind::PackedSwitch { .. }
969 | InsnKind::SparseSwitch { .. }
970 | InsnKind::If { .. } => {}
971 InsnKind::Const { dst, val, wide } => {
972 let e = if *wide {
973 Expr::Const(ConstVal::Long(*val))
974 } else {
975 Expr::Const(ConstVal::Int(*val as i32))
976 };
977 self.write(*dst, e, ins.pc, *wide);
978 }
979 InsnKind::ConstString { dst, str_idx } => {
980 let s = std::sync::Arc::from(self.env.dex.string(*str_idx));
981 self.write(*dst, Expr::Const(ConstVal::Str(s)), ins.pc, false);
982 }
983 InsnKind::ConstClass { dst, type_idx } => {
984 let ty = TypeRef::J(self.env.java_type(*type_idx));
985 self.write(*dst, Expr::Const(ConstVal::ClassLit(ty)), ins.pc, false);
986 }
987 InsnKind::MonitorEnter { reg } => {
988 let e = self.read_nest(*reg);
989 *self.mflags = self.mflags.with_monitor();
990 self.stmts.push(Stmt::MonitorEnter(e));
991 }
992 InsnKind::MonitorExit { reg } => {
993 let e = self.read_nest(*reg);
994 *self.mflags = self.mflags.with_monitor();
995 self.stmts.push(Stmt::MonitorExit(e));
996 }
997 InsnKind::CheckCast { reg, type_idx } => {
998 let e = self.read_nest(*reg);
999 let ty = TypeRef::J(self.env.java_type(*type_idx));
1000 self.write(*reg, Expr::Cast { ty, e: Box::new(e) }, ins.pc, false);
1001 }
1002 InsnKind::InstanceOf { dst, src, type_idx } => {
1003 let e = self.read_nest(*src);
1004 let ty = TypeRef::J(self.env.java_type(*type_idx));
1005 self.write(*dst, Expr::InstanceOf { e: Box::new(e), ty }, ins.pc, false);
1006 }
1007 InsnKind::ArrayLength { dst, src } => {
1008 let e = self.read_nest(*src);
1009 let length = Expr::Field {
1010 owner: Some(Box::new(e)),
1011 cls: std::sync::Arc::from(""),
1012 name: "length".into(),
1013 ty: TypeRef::J(JavaType::Int),
1014 is_static: false,
1015 };
1016 self.write(*dst, length, ins.pc, false);
1017 }
1018 InsnKind::NewInstance { dst, type_idx } => {
1019 let cls = self.env.type_name_arc(*type_idx);
1020 let ty = TypeRef::J(JavaType::Object(cls.clone()));
1021 self.write(
1022 *dst,
1023 Expr::New {
1024 cls,
1025 ty,
1026 args: vec![],
1027 arg_tys: Vec::new(),
1028 raw: true,
1029 },
1030 ins.pc,
1031 false,
1032 );
1033 }
1034 InsnKind::NewArray {
1035 dst,
1036 size,
1037 type_idx,
1038 } => {
1039 let elem_desc = self.env.type_name(*type_idx);
1040 let elem = crate::desc_type(elem_desc.trim_start_matches('['));
1042 let n_brackets = elem_desc.chars().take_while(|&c| c == '[').count();
1053 let trailing_dims = n_brackets.saturating_sub(1).min(u8::MAX as usize) as u8;
1054 let dims = self.read_nest(*size);
1055 self.write(
1056 *dst,
1057 Expr::NewArray {
1058 elem: TypeRef::J(elem),
1059 dims: vec![dims],
1060 trailing_dims,
1061 init: None,
1062 },
1063 ins.pc,
1064 false,
1065 );
1066 }
1067 InsnKind::FilledNewArray { regs, type_idx } => {
1068 self.drop_pending_call();
1069 let elem_desc = self.env.type_name(*type_idx);
1070 let base = elem_desc.trim_start_matches('[');
1071 let depth = elem_desc.len() - base.len();
1081 let elem = crate::desc_type(base);
1082 let wide = depth == 1 && elem.is_wide();
1089 let mut args: Vec<Expr> = Vec::with_capacity(regs.len());
1090 let mut it = regs.iter();
1091 while let Some(&r) = it.next() {
1092 args.push(self.read_nest(r));
1093 if wide {
1094 it.next();
1095 }
1096 }
1097 self.pending_call = Some(Expr::NewArray {
1098 elem: TypeRef::J(elem),
1099 dims: vec![],
1100 trailing_dims: depth.saturating_sub(1).min(255) as u8,
1101 init: Some(args),
1102 });
1103 }
1104 InsnKind::FillArrayData { reg, payload_pc } => {
1105 if let Some(Payload::ArrayData {
1106 elem_width,
1107 size,
1108 data,
1109 }) = payloads.get(payload_pc)
1110 {
1111 let elems = payload_consts(*elem_width, *size, data);
1112 let cur = self.regs.get(*reg as usize).cloned().unwrap_or(Reg::Undef);
1113 if let Reg::Pending(Expr::NewArray { elem, init, .. }) = cur {
1114 if init.is_none() {
1115 self.write(
1116 *reg,
1117 Expr::NewArray {
1118 elem,
1119 dims: vec![Expr::Const(ConstVal::Int(*size as i32))],
1120 trailing_dims: 0,
1121 init: Some(elems),
1122 },
1123 ins.pc,
1124 false,
1125 );
1126 continue;
1127 }
1128 }
1129 let arr = self.read_nest(*reg);
1131 for (i, e) in elems.into_iter().enumerate() {
1132 let idx = Expr::Const(ConstVal::Int(i as i32));
1133 let target = Expr::ArrayIndex {
1134 array: Box::new(arr.clone()),
1135 index: Box::new(idx),
1136 };
1137 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
1138 target: Box::new(target),
1139 op: AssignOp::Plain,
1140 value: Box::new(e),
1141 }));
1142 }
1143 }
1144 }
1145 InsnKind::Cmp { dst, a, b, kind } => {
1146 let l = self.read_nest(*a);
1147 let r = self.read_nest(*b);
1148 *self.mflags = self.mflags.with_cmp();
1149 self.write(*dst, Self::cmp_sentinel(*kind, l, r), ins.pc, false);
1150 }
1151 InsnKind::AGet {
1152 dst, array, index, ..
1153 } => {
1154 let a = self.read_nest(*array);
1155 let i = self.read_nest(*index);
1156 self.write(
1157 *dst,
1158 Expr::ArrayIndex {
1159 array: Box::new(a),
1160 index: Box::new(i),
1161 },
1162 ins.pc,
1163 false,
1164 );
1165 }
1166 InsnKind::APut {
1167 value,
1168 array,
1169 index,
1170 ..
1171 } => {
1172 self.drop_pending_call();
1173 let v = self.read_nest(*value);
1174 let a = self.read_nest(*array);
1175 let i = self.read_nest(*index);
1176 let target = Expr::ArrayIndex {
1177 array: Box::new(a),
1178 index: Box::new(i),
1179 };
1180 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
1181 target: Box::new(target),
1182 op: AssignOp::Plain,
1183 value: Box::new(v),
1184 }));
1185 }
1186 InsnKind::IGet {
1187 dst,
1188 obj,
1189 field_idx,
1190 } => {
1191 self.drop_pending_call();
1192 let (cls, name, ty) = self.env.field_ref(*field_idx);
1193 let owner = self.read_nest(*obj);
1194 let owner_opt = self.owner_expr(owner, &cls);
1195 self.write(
1196 *dst,
1197 Expr::Field {
1198 owner: owner_opt,
1199 cls,
1200 name,
1201 ty: TypeRef::J(ty),
1202 is_static: false,
1203 },
1204 ins.pc,
1205 false,
1206 );
1207 }
1208 InsnKind::IPut {
1209 value,
1210 obj,
1211 field_idx,
1212 } => {
1213 self.drop_pending_call();
1214 let (cls, name, ty) = self.env.field_ref(*field_idx);
1215 let v = null_in_obj_ctx(self.read_nest(*value), &ty);
1216 let owner = self.read_nest(*obj);
1217 let owner_opt = self.owner_expr(owner, &cls);
1218 let target = Expr::Field {
1219 owner: owner_opt,
1220 cls,
1221 name,
1222 ty: TypeRef::J(ty),
1223 is_static: false,
1224 };
1225 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
1226 target: Box::new(target),
1227 op: AssignOp::Plain,
1228 value: Box::new(v),
1229 }));
1230 }
1231 InsnKind::SGet { dst, field_idx } => {
1232 self.drop_pending_call();
1233 let (cls, name, ty) = self.env.field_ref(*field_idx);
1234 self.write(
1235 *dst,
1236 Expr::Field {
1237 owner: None,
1238 cls,
1239 name,
1240 ty: TypeRef::J(ty),
1241 is_static: true,
1242 },
1243 ins.pc,
1244 false,
1245 );
1246 }
1247 InsnKind::SPut { value, field_idx } => {
1248 self.drop_pending_call();
1249 let (cls, name, ty) = self.env.field_ref(*field_idx);
1250 let v = null_in_obj_ctx(self.read_nest(*value), &ty);
1251 let target = Expr::Field {
1252 owner: None,
1253 cls,
1254 name,
1255 ty: TypeRef::J(ty),
1256 is_static: true,
1257 };
1258 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
1259 target: Box::new(target),
1260 op: AssignOp::Plain,
1261 value: Box::new(v),
1262 }));
1263 }
1264 InsnKind::Invoke {
1265 kind,
1266 regs,
1267 method_idx,
1268 } => {
1269 self.drop_pending_call();
1270 self.do_invoke(*kind, regs, *method_idx, ins.pc)?;
1271 }
1272 InsnKind::InvokeCustom {
1273 call_site_idx,
1274 regs,
1275 } => {
1276 self.drop_pending_call();
1277 let e = self.build_invoke_custom(*call_site_idx, regs, ins.pc);
1278 self.pending_call = Some(e);
1279 }
1280 InsnKind::Un {
1281 dst,
1282 src,
1283 op,
1284 from: _,
1285 to,
1286 } => {
1287 let e = self.read_nest(*src);
1288 let ne = match op {
1289 ddc_dex::insn::UnArith::Neg => Expr::Un {
1290 op: UnOp::Neg,
1291 e: Box::new(e),
1292 },
1293 ddc_dex::insn::UnArith::Not => Expr::Un {
1294 op: UnOp::BitNot,
1295 e: Box::new(e),
1296 },
1297 ddc_dex::insn::UnArith::Conv => Expr::Cast {
1298 ty: TypeRef::J(primitive_type(*to)),
1299 e: Box::new(e),
1300 },
1301 };
1302 let wide = matches!(*to, 'J' | 'D');
1303 self.write(*dst, ne, ins.pc, wide);
1304 }
1305 InsnKind::Bin { op, dst, a, b, ty } => {
1306 let l = self.read_nest(*a);
1307 let r = self.read_nest(*b);
1308 let jt = primitive_type(*ty);
1309 let e = Expr::Bin {
1310 op: Self::arith_op(*op),
1311 l: Box::new(l),
1312 r: Box::new(r),
1313 ty: Some(TypeRef::J(jt)),
1314 };
1315 let wide = matches!(*ty, 'J' | 'D');
1316 self.write(*dst, e, ins.pc, wide);
1317 }
1318 InsnKind::BinLit {
1319 op,
1320 dst,
1321 a,
1322 lit,
1323 rsub,
1324 } => {
1325 let av = self.read_nest(*a);
1326 let e = if *rsub {
1327 Expr::Bin {
1328 op: BinOp::Sub,
1329 l: Box::new(Expr::Const(ConstVal::Int(*lit))),
1330 r: Box::new(av),
1331 ty: Some(TypeRef::J(JavaType::Int)),
1332 }
1333 } else {
1334 Expr::Bin {
1335 op: Self::arith_op(*op),
1336 l: Box::new(av),
1337 r: Box::new(Expr::Const(ConstVal::Int(*lit))),
1338 ty: Some(TypeRef::J(JavaType::Int)),
1339 }
1340 };
1341 self.write(*dst, e, ins.pc, false);
1342 }
1343 InsnKind::ConstMethodHandle { dst, handle_idx } => {
1344 self.drop_pending_call();
1345 let e = self.method_handle_expr(*handle_idx);
1346 self.write(*dst, e, ins.pc, false);
1347 }
1348 InsnKind::ConstMethodType { dst, proto_idx } => {
1349 self.drop_pending_call();
1350 let proto = self.env.dex.proto(*proto_idx);
1351 let desc = format!(
1352 "({}){}",
1353 self.env
1354 .dex
1355 .proto_params(*proto_idx)
1356 .iter()
1357 .map(|&t| self.env.dex.type_name(t))
1358 .collect::<Vec<_>>()
1359 .join(""),
1360 self.env.dex.type_name(proto.return_type_idx)
1361 );
1362 self.write(*dst, Expr::Raw(desc), ins.pc, false);
1363 }
1364 }
1365 }
1366
1367 let term = self.block_term(ins);
1369
1370 self.drop_pending_call();
1374 self.materialize_impure_at_exit();
1375
1376 let out = OutState {
1380 regs: std::mem::take(&mut self.regs),
1381 write_pc: std::mem::take(&mut self.write_pc),
1382 };
1383 Ok((
1384 BlockResult {
1385 stmts: std::mem::take(&mut self.stmts),
1386 out_stack: Vec::new(),
1387 term,
1388 },
1389 out,
1390 ))
1391 }
1392
1393 fn do_invoke(
1394 &mut self,
1395 kind: InvokeKind,
1396 regs: &[u16],
1397 method_idx: u32,
1398 pc: u32,
1399 ) -> BResult<()> {
1400 let (cls, name, md) = self.env.method_ref(method_idx);
1401 if cls.as_ref() == "java/lang/StringBuilder" || cls.as_ref() == "java/lang/StringBuffer" {
1402 *self.mflags = self.mflags.with_sb();
1403 }
1404 let is_static = matches!(kind, InvokeKind::Static);
1405 let receiver_reg = if is_static {
1406 None
1407 } else {
1408 regs.first().copied()
1409 };
1410 let arg_regs: Vec<u16> = if is_static {
1411 regs.to_vec()
1412 } else {
1413 regs.iter().skip(1).copied().collect()
1414 };
1415 let mut args: Vec<Expr> = Vec::with_capacity(md.args.len());
1416 let mut ri = 0usize;
1424 for at in md.args.iter() {
1425 let r = arg_regs.get(ri).copied().unwrap_or(0);
1426 args.push(null_in_obj_ctx(self.read_nest(r), at));
1427 ri += if at.is_wide() { 2 } else { 1 };
1428 }
1429 if name.as_ref() == "<init>" && matches!(kind, InvokeKind::Direct) {
1435 let recv_reg = receiver_reg.unwrap_or(0);
1436 let recv_state = self
1437 .regs
1438 .get(recv_reg as usize)
1439 .cloned()
1440 .unwrap_or(Reg::Undef);
1441 if let Reg::Pending(Expr::New {
1442 raw: true, cls: nc, ..
1443 }) = &recv_state
1444 {
1445 if nc == &cls || self.env.pool.is_subtype(nc.as_ref(), cls.as_ref()) {
1456 let folded = Expr::New {
1457 cls: nc.clone(),
1458 ty: TypeRef::J(JavaType::Object(nc.clone())),
1459 arg_tys: md.args.clone(),
1460 args,
1461 raw: false,
1462 };
1463 let origin = self.write_pc.get(recv_reg as usize).copied();
1472 let aliases: Vec<usize> = origin
1473 .map(|w| {
1474 (0..self.regs.len())
1475 .filter(|&r| {
1476 r != recv_reg as usize
1477 && self.write_pc.get(r).copied() == Some(w)
1478 && matches!(
1479 &self.regs[r],
1480 Reg::Pending(Expr::New { raw: true, cls: c, .. })
1481 if c == nc
1482 )
1483 })
1484 .collect()
1485 })
1486 .unwrap_or_default();
1487 self.write(recv_reg, folded.clone(), pc, false);
1488 match self.regs.get(recv_reg as usize) {
1493 Some(Reg::Live(v)) => {
1494 let v = *v;
1495 for r in aliases {
1496 self.regs[r] = Reg::Live(v);
1497 }
1498 }
1499 _ => {
1500 for r in aliases {
1501 self.regs[r] = Reg::Pending(folded.clone());
1502 self.write_pc[r] = pc;
1503 }
1504 }
1505 }
1506 return Ok(());
1507 }
1508 }
1509 let recv_expr = receiver_reg.map(|r| self.read_nest(r));
1510 let owner_expr_v = recv_expr.unwrap_or(Expr::This);
1511 let owner_expr_v = if !self.env.is_static
1518 && matches!(&owner_expr_v, Expr::Local { var: 0, .. })
1519 {
1520 Expr::This
1521 } else {
1522 owner_expr_v
1523 };
1524 let is_super = cls.as_ref() != self.env.class_name.as_str();
1525 let owner = if matches!(owner_expr_v, Expr::This) && is_super {
1526 None
1527 } else {
1528 Some(Box::new(owner_expr_v))
1529 };
1530 let call = Expr::Method {
1531 owner,
1532 cls,
1533 name: "<init>".into(),
1534 desc: md,
1535 args,
1536 is_static: false,
1537 is_interface: false,
1538 is_special: true,
1539 is_super,
1540 is_dynamic: false,
1541 type_args: vec![],
1542 };
1543 self.stmts.push(Stmt::ExprStmt(call));
1544 return Ok(());
1545 }
1546
1547 let recv_expr = receiver_reg.map(|r| self.read_nest(r));
1548 let is_interface = matches!(kind, InvokeKind::Interface);
1549 let recv_is_this = match &recv_expr {
1563 Some(Expr::This) => true,
1564 Some(Expr::Raw(t)) => t == "\u{3}" || t == "this",
1565 Some(Expr::Local { var, .. }) => {
1566 (*var as usize) < self.vt.vars.len() && self.vt.vars[*var as usize].name == "this"
1567 }
1568 _ => false,
1569 };
1570 let is_super = match kind {
1571 InvokeKind::Super | InvokeKind::Direct => {
1572 recv_is_this && cls.as_ref() != self.env.class_name.as_str()
1573 }
1574 _ => false,
1575 };
1576 let owner: Option<Box<Expr>> = match recv_expr {
1577 None => None,
1578 Some(o) => {
1579 if is_super && matches!(o, Expr::This) {
1580 None
1581 } else {
1582 self.owner_expr(o, &cls)
1583 }
1584 }
1585 };
1586 let is_dynamic = matches!(kind, InvokeKind::Polymorphic);
1587 let call = Expr::Method {
1588 owner,
1589 cls,
1590 name,
1591 desc: md,
1592 args,
1593 is_static,
1594 is_interface,
1595 is_special: matches!(kind, InvokeKind::Direct),
1596 is_super,
1597 is_dynamic,
1598 type_args: vec![],
1599 };
1600 self.pending_call = Some(call);
1601 Ok(())
1602 }
1603
1604 fn method_handle_expr(&mut self, handle_idx: u32) -> Expr {
1607 let Some(h) = self.env.dex.method_handle(handle_idx) else {
1608 return Expr::Raw(format!("methodHandle@{}", handle_idx));
1609 };
1610 if h.is_field {
1611 let f = self.env.dex.field(h.target_id);
1612 let cls = self.env.dex.class_name(f.class_idx);
1613 let name = self.env.dex.string(f.name_idx).to_string();
1614 return Expr::Raw(format!("{}::{}", cls.replace('/', "."), name));
1615 }
1616 let m = self.env.dex.method(h.target_id);
1617 let cls = self.env.dex.class_name(m.class_idx);
1618 let name = self.env.dex.string(m.name_idx).to_string();
1619 Expr::Raw(format!("{}::{}", cls.replace('/', "."), name))
1620 }
1621
1622 fn build_invoke_custom(&mut self, cs_idx: u32, regs: &[u16], pc: u32) -> Expr {
1624 let Some(cs) = self.env.dex.call_site(cs_idx).cloned() else {
1625 return Expr::Invokedynamic {
1626 name: format!("call-site#{}", cs_idx),
1627 desc: jdc_core::types::MethodDescriptor {
1628 args: vec![],
1629 ret: JavaType::Object("java/lang/Object".into()),
1630 },
1631 args: vec![],
1632 bsm_text: "unresolved".into(),
1633 bsm_static_args: vec![],
1634 };
1635 };
1636 let site_name = self.env.dex.string(cs.name_idx).to_string();
1637 let params: Vec<JavaType> = self
1638 .env
1639 .dex
1640 .proto_params(cs.proto_idx)
1641 .iter()
1642 .map(|&t| self.env.java_type(t))
1643 .collect();
1644 let ret = self
1645 .env
1646 .java_type(self.env.dex.proto(cs.proto_idx).return_type_idx);
1647
1648 let (bs_cls, bs_name) = match self.env.dex.method_handle(cs.bootstrap_handle) {
1649 Some(h) if !h.is_field => {
1650 let m = self.env.dex.method(h.target_id);
1651 (
1652 self.env.dex.class_name(m.class_idx),
1653 self.env.dex.string(m.name_idx).to_string(),
1654 )
1655 }
1656 _ => (String::new(), String::new()),
1657 };
1658
1659 let mut args: Vec<Expr> = Vec::with_capacity(params.len().min(regs.len()));
1664 let mut ri = 0usize;
1665 for at in params.iter() {
1666 if let Some(&r) = regs.get(ri) {
1667 args.push(self.read_nest(r));
1668 }
1669 ri += if at.is_wide() { 2 } else { 1 };
1670 }
1671
1672 if bs_name == "makeConcatWithConstants" {
1674 let recipe = cs
1675 .linker_args
1676 .iter()
1677 .find_map(|v| match v {
1678 ddc_dex::annotations::EncodedValue::String(s) => {
1679 Some(self.env.dex.string(*s).to_string())
1680 }
1681 _ => None,
1682 })
1683 .unwrap_or_default();
1684 return concat_from_recipe(&recipe, args);
1685 }
1686
1687 let mut impl_m = None;
1691 let mut instantiated_proto: Option<u32> = None;
1692 if bs_name == "metafactory" || bs_name == "altMetafactory" {
1693 let mut seen_handle = false;
1694 for lv in &cs.linker_args {
1695 match lv {
1696 ddc_dex::annotations::EncodedValue::MethodHandle(hi) => {
1697 if !seen_handle {
1698 seen_handle = true;
1699 if let Some(h) = self.env.dex.method_handle(*hi) {
1700 if !h.is_field {
1701 let m = self.env.dex.method(h.target_id);
1702 impl_m = Some((
1703 self.env.dex.class_name(m.class_idx).replace('/', "."),
1704 self.env.dex.string(m.name_idx).to_string(),
1705 ));
1706 }
1707 }
1708 }
1709 }
1710 ddc_dex::annotations::EncodedValue::MethodType(p) if seen_handle => {
1711 instantiated_proto = Some(*p);
1712 }
1713 _ => {}
1714 }
1715 }
1716 }
1717 let _ = pc;
1718
1719 let mut captures = Vec::new();
1721 for &r in regs.iter().skip(params.len().min(regs.len())) {
1722 captures.push(self.read_nest(r));
1723 }
1724
1725 if let Some((impl_cls, impl_name)) = impl_m {
1726 if captures.is_empty() {
1727 if impl_name.starts_with("lambda$") {
1731 let n_params = match instantiated_proto {
1735 Some(p) => self.env.dex.proto_params(p).len(),
1736 None => params.len(),
1737 };
1738 let pnames: Vec<String> = (0..n_params).map(|i| format!("a{}", i)).collect();
1739 return Expr::Raw(format!(
1740 "({}) -> {}.{}({})",
1741 pnames.join(", "),
1742 impl_cls,
1743 impl_name,
1744 pnames.join(", ")
1745 ));
1746 }
1747 return Expr::Raw(format!("{}::{}", impl_cls, impl_name));
1748 }
1749 let mut call_args = args;
1752 call_args.extend(captures);
1753 return Expr::Invokedynamic {
1754 name: format!("{} -> {}.{}", site_name, impl_cls, impl_name),
1755 desc: jdc_core::types::MethodDescriptor { args: params, ret },
1756 args: call_args,
1757 bsm_text: format!("{}::{}", bs_cls, bs_name),
1758 bsm_static_args: vec![],
1759 };
1760 }
1761
1762 Expr::Invokedynamic {
1763 name: site_name,
1764 desc: jdc_core::types::MethodDescriptor { args: params, ret },
1765 args: captures,
1766 bsm_text: format!("{}::{}", bs_cls, bs_name),
1767 bsm_static_args: vec![],
1768 }
1769 }
1770
1771 fn materialize_impure_at_exit(&mut self) {
1772 let n = self.regs.len();
1773 for r in 0..n {
1774 let st = self.regs[r].clone();
1775 let impure = match &st {
1776 Reg::PendingCall(_) => true,
1777 Reg::Pending(e) => {
1778 has_side_effects(e)
1779 && !matches!(e, Expr::New { raw: true, .. })
1780 && !matches!(e, Expr::NewArray { init: None, .. })
1781 }
1782 _ => false,
1783 };
1784 if impure {
1785 self.materialize(r as u16);
1786 }
1787 }
1788 }
1789
1790 fn block_term(&mut self, ins: &[Insn]) -> Term {
1792 let Some(last) = ins.last() else {
1793 return Term::Goto;
1794 };
1795 match &last.kind {
1796 InsnKind::Goto { .. } => Term::Goto,
1797 InsnKind::ReturnVoid => Term::Return(None),
1798 InsnKind::Return { src } => {
1799 let v = null_in_obj_ctx(self.read_term(*src), &self.env.desc.ret);
1800 Term::Return(Some(v))
1801 }
1802 InsnKind::Throw { reg } => {
1803 let mut v = self.read_term(*reg);
1805 if let Expr::Const(ConstVal::Int(0)) = v {
1806 v = Expr::Const(ConstVal::Null);
1807 }
1808 Term::Throw(v)
1809 }
1810 InsnKind::If { op, a, b, z, .. } => {
1811 let cond = if *z {
1812 let v = self.read_nest(*a);
1813 let (l, r, realop) = jdc_core::ir::build::unfold_cmp(v, Self::cmp_op(*op));
1814 Expr::Bin {
1815 op: realop,
1816 l: Box::new(l),
1817 r: Box::new(r),
1818 ty: Some(TypeRef::J(JavaType::Boolean)),
1819 }
1820 } else {
1821 let l = self.read_nest(*a);
1822 let r = self.read_nest(*b);
1823 Expr::Bin {
1824 op: Self::cmp_op(*op),
1825 l: Box::new(l),
1826 r: Box::new(r),
1827 ty: Some(TypeRef::J(JavaType::Boolean)),
1828 }
1829 };
1830 Term::Cond { cond }
1831 }
1832 InsnKind::PackedSwitch { reg, payload_pc }
1833 | InsnKind::SparseSwitch { reg, payload_pc } => {
1834 let selector = self.read_term(*reg);
1835 let default = Some(last.pc + last.size);
1836 let targets = match self.env.code.payloads.get(payload_pc) {
1837 Some(Payload::Packed { first_key, targets }) => SwitchTargets::Table {
1838 low: *first_key,
1839 targets: targets
1840 .iter()
1841 .map(|t| (last.pc as i64 + *t as i64) as u32)
1842 .collect(),
1843 },
1844 Some(Payload::Sparse { pairs }) => SwitchTargets::Lookup {
1845 pairs: pairs
1846 .iter()
1847 .map(|(k, t)| (*k, (last.pc as i64 + *t as i64) as u32))
1848 .collect(),
1849 },
1850 _ => SwitchTargets::Lookup { pairs: vec![] },
1851 };
1852 Term::Switch {
1853 selector,
1854 targets,
1855 default,
1856 }
1857 }
1858 _ => {
1860 let next = last.pc + last.size;
1861 if next < self.code_units {
1862 Term::Fallthrough
1863 } else {
1864 Term::Goto
1865 }
1866 }
1867 }
1868 }
1869}
1870
1871fn concat_from_recipe(recipe: &str, args: Vec<Expr>) -> Expr {
1875 use jdc_core::ir::expr::ConcatPart;
1876 let mut parts: Vec<ConcatPart> = Vec::new();
1877 let mut arg_i = 0usize;
1878 let mut const_buf = String::new();
1879 for c in recipe.chars() {
1880 match c {
1881 '\u{1}' => {
1882 if !const_buf.is_empty() {
1883 parts.push(ConcatPart::Const(std::mem::take(&mut const_buf)));
1884 }
1885 if arg_i < args.len() {
1886 parts.push(ConcatPart::Str(args[arg_i].clone()));
1887 arg_i += 1;
1888 }
1889 }
1890 '\u{2}' => {
1891 }
1894 c => const_buf.push(c),
1895 }
1896 }
1897 if !const_buf.is_empty() {
1898 parts.push(ConcatPart::Const(const_buf));
1899 }
1900 Expr::StringConcat(parts)
1901}
1902
1903fn expr_size(e: &Expr) -> usize {
1907 let mut n = 0usize;
1908 count_nodes(e, &mut n);
1909 n
1910}
1911
1912fn count_nodes(e: &Expr, n: &mut usize) {
1913 *n += 1;
1914 if *n > 512 {
1915 return; }
1917 match e {
1918 Expr::Un { e, .. } | Expr::Cast { e, .. } | Expr::InstanceOf { e, .. } => count_nodes(e, n),
1919 Expr::Bin { l, r, .. } => {
1920 count_nodes(l, n);
1921 count_nodes(r, n);
1922 }
1923 Expr::Cond { c, t, f } => {
1924 count_nodes(c, n);
1925 count_nodes(t, n);
1926 count_nodes(f, n);
1927 }
1928 Expr::Assign { target, value, .. } => {
1929 count_nodes(target, n);
1930 count_nodes(value, n);
1931 }
1932 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => count_nodes(e, n),
1933 Expr::Field { owner: Some(o), .. } => count_nodes(o, n),
1934 Expr::Method {
1935 owner: Some(o),
1936 args,
1937 ..
1938 } => {
1939 count_nodes(o, n);
1940 for a in args {
1941 count_nodes(a, n);
1942 }
1943 }
1944 Expr::ArrayIndex { array, index } => {
1945 count_nodes(array, n);
1946 count_nodes(index, n);
1947 }
1948 Expr::New { args, .. } => {
1949 for a in args {
1950 count_nodes(a, n);
1951 }
1952 }
1953 Expr::NewArray { dims, init, .. } => {
1954 for d in dims {
1955 count_nodes(d, n);
1956 }
1957 if let Some(v) = init {
1958 for x in v {
1959 count_nodes(x, n);
1960 }
1961 }
1962 }
1963 Expr::NewMultiArray { dims, .. } => {
1964 for d in dims {
1965 count_nodes(d, n);
1966 }
1967 }
1968 Expr::StringConcat(parts) => {
1969 for p in parts {
1970 if let ConcatPart::Str(x) = p {
1971 count_nodes(x, n);
1972 }
1973 }
1974 }
1975 _ => {}
1976 }
1977}
1978
1979fn reg_is_wide(st: &Reg, vt: &VarTable) -> bool {
1980 match st {
1981 Reg::Pending(e) | Reg::PendingCall(e) => e.type_ref().erased().is_wide(),
1982 Reg::Live(v) => vt.var(*v).ty.erased().is_wide(),
1983 Reg::WideHi => true,
1984 Reg::Undef => false,
1985 }
1986}
1987
1988fn null_in_obj_ctx(mut v: Expr, ctx_ty: &JavaType) -> Expr {
1994 if ctx_ty.is_reference() {
1995 if let Expr::Const(ConstVal::Int(0)) = v {
1996 v = Expr::Const(ConstVal::Null);
1997 }
1998 }
1999 v
2000}
2001
2002fn value_of_cmp(e: &Expr) -> Expr {
2004 if let Expr::Invokedynamic { name, args, .. } = e {
2005 if name.starts_with('\0') && args.len() == 2 {
2006 let (cls, ty) = match name.as_str() {
2007 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
2008 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
2009 _ => ("java/lang/Double", JavaType::Double),
2010 };
2011 let desc = MethodDescriptor {
2012 args: vec![ty.clone(), ty],
2013 ret: JavaType::Int,
2014 };
2015 return Expr::Method {
2016 owner: None,
2017 cls: cls.into(),
2018 name: "compare".into(),
2019 desc: std::sync::Arc::new(desc),
2020 args: args.clone(),
2021 is_static: true,
2022 is_interface: false,
2023 is_special: false,
2024 is_super: false,
2025 is_dynamic: false,
2026 type_args: vec![],
2027 };
2028 }
2029 }
2030 e.clone()
2031}
2032
2033pub fn primitive_type(c: char) -> JavaType {
2034 match c {
2035 'Z' => JavaType::Boolean,
2036 'B' => JavaType::Byte,
2037 'C' => JavaType::Char,
2038 'S' => JavaType::Short,
2039 'F' => JavaType::Float,
2040 'J' => JavaType::Long,
2041 'D' => JavaType::Double,
2042 _ => JavaType::Int,
2043 }
2044}
2045
2046fn payload_consts(elem_width: u16, size: u32, data: &[u8]) -> Vec<Expr> {
2049 let n = (size as usize).min(data.len() / elem_width.max(1) as usize);
2050 let mut out = Vec::with_capacity(n);
2051 for i in 0..n {
2052 let off = i * elem_width as usize;
2053 let e = match elem_width {
2054 1 => Expr::Const(ConstVal::Int(data[off] as i8 as i32)),
2055 2 => {
2056 let v = i16::from_le_bytes([data[off], data[off + 1]]);
2057 Expr::Const(ConstVal::Int(v as i32))
2058 }
2059 4 => {
2060 let mut b = [0u8; 4];
2061 b.copy_from_slice(&data[off..off + 4]);
2062 Expr::Const(ConstVal::Int(i32::from_le_bytes(b)))
2063 }
2064 8 => {
2065 let mut b = [0u8; 8];
2066 b.copy_from_slice(&data[off..off + 8]);
2067 Expr::Const(ConstVal::Long(i64::from_le_bytes(b)))
2068 }
2069 _ => Expr::Const(ConstVal::Int(0)),
2070 };
2071 out.push(e);
2072 }
2073 out
2074}
2075
2076pub fn entry_regs(vt: &mut VarTable, env: &MethodEnv, param_names: &[Option<String>]) -> Vec<Reg> {
2078 let code = env.code;
2079 let n = code.registers_size as usize;
2080 let mut regs = vec![Reg::Undef; n.max(1)];
2081 let ins = code.ins_size.min(code.registers_size) as usize;
2082 let mut reg = n.saturating_sub(ins);
2083 let mut pidx = 0;
2084 if !env.is_static && reg < n {
2085 let id = vt.vars.len() as u32;
2087 vt.vars.push(VarInfo {
2088 id,
2089 slot: reg as u16,
2090 name: "this".into(),
2091 ty: TypeRef::J(JavaType::Object(env.class_name.as_str().into())),
2092 is_param: true,
2093 range_start: 0,
2094 range_end: u16::MAX,
2095 synthetic_name: false,
2096 });
2097 while vt.by_slot.len() <= reg {
2098 vt.by_slot.push(Vec::new());
2099 }
2100 vt.by_slot[reg].push((0, u16::MAX, id));
2101 regs[reg] = Reg::Live(id);
2102 reg += 1;
2103 pidx += 1;
2104 }
2105 for (i, arg) in env.desc.args.iter().enumerate() {
2106 if reg >= n {
2107 break;
2108 }
2109 let dbg = param_names.get(i).and_then(|o| o.clone());
2110 let named = dbg.is_some();
2111 let name = dbg
2116 .map(|d| crate::classdec::java_ident(&d).into_owned())
2117 .unwrap_or_else(|| format!("p{}", pidx));
2118 let id = vt.vars.len() as u32;
2119 vt.vars.push(VarInfo {
2120 id,
2121 slot: reg as u16,
2122 name,
2123 ty: TypeRef::J(arg.clone()),
2124 is_param: true,
2125 range_start: 0,
2126 range_end: u16::MAX,
2127 synthetic_name: !named,
2128 });
2129 while vt.by_slot.len() <= reg {
2130 vt.by_slot.push(Vec::new());
2131 }
2132 vt.by_slot[reg].push((0, u16::MAX, id));
2133 regs[reg] = Reg::Live(id);
2134 reg += if arg.is_wide() { 2 } else { 1 };
2135 pidx += 1;
2136 }
2137 regs
2138}