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 = jdc_core::rename::field_display(&owner, raw, self.dex.type_name(f.type_idx))
115 .map(std::sync::Arc::from)
116 .unwrap_or_else(|| std::sync::Arc::from(raw));
117 (owner, name, self.java_type(f.type_idx))
118 }
119 pub fn method_ref(
120 &self,
121 idx: u32,
122 ) -> (
123 std::sync::Arc<str>,
124 std::sync::Arc<str>,
125 std::sync::Arc<MethodDescriptor>,
126 ) {
127 let m = self.dex.method(idx);
131 let owner = std::sync::Arc::from(self.dex.class_name(m.class_idx).as_str());
132 let desc = self.pool.proto_desc_parsed(self.di, m.proto_idx);
133 let name = if jdc_core::rename::member_rename_active() {
138 let raw = self.dex.string(m.name_idx);
139 jdc_core::rename::field_display(&owner, raw, &desc.to_string())
140 .map(std::sync::Arc::from)
141 .unwrap_or_else(|| std::sync::Arc::from(raw))
142 } else {
143 std::sync::Arc::from(self.dex.string(m.name_idx))
144 };
145 (owner, name, desc)
146 }
147}
148
149#[derive(Debug, Clone, PartialEq)]
151pub enum Reg {
152 Undef,
153 Live(u32),
154 Pending(Expr),
155 PendingCall(Expr),
156 WideHi,
157}
158
159#[derive(Debug, Clone)]
161pub struct OutState {
162 pub regs: Vec<Reg>,
163 pub write_pc: Vec<u32>,
166}
167
168fn src_regs(kind: &InsnKind) -> Vec<u16> {
172 let mut out: Vec<u16> = Vec::with_capacity(4);
173 let mut push = |r: u16| out.push(r);
174 match kind {
175 InsnKind::Nop
176 | InsnKind::ReturnVoid
177 | InsnKind::Unknown
178 | InsnKind::NewInstance { .. }
179 | InsnKind::SGet { .. } => {}
180 InsnKind::Move { src, .. }
181 | InsnKind::Un { src, .. }
182 | InsnKind::Return { src }
183 | InsnKind::ArrayLength { src, .. } => push(*src),
184 InsnKind::MonitorEnter { reg }
185 | InsnKind::MonitorExit { reg }
186 | InsnKind::Throw { reg }
187 | InsnKind::CheckCast { reg, .. }
188 | InsnKind::FillArrayData { reg, .. }
189 | InsnKind::PackedSwitch { reg, .. }
190 | InsnKind::SparseSwitch { reg, .. } => push(*reg),
191 InsnKind::MoveResult { .. } | InsnKind::MoveException { .. } => {}
192 InsnKind::Const { .. }
193 | InsnKind::ConstClass { .. }
194 | InsnKind::ConstString { .. }
195 | InsnKind::ConstMethodHandle { .. }
196 | InsnKind::ConstMethodType { .. } => {}
197 InsnKind::InstanceOf { src, .. } => {
198 push(*src);
199 }
200 InsnKind::NewArray { size, .. } => {
201 push(*size);
202 }
203 InsnKind::FilledNewArray { regs, .. } | InsnKind::InvokeCustom { regs, .. } => {
204 for r in regs {
205 push(*r);
206 }
207 }
208 InsnKind::Goto { .. } => {}
209 InsnKind::Cmp { a, b, .. } | InsnKind::Bin { a, b, .. } => {
210 push(*a);
211 push(*b);
212 }
213 InsnKind::BinLit { a, .. } => push(*a),
214 InsnKind::If { a, b, z, .. } => {
215 push(*a);
216 if !*z {
217 push(*b);
218 }
219 }
220 InsnKind::AGet { array, index, .. } => {
221 push(*array);
222 push(*index);
223 }
224 InsnKind::APut {
225 value,
226 array,
227 index,
228 ..
229 } => {
230 push(*value);
231 push(*array);
232 push(*index);
233 }
234 InsnKind::IGet { obj, .. } => {
235 push(*obj);
236 }
237 InsnKind::IPut { value, obj, .. } => {
238 push(*value);
239 push(*obj);
240 }
241 InsnKind::SPut { value, .. } => {
242 push(*value);
243 }
244 InsnKind::Invoke { regs, .. } => {
245 for r in regs {
246 push(*r);
247 }
248 }
249 }
250 out
251}
252
253fn dst_regs(kind: &InsnKind) -> Vec<u16> {
256 let mut out: Vec<u16> = Vec::with_capacity(2);
257 let mut push = |r: u16| out.push(r);
258 match kind {
259 InsnKind::Move { dst, .. }
260 | InsnKind::MoveResult { dst }
261 | InsnKind::MoveException { dst }
262 | InsnKind::Const { dst, .. }
263 | InsnKind::ConstClass { dst, .. }
264 | InsnKind::ConstString { dst, .. }
265 | InsnKind::ConstMethodHandle { dst, .. }
266 | InsnKind::ConstMethodType { dst, .. }
267 | InsnKind::NewInstance { dst, .. }
268 | InsnKind::NewArray { dst, .. }
269 | InsnKind::InstanceOf { dst, .. }
270 | InsnKind::ArrayLength { dst, .. }
271 | InsnKind::Cmp { dst, .. }
272 | InsnKind::AGet { dst, .. }
273 | InsnKind::IGet { dst, .. }
274 | InsnKind::SGet { dst, .. }
275 | InsnKind::Un { dst, .. }
276 | InsnKind::Bin { dst, .. }
277 | InsnKind::BinLit { dst, .. } => push(*dst),
278 _ => {}
279 }
280 out
281}
282
283pub(crate) fn compute_final_reads(
288 ins: &[Insn],
289) -> std::sync::Arc<jdc_core::FxHashSet<(u32, u16)>> {
290 use jdc_core::FxHashMap as HashMap;
291 let mut events: HashMap<u16, Vec<(u32, bool)>> = HashMap::default();
293 for i in ins {
294 for r in src_regs(&i.kind) {
295 events.entry(r).or_default().push((i.pc, true));
296 }
297 for r in dst_regs(&i.kind) {
298 events.entry(r).or_default().push((i.pc, false));
299 }
300 }
301 let mut out = jdc_core::FxHashSet::default();
302 for (reg, evs) in events {
303 let mut dedup: Vec<(u32, bool)> = Vec::with_capacity(evs.len());
304 for (pc, is_read) in evs {
305 if dedup.last() != Some(&(pc, is_read)) {
306 dedup.push((pc, is_read));
307 }
308 }
309 for (i, &(pc, is_read)) in dedup.iter().enumerate() {
310 if !is_read {
311 continue;
312 }
313 let mut final_ = true;
316 if let Some(&(_, is_read2)) = dedup.get(i + 1) {
317 if is_read2 {
318 final_ = false;
319 }
320 }
321 if final_ {
322 out.insert((pc, reg));
323 }
324 }
325 }
326 std::sync::Arc::new(out)
327}
328
329pub struct Lifter<'a> {
330 pub env: &'a MethodEnv<'a>,
331 pub vt: &'a mut VarTable,
332 block_id: usize,
334 stable: &'a mut jdc_core::FxHashMap<(usize, u16, u64), u32>,
341 regs: Vec<Reg>,
342 write_pc: Vec<u32>,
343 stmts: Vec<Stmt>,
344 pending_call: Option<Expr>,
345 code_units: u32,
346 cur_pc: u32,
349 final_read: std::sync::Arc<jdc_core::FxHashSet<(u32, u16)>>,
356 mflags: &'a mut MethodFlags,
360}
361
362impl<'a> Lifter<'a> {
363 pub fn new(
364 env: &'a MethodEnv<'a>,
365 vt: &'a mut VarTable,
366 in_regs: Vec<Reg>,
367 block_id: usize,
368 stable: &'a mut jdc_core::FxHashMap<(usize, u16, u64), u32>,
369 mflags: &'a mut MethodFlags,
370 ) -> Lifter<'a> {
371 let n = env.code.registers_size as usize;
372 let n = n.max(in_regs.len()).max(1);
373 let mut regs = in_regs;
374 regs.resize(n, Reg::Undef);
375 Lifter {
376 env,
377 vt,
378 regs,
379 write_pc: vec![0; n],
380 stmts: Vec::new(),
381 block_id,
382 stable,
383 pending_call: None,
384 code_units: env.code_units,
385 cur_pc: 0,
386 final_read: std::sync::Arc::new(jdc_core::FxHashSet::default()),
387 mflags,
388 }
389 }
390
391 fn ty_key(ty: &TypeRef) -> u64 {
399 use std::hash::{Hash, Hasher};
400 fn jt_key(j: &JavaType, h: &mut jdc_core::fx::FxHasher) {
401 match j {
402 JavaType::Void => 1u64.hash(h),
403 JavaType::Boolean => 2u64.hash(h),
404 JavaType::Byte => 3u64.hash(h),
405 JavaType::Char => 4u64.hash(h),
406 JavaType::Short => 5u64.hash(h),
407 JavaType::Int => 6u64.hash(h),
408 JavaType::Float => 7u64.hash(h),
409 JavaType::Long => 8u64.hash(h),
410 JavaType::Double => 9u64.hash(h),
411 JavaType::Object(n) => {
412 10u64.hash(h);
413 n.hash(h);
414 }
415 JavaType::Array(inner) => {
416 11u64.hash(h);
417 jt_key(inner, h);
418 }
419 }
420 }
421 let mut h = jdc_core::fx::FxHasher::default();
422 match ty {
423 TypeRef::J(j) => jt_key(j, &mut h),
424 TypeRef::G(_) => 99u64.hash(&mut h),
425 }
426 h.finish()
427 }
428
429 fn fresh_var(&mut self, slot: u16, ty: TypeRef) -> u32 {
430 let key = (self.block_id, slot, Self::ty_key(&ty));
440 if let Some(&id) = self.stable.get(&key) {
441 return id;
442 }
443 if let Some(name) = self.debug_name(slot, &ty) {
448 let tkey = Self::ty_key(&ty);
454 if let Some(id) = self
455 .vt
456 .vars
457 .iter()
458 .find(|v| {
459 v.slot == slot
460 && !v.synthetic_name
461 && v.name == name
462 && Self::ty_key(&v.ty) == tkey
463 })
464 .map(|v| v.id)
465 {
466 self.stable.insert(key, id);
467 return id;
468 }
469 let id = self.vt.vars.len() as u32;
470 self.stable.insert(key, id);
471 self.push_var(id, slot, name, ty, false);
472 return id;
473 }
474 let id = self.vt.vars.len() as u32;
475 let name = format!("v{}", id);
476 self.stable.insert(key, id);
477 self.push_var(id, slot, name, ty, true);
478 id
479 }
480
481 fn push_var(&mut self, id: u32, slot: u16, name: String, ty: TypeRef, synthetic: bool) {
482 self.vt.vars.push(VarInfo {
483 id,
484 slot,
485 name,
486 ty,
487 is_param: false,
488 range_start: 0,
489 range_end: u16::MAX,
490 synthetic_name: synthetic,
491 });
492 while self.vt.by_slot.len() <= slot as usize {
493 self.vt.by_slot.push(Vec::new());
494 }
495 self.vt.by_slot[slot as usize].push((0, u16::MAX, id));
496 }
497
498 fn debug_name(&self, slot: u16, ty: &TypeRef) -> Option<String> {
503 let locals = &self.env.debug_locals;
504 if locals.is_empty() {
505 return None;
506 }
507 let want = ty.erased();
508 let covers = |pc: u32| {
509 locals.iter().find(|l| {
510 l.reg == slot
511 && pc >= l.start
512 && pc < l.end
513 && l.ty.as_deref().map(|d| crate::desc_type(d) == want).unwrap_or(true)
518 })
519 };
520 let hit = covers(self.cur_pc).or_else(|| {
525 let wpc = *self.write_pc.get(slot as usize)?;
526 if wpc == 0 {
527 None
528 } else {
529 covers(wpc)
530 }
531 })?;
532 let name = crate::classdec::java_ident(&hit.name);
533 if name.is_empty() {
534 return None;
535 }
536 Some(name.into_owned())
537 }
538
539 fn local_expr(&self, v: u32) -> Expr {
540 Expr::Local {
541 var: v,
542 ty: self.vt.var(v).ty.clone(),
543 }
544 }
545
546 fn folded_new_aliases(&self, r: u16) -> Vec<usize> {
553 let Some(w) = self.write_pc.get(r as usize).copied() else {
554 return Vec::new();
555 };
556 (0..self.regs.len())
557 .filter(|&rr| {
558 rr != r as usize
559 && self.write_pc.get(rr).copied() == Some(w)
560 && matches!(self.regs[rr], Reg::Pending(Expr::New { raw: false, .. }))
561 })
562 .collect()
563 }
564
565 #[track_caller]
569 fn read_nest(&mut self, r: u16) -> Expr {
570 match self.regs.get(r as usize).cloned().unwrap_or(Reg::Undef) {
571 Reg::Undef | Reg::WideHi => {
572 let ty = TypeRef::J(JavaType::Int);
576 let v = self.fresh_var(r, ty);
577 if (r as usize) < self.regs.len() {
578 self.regs[r as usize] = Reg::Live(v);
579 }
580 self.local_expr(v)
581 }
582 Reg::Live(v) => self.local_expr(v),
583 Reg::Pending(e) => {
584 let alias_needs_share = matches!(&e, Expr::New { raw: false, .. })
602 && self.folded_new_aliases(r).iter().any(|&rr| {
603 self.final_read
604 .iter()
605 .any(|&(pc2, r2)| r2 == rr as u16 && pc2 >= self.cur_pc)
606 });
607 let alloc = alias_needs_share
608 || matches!(&e, Expr::NewArray { .. })
609 || (matches!(&e, Expr::New { raw: false, .. })
610 && !self.final_read.contains(&(self.cur_pc, r)));
611 if alloc {
612 let v = self.materialize(r);
613 self.local_expr(v)
614 } else {
615 if matches!(&e, Expr::NewArray { .. })
626 || matches!(&e, Expr::New { raw: false, .. })
627 {
628 let aliases = if matches!(&e, Expr::New { raw: false, .. }) {
629 self.folded_new_aliases(r)
630 } else {
631 Vec::new()
632 };
633 if let Some(slot) = self.regs.get_mut(r as usize) {
634 *slot = Reg::Undef;
635 }
636 for rr in aliases {
640 self.regs[rr] = Reg::Undef;
641 }
642 }
643 e
644 }
645 }
646 Reg::PendingCall(_) => {
647 let v = self.materialize(r);
648 self.local_expr(v)
649 }
650 }
651 }
652
653 fn read_term(&mut self, r: u16) -> Expr {
657 match self.regs.get(r as usize).cloned().unwrap_or(Reg::Undef) {
658 Reg::PendingCall(e) | Reg::Pending(e) => {
659 let aliases = if matches!(&e, Expr::New { raw: false, .. }) {
663 self.folded_new_aliases(r)
664 } else {
665 Vec::new()
666 };
667 if (r as usize) < self.regs.len() {
668 self.regs[r as usize] = Reg::Undef;
669 }
670 for rr in aliases {
671 self.regs[rr] = Reg::Undef;
672 }
673 self.pending_call = None;
674 e
675 }
676 _ => self.read_nest(r),
677 }
678 }
679
680 fn write(&mut self, r: u16, e: Expr, pc: u32, wide: bool) {
681 if r as usize >= self.regs.len() {
682 return;
683 }
684 self.write_pc[r as usize] = pc;
685 if expr_size(&e) > 32 {
690 self.write_pc[r as usize] = pc;
691 let v = self.materialize_value(r, e);
692 if (r as usize) < self.regs.len() {
693 self.regs[r as usize] = Reg::Live(v);
694 }
695 if wide {
696 self.mark_wide_hi(r + 1, pc);
697 }
698 return;
699 }
700 self.regs[r as usize] = Reg::Pending(e);
701 if wide {
702 self.mark_wide_hi(r + 1, pc);
703 }
704 }
705
706 fn materialize_value(&mut self, r: u16, e: Expr) -> u32 {
709 let e = value_of_cmp(&e);
710 let ty = e.type_ref();
711 let v = self.fresh_var(r, ty);
715 self.stmts.push(Stmt::LocalDef {
716 var: v,
717 init: Some(e),
718 is_final: false,
719 force_type: true,
720 });
721 v
722 }
723
724 fn write_call_result(&mut self, r: u16, e: Expr, pc: u32) {
727 if r as usize >= self.regs.len() {
728 return;
729 }
730 self.write_pc[r as usize] = pc;
731 self.regs[r as usize] = Reg::PendingCall(e);
732 }
733
734 fn mark_wide_hi(&mut self, r: u16, pc: u32) {
735 if (r as usize) < self.regs.len() {
736 self.regs[r as usize] = Reg::WideHi;
737 self.write_pc[r as usize] = pc;
738 }
739 }
740
741 fn materialize(&mut self, r: u16) -> u32 {
743 let cur = self.regs.get(r as usize).cloned().unwrap_or(Reg::Undef);
744 let e = match cur {
745 Reg::Pending(e) | Reg::PendingCall(e) => e,
746 Reg::Live(v) => return v,
747 _ => {
750 let v = self.fresh_var(r, TypeRef::J(JavaType::Object("java/lang/Object".into())));
751 self.stmts.push(Stmt::LocalDef {
752 var: v,
753 init: None,
754 is_final: false,
755 force_type: false,
756 });
757 if (r as usize) < self.regs.len() {
758 self.regs[r as usize] = Reg::Live(v);
759 }
760 return v;
761 }
762 };
763 let e = value_of_cmp(&e);
765 let ty = e.type_ref();
766 let v = self.fresh_var(r, ty);
770 self.stmts.push(Stmt::LocalDef {
771 var: v,
772 init: Some(e),
773 is_final: false,
774 force_type: true,
775 });
776 if (r as usize) < self.regs.len() {
777 self.regs[r as usize] = Reg::Live(v);
778 }
779 for rr in self.folded_new_aliases(r) {
783 self.regs[rr] = Reg::Live(v);
784 }
785 v
786 }
787
788 fn drop_pending_call(&mut self) {
790 if let Some(e) = self.pending_call.take() {
791 let e = value_of_cmp(&e);
792 if has_side_effects(&e) {
793 self.stmts.push(Stmt::ExprStmt(e));
794 }
795 }
796 }
797
798 fn owner_expr(&self, obj: Expr, cls: &str) -> Option<Box<Expr>> {
801 if cls == self.env.class_name && matches!(obj, Expr::This) {
802 return None;
803 }
804 Some(Box::new(obj))
805 }
806
807 fn arith_op(op: ArithOp) -> BinOp {
808 match op {
809 ArithOp::Add => BinOp::Add,
810 ArithOp::Sub => BinOp::Sub,
811 ArithOp::Mul => BinOp::Mul,
812 ArithOp::Div => BinOp::Div,
813 ArithOp::Rem => BinOp::Rem,
814 ArithOp::And => BinOp::And,
815 ArithOp::Or => BinOp::Or,
816 ArithOp::Xor => BinOp::Xor,
817 ArithOp::Shl => BinOp::Shl,
818 ArithOp::Shr => BinOp::Shr,
819 ArithOp::Ushr => BinOp::Ushr,
820 }
821 }
822
823 fn cmp_op(op: CmpOp) -> BinOp {
824 match op {
825 CmpOp::Eq => BinOp::Eq,
826 CmpOp::Ne => BinOp::Ne,
827 CmpOp::Lt => BinOp::Lt,
828 CmpOp::Ge => BinOp::Ge,
829 CmpOp::Gt => BinOp::Gt,
830 CmpOp::Le => BinOp::Le,
831 }
832 }
833
834 fn cmp_sentinel(kind: CmpKind, l: Expr, r: Expr) -> Expr {
835 let name = match kind {
836 CmpKind::CmplF => "\0cmpl-float",
837 CmpKind::CmpgF => "\0cmpg-float",
838 CmpKind::CmplD => "\0cmpl-double",
839 CmpKind::CmpgD => "\0cmpg-double",
840 CmpKind::CmpJ => "\0cmp-long",
841 };
842 Expr::Invokedynamic {
843 name: name.into(),
844 desc: MethodDescriptor {
845 args: vec![],
846 ret: JavaType::Int,
847 },
848 args: vec![l, r],
849 bsm_text: String::new(),
850 bsm_static_args: vec![],
851 }
852 }
853
854 pub fn build_block(
859 mut self,
860 ins: &[Insn],
861 handler_types: &[Option<std::sync::Arc<str>>],
862 final_read: std::sync::Arc<jdc_core::FxHashSet<(u32, u16)>>,
863 ) -> BResult<(BlockResult, OutState)> {
864 let payloads = &self.env.code.payloads;
865 self.final_read = final_read;
875 for ins in ins {
876 self.cur_pc = ins.pc;
877 match &ins.kind {
878 InsnKind::Nop => {}
879 InsnKind::Unknown => {
880 return Err(format!(
881 "unsupported opcode {:#04x} ({}) at pc {}",
882 ins.op,
883 ddc_dex::insn::op_name(ins.op),
884 ins.pc
885 ));
886 }
887 InsnKind::Move { dst, src } => {
888 let src_state = self.regs.get(*src as usize).cloned().unwrap_or(Reg::Undef);
899 if matches!(src_state, Reg::PendingCall(_))
900 || matches!(&src_state, Reg::Pending(e)
901 if has_side_effects(e) && !matches!(e, Expr::New { raw: true, .. }))
902 {
903 self.materialize(*src);
904 }
905 let st = self.regs.get(*src as usize).cloned().unwrap_or(Reg::Undef);
906 let wide = reg_is_wide(&st, self.vt);
907 if (*dst as usize) < self.regs.len() {
908 self.regs[*dst as usize] = st;
909 self.write_pc[*dst as usize] =
910 self.write_pc.get(*src as usize).copied().unwrap_or(0);
911 }
912 if wide {
913 self.mark_wide_hi(*dst + 1, ins.pc);
914 }
915 }
916 InsnKind::MoveResult { dst } => {
917 if let Some(e) = self.pending_call.take() {
918 let wide =
919 matches!(e.type_ref().erased(), JavaType::Long | JavaType::Double);
920 self.write_call_result(*dst, e, ins.pc);
921 if wide {
922 self.mark_wide_hi(*dst + 1, ins.pc);
923 }
924 } else {
925 let v = self.fresh_var(*dst, TypeRef::J(JavaType::Int));
927 if (*dst as usize) < self.regs.len() {
928 self.regs[*dst as usize] = Reg::Live(v);
929 }
930 }
931 }
932 InsnKind::MoveException { dst } => {
933 let ty = handler_types
934 .iter()
935 .flatten()
936 .next()
937 .map(|t| TypeRef::J(JavaType::Object(t.clone())))
938 .unwrap_or_else(|| {
939 TypeRef::J(JavaType::Object("java/lang/Throwable".into()))
940 });
941 let v = self.fresh_var(*dst, ty);
942 self.stmts.push(Stmt::LocalDef {
943 var: v,
944 init: None,
945 is_final: false,
946 force_type: true,
947 });
948 if (*dst as usize) < self.regs.len() {
949 self.regs[*dst as usize] = Reg::Live(v);
950 }
951 }
952 InsnKind::ReturnVoid
953 | InsnKind::Return { .. }
954 | InsnKind::Throw { .. }
955 | InsnKind::Goto { .. }
956 | InsnKind::PackedSwitch { .. }
957 | InsnKind::SparseSwitch { .. }
958 | InsnKind::If { .. } => {}
959 InsnKind::Const { dst, val, wide } => {
960 let e = if *wide {
961 Expr::Const(ConstVal::Long(*val))
962 } else {
963 Expr::Const(ConstVal::Int(*val as i32))
964 };
965 self.write(*dst, e, ins.pc, *wide);
966 }
967 InsnKind::ConstString { dst, str_idx } => {
968 let s = std::sync::Arc::from(self.env.dex.string(*str_idx));
969 self.write(*dst, Expr::Const(ConstVal::Str(s)), ins.pc, false);
970 }
971 InsnKind::ConstClass { dst, type_idx } => {
972 let ty = TypeRef::J(self.env.java_type(*type_idx));
973 self.write(*dst, Expr::Const(ConstVal::ClassLit(ty)), ins.pc, false);
974 }
975 InsnKind::MonitorEnter { reg } => {
976 let e = self.read_nest(*reg);
977 *self.mflags = self.mflags.with_monitor();
978 self.stmts.push(Stmt::MonitorEnter(e));
979 }
980 InsnKind::MonitorExit { reg } => {
981 let e = self.read_nest(*reg);
982 *self.mflags = self.mflags.with_monitor();
983 self.stmts.push(Stmt::MonitorExit(e));
984 }
985 InsnKind::CheckCast { reg, type_idx } => {
986 let e = self.read_nest(*reg);
987 let ty = TypeRef::J(self.env.java_type(*type_idx));
988 self.write(*reg, Expr::Cast { ty, e: Box::new(e) }, ins.pc, false);
989 }
990 InsnKind::InstanceOf { dst, src, type_idx } => {
991 let e = self.read_nest(*src);
992 let ty = TypeRef::J(self.env.java_type(*type_idx));
993 self.write(*dst, Expr::InstanceOf { e: Box::new(e), ty }, ins.pc, false);
994 }
995 InsnKind::ArrayLength { dst, src } => {
996 let e = self.read_nest(*src);
997 let length = Expr::Field {
998 owner: Some(Box::new(e)),
999 cls: std::sync::Arc::from(""),
1000 name: "length".into(),
1001 ty: TypeRef::J(JavaType::Int),
1002 is_static: false,
1003 };
1004 self.write(*dst, length, ins.pc, false);
1005 }
1006 InsnKind::NewInstance { dst, type_idx } => {
1007 let cls = self.env.type_name_arc(*type_idx);
1008 let ty = TypeRef::J(JavaType::Object(cls.clone()));
1009 self.write(
1010 *dst,
1011 Expr::New {
1012 cls,
1013 ty,
1014 args: vec![],
1015 arg_tys: Vec::new(),
1016 raw: true,
1017 },
1018 ins.pc,
1019 false,
1020 );
1021 }
1022 InsnKind::NewArray {
1023 dst,
1024 size,
1025 type_idx,
1026 } => {
1027 let elem_desc = self.env.type_name(*type_idx);
1028 let elem = crate::desc_type(elem_desc.trim_start_matches('['));
1030 let n_brackets = elem_desc.chars().take_while(|&c| c == '[').count();
1041 let trailing_dims = n_brackets.saturating_sub(1).min(u8::MAX as usize) as u8;
1042 let dims = self.read_nest(*size);
1043 self.write(
1044 *dst,
1045 Expr::NewArray {
1046 elem: TypeRef::J(elem),
1047 dims: vec![dims],
1048 trailing_dims,
1049 init: None,
1050 },
1051 ins.pc,
1052 false,
1053 );
1054 }
1055 InsnKind::FilledNewArray { regs, type_idx } => {
1056 self.drop_pending_call();
1057 let elem_desc = self.env.type_name(*type_idx);
1058 let elem = crate::desc_type(elem_desc.trim_start_matches('['));
1059 let wide = elem.is_wide();
1065 let mut args: Vec<Expr> = Vec::with_capacity(regs.len());
1066 let mut it = regs.iter();
1067 while let Some(&r) = it.next() {
1068 args.push(self.read_nest(r));
1069 if wide {
1070 it.next();
1071 }
1072 }
1073 self.pending_call = Some(Expr::NewArray {
1074 elem: TypeRef::J(elem),
1075 dims: vec![],
1076 trailing_dims: 0,
1077 init: Some(args),
1078 });
1079 }
1080 InsnKind::FillArrayData { reg, payload_pc } => {
1081 if let Some(Payload::ArrayData {
1082 elem_width,
1083 size,
1084 data,
1085 }) = payloads.get(payload_pc)
1086 {
1087 let elems = payload_consts(*elem_width, *size, data);
1088 let cur = self.regs.get(*reg as usize).cloned().unwrap_or(Reg::Undef);
1089 if let Reg::Pending(Expr::NewArray { elem, init, .. }) = cur {
1090 if init.is_none() {
1091 self.write(
1092 *reg,
1093 Expr::NewArray {
1094 elem,
1095 dims: vec![Expr::Const(ConstVal::Int(*size as i32))],
1096 trailing_dims: 0,
1097 init: Some(elems),
1098 },
1099 ins.pc,
1100 false,
1101 );
1102 continue;
1103 }
1104 }
1105 let arr = self.read_nest(*reg);
1107 for (i, e) in elems.into_iter().enumerate() {
1108 let idx = Expr::Const(ConstVal::Int(i as i32));
1109 let target = Expr::ArrayIndex {
1110 array: Box::new(arr.clone()),
1111 index: Box::new(idx),
1112 };
1113 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
1114 target: Box::new(target),
1115 op: AssignOp::Plain,
1116 value: Box::new(e),
1117 }));
1118 }
1119 }
1120 }
1121 InsnKind::Cmp { dst, a, b, kind } => {
1122 let l = self.read_nest(*a);
1123 let r = self.read_nest(*b);
1124 *self.mflags = self.mflags.with_cmp();
1125 self.write(*dst, Self::cmp_sentinel(*kind, l, r), ins.pc, false);
1126 }
1127 InsnKind::AGet {
1128 dst, array, index, ..
1129 } => {
1130 let a = self.read_nest(*array);
1131 let i = self.read_nest(*index);
1132 self.write(
1133 *dst,
1134 Expr::ArrayIndex {
1135 array: Box::new(a),
1136 index: Box::new(i),
1137 },
1138 ins.pc,
1139 false,
1140 );
1141 }
1142 InsnKind::APut {
1143 value,
1144 array,
1145 index,
1146 ..
1147 } => {
1148 self.drop_pending_call();
1149 let v = self.read_nest(*value);
1150 let a = self.read_nest(*array);
1151 let i = self.read_nest(*index);
1152 let target = Expr::ArrayIndex {
1153 array: Box::new(a),
1154 index: Box::new(i),
1155 };
1156 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
1157 target: Box::new(target),
1158 op: AssignOp::Plain,
1159 value: Box::new(v),
1160 }));
1161 }
1162 InsnKind::IGet {
1163 dst,
1164 obj,
1165 field_idx,
1166 } => {
1167 self.drop_pending_call();
1168 let (cls, name, ty) = self.env.field_ref(*field_idx);
1169 let owner = self.read_nest(*obj);
1170 let owner_opt = self.owner_expr(owner, &cls);
1171 self.write(
1172 *dst,
1173 Expr::Field {
1174 owner: owner_opt,
1175 cls,
1176 name,
1177 ty: TypeRef::J(ty),
1178 is_static: false,
1179 },
1180 ins.pc,
1181 false,
1182 );
1183 }
1184 InsnKind::IPut {
1185 value,
1186 obj,
1187 field_idx,
1188 } => {
1189 self.drop_pending_call();
1190 let (cls, name, ty) = self.env.field_ref(*field_idx);
1191 let v = null_in_obj_ctx(self.read_nest(*value), &ty);
1192 let owner = self.read_nest(*obj);
1193 let owner_opt = self.owner_expr(owner, &cls);
1194 let target = Expr::Field {
1195 owner: owner_opt,
1196 cls,
1197 name,
1198 ty: TypeRef::J(ty),
1199 is_static: false,
1200 };
1201 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
1202 target: Box::new(target),
1203 op: AssignOp::Plain,
1204 value: Box::new(v),
1205 }));
1206 }
1207 InsnKind::SGet { dst, field_idx } => {
1208 self.drop_pending_call();
1209 let (cls, name, ty) = self.env.field_ref(*field_idx);
1210 self.write(
1211 *dst,
1212 Expr::Field {
1213 owner: None,
1214 cls,
1215 name,
1216 ty: TypeRef::J(ty),
1217 is_static: true,
1218 },
1219 ins.pc,
1220 false,
1221 );
1222 }
1223 InsnKind::SPut { value, field_idx } => {
1224 self.drop_pending_call();
1225 let (cls, name, ty) = self.env.field_ref(*field_idx);
1226 let v = null_in_obj_ctx(self.read_nest(*value), &ty);
1227 let target = Expr::Field {
1228 owner: None,
1229 cls,
1230 name,
1231 ty: TypeRef::J(ty),
1232 is_static: true,
1233 };
1234 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
1235 target: Box::new(target),
1236 op: AssignOp::Plain,
1237 value: Box::new(v),
1238 }));
1239 }
1240 InsnKind::Invoke {
1241 kind,
1242 regs,
1243 method_idx,
1244 } => {
1245 self.drop_pending_call();
1246 self.do_invoke(*kind, regs, *method_idx, ins.pc)?;
1247 }
1248 InsnKind::InvokeCustom {
1249 call_site_idx,
1250 regs,
1251 } => {
1252 self.drop_pending_call();
1253 let e = self.build_invoke_custom(*call_site_idx, regs, ins.pc);
1254 self.pending_call = Some(e);
1255 }
1256 InsnKind::Un {
1257 dst,
1258 src,
1259 op,
1260 from: _,
1261 to,
1262 } => {
1263 let e = self.read_nest(*src);
1264 let ne = match op {
1265 ddc_dex::insn::UnArith::Neg => Expr::Un {
1266 op: UnOp::Neg,
1267 e: Box::new(e),
1268 },
1269 ddc_dex::insn::UnArith::Not => Expr::Un {
1270 op: UnOp::BitNot,
1271 e: Box::new(e),
1272 },
1273 ddc_dex::insn::UnArith::Conv => Expr::Cast {
1274 ty: TypeRef::J(primitive_type(*to)),
1275 e: Box::new(e),
1276 },
1277 };
1278 let wide = matches!(*to, 'J' | 'D');
1279 self.write(*dst, ne, ins.pc, wide);
1280 }
1281 InsnKind::Bin { op, dst, a, b, ty } => {
1282 let l = self.read_nest(*a);
1283 let r = self.read_nest(*b);
1284 let jt = primitive_type(*ty);
1285 let e = Expr::Bin {
1286 op: Self::arith_op(*op),
1287 l: Box::new(l),
1288 r: Box::new(r),
1289 ty: Some(TypeRef::J(jt)),
1290 };
1291 let wide = matches!(*ty, 'J' | 'D');
1292 self.write(*dst, e, ins.pc, wide);
1293 }
1294 InsnKind::BinLit {
1295 op,
1296 dst,
1297 a,
1298 lit,
1299 rsub,
1300 } => {
1301 let av = self.read_nest(*a);
1302 let e = if *rsub {
1303 Expr::Bin {
1304 op: BinOp::Sub,
1305 l: Box::new(Expr::Const(ConstVal::Int(*lit))),
1306 r: Box::new(av),
1307 ty: Some(TypeRef::J(JavaType::Int)),
1308 }
1309 } else {
1310 Expr::Bin {
1311 op: Self::arith_op(*op),
1312 l: Box::new(av),
1313 r: Box::new(Expr::Const(ConstVal::Int(*lit))),
1314 ty: Some(TypeRef::J(JavaType::Int)),
1315 }
1316 };
1317 self.write(*dst, e, ins.pc, false);
1318 }
1319 InsnKind::ConstMethodHandle { dst, handle_idx } => {
1320 self.drop_pending_call();
1321 let e = self.method_handle_expr(*handle_idx);
1322 self.write(*dst, e, ins.pc, false);
1323 }
1324 InsnKind::ConstMethodType { dst, proto_idx } => {
1325 self.drop_pending_call();
1326 let proto = self.env.dex.proto(*proto_idx);
1327 let desc = format!(
1328 "({}){}",
1329 self.env
1330 .dex
1331 .proto_params(*proto_idx)
1332 .iter()
1333 .map(|&t| self.env.dex.type_name(t))
1334 .collect::<Vec<_>>()
1335 .join(""),
1336 self.env.dex.type_name(proto.return_type_idx)
1337 );
1338 self.write(*dst, Expr::Raw(desc), ins.pc, false);
1339 }
1340 }
1341 }
1342
1343 let term = self.block_term(ins);
1345
1346 self.drop_pending_call();
1350 self.materialize_impure_at_exit();
1351
1352 let out = OutState {
1356 regs: std::mem::take(&mut self.regs),
1357 write_pc: std::mem::take(&mut self.write_pc),
1358 };
1359 Ok((
1360 BlockResult {
1361 stmts: std::mem::take(&mut self.stmts),
1362 out_stack: Vec::new(),
1363 term,
1364 },
1365 out,
1366 ))
1367 }
1368
1369 fn do_invoke(
1370 &mut self,
1371 kind: InvokeKind,
1372 regs: &[u16],
1373 method_idx: u32,
1374 pc: u32,
1375 ) -> BResult<()> {
1376 let (cls, name, md) = self.env.method_ref(method_idx);
1377 if cls.as_ref() == "java/lang/StringBuilder" || cls.as_ref() == "java/lang/StringBuffer" {
1378 *self.mflags = self.mflags.with_sb();
1379 }
1380 let is_static = matches!(kind, InvokeKind::Static);
1381 let receiver_reg = if is_static {
1382 None
1383 } else {
1384 regs.first().copied()
1385 };
1386 let arg_regs: Vec<u16> = if is_static {
1387 regs.to_vec()
1388 } else {
1389 regs.iter().skip(1).copied().collect()
1390 };
1391 let mut args: Vec<Expr> = Vec::with_capacity(md.args.len());
1392 let mut ri = 0usize;
1400 for at in md.args.iter() {
1401 let r = arg_regs.get(ri).copied().unwrap_or(0);
1402 args.push(null_in_obj_ctx(self.read_nest(r), at));
1403 ri += if at.is_wide() { 2 } else { 1 };
1404 }
1405 let recv_expr = receiver_reg.map(|r| self.read_nest(r));
1406
1407 if name.as_ref() == "<init>" && matches!(kind, InvokeKind::Direct) {
1409 let recv_reg = receiver_reg.unwrap_or(0);
1410 let recv_state = self
1411 .regs
1412 .get(recv_reg as usize)
1413 .cloned()
1414 .unwrap_or(Reg::Undef);
1415 if let Reg::Pending(Expr::New {
1416 raw: true, cls: nc, ..
1417 }) = &recv_state
1418 {
1419 if nc == &cls {
1420 let folded = Expr::New {
1421 cls: cls.clone(),
1422 ty: TypeRef::J(JavaType::Object(cls.clone())),
1423 arg_tys: md.args.clone(),
1424 args,
1425 raw: false,
1426 };
1427 let origin = self.write_pc.get(recv_reg as usize).copied();
1436 let aliases: Vec<usize> = origin
1437 .map(|w| {
1438 (0..self.regs.len())
1439 .filter(|&r| {
1440 r != recv_reg as usize
1441 && self.write_pc.get(r).copied() == Some(w)
1442 && matches!(
1443 &self.regs[r],
1444 Reg::Pending(Expr::New { raw: true, cls: c, .. })
1445 if c == nc
1446 )
1447 })
1448 .collect()
1449 })
1450 .unwrap_or_default();
1451 self.write(recv_reg, folded.clone(), pc, false);
1452 match self.regs.get(recv_reg as usize) {
1457 Some(Reg::Live(v)) => {
1458 let v = *v;
1459 for r in aliases {
1460 self.regs[r] = Reg::Live(v);
1461 }
1462 }
1463 _ => {
1464 for r in aliases {
1465 self.regs[r] = Reg::Pending(folded.clone());
1466 self.write_pc[r] = pc;
1467 }
1468 }
1469 }
1470 return Ok(());
1471 }
1472 }
1473 let owner_expr_v = recv_expr.unwrap_or(Expr::This);
1474 let owner_expr_v = if !self.env.is_static
1481 && matches!(&owner_expr_v, Expr::Local { var: 0, .. })
1482 {
1483 Expr::This
1484 } else {
1485 owner_expr_v
1486 };
1487 let is_super = cls.as_ref() != self.env.class_name.as_str();
1488 let owner = if matches!(owner_expr_v, Expr::This) && is_super {
1489 None
1490 } else {
1491 Some(Box::new(owner_expr_v))
1492 };
1493 let call = Expr::Method {
1494 owner,
1495 cls,
1496 name: "<init>".into(),
1497 desc: md,
1498 args,
1499 is_static: false,
1500 is_interface: false,
1501 is_special: true,
1502 is_super,
1503 is_dynamic: false,
1504 type_args: vec![],
1505 };
1506 self.stmts.push(Stmt::ExprStmt(call));
1507 return Ok(());
1508 }
1509
1510 let is_interface = matches!(kind, InvokeKind::Interface);
1511 let is_super = match kind {
1512 InvokeKind::Super => true,
1513 InvokeKind::Direct if !is_static => cls.as_ref() != self.env.class_name.as_str(),
1514 _ => false,
1515 };
1516 let owner: Option<Box<Expr>> = match recv_expr {
1517 None => None,
1518 Some(o) => {
1519 if is_super && matches!(o, Expr::This) {
1520 None
1521 } else {
1522 self.owner_expr(o, &cls)
1523 }
1524 }
1525 };
1526 let is_dynamic = matches!(kind, InvokeKind::Polymorphic);
1527 let call = Expr::Method {
1528 owner,
1529 cls,
1530 name,
1531 desc: md,
1532 args,
1533 is_static,
1534 is_interface,
1535 is_special: matches!(kind, InvokeKind::Direct),
1536 is_super,
1537 is_dynamic,
1538 type_args: vec![],
1539 };
1540 self.pending_call = Some(call);
1541 Ok(())
1542 }
1543
1544 fn method_handle_expr(&mut self, handle_idx: u32) -> Expr {
1547 let Some(h) = self.env.dex.method_handle(handle_idx) else {
1548 return Expr::Raw(format!("methodHandle@{}", handle_idx));
1549 };
1550 if h.is_field {
1551 let f = self.env.dex.field(h.target_id);
1552 let cls = self.env.dex.class_name(f.class_idx);
1553 let name = self.env.dex.string(f.name_idx).to_string();
1554 return Expr::Raw(format!("{}::{}", cls.replace('/', "."), name));
1555 }
1556 let m = self.env.dex.method(h.target_id);
1557 let cls = self.env.dex.class_name(m.class_idx);
1558 let name = self.env.dex.string(m.name_idx).to_string();
1559 Expr::Raw(format!("{}::{}", cls.replace('/', "."), name))
1560 }
1561
1562 fn build_invoke_custom(&mut self, cs_idx: u32, regs: &[u16], pc: u32) -> Expr {
1564 let Some(cs) = self.env.dex.call_site(cs_idx).cloned() else {
1565 return Expr::Invokedynamic {
1566 name: format!("call-site#{}", cs_idx),
1567 desc: jdc_core::types::MethodDescriptor {
1568 args: vec![],
1569 ret: JavaType::Object("java/lang/Object".into()),
1570 },
1571 args: vec![],
1572 bsm_text: "unresolved".into(),
1573 bsm_static_args: vec![],
1574 };
1575 };
1576 let site_name = self.env.dex.string(cs.name_idx).to_string();
1577 let params: Vec<JavaType> = self
1578 .env
1579 .dex
1580 .proto_params(cs.proto_idx)
1581 .iter()
1582 .map(|&t| self.env.java_type(t))
1583 .collect();
1584 let ret = self
1585 .env
1586 .java_type(self.env.dex.proto(cs.proto_idx).return_type_idx);
1587
1588 let (bs_cls, bs_name) = match self.env.dex.method_handle(cs.bootstrap_handle) {
1589 Some(h) if !h.is_field => {
1590 let m = self.env.dex.method(h.target_id);
1591 (
1592 self.env.dex.class_name(m.class_idx),
1593 self.env.dex.string(m.name_idx).to_string(),
1594 )
1595 }
1596 _ => (String::new(), String::new()),
1597 };
1598
1599 let mut args: Vec<Expr> = Vec::with_capacity(params.len().min(regs.len()));
1604 let mut ri = 0usize;
1605 for at in params.iter() {
1606 if let Some(&r) = regs.get(ri) {
1607 args.push(self.read_nest(r));
1608 }
1609 ri += if at.is_wide() { 2 } else { 1 };
1610 }
1611
1612 if bs_name == "makeConcatWithConstants" {
1614 let recipe = cs
1615 .linker_args
1616 .iter()
1617 .find_map(|v| match v {
1618 ddc_dex::annotations::EncodedValue::String(s) => {
1619 Some(self.env.dex.string(*s).to_string())
1620 }
1621 _ => None,
1622 })
1623 .unwrap_or_default();
1624 return concat_from_recipe(&recipe, args);
1625 }
1626
1627 let mut impl_m = None;
1631 let mut instantiated_proto: Option<u32> = None;
1632 if bs_name == "metafactory" || bs_name == "altMetafactory" {
1633 let mut seen_handle = false;
1634 for lv in &cs.linker_args {
1635 match lv {
1636 ddc_dex::annotations::EncodedValue::MethodHandle(hi) => {
1637 if !seen_handle {
1638 seen_handle = true;
1639 if let Some(h) = self.env.dex.method_handle(*hi) {
1640 if !h.is_field {
1641 let m = self.env.dex.method(h.target_id);
1642 impl_m = Some((
1643 self.env.dex.class_name(m.class_idx).replace('/', "."),
1644 self.env.dex.string(m.name_idx).to_string(),
1645 ));
1646 }
1647 }
1648 }
1649 }
1650 ddc_dex::annotations::EncodedValue::MethodType(p) if seen_handle => {
1651 instantiated_proto = Some(*p);
1652 }
1653 _ => {}
1654 }
1655 }
1656 }
1657 let _ = pc;
1658
1659 let mut captures = Vec::new();
1661 for &r in regs.iter().skip(params.len().min(regs.len())) {
1662 captures.push(self.read_nest(r));
1663 }
1664
1665 if let Some((impl_cls, impl_name)) = impl_m {
1666 if captures.is_empty() {
1667 if impl_name.starts_with("lambda$") {
1671 let n_params = match instantiated_proto {
1675 Some(p) => self.env.dex.proto_params(p).len(),
1676 None => params.len(),
1677 };
1678 let pnames: Vec<String> = (0..n_params).map(|i| format!("a{}", i)).collect();
1679 return Expr::Raw(format!(
1680 "({}) -> {}.{}({})",
1681 pnames.join(", "),
1682 impl_cls,
1683 impl_name,
1684 pnames.join(", ")
1685 ));
1686 }
1687 return Expr::Raw(format!("{}::{}", impl_cls, impl_name));
1688 }
1689 let mut call_args = args;
1692 call_args.extend(captures);
1693 return Expr::Invokedynamic {
1694 name: format!("{} -> {}.{}", site_name, impl_cls, impl_name),
1695 desc: jdc_core::types::MethodDescriptor { args: params, ret },
1696 args: call_args,
1697 bsm_text: format!("{}::{}", bs_cls, bs_name),
1698 bsm_static_args: vec![],
1699 };
1700 }
1701
1702 Expr::Invokedynamic {
1703 name: site_name,
1704 desc: jdc_core::types::MethodDescriptor { args: params, ret },
1705 args: captures,
1706 bsm_text: format!("{}::{}", bs_cls, bs_name),
1707 bsm_static_args: vec![],
1708 }
1709 }
1710
1711 fn materialize_impure_at_exit(&mut self) {
1712 let n = self.regs.len();
1713 for r in 0..n {
1714 let st = self.regs[r].clone();
1715 let impure = match &st {
1716 Reg::PendingCall(_) => true,
1717 Reg::Pending(e) => {
1718 has_side_effects(e)
1719 && !matches!(e, Expr::New { raw: true, .. })
1720 && !matches!(e, Expr::NewArray { init: None, .. })
1721 }
1722 _ => false,
1723 };
1724 if impure {
1725 self.materialize(r as u16);
1726 }
1727 }
1728 }
1729
1730 fn block_term(&mut self, ins: &[Insn]) -> Term {
1732 let Some(last) = ins.last() else {
1733 return Term::Goto;
1734 };
1735 match &last.kind {
1736 InsnKind::Goto { .. } => Term::Goto,
1737 InsnKind::ReturnVoid => Term::Return(None),
1738 InsnKind::Return { src } => {
1739 let v = null_in_obj_ctx(self.read_term(*src), &self.env.desc.ret);
1740 Term::Return(Some(v))
1741 }
1742 InsnKind::Throw { reg } => {
1743 let mut v = self.read_term(*reg);
1745 if let Expr::Const(ConstVal::Int(0)) = v {
1746 v = Expr::Const(ConstVal::Null);
1747 }
1748 Term::Throw(v)
1749 }
1750 InsnKind::If { op, a, b, z, .. } => {
1751 let cond = if *z {
1752 let v = self.read_nest(*a);
1753 let (l, r, realop) = jdc_core::ir::build::unfold_cmp(v, Self::cmp_op(*op));
1754 Expr::Bin {
1755 op: realop,
1756 l: Box::new(l),
1757 r: Box::new(r),
1758 ty: Some(TypeRef::J(JavaType::Boolean)),
1759 }
1760 } else {
1761 let l = self.read_nest(*a);
1762 let r = self.read_nest(*b);
1763 Expr::Bin {
1764 op: Self::cmp_op(*op),
1765 l: Box::new(l),
1766 r: Box::new(r),
1767 ty: Some(TypeRef::J(JavaType::Boolean)),
1768 }
1769 };
1770 Term::Cond { cond }
1771 }
1772 InsnKind::PackedSwitch { reg, payload_pc }
1773 | InsnKind::SparseSwitch { reg, payload_pc } => {
1774 let selector = self.read_term(*reg);
1775 let default = Some(last.pc + last.size);
1776 let targets = match self.env.code.payloads.get(payload_pc) {
1777 Some(Payload::Packed { first_key, targets }) => SwitchTargets::Table {
1778 low: *first_key,
1779 targets: targets
1780 .iter()
1781 .map(|t| (last.pc as i64 + *t as i64) as u32)
1782 .collect(),
1783 },
1784 Some(Payload::Sparse { pairs }) => SwitchTargets::Lookup {
1785 pairs: pairs
1786 .iter()
1787 .map(|(k, t)| (*k, (last.pc as i64 + *t as i64) as u32))
1788 .collect(),
1789 },
1790 _ => SwitchTargets::Lookup { pairs: vec![] },
1791 };
1792 Term::Switch {
1793 selector,
1794 targets,
1795 default,
1796 }
1797 }
1798 _ => {
1800 let next = last.pc + last.size;
1801 if next < self.code_units {
1802 Term::Fallthrough
1803 } else {
1804 Term::Goto
1805 }
1806 }
1807 }
1808 }
1809}
1810
1811fn concat_from_recipe(recipe: &str, args: Vec<Expr>) -> Expr {
1815 use jdc_core::ir::expr::ConcatPart;
1816 let mut parts: Vec<ConcatPart> = Vec::new();
1817 let mut arg_i = 0usize;
1818 let mut const_buf = String::new();
1819 for c in recipe.chars() {
1820 match c {
1821 '\u{1}' => {
1822 if !const_buf.is_empty() {
1823 parts.push(ConcatPart::Const(std::mem::take(&mut const_buf)));
1824 }
1825 if arg_i < args.len() {
1826 parts.push(ConcatPart::Str(args[arg_i].clone()));
1827 arg_i += 1;
1828 }
1829 }
1830 '\u{2}' => {
1831 }
1834 c => const_buf.push(c),
1835 }
1836 }
1837 if !const_buf.is_empty() {
1838 parts.push(ConcatPart::Const(const_buf));
1839 }
1840 Expr::StringConcat(parts)
1841}
1842
1843fn expr_size(e: &Expr) -> usize {
1847 let mut n = 0usize;
1848 count_nodes(e, &mut n);
1849 n
1850}
1851
1852fn count_nodes(e: &Expr, n: &mut usize) {
1853 *n += 1;
1854 if *n > 512 {
1855 return; }
1857 match e {
1858 Expr::Un { e, .. } | Expr::Cast { e, .. } | Expr::InstanceOf { e, .. } => count_nodes(e, n),
1859 Expr::Bin { l, r, .. } => {
1860 count_nodes(l, n);
1861 count_nodes(r, n);
1862 }
1863 Expr::Cond { c, t, f } => {
1864 count_nodes(c, n);
1865 count_nodes(t, n);
1866 count_nodes(f, n);
1867 }
1868 Expr::Assign { target, value, .. } => {
1869 count_nodes(target, n);
1870 count_nodes(value, n);
1871 }
1872 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => count_nodes(e, n),
1873 Expr::Field { owner: Some(o), .. } => count_nodes(o, n),
1874 Expr::Method {
1875 owner: Some(o),
1876 args,
1877 ..
1878 } => {
1879 count_nodes(o, n);
1880 for a in args {
1881 count_nodes(a, n);
1882 }
1883 }
1884 Expr::ArrayIndex { array, index } => {
1885 count_nodes(array, n);
1886 count_nodes(index, n);
1887 }
1888 Expr::New { args, .. } => {
1889 for a in args {
1890 count_nodes(a, n);
1891 }
1892 }
1893 Expr::NewArray { dims, init, .. } => {
1894 for d in dims {
1895 count_nodes(d, n);
1896 }
1897 if let Some(v) = init {
1898 for x in v {
1899 count_nodes(x, n);
1900 }
1901 }
1902 }
1903 Expr::NewMultiArray { dims, .. } => {
1904 for d in dims {
1905 count_nodes(d, n);
1906 }
1907 }
1908 Expr::StringConcat(parts) => {
1909 for p in parts {
1910 if let ConcatPart::Str(x) = p {
1911 count_nodes(x, n);
1912 }
1913 }
1914 }
1915 _ => {}
1916 }
1917}
1918
1919fn reg_is_wide(st: &Reg, vt: &VarTable) -> bool {
1920 match st {
1921 Reg::Pending(e) | Reg::PendingCall(e) => e.type_ref().erased().is_wide(),
1922 Reg::Live(v) => vt.var(*v).ty.erased().is_wide(),
1923 Reg::WideHi => true,
1924 Reg::Undef => false,
1925 }
1926}
1927
1928fn null_in_obj_ctx(mut v: Expr, ctx_ty: &JavaType) -> Expr {
1934 if ctx_ty.is_reference() {
1935 if let Expr::Const(ConstVal::Int(0)) = v {
1936 v = Expr::Const(ConstVal::Null);
1937 }
1938 }
1939 v
1940}
1941
1942fn value_of_cmp(e: &Expr) -> Expr {
1944 if let Expr::Invokedynamic { name, args, .. } = e {
1945 if name.starts_with('\0') && args.len() == 2 {
1946 let (cls, ty) = match name.as_str() {
1947 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
1948 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
1949 _ => ("java/lang/Double", JavaType::Double),
1950 };
1951 let desc = MethodDescriptor {
1952 args: vec![ty.clone(), ty],
1953 ret: JavaType::Int,
1954 };
1955 return Expr::Method {
1956 owner: None,
1957 cls: cls.into(),
1958 name: "compare".into(),
1959 desc: std::sync::Arc::new(desc),
1960 args: args.clone(),
1961 is_static: true,
1962 is_interface: false,
1963 is_special: false,
1964 is_super: false,
1965 is_dynamic: false,
1966 type_args: vec![],
1967 };
1968 }
1969 }
1970 e.clone()
1971}
1972
1973pub fn primitive_type(c: char) -> JavaType {
1974 match c {
1975 'Z' => JavaType::Boolean,
1976 'B' => JavaType::Byte,
1977 'C' => JavaType::Char,
1978 'S' => JavaType::Short,
1979 'F' => JavaType::Float,
1980 'J' => JavaType::Long,
1981 'D' => JavaType::Double,
1982 _ => JavaType::Int,
1983 }
1984}
1985
1986fn payload_consts(elem_width: u16, size: u32, data: &[u8]) -> Vec<Expr> {
1989 let n = (size as usize).min(data.len() / elem_width.max(1) as usize);
1990 let mut out = Vec::with_capacity(n);
1991 for i in 0..n {
1992 let off = i * elem_width as usize;
1993 let e = match elem_width {
1994 1 => Expr::Const(ConstVal::Int(data[off] as i8 as i32)),
1995 2 => {
1996 let v = i16::from_le_bytes([data[off], data[off + 1]]);
1997 Expr::Const(ConstVal::Int(v as i32))
1998 }
1999 4 => {
2000 let mut b = [0u8; 4];
2001 b.copy_from_slice(&data[off..off + 4]);
2002 Expr::Const(ConstVal::Int(i32::from_le_bytes(b)))
2003 }
2004 8 => {
2005 let mut b = [0u8; 8];
2006 b.copy_from_slice(&data[off..off + 8]);
2007 Expr::Const(ConstVal::Long(i64::from_le_bytes(b)))
2008 }
2009 _ => Expr::Const(ConstVal::Int(0)),
2010 };
2011 out.push(e);
2012 }
2013 out
2014}
2015
2016pub fn entry_regs(vt: &mut VarTable, env: &MethodEnv, param_names: &[Option<String>]) -> Vec<Reg> {
2018 let code = env.code;
2019 let n = code.registers_size as usize;
2020 let mut regs = vec![Reg::Undef; n.max(1)];
2021 let ins = code.ins_size.min(code.registers_size) as usize;
2022 let mut reg = n.saturating_sub(ins);
2023 let mut pidx = 0;
2024 if !env.is_static && reg < n {
2025 let id = vt.vars.len() as u32;
2027 vt.vars.push(VarInfo {
2028 id,
2029 slot: reg as u16,
2030 name: "this".into(),
2031 ty: TypeRef::J(JavaType::Object(env.class_name.as_str().into())),
2032 is_param: true,
2033 range_start: 0,
2034 range_end: u16::MAX,
2035 synthetic_name: false,
2036 });
2037 while vt.by_slot.len() <= reg {
2038 vt.by_slot.push(Vec::new());
2039 }
2040 vt.by_slot[reg].push((0, u16::MAX, id));
2041 regs[reg] = Reg::Live(id);
2042 reg += 1;
2043 pidx += 1;
2044 }
2045 for (i, arg) in env.desc.args.iter().enumerate() {
2046 if reg >= n {
2047 break;
2048 }
2049 let dbg = param_names.get(i).and_then(|o| o.clone());
2050 let named = dbg.is_some();
2051 let name = dbg
2056 .map(|d| crate::classdec::java_ident(&d).into_owned())
2057 .unwrap_or_else(|| format!("p{}", pidx));
2058 let id = vt.vars.len() as u32;
2059 vt.vars.push(VarInfo {
2060 id,
2061 slot: reg as u16,
2062 name,
2063 ty: TypeRef::J(arg.clone()),
2064 is_param: true,
2065 range_start: 0,
2066 range_end: u16::MAX,
2067 synthetic_name: !named,
2068 });
2069 while vt.by_slot.len() <= reg {
2070 vt.by_slot.push(Vec::new());
2071 }
2072 vt.by_slot[reg].push((0, u16::MAX, id));
2073 regs[reg] = Reg::Live(id);
2074 reg += if arg.is_wide() { 2 } else { 1 };
2075 pidx += 1;
2076 }
2077 regs
2078}