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
283fn compute_final_reads(ins: &[Insn]) -> jdc_core::FxHashSet<(u32, u16)> {
288 use jdc_core::FxHashMap as HashMap;
289 let mut events: HashMap<u16, Vec<(u32, bool)>> = HashMap::default();
291 for i in ins {
292 for r in src_regs(&i.kind) {
293 events.entry(r).or_default().push((i.pc, true));
294 }
295 for r in dst_regs(&i.kind) {
296 events.entry(r).or_default().push((i.pc, false));
297 }
298 }
299 let mut out = jdc_core::FxHashSet::default();
300 for (reg, evs) in events {
301 let mut dedup: Vec<(u32, bool)> = Vec::with_capacity(evs.len());
302 for (pc, is_read) in evs {
303 if dedup.last() != Some(&(pc, is_read)) {
304 dedup.push((pc, is_read));
305 }
306 }
307 for (i, &(pc, is_read)) in dedup.iter().enumerate() {
308 if !is_read {
309 continue;
310 }
311 let mut final_ = true;
314 if let Some(&(_, is_read2)) = dedup.get(i + 1) {
315 if is_read2 {
316 final_ = false;
317 }
318 }
319 if final_ {
320 out.insert((pc, reg));
321 }
322 }
323 }
324 out
325}
326
327pub struct Lifter<'a> {
328 pub env: &'a MethodEnv<'a>,
329 pub vt: &'a mut VarTable,
330 block_id: usize,
332 stable: &'a mut jdc_core::FxHashMap<(usize, u16, u64), u32>,
339 regs: Vec<Reg>,
340 write_pc: Vec<u32>,
341 stmts: Vec<Stmt>,
342 pending_call: Option<Expr>,
343 code_units: u32,
344 cur_pc: u32,
347 final_read: jdc_core::FxHashSet<(u32, u16)>,
354 mflags: &'a mut MethodFlags,
358}
359
360impl<'a> Lifter<'a> {
361 pub fn new(
362 env: &'a MethodEnv<'a>,
363 vt: &'a mut VarTable,
364 in_regs: Vec<Reg>,
365 block_id: usize,
366 stable: &'a mut jdc_core::FxHashMap<(usize, u16, u64), u32>,
367 mflags: &'a mut MethodFlags,
368 ) -> Lifter<'a> {
369 let n = env.code.registers_size as usize;
370 let n = n.max(in_regs.len()).max(1);
371 let mut regs = in_regs;
372 regs.resize(n, Reg::Undef);
373 Lifter {
374 env,
375 vt,
376 regs,
377 write_pc: vec![0; n],
378 stmts: Vec::new(),
379 block_id,
380 stable,
381 pending_call: None,
382 code_units: env.code_units,
383 cur_pc: 0,
384 final_read: jdc_core::FxHashSet::default(),
385 mflags,
386 }
387 }
388
389 fn ty_key(ty: &TypeRef) -> u64 {
397 use std::hash::{Hash, Hasher};
398 fn jt_key(j: &JavaType, h: &mut jdc_core::fx::FxHasher) {
399 match j {
400 JavaType::Void => 1u64.hash(h),
401 JavaType::Boolean => 2u64.hash(h),
402 JavaType::Byte => 3u64.hash(h),
403 JavaType::Char => 4u64.hash(h),
404 JavaType::Short => 5u64.hash(h),
405 JavaType::Int => 6u64.hash(h),
406 JavaType::Float => 7u64.hash(h),
407 JavaType::Long => 8u64.hash(h),
408 JavaType::Double => 9u64.hash(h),
409 JavaType::Object(n) => {
410 10u64.hash(h);
411 n.hash(h);
412 }
413 JavaType::Array(inner) => {
414 11u64.hash(h);
415 jt_key(inner, h);
416 }
417 }
418 }
419 let mut h = jdc_core::fx::FxHasher::default();
420 match ty {
421 TypeRef::J(j) => jt_key(j, &mut h),
422 TypeRef::G(_) => 99u64.hash(&mut h),
423 }
424 h.finish()
425 }
426
427 fn fresh_var(&mut self, slot: u16, ty: TypeRef) -> u32 {
428 let key = (self.block_id, slot, Self::ty_key(&ty));
438 if let Some(&id) = self.stable.get(&key) {
439 return id;
440 }
441 if let Some(name) = self.debug_name(slot, &ty) {
446 let tkey = Self::ty_key(&ty);
452 if let Some(id) = self
453 .vt
454 .vars
455 .iter()
456 .find(|v| {
457 v.slot == slot
458 && !v.synthetic_name
459 && v.name == name
460 && Self::ty_key(&v.ty) == tkey
461 })
462 .map(|v| v.id)
463 {
464 self.stable.insert(key, id);
465 return id;
466 }
467 let id = self.vt.vars.len() as u32;
468 self.stable.insert(key, id);
469 self.push_var(id, slot, name, ty, false);
470 return id;
471 }
472 let id = self.vt.vars.len() as u32;
473 let name = format!("v{}", id);
474 self.stable.insert(key, id);
475 self.push_var(id, slot, name, ty, true);
476 id
477 }
478
479 fn push_var(&mut self, id: u32, slot: u16, name: String, ty: TypeRef, synthetic: bool) {
480 self.vt.vars.push(VarInfo {
481 id,
482 slot,
483 name,
484 ty,
485 is_param: false,
486 range_start: 0,
487 range_end: u16::MAX,
488 synthetic_name: synthetic,
489 });
490 while self.vt.by_slot.len() <= slot as usize {
491 self.vt.by_slot.push(Vec::new());
492 }
493 self.vt.by_slot[slot as usize].push((0, u16::MAX, id));
494 }
495
496 fn debug_name(&self, slot: u16, ty: &TypeRef) -> Option<String> {
501 let locals = &self.env.debug_locals;
502 if locals.is_empty() {
503 return None;
504 }
505 let want = ty.erased();
506 let covers = |pc: u32| {
507 locals.iter().find(|l| {
508 l.reg == slot
509 && pc >= l.start
510 && pc < l.end
511 && l.ty.as_deref().map(|d| crate::desc_type(d) == want).unwrap_or(true)
516 })
517 };
518 let hit = covers(self.cur_pc).or_else(|| {
523 let wpc = *self.write_pc.get(slot as usize)?;
524 if wpc == 0 {
525 None
526 } else {
527 covers(wpc)
528 }
529 })?;
530 let name = crate::classdec::java_ident(&hit.name);
531 if name.is_empty() {
532 return None;
533 }
534 Some(name.into_owned())
535 }
536
537 fn local_expr(&self, v: u32) -> Expr {
538 Expr::Local {
539 var: v,
540 ty: self.vt.var(v).ty.clone(),
541 }
542 }
543
544 #[track_caller]
548 fn read_nest(&mut self, r: u16) -> Expr {
549 match self.regs.get(r as usize).cloned().unwrap_or(Reg::Undef) {
550 Reg::Undef | Reg::WideHi => {
551 let ty = TypeRef::J(JavaType::Int);
555 let v = self.fresh_var(r, ty);
556 if (r as usize) < self.regs.len() {
557 self.regs[r as usize] = Reg::Live(v);
558 }
559 self.local_expr(v)
560 }
561 Reg::Live(v) => self.local_expr(v),
562 Reg::Pending(e) => {
563 let alloc = matches!(&e, Expr::NewArray { .. })
575 || (matches!(&e, Expr::New { raw: false, .. })
576 && !self.final_read.contains(&(self.cur_pc, r)));
577 if alloc {
578 let v = self.materialize(r);
579 self.local_expr(v)
580 } else {
581 if matches!(&e, Expr::NewArray { .. })
592 || matches!(&e, Expr::New { raw: false, .. })
593 {
594 if let Some(slot) = self.regs.get_mut(r as usize) {
595 *slot = Reg::Undef;
596 }
597 }
598 e
599 }
600 }
601 Reg::PendingCall(_) => {
602 let v = self.materialize(r);
603 self.local_expr(v)
604 }
605 }
606 }
607
608 fn read_term(&mut self, r: u16) -> Expr {
612 match self.regs.get(r as usize).cloned().unwrap_or(Reg::Undef) {
613 Reg::PendingCall(e) | Reg::Pending(e) => {
614 if (r as usize) < self.regs.len() {
615 self.regs[r as usize] = Reg::Undef;
616 }
617 self.pending_call = None;
618 e
619 }
620 _ => self.read_nest(r),
621 }
622 }
623
624 fn write(&mut self, r: u16, e: Expr, pc: u32, wide: bool) {
625 if r as usize >= self.regs.len() {
626 return;
627 }
628 self.write_pc[r as usize] = pc;
629 if expr_size(&e) > 32 {
634 self.write_pc[r as usize] = pc;
635 let v = self.materialize_value(r, e);
636 if (r as usize) < self.regs.len() {
637 self.regs[r as usize] = Reg::Live(v);
638 }
639 if wide {
640 self.mark_wide_hi(r + 1, pc);
641 }
642 return;
643 }
644 self.regs[r as usize] = Reg::Pending(e);
645 if wide {
646 self.mark_wide_hi(r + 1, pc);
647 }
648 }
649
650 fn materialize_value(&mut self, r: u16, e: Expr) -> u32 {
653 let e = value_of_cmp(&e);
654 let ty = e.type_ref();
655 let v = self.fresh_var(r, ty);
659 self.stmts.push(Stmt::LocalDef {
660 var: v,
661 init: Some(e),
662 is_final: false,
663 force_type: true,
664 });
665 v
666 }
667
668 fn write_call_result(&mut self, r: u16, e: Expr, pc: u32) {
671 if r as usize >= self.regs.len() {
672 return;
673 }
674 self.write_pc[r as usize] = pc;
675 self.regs[r as usize] = Reg::PendingCall(e);
676 }
677
678 fn mark_wide_hi(&mut self, r: u16, pc: u32) {
679 if (r as usize) < self.regs.len() {
680 self.regs[r as usize] = Reg::WideHi;
681 self.write_pc[r as usize] = pc;
682 }
683 }
684
685 fn materialize(&mut self, r: u16) -> u32 {
687 let cur = self.regs.get(r as usize).cloned().unwrap_or(Reg::Undef);
688 let e = match cur {
689 Reg::Pending(e) | Reg::PendingCall(e) => e,
690 Reg::Live(v) => return v,
691 _ => {
694 let v = self.fresh_var(r, TypeRef::J(JavaType::Object("java/lang/Object".into())));
695 self.stmts.push(Stmt::LocalDef {
696 var: v,
697 init: None,
698 is_final: false,
699 force_type: false,
700 });
701 if (r as usize) < self.regs.len() {
702 self.regs[r as usize] = Reg::Live(v);
703 }
704 return v;
705 }
706 };
707 let e = value_of_cmp(&e);
709 let ty = e.type_ref();
710 let v = self.fresh_var(r, ty);
714 self.stmts.push(Stmt::LocalDef {
715 var: v,
716 init: Some(e),
717 is_final: false,
718 force_type: true,
719 });
720 if (r as usize) < self.regs.len() {
721 self.regs[r as usize] = Reg::Live(v);
722 }
723 v
724 }
725
726 fn drop_pending_call(&mut self) {
728 if let Some(e) = self.pending_call.take() {
729 let e = value_of_cmp(&e);
730 if has_side_effects(&e) {
731 self.stmts.push(Stmt::ExprStmt(e));
732 }
733 }
734 }
735
736 fn owner_expr(&self, obj: Expr, cls: &str) -> Option<Box<Expr>> {
739 if cls == self.env.class_name && matches!(obj, Expr::This) {
740 return None;
741 }
742 Some(Box::new(obj))
743 }
744
745 fn arith_op(op: ArithOp) -> BinOp {
746 match op {
747 ArithOp::Add => BinOp::Add,
748 ArithOp::Sub => BinOp::Sub,
749 ArithOp::Mul => BinOp::Mul,
750 ArithOp::Div => BinOp::Div,
751 ArithOp::Rem => BinOp::Rem,
752 ArithOp::And => BinOp::And,
753 ArithOp::Or => BinOp::Or,
754 ArithOp::Xor => BinOp::Xor,
755 ArithOp::Shl => BinOp::Shl,
756 ArithOp::Shr => BinOp::Shr,
757 ArithOp::Ushr => BinOp::Ushr,
758 }
759 }
760
761 fn cmp_op(op: CmpOp) -> BinOp {
762 match op {
763 CmpOp::Eq => BinOp::Eq,
764 CmpOp::Ne => BinOp::Ne,
765 CmpOp::Lt => BinOp::Lt,
766 CmpOp::Ge => BinOp::Ge,
767 CmpOp::Gt => BinOp::Gt,
768 CmpOp::Le => BinOp::Le,
769 }
770 }
771
772 fn cmp_sentinel(kind: CmpKind, l: Expr, r: Expr) -> Expr {
773 let name = match kind {
774 CmpKind::CmplF => "\0cmpl-float",
775 CmpKind::CmpgF => "\0cmpg-float",
776 CmpKind::CmplD => "\0cmpl-double",
777 CmpKind::CmpgD => "\0cmpg-double",
778 CmpKind::CmpJ => "\0cmp-long",
779 };
780 Expr::Invokedynamic {
781 name: name.into(),
782 desc: MethodDescriptor {
783 args: vec![],
784 ret: JavaType::Int,
785 },
786 args: vec![l, r],
787 bsm_text: String::new(),
788 bsm_static_args: vec![],
789 }
790 }
791
792 pub fn build_block(
797 mut self,
798 ins: &[Insn],
799 handler_types: &[Option<std::sync::Arc<str>>],
800 ) -> BResult<(BlockResult, OutState)> {
801 let payloads = &self.env.code.payloads;
802 self.final_read = compute_final_reads(ins);
812 for ins in ins {
813 self.cur_pc = ins.pc;
814 match &ins.kind {
815 InsnKind::Nop => {}
816 InsnKind::Unknown => {
817 return Err(format!(
818 "unsupported opcode {:#04x} ({}) at pc {}",
819 ins.op,
820 ddc_dex::insn::op_name(ins.op),
821 ins.pc
822 ));
823 }
824 InsnKind::Move { dst, src } => {
825 let src_state = self.regs.get(*src as usize).cloned().unwrap_or(Reg::Undef);
828 if matches!(src_state, Reg::PendingCall(_))
829 || matches!(&src_state, Reg::Pending(e) if has_side_effects(e))
830 {
831 self.materialize(*src);
832 }
833 let st = self.regs.get(*src as usize).cloned().unwrap_or(Reg::Undef);
834 let wide = reg_is_wide(&st, self.vt);
835 if (*dst as usize) < self.regs.len() {
836 self.regs[*dst as usize] = st;
837 self.write_pc[*dst as usize] =
838 self.write_pc.get(*src as usize).copied().unwrap_or(0);
839 }
840 if wide {
841 self.mark_wide_hi(*dst + 1, ins.pc);
842 }
843 }
844 InsnKind::MoveResult { dst } => {
845 if let Some(e) = self.pending_call.take() {
846 let wide =
847 matches!(e.type_ref().erased(), JavaType::Long | JavaType::Double);
848 self.write_call_result(*dst, e, ins.pc);
849 if wide {
850 self.mark_wide_hi(*dst + 1, ins.pc);
851 }
852 } else {
853 let v = self.fresh_var(*dst, TypeRef::J(JavaType::Int));
855 if (*dst as usize) < self.regs.len() {
856 self.regs[*dst as usize] = Reg::Live(v);
857 }
858 }
859 }
860 InsnKind::MoveException { dst } => {
861 let ty = handler_types
862 .iter()
863 .flatten()
864 .next()
865 .map(|t| TypeRef::J(JavaType::Object(t.clone())))
866 .unwrap_or_else(|| {
867 TypeRef::J(JavaType::Object("java/lang/Throwable".into()))
868 });
869 let v = self.fresh_var(*dst, ty);
870 self.stmts.push(Stmt::LocalDef {
871 var: v,
872 init: None,
873 is_final: false,
874 force_type: true,
875 });
876 if (*dst as usize) < self.regs.len() {
877 self.regs[*dst as usize] = Reg::Live(v);
878 }
879 }
880 InsnKind::ReturnVoid
881 | InsnKind::Return { .. }
882 | InsnKind::Throw { .. }
883 | InsnKind::Goto { .. }
884 | InsnKind::PackedSwitch { .. }
885 | InsnKind::SparseSwitch { .. }
886 | InsnKind::If { .. } => {}
887 InsnKind::Const { dst, val, wide } => {
888 let e = if *wide {
889 Expr::Const(ConstVal::Long(*val))
890 } else {
891 Expr::Const(ConstVal::Int(*val as i32))
892 };
893 self.write(*dst, e, ins.pc, *wide);
894 }
895 InsnKind::ConstString { dst, str_idx } => {
896 let s = std::sync::Arc::from(self.env.dex.string(*str_idx));
897 self.write(*dst, Expr::Const(ConstVal::Str(s)), ins.pc, false);
898 }
899 InsnKind::ConstClass { dst, type_idx } => {
900 let ty = TypeRef::J(self.env.java_type(*type_idx));
901 self.write(*dst, Expr::Const(ConstVal::ClassLit(ty)), ins.pc, false);
902 }
903 InsnKind::MonitorEnter { reg } => {
904 let e = self.read_nest(*reg);
905 *self.mflags = self.mflags.with_monitor();
906 self.stmts.push(Stmt::MonitorEnter(e));
907 }
908 InsnKind::MonitorExit { reg } => {
909 let e = self.read_nest(*reg);
910 *self.mflags = self.mflags.with_monitor();
911 self.stmts.push(Stmt::MonitorExit(e));
912 }
913 InsnKind::CheckCast { reg, type_idx } => {
914 let e = self.read_nest(*reg);
915 let ty = TypeRef::J(self.env.java_type(*type_idx));
916 self.write(*reg, Expr::Cast { ty, e: Box::new(e) }, ins.pc, false);
917 }
918 InsnKind::InstanceOf { dst, src, type_idx } => {
919 let e = self.read_nest(*src);
920 let ty = TypeRef::J(self.env.java_type(*type_idx));
921 self.write(*dst, Expr::InstanceOf { e: Box::new(e), ty }, ins.pc, false);
922 }
923 InsnKind::ArrayLength { dst, src } => {
924 let e = self.read_nest(*src);
925 let length = Expr::Field {
926 owner: Some(Box::new(e)),
927 cls: std::sync::Arc::from(""),
928 name: "length".into(),
929 ty: TypeRef::J(JavaType::Int),
930 is_static: false,
931 };
932 self.write(*dst, length, ins.pc, false);
933 }
934 InsnKind::NewInstance { dst, type_idx } => {
935 let cls = self.env.type_name_arc(*type_idx);
936 let ty = TypeRef::J(JavaType::Object(cls.clone()));
937 self.write(
938 *dst,
939 Expr::New {
940 cls,
941 ty,
942 args: vec![],
943 raw: true,
944 },
945 ins.pc,
946 false,
947 );
948 }
949 InsnKind::NewArray {
950 dst,
951 size,
952 type_idx,
953 } => {
954 let elem_desc = self.env.type_name(*type_idx);
955 let elem = crate::desc_type(elem_desc.trim_start_matches('['));
957 let n_brackets = elem_desc.chars().take_while(|&c| c == '[').count();
968 let trailing_dims = n_brackets.saturating_sub(1).min(u8::MAX as usize) as u8;
969 let dims = self.read_nest(*size);
970 self.write(
971 *dst,
972 Expr::NewArray {
973 elem: TypeRef::J(elem),
974 dims: vec![dims],
975 trailing_dims,
976 init: None,
977 },
978 ins.pc,
979 false,
980 );
981 }
982 InsnKind::FilledNewArray { regs, type_idx } => {
983 self.drop_pending_call();
984 let elem_desc = self.env.type_name(*type_idx);
985 let elem = crate::desc_type(elem_desc.trim_start_matches('['));
986 let args: Vec<Expr> = regs.iter().map(|&r| self.read_nest(r)).collect();
987 self.pending_call = Some(Expr::NewArray {
988 elem: TypeRef::J(elem),
989 dims: vec![],
990 trailing_dims: 0,
991 init: Some(args),
992 });
993 }
994 InsnKind::FillArrayData { reg, payload_pc } => {
995 if let Some(Payload::ArrayData {
996 elem_width,
997 size,
998 data,
999 }) = payloads.get(payload_pc)
1000 {
1001 let elems = payload_consts(*elem_width, *size, data);
1002 let cur = self.regs.get(*reg as usize).cloned().unwrap_or(Reg::Undef);
1003 if let Reg::Pending(Expr::NewArray { elem, init, .. }) = cur {
1004 if init.is_none() {
1005 self.write(
1006 *reg,
1007 Expr::NewArray {
1008 elem,
1009 dims: vec![Expr::Const(ConstVal::Int(*size as i32))],
1010 trailing_dims: 0,
1011 init: Some(elems),
1012 },
1013 ins.pc,
1014 false,
1015 );
1016 continue;
1017 }
1018 }
1019 let arr = self.read_nest(*reg);
1021 for (i, e) in elems.into_iter().enumerate() {
1022 let idx = Expr::Const(ConstVal::Int(i as i32));
1023 let target = Expr::ArrayIndex {
1024 array: Box::new(arr.clone()),
1025 index: Box::new(idx),
1026 };
1027 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
1028 target: Box::new(target),
1029 op: AssignOp::Plain,
1030 value: Box::new(e),
1031 }));
1032 }
1033 }
1034 }
1035 InsnKind::Cmp { dst, a, b, kind } => {
1036 let l = self.read_nest(*a);
1037 let r = self.read_nest(*b);
1038 *self.mflags = self.mflags.with_cmp();
1039 self.write(*dst, Self::cmp_sentinel(*kind, l, r), ins.pc, false);
1040 }
1041 InsnKind::AGet {
1042 dst, array, index, ..
1043 } => {
1044 let a = self.read_nest(*array);
1045 let i = self.read_nest(*index);
1046 self.write(
1047 *dst,
1048 Expr::ArrayIndex {
1049 array: Box::new(a),
1050 index: Box::new(i),
1051 },
1052 ins.pc,
1053 false,
1054 );
1055 }
1056 InsnKind::APut {
1057 value,
1058 array,
1059 index,
1060 ..
1061 } => {
1062 self.drop_pending_call();
1063 let v = self.read_nest(*value);
1064 let a = self.read_nest(*array);
1065 let i = self.read_nest(*index);
1066 let target = Expr::ArrayIndex {
1067 array: Box::new(a),
1068 index: Box::new(i),
1069 };
1070 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
1071 target: Box::new(target),
1072 op: AssignOp::Plain,
1073 value: Box::new(v),
1074 }));
1075 }
1076 InsnKind::IGet {
1077 dst,
1078 obj,
1079 field_idx,
1080 } => {
1081 self.drop_pending_call();
1082 let (cls, name, ty) = self.env.field_ref(*field_idx);
1083 let owner = self.read_nest(*obj);
1084 let owner_opt = self.owner_expr(owner, &cls);
1085 self.write(
1086 *dst,
1087 Expr::Field {
1088 owner: owner_opt,
1089 cls,
1090 name,
1091 ty: TypeRef::J(ty),
1092 is_static: false,
1093 },
1094 ins.pc,
1095 false,
1096 );
1097 }
1098 InsnKind::IPut {
1099 value,
1100 obj,
1101 field_idx,
1102 } => {
1103 self.drop_pending_call();
1104 let (cls, name, ty) = self.env.field_ref(*field_idx);
1105 let v = null_in_obj_ctx(self.read_nest(*value), &ty);
1106 let owner = self.read_nest(*obj);
1107 let owner_opt = self.owner_expr(owner, &cls);
1108 let target = Expr::Field {
1109 owner: owner_opt,
1110 cls,
1111 name,
1112 ty: TypeRef::J(ty),
1113 is_static: false,
1114 };
1115 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
1116 target: Box::new(target),
1117 op: AssignOp::Plain,
1118 value: Box::new(v),
1119 }));
1120 }
1121 InsnKind::SGet { dst, field_idx } => {
1122 self.drop_pending_call();
1123 let (cls, name, ty) = self.env.field_ref(*field_idx);
1124 self.write(
1125 *dst,
1126 Expr::Field {
1127 owner: None,
1128 cls,
1129 name,
1130 ty: TypeRef::J(ty),
1131 is_static: true,
1132 },
1133 ins.pc,
1134 false,
1135 );
1136 }
1137 InsnKind::SPut { value, field_idx } => {
1138 self.drop_pending_call();
1139 let (cls, name, ty) = self.env.field_ref(*field_idx);
1140 let v = null_in_obj_ctx(self.read_nest(*value), &ty);
1141 let target = Expr::Field {
1142 owner: None,
1143 cls,
1144 name,
1145 ty: TypeRef::J(ty),
1146 is_static: true,
1147 };
1148 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
1149 target: Box::new(target),
1150 op: AssignOp::Plain,
1151 value: Box::new(v),
1152 }));
1153 }
1154 InsnKind::Invoke {
1155 kind,
1156 regs,
1157 method_idx,
1158 } => {
1159 self.drop_pending_call();
1160 self.do_invoke(*kind, regs, *method_idx, ins.pc)?;
1161 }
1162 InsnKind::InvokeCustom {
1163 call_site_idx,
1164 regs,
1165 } => {
1166 self.drop_pending_call();
1167 let e = self.build_invoke_custom(*call_site_idx, regs, ins.pc);
1168 self.pending_call = Some(e);
1169 }
1170 InsnKind::Un {
1171 dst,
1172 src,
1173 op,
1174 from: _,
1175 to,
1176 } => {
1177 let e = self.read_nest(*src);
1178 let ne = match op {
1179 ddc_dex::insn::UnArith::Neg => Expr::Un {
1180 op: UnOp::Neg,
1181 e: Box::new(e),
1182 },
1183 ddc_dex::insn::UnArith::Not => Expr::Un {
1184 op: UnOp::BitNot,
1185 e: Box::new(e),
1186 },
1187 ddc_dex::insn::UnArith::Conv => Expr::Cast {
1188 ty: TypeRef::J(primitive_type(*to)),
1189 e: Box::new(e),
1190 },
1191 };
1192 let wide = matches!(*to, 'J' | 'D');
1193 self.write(*dst, ne, ins.pc, wide);
1194 }
1195 InsnKind::Bin { op, dst, a, b, ty } => {
1196 let l = self.read_nest(*a);
1197 let r = self.read_nest(*b);
1198 let jt = primitive_type(*ty);
1199 let e = Expr::Bin {
1200 op: Self::arith_op(*op),
1201 l: Box::new(l),
1202 r: Box::new(r),
1203 ty: Some(TypeRef::J(jt)),
1204 };
1205 let wide = matches!(*ty, 'J' | 'D');
1206 self.write(*dst, e, ins.pc, wide);
1207 }
1208 InsnKind::BinLit {
1209 op,
1210 dst,
1211 a,
1212 lit,
1213 rsub,
1214 } => {
1215 let av = self.read_nest(*a);
1216 let e = if *rsub {
1217 Expr::Bin {
1218 op: BinOp::Sub,
1219 l: Box::new(Expr::Const(ConstVal::Int(*lit))),
1220 r: Box::new(av),
1221 ty: Some(TypeRef::J(JavaType::Int)),
1222 }
1223 } else {
1224 Expr::Bin {
1225 op: Self::arith_op(*op),
1226 l: Box::new(av),
1227 r: Box::new(Expr::Const(ConstVal::Int(*lit))),
1228 ty: Some(TypeRef::J(JavaType::Int)),
1229 }
1230 };
1231 self.write(*dst, e, ins.pc, false);
1232 }
1233 InsnKind::ConstMethodHandle { dst, handle_idx } => {
1234 self.drop_pending_call();
1235 let e = self.method_handle_expr(*handle_idx);
1236 self.write(*dst, e, ins.pc, false);
1237 }
1238 InsnKind::ConstMethodType { dst, proto_idx } => {
1239 self.drop_pending_call();
1240 let proto = self.env.dex.proto(*proto_idx);
1241 let desc = format!(
1242 "({}){}",
1243 self.env
1244 .dex
1245 .proto_params(*proto_idx)
1246 .iter()
1247 .map(|&t| self.env.dex.type_name(t))
1248 .collect::<Vec<_>>()
1249 .join(""),
1250 self.env.dex.type_name(proto.return_type_idx)
1251 );
1252 self.write(*dst, Expr::Raw(desc), ins.pc, false);
1253 }
1254 }
1255 }
1256
1257 let term = self.block_term(ins);
1259
1260 self.drop_pending_call();
1264 self.materialize_impure_at_exit();
1265
1266 let out = OutState {
1270 regs: std::mem::take(&mut self.regs),
1271 write_pc: std::mem::take(&mut self.write_pc),
1272 };
1273 Ok((
1274 BlockResult {
1275 stmts: std::mem::take(&mut self.stmts),
1276 out_stack: Vec::new(),
1277 term,
1278 },
1279 out,
1280 ))
1281 }
1282
1283 fn do_invoke(
1284 &mut self,
1285 kind: InvokeKind,
1286 regs: &[u16],
1287 method_idx: u32,
1288 pc: u32,
1289 ) -> BResult<()> {
1290 let (cls, name, md) = self.env.method_ref(method_idx);
1291 if cls.as_ref() == "java/lang/StringBuilder" || cls.as_ref() == "java/lang/StringBuffer" {
1292 *self.mflags = self.mflags.with_sb();
1293 }
1294 let is_static = matches!(kind, InvokeKind::Static);
1295 let receiver_reg = if is_static {
1296 None
1297 } else {
1298 regs.first().copied()
1299 };
1300 let arg_regs: Vec<u16> = if is_static {
1301 regs.to_vec()
1302 } else {
1303 regs.iter().skip(1).copied().collect()
1304 };
1305 let mut args: Vec<Expr> = Vec::with_capacity(md.args.len());
1306 for (i, at) in md.args.iter().enumerate() {
1307 let r = arg_regs.get(i).copied().unwrap_or(0);
1308 args.push(null_in_obj_ctx(self.read_nest(r), at));
1309 }
1310 let recv_expr = receiver_reg.map(|r| self.read_nest(r));
1311
1312 if name.as_ref() == "<init>" && matches!(kind, InvokeKind::Direct) {
1314 let recv_reg = receiver_reg.unwrap_or(0);
1315 let recv_state = self
1316 .regs
1317 .get(recv_reg as usize)
1318 .cloned()
1319 .unwrap_or(Reg::Undef);
1320 if let Reg::Pending(Expr::New {
1321 raw: true, cls: nc, ..
1322 }) = &recv_state
1323 {
1324 if nc == &cls {
1325 let ty = TypeRef::J(JavaType::Object(cls.clone()));
1326 self.write(
1327 recv_reg,
1328 Expr::New {
1329 cls: cls.clone(),
1330 ty,
1331 args,
1332 raw: false,
1333 },
1334 pc,
1335 false,
1336 );
1337 return Ok(());
1338 }
1339 }
1340 let owner_expr_v = recv_expr.unwrap_or(Expr::This);
1341 let owner_expr_v = if !self.env.is_static
1348 && matches!(&owner_expr_v, Expr::Local { var: 0, .. })
1349 {
1350 Expr::This
1351 } else {
1352 owner_expr_v
1353 };
1354 let is_super = cls.as_ref() != self.env.class_name.as_str();
1355 let owner = if matches!(owner_expr_v, Expr::This) && is_super {
1356 None
1357 } else {
1358 Some(Box::new(owner_expr_v))
1359 };
1360 let call = Expr::Method {
1361 owner,
1362 cls,
1363 name: "<init>".into(),
1364 desc: md,
1365 args,
1366 is_static: false,
1367 is_interface: false,
1368 is_special: true,
1369 is_super,
1370 is_dynamic: false,
1371 type_args: vec![],
1372 };
1373 self.stmts.push(Stmt::ExprStmt(call));
1374 return Ok(());
1375 }
1376
1377 let is_interface = matches!(kind, InvokeKind::Interface);
1378 let is_super = match kind {
1379 InvokeKind::Super => true,
1380 InvokeKind::Direct if !is_static => cls.as_ref() != self.env.class_name.as_str(),
1381 _ => false,
1382 };
1383 let owner: Option<Box<Expr>> = match recv_expr {
1384 None => None,
1385 Some(o) => {
1386 if is_super && matches!(o, Expr::This) {
1387 None
1388 } else {
1389 self.owner_expr(o, &cls)
1390 }
1391 }
1392 };
1393 let is_dynamic = matches!(kind, InvokeKind::Polymorphic);
1394 let call = Expr::Method {
1395 owner,
1396 cls,
1397 name,
1398 desc: md,
1399 args,
1400 is_static,
1401 is_interface,
1402 is_special: matches!(kind, InvokeKind::Direct),
1403 is_super,
1404 is_dynamic,
1405 type_args: vec![],
1406 };
1407 self.pending_call = Some(call);
1408 Ok(())
1409 }
1410
1411 fn method_handle_expr(&mut self, handle_idx: u32) -> Expr {
1414 let Some(h) = self.env.dex.method_handle(handle_idx) else {
1415 return Expr::Raw(format!("methodHandle@{}", handle_idx));
1416 };
1417 if h.is_field {
1418 let f = self.env.dex.field(h.target_id);
1419 let cls = self.env.dex.class_name(f.class_idx);
1420 let name = self.env.dex.string(f.name_idx).to_string();
1421 return Expr::Raw(format!("{}::{}", cls.replace('/', "."), name));
1422 }
1423 let m = self.env.dex.method(h.target_id);
1424 let cls = self.env.dex.class_name(m.class_idx);
1425 let name = self.env.dex.string(m.name_idx).to_string();
1426 Expr::Raw(format!("{}::{}", cls.replace('/', "."), name))
1427 }
1428
1429 fn build_invoke_custom(&mut self, cs_idx: u32, regs: &[u16], pc: u32) -> Expr {
1431 let Some(cs) = self.env.dex.call_site(cs_idx).cloned() else {
1432 return Expr::Invokedynamic {
1433 name: format!("call-site#{}", cs_idx),
1434 desc: jdc_core::types::MethodDescriptor {
1435 args: vec![],
1436 ret: JavaType::Object("java/lang/Object".into()),
1437 },
1438 args: vec![],
1439 bsm_text: "unresolved".into(),
1440 bsm_static_args: vec![],
1441 };
1442 };
1443 let site_name = self.env.dex.string(cs.name_idx).to_string();
1444 let params: Vec<JavaType> = self
1445 .env
1446 .dex
1447 .proto_params(cs.proto_idx)
1448 .iter()
1449 .map(|&t| self.env.java_type(t))
1450 .collect();
1451 let ret = self
1452 .env
1453 .java_type(self.env.dex.proto(cs.proto_idx).return_type_idx);
1454
1455 let (bs_cls, bs_name) = match self.env.dex.method_handle(cs.bootstrap_handle) {
1456 Some(h) if !h.is_field => {
1457 let m = self.env.dex.method(h.target_id);
1458 (
1459 self.env.dex.class_name(m.class_idx),
1460 self.env.dex.string(m.name_idx).to_string(),
1461 )
1462 }
1463 _ => (String::new(), String::new()),
1464 };
1465
1466 let mut args: Vec<Expr> = Vec::with_capacity(params.len().min(regs.len()));
1468 for (i, _) in params.iter().enumerate() {
1469 if let Some(&r) = regs.get(i) {
1470 args.push(self.read_nest(r));
1471 }
1472 }
1473
1474 if bs_name == "makeConcatWithConstants" {
1476 let recipe = cs
1477 .linker_args
1478 .iter()
1479 .find_map(|v| match v {
1480 ddc_dex::annotations::EncodedValue::String(s) => {
1481 Some(self.env.dex.string(*s).to_string())
1482 }
1483 _ => None,
1484 })
1485 .unwrap_or_default();
1486 return concat_from_recipe(&recipe, args);
1487 }
1488
1489 let mut impl_m = None;
1493 let mut instantiated_proto: Option<u32> = None;
1494 if bs_name == "metafactory" || bs_name == "altMetafactory" {
1495 let mut seen_handle = false;
1496 for lv in &cs.linker_args {
1497 match lv {
1498 ddc_dex::annotations::EncodedValue::MethodHandle(hi) => {
1499 if !seen_handle {
1500 seen_handle = true;
1501 if let Some(h) = self.env.dex.method_handle(*hi) {
1502 if !h.is_field {
1503 let m = self.env.dex.method(h.target_id);
1504 impl_m = Some((
1505 self.env.dex.class_name(m.class_idx).replace('/', "."),
1506 self.env.dex.string(m.name_idx).to_string(),
1507 ));
1508 }
1509 }
1510 }
1511 }
1512 ddc_dex::annotations::EncodedValue::MethodType(p) if seen_handle => {
1513 instantiated_proto = Some(*p);
1514 }
1515 _ => {}
1516 }
1517 }
1518 }
1519 let _ = pc;
1520
1521 let mut captures = Vec::new();
1523 for &r in regs.iter().skip(params.len().min(regs.len())) {
1524 captures.push(self.read_nest(r));
1525 }
1526
1527 if let Some((impl_cls, impl_name)) = impl_m {
1528 if captures.is_empty() {
1529 if impl_name.starts_with("lambda$") {
1533 let n_params = match instantiated_proto {
1537 Some(p) => self.env.dex.proto_params(p).len(),
1538 None => params.len(),
1539 };
1540 let pnames: Vec<String> = (0..n_params).map(|i| format!("a{}", i)).collect();
1541 return Expr::Raw(format!(
1542 "({}) -> {}.{}({})",
1543 pnames.join(", "),
1544 impl_cls,
1545 impl_name,
1546 pnames.join(", ")
1547 ));
1548 }
1549 return Expr::Raw(format!("{}::{}", impl_cls, impl_name));
1550 }
1551 let mut call_args = args;
1554 call_args.extend(captures);
1555 return Expr::Invokedynamic {
1556 name: format!("{} -> {}.{}", site_name, impl_cls, impl_name),
1557 desc: jdc_core::types::MethodDescriptor { args: params, ret },
1558 args: call_args,
1559 bsm_text: format!("{}::{}", bs_cls, bs_name),
1560 bsm_static_args: vec![],
1561 };
1562 }
1563
1564 Expr::Invokedynamic {
1565 name: site_name,
1566 desc: jdc_core::types::MethodDescriptor { args: params, ret },
1567 args: captures,
1568 bsm_text: format!("{}::{}", bs_cls, bs_name),
1569 bsm_static_args: vec![],
1570 }
1571 }
1572
1573 fn materialize_impure_at_exit(&mut self) {
1574 let n = self.regs.len();
1575 for r in 0..n {
1576 let st = self.regs[r].clone();
1577 let impure = match &st {
1578 Reg::PendingCall(_) => true,
1579 Reg::Pending(e) => {
1580 has_side_effects(e)
1581 && !matches!(e, Expr::New { raw: true, .. })
1582 && !matches!(e, Expr::NewArray { init: None, .. })
1583 }
1584 _ => false,
1585 };
1586 if impure {
1587 self.materialize(r as u16);
1588 }
1589 }
1590 }
1591
1592 fn block_term(&mut self, ins: &[Insn]) -> Term {
1594 let Some(last) = ins.last() else {
1595 return Term::Goto;
1596 };
1597 match &last.kind {
1598 InsnKind::Goto { .. } => Term::Goto,
1599 InsnKind::ReturnVoid => Term::Return(None),
1600 InsnKind::Return { src } => {
1601 let v = null_in_obj_ctx(self.read_term(*src), &self.env.desc.ret);
1602 Term::Return(Some(v))
1603 }
1604 InsnKind::Throw { reg } => {
1605 let mut v = self.read_term(*reg);
1607 if let Expr::Const(ConstVal::Int(0)) = v {
1608 v = Expr::Const(ConstVal::Null);
1609 }
1610 Term::Throw(v)
1611 }
1612 InsnKind::If { op, a, b, z, .. } => {
1613 let cond = if *z {
1614 let v = self.read_nest(*a);
1615 let (l, r, realop) = jdc_core::ir::build::unfold_cmp(v, Self::cmp_op(*op));
1616 Expr::Bin {
1617 op: realop,
1618 l: Box::new(l),
1619 r: Box::new(r),
1620 ty: Some(TypeRef::J(JavaType::Boolean)),
1621 }
1622 } else {
1623 let l = self.read_nest(*a);
1624 let r = self.read_nest(*b);
1625 Expr::Bin {
1626 op: Self::cmp_op(*op),
1627 l: Box::new(l),
1628 r: Box::new(r),
1629 ty: Some(TypeRef::J(JavaType::Boolean)),
1630 }
1631 };
1632 Term::Cond { cond }
1633 }
1634 InsnKind::PackedSwitch { reg, payload_pc }
1635 | InsnKind::SparseSwitch { reg, payload_pc } => {
1636 let selector = self.read_term(*reg);
1637 let default = Some(last.pc + last.size);
1638 let targets = match self.env.code.payloads.get(payload_pc) {
1639 Some(Payload::Packed { first_key, targets }) => SwitchTargets::Table {
1640 low: *first_key,
1641 targets: targets
1642 .iter()
1643 .map(|t| (last.pc as i64 + *t as i64) as u32)
1644 .collect(),
1645 },
1646 Some(Payload::Sparse { pairs }) => SwitchTargets::Lookup {
1647 pairs: pairs
1648 .iter()
1649 .map(|(k, t)| (*k, (last.pc as i64 + *t as i64) as u32))
1650 .collect(),
1651 },
1652 _ => SwitchTargets::Lookup { pairs: vec![] },
1653 };
1654 Term::Switch {
1655 selector,
1656 targets,
1657 default,
1658 }
1659 }
1660 _ => {
1662 let next = last.pc + last.size;
1663 if next < self.code_units {
1664 Term::Fallthrough
1665 } else {
1666 Term::Goto
1667 }
1668 }
1669 }
1670 }
1671}
1672
1673fn concat_from_recipe(recipe: &str, args: Vec<Expr>) -> Expr {
1677 use jdc_core::ir::expr::ConcatPart;
1678 let mut parts: Vec<ConcatPart> = Vec::new();
1679 let mut arg_i = 0usize;
1680 let mut const_buf = String::new();
1681 for c in recipe.chars() {
1682 match c {
1683 '\u{1}' => {
1684 if !const_buf.is_empty() {
1685 parts.push(ConcatPart::Const(std::mem::take(&mut const_buf)));
1686 }
1687 if arg_i < args.len() {
1688 parts.push(ConcatPart::Str(args[arg_i].clone()));
1689 arg_i += 1;
1690 }
1691 }
1692 '\u{2}' => {
1693 }
1696 c => const_buf.push(c),
1697 }
1698 }
1699 if !const_buf.is_empty() {
1700 parts.push(ConcatPart::Const(const_buf));
1701 }
1702 Expr::StringConcat(parts)
1703}
1704
1705fn expr_size(e: &Expr) -> usize {
1709 let mut n = 0usize;
1710 count_nodes(e, &mut n);
1711 n
1712}
1713
1714fn count_nodes(e: &Expr, n: &mut usize) {
1715 *n += 1;
1716 if *n > 512 {
1717 return; }
1719 match e {
1720 Expr::Un { e, .. } | Expr::Cast { e, .. } | Expr::InstanceOf { e, .. } => count_nodes(e, n),
1721 Expr::Bin { l, r, .. } => {
1722 count_nodes(l, n);
1723 count_nodes(r, n);
1724 }
1725 Expr::Cond { c, t, f } => {
1726 count_nodes(c, n);
1727 count_nodes(t, n);
1728 count_nodes(f, n);
1729 }
1730 Expr::Assign { target, value, .. } => {
1731 count_nodes(target, n);
1732 count_nodes(value, n);
1733 }
1734 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => count_nodes(e, n),
1735 Expr::Field { owner: Some(o), .. } => count_nodes(o, n),
1736 Expr::Method {
1737 owner: Some(o),
1738 args,
1739 ..
1740 } => {
1741 count_nodes(o, n);
1742 for a in args {
1743 count_nodes(a, n);
1744 }
1745 }
1746 Expr::ArrayIndex { array, index } => {
1747 count_nodes(array, n);
1748 count_nodes(index, n);
1749 }
1750 Expr::New { args, .. } => {
1751 for a in args {
1752 count_nodes(a, n);
1753 }
1754 }
1755 Expr::NewArray { dims, init, .. } => {
1756 for d in dims {
1757 count_nodes(d, n);
1758 }
1759 if let Some(v) = init {
1760 for x in v {
1761 count_nodes(x, n);
1762 }
1763 }
1764 }
1765 Expr::NewMultiArray { dims, .. } => {
1766 for d in dims {
1767 count_nodes(d, n);
1768 }
1769 }
1770 Expr::StringConcat(parts) => {
1771 for p in parts {
1772 if let ConcatPart::Str(x) = p {
1773 count_nodes(x, n);
1774 }
1775 }
1776 }
1777 _ => {}
1778 }
1779}
1780
1781fn reg_is_wide(st: &Reg, vt: &VarTable) -> bool {
1782 match st {
1783 Reg::Pending(e) | Reg::PendingCall(e) => e.type_ref().erased().is_wide(),
1784 Reg::Live(v) => vt.var(*v).ty.erased().is_wide(),
1785 Reg::WideHi => true,
1786 Reg::Undef => false,
1787 }
1788}
1789
1790fn null_in_obj_ctx(mut v: Expr, ctx_ty: &JavaType) -> Expr {
1796 if ctx_ty.is_reference() {
1797 if let Expr::Const(ConstVal::Int(0)) = v {
1798 v = Expr::Const(ConstVal::Null);
1799 }
1800 }
1801 v
1802}
1803
1804fn value_of_cmp(e: &Expr) -> Expr {
1806 if let Expr::Invokedynamic { name, args, .. } = e {
1807 if name.starts_with('\0') && args.len() == 2 {
1808 let (cls, ty) = match name.as_str() {
1809 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
1810 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
1811 _ => ("java/lang/Double", JavaType::Double),
1812 };
1813 let desc = MethodDescriptor {
1814 args: vec![ty.clone(), ty],
1815 ret: JavaType::Int,
1816 };
1817 return Expr::Method {
1818 owner: None,
1819 cls: cls.into(),
1820 name: "compare".into(),
1821 desc: std::sync::Arc::new(desc),
1822 args: args.clone(),
1823 is_static: true,
1824 is_interface: false,
1825 is_special: false,
1826 is_super: false,
1827 is_dynamic: false,
1828 type_args: vec![],
1829 };
1830 }
1831 }
1832 e.clone()
1833}
1834
1835pub fn primitive_type(c: char) -> JavaType {
1836 match c {
1837 'Z' => JavaType::Boolean,
1838 'B' => JavaType::Byte,
1839 'C' => JavaType::Char,
1840 'S' => JavaType::Short,
1841 'F' => JavaType::Float,
1842 'J' => JavaType::Long,
1843 'D' => JavaType::Double,
1844 _ => JavaType::Int,
1845 }
1846}
1847
1848fn payload_consts(elem_width: u16, size: u32, data: &[u8]) -> Vec<Expr> {
1851 let n = (size as usize).min(data.len() / elem_width.max(1) as usize);
1852 let mut out = Vec::with_capacity(n);
1853 for i in 0..n {
1854 let off = i * elem_width as usize;
1855 let e = match elem_width {
1856 1 => Expr::Const(ConstVal::Int(data[off] as i8 as i32)),
1857 2 => {
1858 let v = i16::from_le_bytes([data[off], data[off + 1]]);
1859 Expr::Const(ConstVal::Int(v as i32))
1860 }
1861 4 => {
1862 let mut b = [0u8; 4];
1863 b.copy_from_slice(&data[off..off + 4]);
1864 Expr::Const(ConstVal::Int(i32::from_le_bytes(b)))
1865 }
1866 8 => {
1867 let mut b = [0u8; 8];
1868 b.copy_from_slice(&data[off..off + 8]);
1869 Expr::Const(ConstVal::Long(i64::from_le_bytes(b)))
1870 }
1871 _ => Expr::Const(ConstVal::Int(0)),
1872 };
1873 out.push(e);
1874 }
1875 out
1876}
1877
1878pub fn entry_regs(vt: &mut VarTable, env: &MethodEnv, param_names: &[Option<String>]) -> Vec<Reg> {
1880 let code = env.code;
1881 let n = code.registers_size as usize;
1882 let mut regs = vec![Reg::Undef; n.max(1)];
1883 let ins = code.ins_size.min(code.registers_size) as usize;
1884 let mut reg = n.saturating_sub(ins);
1885 let mut pidx = 0;
1886 if !env.is_static && reg < n {
1887 let id = vt.vars.len() as u32;
1889 vt.vars.push(VarInfo {
1890 id,
1891 slot: reg as u16,
1892 name: "this".into(),
1893 ty: TypeRef::J(JavaType::Object(env.class_name.as_str().into())),
1894 is_param: true,
1895 range_start: 0,
1896 range_end: u16::MAX,
1897 synthetic_name: false,
1898 });
1899 while vt.by_slot.len() <= reg {
1900 vt.by_slot.push(Vec::new());
1901 }
1902 vt.by_slot[reg].push((0, u16::MAX, id));
1903 regs[reg] = Reg::Live(id);
1904 reg += 1;
1905 pidx += 1;
1906 }
1907 for (i, arg) in env.desc.args.iter().enumerate() {
1908 if reg >= n {
1909 break;
1910 }
1911 let dbg = param_names.get(i).and_then(|o| o.clone());
1912 let named = dbg.is_some();
1913 let name = dbg
1918 .map(|d| crate::classdec::java_ident(&d).into_owned())
1919 .unwrap_or_else(|| format!("p{}", pidx));
1920 let id = vt.vars.len() as u32;
1921 vt.vars.push(VarInfo {
1922 id,
1923 slot: reg as u16,
1924 name,
1925 ty: TypeRef::J(arg.clone()),
1926 is_param: true,
1927 range_start: 0,
1928 range_end: u16::MAX,
1929 synthetic_name: !named,
1930 });
1931 while vt.by_slot.len() <= reg {
1932 vt.by_slot.push(Vec::new());
1933 }
1934 vt.by_slot[reg].push((0, u16::MAX, id));
1935 regs[reg] = Reg::Live(id);
1936 reg += if arg.is_wide() { 2 } else { 1 };
1937 pidx += 1;
1938 }
1939 regs
1940}