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 (
109 std::sync::Arc::from(self.dex.class_name(f.class_idx).as_str()),
110 std::sync::Arc::from(self.dex.string(f.name_idx)),
111 self.java_type(f.type_idx),
112 )
113 }
114 pub fn method_ref(
115 &self,
116 idx: u32,
117 ) -> (
118 std::sync::Arc<str>,
119 std::sync::Arc<str>,
120 std::sync::Arc<MethodDescriptor>,
121 ) {
122 let m = self.dex.method(idx);
126 (
127 std::sync::Arc::from(self.dex.class_name(m.class_idx).as_str()),
128 std::sync::Arc::from(self.dex.string(m.name_idx)),
129 self.pool.proto_desc_parsed(self.di, m.proto_idx),
130 )
131 }
132}
133
134#[derive(Debug, Clone, PartialEq)]
136pub enum Reg {
137 Undef,
138 Live(u32),
139 Pending(Expr),
140 PendingCall(Expr),
141 WideHi,
142}
143
144#[derive(Debug, Clone)]
146pub struct OutState {
147 pub regs: Vec<Reg>,
148 pub write_pc: Vec<u32>,
151}
152
153fn src_regs(kind: &InsnKind) -> Vec<u16> {
157 let mut out: Vec<u16> = Vec::with_capacity(4);
158 let mut push = |r: u16| out.push(r);
159 match kind {
160 InsnKind::Nop
161 | InsnKind::ReturnVoid
162 | InsnKind::Unknown
163 | InsnKind::NewInstance { .. }
164 | InsnKind::SGet { .. } => {}
165 InsnKind::Move { src, .. }
166 | InsnKind::Un { src, .. }
167 | InsnKind::Return { src }
168 | InsnKind::ArrayLength { src, .. } => push(*src),
169 InsnKind::MonitorEnter { reg }
170 | InsnKind::MonitorExit { reg }
171 | InsnKind::Throw { reg }
172 | InsnKind::CheckCast { reg, .. }
173 | InsnKind::FillArrayData { reg, .. }
174 | InsnKind::PackedSwitch { reg, .. }
175 | InsnKind::SparseSwitch { reg, .. } => push(*reg),
176 InsnKind::MoveResult { .. } | InsnKind::MoveException { .. } => {}
177 InsnKind::Const { .. }
178 | InsnKind::ConstClass { .. }
179 | InsnKind::ConstString { .. }
180 | InsnKind::ConstMethodHandle { .. }
181 | InsnKind::ConstMethodType { .. } => {}
182 InsnKind::InstanceOf { src, .. } => {
183 push(*src);
184 }
185 InsnKind::NewArray { size, .. } => {
186 push(*size);
187 }
188 InsnKind::FilledNewArray { regs, .. } | InsnKind::InvokeCustom { regs, .. } => {
189 for r in regs {
190 push(*r);
191 }
192 }
193 InsnKind::Goto { .. } => {}
194 InsnKind::Cmp { a, b, .. } | InsnKind::Bin { a, b, .. } => {
195 push(*a);
196 push(*b);
197 }
198 InsnKind::BinLit { a, .. } => push(*a),
199 InsnKind::If { a, b, z, .. } => {
200 push(*a);
201 if !*z {
202 push(*b);
203 }
204 }
205 InsnKind::AGet { array, index, .. } => {
206 push(*array);
207 push(*index);
208 }
209 InsnKind::APut {
210 value,
211 array,
212 index,
213 ..
214 } => {
215 push(*value);
216 push(*array);
217 push(*index);
218 }
219 InsnKind::IGet { obj, .. } => {
220 push(*obj);
221 }
222 InsnKind::IPut { value, obj, .. } => {
223 push(*value);
224 push(*obj);
225 }
226 InsnKind::SPut { value, .. } => {
227 push(*value);
228 }
229 InsnKind::Invoke { regs, .. } => {
230 for r in regs {
231 push(*r);
232 }
233 }
234 }
235 out
236}
237
238fn dst_regs(kind: &InsnKind) -> Vec<u16> {
241 let mut out: Vec<u16> = Vec::with_capacity(2);
242 let mut push = |r: u16| out.push(r);
243 match kind {
244 InsnKind::Move { dst, .. }
245 | InsnKind::MoveResult { dst }
246 | InsnKind::MoveException { dst }
247 | InsnKind::Const { dst, .. }
248 | InsnKind::ConstClass { dst, .. }
249 | InsnKind::ConstString { dst, .. }
250 | InsnKind::ConstMethodHandle { dst, .. }
251 | InsnKind::ConstMethodType { dst, .. }
252 | InsnKind::NewInstance { dst, .. }
253 | InsnKind::NewArray { dst, .. }
254 | InsnKind::InstanceOf { dst, .. }
255 | InsnKind::ArrayLength { dst, .. }
256 | InsnKind::Cmp { dst, .. }
257 | InsnKind::AGet { dst, .. }
258 | InsnKind::IGet { dst, .. }
259 | InsnKind::SGet { dst, .. }
260 | InsnKind::Un { dst, .. }
261 | InsnKind::Bin { dst, .. }
262 | InsnKind::BinLit { dst, .. } => push(*dst),
263 _ => {}
264 }
265 out
266}
267
268fn compute_final_reads(ins: &[Insn]) -> jdc_core::FxHashSet<(u32, u16)> {
273 use jdc_core::FxHashMap as HashMap;
274 let mut events: HashMap<u16, Vec<(u32, bool)>> = HashMap::default();
276 for i in ins {
277 for r in src_regs(&i.kind) {
278 events.entry(r).or_default().push((i.pc, true));
279 }
280 for r in dst_regs(&i.kind) {
281 events.entry(r).or_default().push((i.pc, false));
282 }
283 }
284 let mut out = jdc_core::FxHashSet::default();
285 for (reg, evs) in events {
286 let mut dedup: Vec<(u32, bool)> = Vec::with_capacity(evs.len());
287 for (pc, is_read) in evs {
288 if dedup.last() != Some(&(pc, is_read)) {
289 dedup.push((pc, is_read));
290 }
291 }
292 for (i, &(pc, is_read)) in dedup.iter().enumerate() {
293 if !is_read {
294 continue;
295 }
296 let mut final_ = true;
299 if let Some(&(_, is_read2)) = dedup.get(i + 1) {
300 if is_read2 {
301 final_ = false;
302 }
303 }
304 if final_ {
305 out.insert((pc, reg));
306 }
307 }
308 }
309 out
310}
311
312pub struct Lifter<'a> {
313 pub env: &'a MethodEnv<'a>,
314 pub vt: &'a mut VarTable,
315 block_id: usize,
317 stable: &'a mut jdc_core::FxHashMap<(usize, u16, u64), u32>,
324 regs: Vec<Reg>,
325 write_pc: Vec<u32>,
326 stmts: Vec<Stmt>,
327 pending_call: Option<Expr>,
328 code_units: u32,
329 cur_pc: u32,
332 final_read: jdc_core::FxHashSet<(u32, u16)>,
339 mflags: &'a mut MethodFlags,
343}
344
345impl<'a> Lifter<'a> {
346 pub fn new(
347 env: &'a MethodEnv<'a>,
348 vt: &'a mut VarTable,
349 in_regs: Vec<Reg>,
350 block_id: usize,
351 stable: &'a mut jdc_core::FxHashMap<(usize, u16, u64), u32>,
352 mflags: &'a mut MethodFlags,
353 ) -> Lifter<'a> {
354 let n = env.code.registers_size as usize;
355 let n = n.max(in_regs.len()).max(1);
356 let mut regs = in_regs;
357 regs.resize(n, Reg::Undef);
358 Lifter {
359 env,
360 vt,
361 regs,
362 write_pc: vec![0; n],
363 stmts: Vec::new(),
364 block_id,
365 stable,
366 pending_call: None,
367 code_units: env.code_units,
368 cur_pc: 0,
369 final_read: jdc_core::FxHashSet::default(),
370 mflags,
371 }
372 }
373
374 fn ty_key(ty: &TypeRef) -> u64 {
382 use std::hash::{Hash, Hasher};
383 fn jt_key(j: &JavaType, h: &mut jdc_core::fx::FxHasher) {
384 match j {
385 JavaType::Void => 1u64.hash(h),
386 JavaType::Boolean => 2u64.hash(h),
387 JavaType::Byte => 3u64.hash(h),
388 JavaType::Char => 4u64.hash(h),
389 JavaType::Short => 5u64.hash(h),
390 JavaType::Int => 6u64.hash(h),
391 JavaType::Float => 7u64.hash(h),
392 JavaType::Long => 8u64.hash(h),
393 JavaType::Double => 9u64.hash(h),
394 JavaType::Object(n) => {
395 10u64.hash(h);
396 n.hash(h);
397 }
398 JavaType::Array(inner) => {
399 11u64.hash(h);
400 jt_key(inner, h);
401 }
402 }
403 }
404 let mut h = jdc_core::fx::FxHasher::default();
405 match ty {
406 TypeRef::J(j) => jt_key(j, &mut h),
407 TypeRef::G(_) => 99u64.hash(&mut h),
408 }
409 h.finish()
410 }
411
412 fn fresh_var(&mut self, slot: u16, ty: TypeRef) -> u32 {
413 let key = (self.block_id, slot, Self::ty_key(&ty));
423 if let Some(&id) = self.stable.get(&key) {
424 return id;
425 }
426 if let Some(name) = self.debug_name(slot, &ty) {
431 let tkey = Self::ty_key(&ty);
437 if let Some(id) = self
438 .vt
439 .vars
440 .iter()
441 .find(|v| {
442 v.slot == slot
443 && !v.synthetic_name
444 && v.name == name
445 && Self::ty_key(&v.ty) == tkey
446 })
447 .map(|v| v.id)
448 {
449 self.stable.insert(key, id);
450 return id;
451 }
452 let id = self.vt.vars.len() as u32;
453 self.stable.insert(key, id);
454 self.push_var(id, slot, name, ty, false);
455 return id;
456 }
457 let id = self.vt.vars.len() as u32;
458 let name = format!("v{}", id);
459 self.stable.insert(key, id);
460 self.push_var(id, slot, name, ty, true);
461 id
462 }
463
464 fn push_var(&mut self, id: u32, slot: u16, name: String, ty: TypeRef, synthetic: bool) {
465 self.vt.vars.push(VarInfo {
466 id,
467 slot,
468 name,
469 ty,
470 is_param: false,
471 range_start: 0,
472 range_end: u16::MAX,
473 synthetic_name: synthetic,
474 });
475 while self.vt.by_slot.len() <= slot as usize {
476 self.vt.by_slot.push(Vec::new());
477 }
478 self.vt.by_slot[slot as usize].push((0, u16::MAX, id));
479 }
480
481 fn debug_name(&self, slot: u16, ty: &TypeRef) -> Option<String> {
486 let locals = &self.env.debug_locals;
487 if locals.is_empty() {
488 return None;
489 }
490 let want = ty.erased();
491 let covers = |pc: u32| {
492 locals.iter().find(|l| {
493 l.reg == slot
494 && pc >= l.start
495 && pc < l.end
496 && l.ty.as_deref().map(|d| crate::desc_type(d) == want).unwrap_or(true)
501 })
502 };
503 let hit = covers(self.cur_pc).or_else(|| {
508 let wpc = *self.write_pc.get(slot as usize)?;
509 if wpc == 0 {
510 None
511 } else {
512 covers(wpc)
513 }
514 })?;
515 let name = crate::classdec::java_ident(&hit.name);
516 if name.is_empty() {
517 return None;
518 }
519 Some(name.into_owned())
520 }
521
522 fn local_expr(&self, v: u32) -> Expr {
523 Expr::Local {
524 var: v,
525 ty: self.vt.var(v).ty.clone(),
526 }
527 }
528
529 #[track_caller]
533 fn read_nest(&mut self, r: u16) -> Expr {
534 match self.regs.get(r as usize).cloned().unwrap_or(Reg::Undef) {
535 Reg::Undef | Reg::WideHi => {
536 let ty = TypeRef::J(JavaType::Int);
540 let v = self.fresh_var(r, ty);
541 if (r as usize) < self.regs.len() {
542 self.regs[r as usize] = Reg::Live(v);
543 }
544 self.local_expr(v)
545 }
546 Reg::Live(v) => self.local_expr(v),
547 Reg::Pending(e) => {
548 let alloc = matches!(&e, Expr::NewArray { .. })
560 || (matches!(&e, Expr::New { raw: false, .. })
561 && !self.final_read.contains(&(self.cur_pc, r)));
562 if alloc {
563 let v = self.materialize(r);
564 self.local_expr(v)
565 } else {
566 if matches!(&e, Expr::NewArray { .. })
577 || matches!(&e, Expr::New { raw: false, .. })
578 {
579 if let Some(slot) = self.regs.get_mut(r as usize) {
580 *slot = Reg::Undef;
581 }
582 }
583 e
584 }
585 }
586 Reg::PendingCall(_) => {
587 let v = self.materialize(r);
588 self.local_expr(v)
589 }
590 }
591 }
592
593 fn read_term(&mut self, r: u16) -> Expr {
597 match self.regs.get(r as usize).cloned().unwrap_or(Reg::Undef) {
598 Reg::PendingCall(e) | Reg::Pending(e) => {
599 if (r as usize) < self.regs.len() {
600 self.regs[r as usize] = Reg::Undef;
601 }
602 self.pending_call = None;
603 e
604 }
605 _ => self.read_nest(r),
606 }
607 }
608
609 fn write(&mut self, r: u16, e: Expr, pc: u32, wide: bool) {
610 if r as usize >= self.regs.len() {
611 return;
612 }
613 self.write_pc[r as usize] = pc;
614 if expr_size(&e) > 32 {
619 self.write_pc[r as usize] = pc;
620 let v = self.materialize_value(r, e);
621 if (r as usize) < self.regs.len() {
622 self.regs[r as usize] = Reg::Live(v);
623 }
624 if wide {
625 self.mark_wide_hi(r + 1, pc);
626 }
627 return;
628 }
629 self.regs[r as usize] = Reg::Pending(e);
630 if wide {
631 self.mark_wide_hi(r + 1, pc);
632 }
633 }
634
635 fn materialize_value(&mut self, r: u16, e: Expr) -> u32 {
638 let e = value_of_cmp(&e);
639 let ty = e.type_ref();
640 let v = self.fresh_var(r, ty);
644 self.stmts.push(Stmt::LocalDef {
645 var: v,
646 init: Some(e),
647 is_final: false,
648 force_type: true,
649 });
650 v
651 }
652
653 fn write_call_result(&mut self, r: u16, e: Expr, pc: u32) {
656 if r as usize >= self.regs.len() {
657 return;
658 }
659 self.write_pc[r as usize] = pc;
660 self.regs[r as usize] = Reg::PendingCall(e);
661 }
662
663 fn mark_wide_hi(&mut self, r: u16, pc: u32) {
664 if (r as usize) < self.regs.len() {
665 self.regs[r as usize] = Reg::WideHi;
666 self.write_pc[r as usize] = pc;
667 }
668 }
669
670 fn materialize(&mut self, r: u16) -> u32 {
672 let cur = self.regs.get(r as usize).cloned().unwrap_or(Reg::Undef);
673 let e = match cur {
674 Reg::Pending(e) | Reg::PendingCall(e) => e,
675 Reg::Live(v) => return v,
676 _ => {
679 let v = self.fresh_var(r, TypeRef::J(JavaType::Object("java/lang/Object".into())));
680 self.stmts.push(Stmt::LocalDef {
681 var: v,
682 init: None,
683 is_final: false,
684 force_type: false,
685 });
686 if (r as usize) < self.regs.len() {
687 self.regs[r as usize] = Reg::Live(v);
688 }
689 return v;
690 }
691 };
692 let e = value_of_cmp(&e);
694 let ty = e.type_ref();
695 let v = self.fresh_var(r, ty);
699 self.stmts.push(Stmt::LocalDef {
700 var: v,
701 init: Some(e),
702 is_final: false,
703 force_type: true,
704 });
705 if (r as usize) < self.regs.len() {
706 self.regs[r as usize] = Reg::Live(v);
707 }
708 v
709 }
710
711 fn drop_pending_call(&mut self) {
713 if let Some(e) = self.pending_call.take() {
714 let e = value_of_cmp(&e);
715 if has_side_effects(&e) {
716 self.stmts.push(Stmt::ExprStmt(e));
717 }
718 }
719 }
720
721 fn owner_expr(&self, obj: Expr, cls: &str) -> Option<Box<Expr>> {
724 if cls == self.env.class_name && matches!(obj, Expr::This) {
725 return None;
726 }
727 Some(Box::new(obj))
728 }
729
730 fn arith_op(op: ArithOp) -> BinOp {
731 match op {
732 ArithOp::Add => BinOp::Add,
733 ArithOp::Sub => BinOp::Sub,
734 ArithOp::Mul => BinOp::Mul,
735 ArithOp::Div => BinOp::Div,
736 ArithOp::Rem => BinOp::Rem,
737 ArithOp::And => BinOp::And,
738 ArithOp::Or => BinOp::Or,
739 ArithOp::Xor => BinOp::Xor,
740 ArithOp::Shl => BinOp::Shl,
741 ArithOp::Shr => BinOp::Shr,
742 ArithOp::Ushr => BinOp::Ushr,
743 }
744 }
745
746 fn cmp_op(op: CmpOp) -> BinOp {
747 match op {
748 CmpOp::Eq => BinOp::Eq,
749 CmpOp::Ne => BinOp::Ne,
750 CmpOp::Lt => BinOp::Lt,
751 CmpOp::Ge => BinOp::Ge,
752 CmpOp::Gt => BinOp::Gt,
753 CmpOp::Le => BinOp::Le,
754 }
755 }
756
757 fn cmp_sentinel(kind: CmpKind, l: Expr, r: Expr) -> Expr {
758 let name = match kind {
759 CmpKind::CmplF => "\0cmpl-float",
760 CmpKind::CmpgF => "\0cmpg-float",
761 CmpKind::CmplD => "\0cmpl-double",
762 CmpKind::CmpgD => "\0cmpg-double",
763 CmpKind::CmpJ => "\0cmp-long",
764 };
765 Expr::Invokedynamic {
766 name: name.into(),
767 desc: MethodDescriptor {
768 args: vec![],
769 ret: JavaType::Int,
770 },
771 args: vec![l, r],
772 bsm_text: String::new(),
773 bsm_static_args: vec![],
774 }
775 }
776
777 pub fn build_block(
782 mut self,
783 ins: &[Insn],
784 handler_types: &[Option<std::sync::Arc<str>>],
785 ) -> BResult<(BlockResult, OutState)> {
786 let payloads = &self.env.code.payloads;
787 self.final_read = compute_final_reads(ins);
797 for ins in ins {
798 self.cur_pc = ins.pc;
799 match &ins.kind {
800 InsnKind::Nop => {}
801 InsnKind::Unknown => {
802 return Err(format!(
803 "unsupported opcode {:#04x} ({}) at pc {}",
804 ins.op,
805 ddc_dex::insn::op_name(ins.op),
806 ins.pc
807 ));
808 }
809 InsnKind::Move { dst, src } => {
810 let src_state = self.regs.get(*src as usize).cloned().unwrap_or(Reg::Undef);
813 if matches!(src_state, Reg::PendingCall(_))
814 || matches!(&src_state, Reg::Pending(e) if has_side_effects(e))
815 {
816 self.materialize(*src);
817 }
818 let st = self.regs.get(*src as usize).cloned().unwrap_or(Reg::Undef);
819 let wide = reg_is_wide(&st, self.vt);
820 if (*dst as usize) < self.regs.len() {
821 self.regs[*dst as usize] = st;
822 self.write_pc[*dst as usize] =
823 self.write_pc.get(*src as usize).copied().unwrap_or(0);
824 }
825 if wide {
826 self.mark_wide_hi(*dst + 1, ins.pc);
827 }
828 }
829 InsnKind::MoveResult { dst } => {
830 if let Some(e) = self.pending_call.take() {
831 let wide =
832 matches!(e.type_ref().erased(), JavaType::Long | JavaType::Double);
833 self.write_call_result(*dst, e, ins.pc);
834 if wide {
835 self.mark_wide_hi(*dst + 1, ins.pc);
836 }
837 } else {
838 let v = self.fresh_var(*dst, TypeRef::J(JavaType::Int));
840 if (*dst as usize) < self.regs.len() {
841 self.regs[*dst as usize] = Reg::Live(v);
842 }
843 }
844 }
845 InsnKind::MoveException { dst } => {
846 let ty = handler_types
847 .iter()
848 .flatten()
849 .next()
850 .map(|t| TypeRef::J(JavaType::Object(t.clone())))
851 .unwrap_or_else(|| {
852 TypeRef::J(JavaType::Object("java/lang/Throwable".into()))
853 });
854 let v = self.fresh_var(*dst, ty);
855 self.stmts.push(Stmt::LocalDef {
856 var: v,
857 init: None,
858 is_final: false,
859 force_type: true,
860 });
861 if (*dst as usize) < self.regs.len() {
862 self.regs[*dst as usize] = Reg::Live(v);
863 }
864 }
865 InsnKind::ReturnVoid
866 | InsnKind::Return { .. }
867 | InsnKind::Throw { .. }
868 | InsnKind::Goto { .. }
869 | InsnKind::PackedSwitch { .. }
870 | InsnKind::SparseSwitch { .. }
871 | InsnKind::If { .. } => {}
872 InsnKind::Const { dst, val, wide } => {
873 let e = if *wide {
874 Expr::Const(ConstVal::Long(*val))
875 } else {
876 Expr::Const(ConstVal::Int(*val as i32))
877 };
878 self.write(*dst, e, ins.pc, *wide);
879 }
880 InsnKind::ConstString { dst, str_idx } => {
881 let s = std::sync::Arc::from(self.env.dex.string(*str_idx));
882 self.write(*dst, Expr::Const(ConstVal::Str(s)), ins.pc, false);
883 }
884 InsnKind::ConstClass { dst, type_idx } => {
885 let ty = TypeRef::J(self.env.java_type(*type_idx));
886 self.write(*dst, Expr::Const(ConstVal::ClassLit(ty)), ins.pc, false);
887 }
888 InsnKind::MonitorEnter { reg } => {
889 let e = self.read_nest(*reg);
890 *self.mflags = self.mflags.with_monitor();
891 self.stmts.push(Stmt::MonitorEnter(e));
892 }
893 InsnKind::MonitorExit { reg } => {
894 let e = self.read_nest(*reg);
895 *self.mflags = self.mflags.with_monitor();
896 self.stmts.push(Stmt::MonitorExit(e));
897 }
898 InsnKind::CheckCast { reg, type_idx } => {
899 let e = self.read_nest(*reg);
900 let ty = TypeRef::J(self.env.java_type(*type_idx));
901 self.write(*reg, Expr::Cast { ty, e: Box::new(e) }, ins.pc, false);
902 }
903 InsnKind::InstanceOf { dst, src, type_idx } => {
904 let e = self.read_nest(*src);
905 let ty = TypeRef::J(self.env.java_type(*type_idx));
906 self.write(*dst, Expr::InstanceOf { e: Box::new(e), ty }, ins.pc, false);
907 }
908 InsnKind::ArrayLength { dst, src } => {
909 let e = self.read_nest(*src);
910 let length = Expr::Field {
911 owner: Some(Box::new(e)),
912 cls: std::sync::Arc::from(""),
913 name: "length".into(),
914 ty: TypeRef::J(JavaType::Int),
915 is_static: false,
916 };
917 self.write(*dst, length, ins.pc, false);
918 }
919 InsnKind::NewInstance { dst, type_idx } => {
920 let cls = self.env.type_name_arc(*type_idx);
921 let ty = TypeRef::J(JavaType::Object(cls.clone()));
922 self.write(
923 *dst,
924 Expr::New {
925 cls,
926 ty,
927 args: vec![],
928 raw: true,
929 },
930 ins.pc,
931 false,
932 );
933 }
934 InsnKind::NewArray {
935 dst,
936 size,
937 type_idx,
938 } => {
939 let elem_desc = self.env.type_name(*type_idx);
940 let elem = crate::desc_type(elem_desc.trim_start_matches('['));
942 let dims = self.read_nest(*size);
943 self.write(
944 *dst,
945 Expr::NewArray {
946 elem: TypeRef::J(elem),
947 dims: vec![dims],
948 trailing_dims: 0,
949 init: None,
950 },
951 ins.pc,
952 false,
953 );
954 }
955 InsnKind::FilledNewArray { regs, type_idx } => {
956 self.drop_pending_call();
957 let elem_desc = self.env.type_name(*type_idx);
958 let elem = crate::desc_type(elem_desc.trim_start_matches('['));
959 let args: Vec<Expr> = regs.iter().map(|&r| self.read_nest(r)).collect();
960 self.pending_call = Some(Expr::NewArray {
961 elem: TypeRef::J(elem),
962 dims: vec![],
963 trailing_dims: 0,
964 init: Some(args),
965 });
966 }
967 InsnKind::FillArrayData { reg, payload_pc } => {
968 if let Some(Payload::ArrayData {
969 elem_width,
970 size,
971 data,
972 }) = payloads.get(payload_pc)
973 {
974 let elems = payload_consts(*elem_width, *size, data);
975 let cur = self.regs.get(*reg as usize).cloned().unwrap_or(Reg::Undef);
976 if let Reg::Pending(Expr::NewArray { elem, init, .. }) = cur {
977 if init.is_none() {
978 self.write(
979 *reg,
980 Expr::NewArray {
981 elem,
982 dims: vec![Expr::Const(ConstVal::Int(*size as i32))],
983 trailing_dims: 0,
984 init: Some(elems),
985 },
986 ins.pc,
987 false,
988 );
989 continue;
990 }
991 }
992 let arr = self.read_nest(*reg);
994 for (i, e) in elems.into_iter().enumerate() {
995 let idx = Expr::Const(ConstVal::Int(i as i32));
996 let target = Expr::ArrayIndex {
997 array: Box::new(arr.clone()),
998 index: Box::new(idx),
999 };
1000 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
1001 target: Box::new(target),
1002 op: AssignOp::Plain,
1003 value: Box::new(e),
1004 }));
1005 }
1006 }
1007 }
1008 InsnKind::Cmp { dst, a, b, kind } => {
1009 let l = self.read_nest(*a);
1010 let r = self.read_nest(*b);
1011 *self.mflags = self.mflags.with_cmp();
1012 self.write(*dst, Self::cmp_sentinel(*kind, l, r), ins.pc, false);
1013 }
1014 InsnKind::AGet {
1015 dst, array, index, ..
1016 } => {
1017 let a = self.read_nest(*array);
1018 let i = self.read_nest(*index);
1019 self.write(
1020 *dst,
1021 Expr::ArrayIndex {
1022 array: Box::new(a),
1023 index: Box::new(i),
1024 },
1025 ins.pc,
1026 false,
1027 );
1028 }
1029 InsnKind::APut {
1030 value,
1031 array,
1032 index,
1033 ..
1034 } => {
1035 self.drop_pending_call();
1036 let v = self.read_nest(*value);
1037 let a = self.read_nest(*array);
1038 let i = self.read_nest(*index);
1039 let target = Expr::ArrayIndex {
1040 array: Box::new(a),
1041 index: Box::new(i),
1042 };
1043 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
1044 target: Box::new(target),
1045 op: AssignOp::Plain,
1046 value: Box::new(v),
1047 }));
1048 }
1049 InsnKind::IGet {
1050 dst,
1051 obj,
1052 field_idx,
1053 } => {
1054 self.drop_pending_call();
1055 let (cls, name, ty) = self.env.field_ref(*field_idx);
1056 let owner = self.read_nest(*obj);
1057 let owner_opt = self.owner_expr(owner, &cls);
1058 self.write(
1059 *dst,
1060 Expr::Field {
1061 owner: owner_opt,
1062 cls,
1063 name,
1064 ty: TypeRef::J(ty),
1065 is_static: false,
1066 },
1067 ins.pc,
1068 false,
1069 );
1070 }
1071 InsnKind::IPut {
1072 value,
1073 obj,
1074 field_idx,
1075 } => {
1076 self.drop_pending_call();
1077 let (cls, name, ty) = self.env.field_ref(*field_idx);
1078 let v = null_in_obj_ctx(self.read_nest(*value), &ty);
1079 let owner = self.read_nest(*obj);
1080 let owner_opt = self.owner_expr(owner, &cls);
1081 let target = Expr::Field {
1082 owner: owner_opt,
1083 cls,
1084 name,
1085 ty: TypeRef::J(ty),
1086 is_static: false,
1087 };
1088 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
1089 target: Box::new(target),
1090 op: AssignOp::Plain,
1091 value: Box::new(v),
1092 }));
1093 }
1094 InsnKind::SGet { dst, field_idx } => {
1095 self.drop_pending_call();
1096 let (cls, name, ty) = self.env.field_ref(*field_idx);
1097 self.write(
1098 *dst,
1099 Expr::Field {
1100 owner: None,
1101 cls,
1102 name,
1103 ty: TypeRef::J(ty),
1104 is_static: true,
1105 },
1106 ins.pc,
1107 false,
1108 );
1109 }
1110 InsnKind::SPut { value, field_idx } => {
1111 self.drop_pending_call();
1112 let (cls, name, ty) = self.env.field_ref(*field_idx);
1113 let v = null_in_obj_ctx(self.read_nest(*value), &ty);
1114 let target = Expr::Field {
1115 owner: None,
1116 cls,
1117 name,
1118 ty: TypeRef::J(ty),
1119 is_static: true,
1120 };
1121 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
1122 target: Box::new(target),
1123 op: AssignOp::Plain,
1124 value: Box::new(v),
1125 }));
1126 }
1127 InsnKind::Invoke {
1128 kind,
1129 regs,
1130 method_idx,
1131 } => {
1132 self.drop_pending_call();
1133 self.do_invoke(*kind, regs, *method_idx, ins.pc)?;
1134 }
1135 InsnKind::InvokeCustom {
1136 call_site_idx,
1137 regs,
1138 } => {
1139 self.drop_pending_call();
1140 let e = self.build_invoke_custom(*call_site_idx, regs, ins.pc);
1141 self.pending_call = Some(e);
1142 }
1143 InsnKind::Un {
1144 dst,
1145 src,
1146 op,
1147 from: _,
1148 to,
1149 } => {
1150 let e = self.read_nest(*src);
1151 let ne = match op {
1152 ddc_dex::insn::UnArith::Neg => Expr::Un {
1153 op: UnOp::Neg,
1154 e: Box::new(e),
1155 },
1156 ddc_dex::insn::UnArith::Not => Expr::Un {
1157 op: UnOp::BitNot,
1158 e: Box::new(e),
1159 },
1160 ddc_dex::insn::UnArith::Conv => Expr::Cast {
1161 ty: TypeRef::J(primitive_type(*to)),
1162 e: Box::new(e),
1163 },
1164 };
1165 let wide = matches!(*to, 'J' | 'D');
1166 self.write(*dst, ne, ins.pc, wide);
1167 }
1168 InsnKind::Bin { op, dst, a, b, ty } => {
1169 let l = self.read_nest(*a);
1170 let r = self.read_nest(*b);
1171 let jt = primitive_type(*ty);
1172 let e = Expr::Bin {
1173 op: Self::arith_op(*op),
1174 l: Box::new(l),
1175 r: Box::new(r),
1176 ty: Some(TypeRef::J(jt)),
1177 };
1178 let wide = matches!(*ty, 'J' | 'D');
1179 self.write(*dst, e, ins.pc, wide);
1180 }
1181 InsnKind::BinLit {
1182 op,
1183 dst,
1184 a,
1185 lit,
1186 rsub,
1187 } => {
1188 let av = self.read_nest(*a);
1189 let e = if *rsub {
1190 Expr::Bin {
1191 op: BinOp::Sub,
1192 l: Box::new(Expr::Const(ConstVal::Int(*lit))),
1193 r: Box::new(av),
1194 ty: Some(TypeRef::J(JavaType::Int)),
1195 }
1196 } else {
1197 Expr::Bin {
1198 op: Self::arith_op(*op),
1199 l: Box::new(av),
1200 r: Box::new(Expr::Const(ConstVal::Int(*lit))),
1201 ty: Some(TypeRef::J(JavaType::Int)),
1202 }
1203 };
1204 self.write(*dst, e, ins.pc, false);
1205 }
1206 InsnKind::ConstMethodHandle { dst, handle_idx } => {
1207 self.drop_pending_call();
1208 let e = self.method_handle_expr(*handle_idx);
1209 self.write(*dst, e, ins.pc, false);
1210 }
1211 InsnKind::ConstMethodType { dst, proto_idx } => {
1212 self.drop_pending_call();
1213 let proto = self.env.dex.proto(*proto_idx);
1214 let desc = format!(
1215 "({}){}",
1216 self.env
1217 .dex
1218 .proto_params(*proto_idx)
1219 .iter()
1220 .map(|&t| self.env.dex.type_name(t))
1221 .collect::<Vec<_>>()
1222 .join(""),
1223 self.env.dex.type_name(proto.return_type_idx)
1224 );
1225 self.write(*dst, Expr::Raw(desc), ins.pc, false);
1226 }
1227 }
1228 }
1229
1230 let term = self.block_term(ins);
1232
1233 self.drop_pending_call();
1237 self.materialize_impure_at_exit();
1238
1239 let out = OutState {
1243 regs: std::mem::take(&mut self.regs),
1244 write_pc: std::mem::take(&mut self.write_pc),
1245 };
1246 Ok((
1247 BlockResult {
1248 stmts: std::mem::take(&mut self.stmts),
1249 out_stack: Vec::new(),
1250 term,
1251 },
1252 out,
1253 ))
1254 }
1255
1256 fn do_invoke(
1257 &mut self,
1258 kind: InvokeKind,
1259 regs: &[u16],
1260 method_idx: u32,
1261 pc: u32,
1262 ) -> BResult<()> {
1263 let (cls, name, md) = self.env.method_ref(method_idx);
1264 if cls.as_ref() == "java/lang/StringBuilder" || cls.as_ref() == "java/lang/StringBuffer" {
1265 *self.mflags = self.mflags.with_sb();
1266 }
1267 let is_static = matches!(kind, InvokeKind::Static);
1268 let receiver_reg = if is_static {
1269 None
1270 } else {
1271 regs.first().copied()
1272 };
1273 let arg_regs: Vec<u16> = if is_static {
1274 regs.to_vec()
1275 } else {
1276 regs.iter().skip(1).copied().collect()
1277 };
1278 let mut args: Vec<Expr> = Vec::with_capacity(md.args.len());
1279 for (i, at) in md.args.iter().enumerate() {
1280 let r = arg_regs.get(i).copied().unwrap_or(0);
1281 args.push(null_in_obj_ctx(self.read_nest(r), at));
1282 }
1283 let recv_expr = receiver_reg.map(|r| self.read_nest(r));
1284
1285 if name.as_ref() == "<init>" && matches!(kind, InvokeKind::Direct) {
1287 let recv_reg = receiver_reg.unwrap_or(0);
1288 let recv_state = self
1289 .regs
1290 .get(recv_reg as usize)
1291 .cloned()
1292 .unwrap_or(Reg::Undef);
1293 if let Reg::Pending(Expr::New {
1294 raw: true, cls: nc, ..
1295 }) = &recv_state
1296 {
1297 if nc == &cls {
1298 let ty = TypeRef::J(JavaType::Object(cls.clone()));
1299 self.write(
1300 recv_reg,
1301 Expr::New {
1302 cls: cls.clone(),
1303 ty,
1304 args,
1305 raw: false,
1306 },
1307 pc,
1308 false,
1309 );
1310 return Ok(());
1311 }
1312 }
1313 let owner_expr_v = recv_expr.unwrap_or(Expr::This);
1314 let owner_expr_v = if !self.env.is_static
1321 && matches!(&owner_expr_v, Expr::Local { var: 0, .. })
1322 {
1323 Expr::This
1324 } else {
1325 owner_expr_v
1326 };
1327 let is_super = cls.as_ref() != self.env.class_name.as_str();
1328 let owner = if matches!(owner_expr_v, Expr::This) && is_super {
1329 None
1330 } else {
1331 Some(Box::new(owner_expr_v))
1332 };
1333 let call = Expr::Method {
1334 owner,
1335 cls,
1336 name: "<init>".into(),
1337 desc: md,
1338 args,
1339 is_static: false,
1340 is_interface: false,
1341 is_special: true,
1342 is_super,
1343 is_dynamic: false,
1344 type_args: vec![],
1345 };
1346 self.stmts.push(Stmt::ExprStmt(call));
1347 return Ok(());
1348 }
1349
1350 let is_interface = matches!(kind, InvokeKind::Interface);
1351 let is_super = match kind {
1352 InvokeKind::Super => true,
1353 InvokeKind::Direct if !is_static => cls.as_ref() != self.env.class_name.as_str(),
1354 _ => false,
1355 };
1356 let owner: Option<Box<Expr>> = match recv_expr {
1357 None => None,
1358 Some(o) => {
1359 if is_super && matches!(o, Expr::This) {
1360 None
1361 } else {
1362 self.owner_expr(o, &cls)
1363 }
1364 }
1365 };
1366 let is_dynamic = matches!(kind, InvokeKind::Polymorphic);
1367 let call = Expr::Method {
1368 owner,
1369 cls,
1370 name,
1371 desc: md,
1372 args,
1373 is_static,
1374 is_interface,
1375 is_special: matches!(kind, InvokeKind::Direct),
1376 is_super,
1377 is_dynamic,
1378 type_args: vec![],
1379 };
1380 self.pending_call = Some(call);
1381 Ok(())
1382 }
1383
1384 fn method_handle_expr(&mut self, handle_idx: u32) -> Expr {
1387 let Some(h) = self.env.dex.method_handle(handle_idx) else {
1388 return Expr::Raw(format!("methodHandle@{}", handle_idx));
1389 };
1390 if h.is_field {
1391 let f = self.env.dex.field(h.target_id);
1392 let cls = self.env.dex.class_name(f.class_idx);
1393 let name = self.env.dex.string(f.name_idx).to_string();
1394 return Expr::Raw(format!("{}::{}", cls.replace('/', "."), name));
1395 }
1396 let m = self.env.dex.method(h.target_id);
1397 let cls = self.env.dex.class_name(m.class_idx);
1398 let name = self.env.dex.string(m.name_idx).to_string();
1399 Expr::Raw(format!("{}::{}", cls.replace('/', "."), name))
1400 }
1401
1402 fn build_invoke_custom(&mut self, cs_idx: u32, regs: &[u16], pc: u32) -> Expr {
1404 let Some(cs) = self.env.dex.call_site(cs_idx).cloned() else {
1405 return Expr::Invokedynamic {
1406 name: format!("call-site#{}", cs_idx),
1407 desc: jdc_core::types::MethodDescriptor {
1408 args: vec![],
1409 ret: JavaType::Object("java/lang/Object".into()),
1410 },
1411 args: vec![],
1412 bsm_text: "unresolved".into(),
1413 bsm_static_args: vec![],
1414 };
1415 };
1416 let site_name = self.env.dex.string(cs.name_idx).to_string();
1417 let params: Vec<JavaType> = self
1418 .env
1419 .dex
1420 .proto_params(cs.proto_idx)
1421 .iter()
1422 .map(|&t| self.env.java_type(t))
1423 .collect();
1424 let ret = self
1425 .env
1426 .java_type(self.env.dex.proto(cs.proto_idx).return_type_idx);
1427
1428 let (bs_cls, bs_name) = match self.env.dex.method_handle(cs.bootstrap_handle) {
1429 Some(h) if !h.is_field => {
1430 let m = self.env.dex.method(h.target_id);
1431 (
1432 self.env.dex.class_name(m.class_idx),
1433 self.env.dex.string(m.name_idx).to_string(),
1434 )
1435 }
1436 _ => (String::new(), String::new()),
1437 };
1438
1439 let mut args: Vec<Expr> = Vec::with_capacity(params.len().min(regs.len()));
1441 for (i, _) in params.iter().enumerate() {
1442 if let Some(&r) = regs.get(i) {
1443 args.push(self.read_nest(r));
1444 }
1445 }
1446
1447 if bs_name == "makeConcatWithConstants" {
1449 let recipe = cs
1450 .linker_args
1451 .iter()
1452 .find_map(|v| match v {
1453 ddc_dex::annotations::EncodedValue::String(s) => {
1454 Some(self.env.dex.string(*s).to_string())
1455 }
1456 _ => None,
1457 })
1458 .unwrap_or_default();
1459 return concat_from_recipe(&recipe, args);
1460 }
1461
1462 let mut impl_m = None;
1466 let mut instantiated_proto: Option<u32> = None;
1467 if bs_name == "metafactory" || bs_name == "altMetafactory" {
1468 let mut seen_handle = false;
1469 for lv in &cs.linker_args {
1470 match lv {
1471 ddc_dex::annotations::EncodedValue::MethodHandle(hi) => {
1472 if !seen_handle {
1473 seen_handle = true;
1474 if let Some(h) = self.env.dex.method_handle(*hi) {
1475 if !h.is_field {
1476 let m = self.env.dex.method(h.target_id);
1477 impl_m = Some((
1478 self.env.dex.class_name(m.class_idx).replace('/', "."),
1479 self.env.dex.string(m.name_idx).to_string(),
1480 ));
1481 }
1482 }
1483 }
1484 }
1485 ddc_dex::annotations::EncodedValue::MethodType(p) if seen_handle => {
1486 instantiated_proto = Some(*p);
1487 }
1488 _ => {}
1489 }
1490 }
1491 }
1492 let _ = pc;
1493
1494 let mut captures = Vec::new();
1496 for &r in regs.iter().skip(params.len().min(regs.len())) {
1497 captures.push(self.read_nest(r));
1498 }
1499
1500 if let Some((impl_cls, impl_name)) = impl_m {
1501 if captures.is_empty() {
1502 if impl_name.starts_with("lambda$") {
1506 let n_params = match instantiated_proto {
1510 Some(p) => self.env.dex.proto_params(p).len(),
1511 None => params.len(),
1512 };
1513 let pnames: Vec<String> = (0..n_params).map(|i| format!("a{}", i)).collect();
1514 return Expr::Raw(format!(
1515 "({}) -> {}.{}({})",
1516 pnames.join(", "),
1517 impl_cls,
1518 impl_name,
1519 pnames.join(", ")
1520 ));
1521 }
1522 return Expr::Raw(format!("{}::{}", impl_cls, impl_name));
1523 }
1524 let mut call_args = args;
1527 call_args.extend(captures);
1528 return Expr::Invokedynamic {
1529 name: format!("{} -> {}.{}", site_name, impl_cls, impl_name),
1530 desc: jdc_core::types::MethodDescriptor { args: params, ret },
1531 args: call_args,
1532 bsm_text: format!("{}::{}", bs_cls, bs_name),
1533 bsm_static_args: vec![],
1534 };
1535 }
1536
1537 Expr::Invokedynamic {
1538 name: site_name,
1539 desc: jdc_core::types::MethodDescriptor { args: params, ret },
1540 args: captures,
1541 bsm_text: format!("{}::{}", bs_cls, bs_name),
1542 bsm_static_args: vec![],
1543 }
1544 }
1545
1546 fn materialize_impure_at_exit(&mut self) {
1547 let n = self.regs.len();
1548 for r in 0..n {
1549 let st = self.regs[r].clone();
1550 let impure = match &st {
1551 Reg::PendingCall(_) => true,
1552 Reg::Pending(e) => {
1553 has_side_effects(e)
1554 && !matches!(e, Expr::New { raw: true, .. })
1555 && !matches!(e, Expr::NewArray { init: None, .. })
1556 }
1557 _ => false,
1558 };
1559 if impure {
1560 self.materialize(r as u16);
1561 }
1562 }
1563 }
1564
1565 fn block_term(&mut self, ins: &[Insn]) -> Term {
1567 let Some(last) = ins.last() else {
1568 return Term::Goto;
1569 };
1570 match &last.kind {
1571 InsnKind::Goto { .. } => Term::Goto,
1572 InsnKind::ReturnVoid => Term::Return(None),
1573 InsnKind::Return { src } => {
1574 let v = null_in_obj_ctx(self.read_term(*src), &self.env.desc.ret);
1575 Term::Return(Some(v))
1576 }
1577 InsnKind::Throw { reg } => {
1578 let mut v = self.read_term(*reg);
1580 if let Expr::Const(ConstVal::Int(0)) = v {
1581 v = Expr::Const(ConstVal::Null);
1582 }
1583 Term::Throw(v)
1584 }
1585 InsnKind::If { op, a, b, z, .. } => {
1586 let cond = if *z {
1587 let v = self.read_nest(*a);
1588 let (l, r, realop) = jdc_core::ir::build::unfold_cmp(v, Self::cmp_op(*op));
1589 Expr::Bin {
1590 op: realop,
1591 l: Box::new(l),
1592 r: Box::new(r),
1593 ty: Some(TypeRef::J(JavaType::Boolean)),
1594 }
1595 } else {
1596 let l = self.read_nest(*a);
1597 let r = self.read_nest(*b);
1598 Expr::Bin {
1599 op: Self::cmp_op(*op),
1600 l: Box::new(l),
1601 r: Box::new(r),
1602 ty: Some(TypeRef::J(JavaType::Boolean)),
1603 }
1604 };
1605 Term::Cond { cond }
1606 }
1607 InsnKind::PackedSwitch { reg, payload_pc }
1608 | InsnKind::SparseSwitch { reg, payload_pc } => {
1609 let selector = self.read_term(*reg);
1610 let default = Some(last.pc + last.size);
1611 let targets = match self.env.code.payloads.get(payload_pc) {
1612 Some(Payload::Packed { first_key, targets }) => SwitchTargets::Table {
1613 low: *first_key,
1614 targets: targets
1615 .iter()
1616 .map(|t| (last.pc as i64 + *t as i64) as u32)
1617 .collect(),
1618 },
1619 Some(Payload::Sparse { pairs }) => SwitchTargets::Lookup {
1620 pairs: pairs
1621 .iter()
1622 .map(|(k, t)| (*k, (last.pc as i64 + *t as i64) as u32))
1623 .collect(),
1624 },
1625 _ => SwitchTargets::Lookup { pairs: vec![] },
1626 };
1627 Term::Switch {
1628 selector,
1629 targets,
1630 default,
1631 }
1632 }
1633 _ => {
1635 let next = last.pc + last.size;
1636 if next < self.code_units {
1637 Term::Fallthrough
1638 } else {
1639 Term::Goto
1640 }
1641 }
1642 }
1643 }
1644}
1645
1646fn concat_from_recipe(recipe: &str, args: Vec<Expr>) -> Expr {
1650 use jdc_core::ir::expr::ConcatPart;
1651 let mut parts: Vec<ConcatPart> = Vec::new();
1652 let mut arg_i = 0usize;
1653 let mut const_buf = String::new();
1654 for c in recipe.chars() {
1655 match c {
1656 '\u{1}' => {
1657 if !const_buf.is_empty() {
1658 parts.push(ConcatPart::Const(std::mem::take(&mut const_buf)));
1659 }
1660 if arg_i < args.len() {
1661 parts.push(ConcatPart::Str(args[arg_i].clone()));
1662 arg_i += 1;
1663 }
1664 }
1665 '\u{2}' => {
1666 }
1669 c => const_buf.push(c),
1670 }
1671 }
1672 if !const_buf.is_empty() {
1673 parts.push(ConcatPart::Const(const_buf));
1674 }
1675 Expr::StringConcat(parts)
1676}
1677
1678fn expr_size(e: &Expr) -> usize {
1682 let mut n = 0usize;
1683 count_nodes(e, &mut n);
1684 n
1685}
1686
1687fn count_nodes(e: &Expr, n: &mut usize) {
1688 *n += 1;
1689 if *n > 512 {
1690 return; }
1692 match e {
1693 Expr::Un { e, .. } | Expr::Cast { e, .. } | Expr::InstanceOf { e, .. } => count_nodes(e, n),
1694 Expr::Bin { l, r, .. } => {
1695 count_nodes(l, n);
1696 count_nodes(r, n);
1697 }
1698 Expr::Cond { c, t, f } => {
1699 count_nodes(c, n);
1700 count_nodes(t, n);
1701 count_nodes(f, n);
1702 }
1703 Expr::Assign { target, value, .. } => {
1704 count_nodes(target, n);
1705 count_nodes(value, n);
1706 }
1707 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => count_nodes(e, n),
1708 Expr::Field { owner: Some(o), .. } => count_nodes(o, n),
1709 Expr::Method {
1710 owner: Some(o),
1711 args,
1712 ..
1713 } => {
1714 count_nodes(o, n);
1715 for a in args {
1716 count_nodes(a, n);
1717 }
1718 }
1719 Expr::ArrayIndex { array, index } => {
1720 count_nodes(array, n);
1721 count_nodes(index, n);
1722 }
1723 Expr::New { args, .. } => {
1724 for a in args {
1725 count_nodes(a, n);
1726 }
1727 }
1728 Expr::NewArray { dims, init, .. } => {
1729 for d in dims {
1730 count_nodes(d, n);
1731 }
1732 if let Some(v) = init {
1733 for x in v {
1734 count_nodes(x, n);
1735 }
1736 }
1737 }
1738 Expr::NewMultiArray { dims, .. } => {
1739 for d in dims {
1740 count_nodes(d, n);
1741 }
1742 }
1743 Expr::StringConcat(parts) => {
1744 for p in parts {
1745 if let ConcatPart::Str(x) = p {
1746 count_nodes(x, n);
1747 }
1748 }
1749 }
1750 _ => {}
1751 }
1752}
1753
1754fn reg_is_wide(st: &Reg, vt: &VarTable) -> bool {
1755 match st {
1756 Reg::Pending(e) | Reg::PendingCall(e) => e.type_ref().erased().is_wide(),
1757 Reg::Live(v) => vt.var(*v).ty.erased().is_wide(),
1758 Reg::WideHi => true,
1759 Reg::Undef => false,
1760 }
1761}
1762
1763fn null_in_obj_ctx(mut v: Expr, ctx_ty: &JavaType) -> Expr {
1769 if ctx_ty.is_reference() {
1770 if let Expr::Const(ConstVal::Int(0)) = v {
1771 v = Expr::Const(ConstVal::Null);
1772 }
1773 }
1774 v
1775}
1776
1777fn value_of_cmp(e: &Expr) -> Expr {
1779 if let Expr::Invokedynamic { name, args, .. } = e {
1780 if name.starts_with('\0') && args.len() == 2 {
1781 let (cls, ty) = match name.as_str() {
1782 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
1783 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
1784 _ => ("java/lang/Double", JavaType::Double),
1785 };
1786 let desc = MethodDescriptor {
1787 args: vec![ty.clone(), ty],
1788 ret: JavaType::Int,
1789 };
1790 return Expr::Method {
1791 owner: None,
1792 cls: cls.into(),
1793 name: "compare".into(),
1794 desc: std::sync::Arc::new(desc),
1795 args: args.clone(),
1796 is_static: true,
1797 is_interface: false,
1798 is_special: false,
1799 is_super: false,
1800 is_dynamic: false,
1801 type_args: vec![],
1802 };
1803 }
1804 }
1805 e.clone()
1806}
1807
1808pub fn primitive_type(c: char) -> JavaType {
1809 match c {
1810 'Z' => JavaType::Boolean,
1811 'B' => JavaType::Byte,
1812 'C' => JavaType::Char,
1813 'S' => JavaType::Short,
1814 'F' => JavaType::Float,
1815 'J' => JavaType::Long,
1816 'D' => JavaType::Double,
1817 _ => JavaType::Int,
1818 }
1819}
1820
1821fn payload_consts(elem_width: u16, size: u32, data: &[u8]) -> Vec<Expr> {
1824 let n = (size as usize).min(data.len() / elem_width.max(1) as usize);
1825 let mut out = Vec::with_capacity(n);
1826 for i in 0..n {
1827 let off = i * elem_width as usize;
1828 let e = match elem_width {
1829 1 => Expr::Const(ConstVal::Int(data[off] as i8 as i32)),
1830 2 => {
1831 let v = i16::from_le_bytes([data[off], data[off + 1]]);
1832 Expr::Const(ConstVal::Int(v as i32))
1833 }
1834 4 => {
1835 let mut b = [0u8; 4];
1836 b.copy_from_slice(&data[off..off + 4]);
1837 Expr::Const(ConstVal::Int(i32::from_le_bytes(b)))
1838 }
1839 8 => {
1840 let mut b = [0u8; 8];
1841 b.copy_from_slice(&data[off..off + 8]);
1842 Expr::Const(ConstVal::Long(i64::from_le_bytes(b)))
1843 }
1844 _ => Expr::Const(ConstVal::Int(0)),
1845 };
1846 out.push(e);
1847 }
1848 out
1849}
1850
1851pub fn entry_regs(vt: &mut VarTable, env: &MethodEnv, param_names: &[Option<String>]) -> Vec<Reg> {
1853 let code = env.code;
1854 let n = code.registers_size as usize;
1855 let mut regs = vec![Reg::Undef; n.max(1)];
1856 let ins = code.ins_size.min(code.registers_size) as usize;
1857 let mut reg = n.saturating_sub(ins);
1858 let mut pidx = 0;
1859 if !env.is_static && reg < n {
1860 let id = vt.vars.len() as u32;
1862 vt.vars.push(VarInfo {
1863 id,
1864 slot: reg as u16,
1865 name: "this".into(),
1866 ty: TypeRef::J(JavaType::Object(env.class_name.as_str().into())),
1867 is_param: true,
1868 range_start: 0,
1869 range_end: u16::MAX,
1870 synthetic_name: false,
1871 });
1872 while vt.by_slot.len() <= reg {
1873 vt.by_slot.push(Vec::new());
1874 }
1875 vt.by_slot[reg].push((0, u16::MAX, id));
1876 regs[reg] = Reg::Live(id);
1877 reg += 1;
1878 pidx += 1;
1879 }
1880 for (i, arg) in env.desc.args.iter().enumerate() {
1881 if reg >= n {
1882 break;
1883 }
1884 let dbg = param_names.get(i).and_then(|o| o.clone());
1885 let named = dbg.is_some();
1886 let name = dbg
1891 .map(|d| crate::classdec::java_ident(&d).into_owned())
1892 .unwrap_or_else(|| format!("p{}", pidx));
1893 let id = vt.vars.len() as u32;
1894 vt.vars.push(VarInfo {
1895 id,
1896 slot: reg as u16,
1897 name,
1898 ty: TypeRef::J(arg.clone()),
1899 is_param: true,
1900 range_start: 0,
1901 range_end: u16::MAX,
1902 synthetic_name: !named,
1903 });
1904 while vt.by_slot.len() <= reg {
1905 vt.by_slot.push(Vec::new());
1906 }
1907 vt.by_slot[reg].push((0, u16::MAX, id));
1908 regs[reg] = Reg::Live(id);
1909 reg += if arg.is_wide() { 2 } else { 1 };
1910 pidx += 1;
1911 }
1912 regs
1913}