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::{parse_method_descriptor, 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 dex: &'a DexFile,
72 pub code: &'a CodeItem,
73 pub class_name: String,
74 pub method_name: String,
75 pub desc: MethodDescriptor,
76 pub is_static: bool,
77 pub code_units: u32,
81}
82
83impl<'a> MethodEnv<'a> {
84 pub fn type_name(&self, idx: u32) -> String {
85 self.dex.type_name(idx).to_string()
86 }
87 pub fn java_type(&self, idx: u32) -> JavaType {
88 crate::desc_type(self.dex.type_name(idx))
89 }
90 pub fn field_ref(&self, idx: u32) -> (String, String, JavaType) {
91 let f = self.dex.field(idx);
92 (
93 self.dex.class_name(f.class_idx),
94 self.dex.string(f.name_idx).to_string(),
95 self.java_type(f.type_idx),
96 )
97 }
98 pub fn method_ref(&self, idx: u32) -> (String, String, MethodDescriptor) {
99 let m = self.dex.method(idx);
100 let proto = self.dex.proto(m.proto_idx);
101 let params: Vec<String> = self
102 .dex
103 .proto_params(m.proto_idx)
104 .into_iter()
105 .map(|t| self.dex.type_name(t).to_string())
106 .collect();
107 let ret = self.dex.type_name(proto.return_type_idx);
108 let desc = format!("({}){}", params.join(""), ret);
109 let md = parse_method_descriptor(&desc).unwrap_or(MethodDescriptor {
110 args: vec![],
111 ret: JavaType::Void,
112 });
113 (
114 self.dex.class_name(m.class_idx),
115 self.dex.string(m.name_idx).to_string(),
116 md,
117 )
118 }
119}
120
121#[derive(Debug, Clone, PartialEq)]
123pub enum Reg {
124 Undef,
125 Live(u32),
126 Pending(Expr),
127 PendingCall(Expr),
128 WideHi,
129}
130
131#[derive(Debug, Clone)]
133pub struct OutState {
134 pub regs: Vec<Reg>,
135 pub write_pc: Vec<u32>,
138}
139
140pub struct Lifter<'a> {
141 pub env: &'a MethodEnv<'a>,
142 pub vt: &'a mut VarTable,
143 block_id: usize,
145 stable: &'a mut std::collections::HashMap<(usize, u16), u32>,
152 regs: Vec<Reg>,
153 write_pc: Vec<u32>,
154 stmts: Vec<Stmt>,
155 pending_call: Option<Expr>,
156 code_units: u32,
157 mflags: &'a mut MethodFlags,
161}
162
163impl<'a> Lifter<'a> {
164 pub fn new(
165 env: &'a MethodEnv<'a>,
166 vt: &'a mut VarTable,
167 in_regs: Vec<Reg>,
168 block_id: usize,
169 stable: &'a mut std::collections::HashMap<(usize, u16), u32>,
170 mflags: &'a mut MethodFlags,
171 ) -> Lifter<'a> {
172 let n = env.code.registers_size as usize;
173 let n = n.max(in_regs.len()).max(1);
174 let mut regs = in_regs;
175 regs.resize(n, Reg::Undef);
176 Lifter {
177 env,
178 vt,
179 regs,
180 write_pc: vec![0; n],
181 stmts: Vec::new(),
182 block_id,
183 stable,
184 pending_call: None,
185 code_units: env.code_units,
186 mflags,
187 }
188 }
189
190 fn fresh_var(&mut self, slot: u16, ty: TypeRef) -> u32 {
193 if let Some(&id) = self.stable.get(&(self.block_id, slot)) {
194 return id;
195 }
196 let id = self.vt.vars.len() as u32;
197 let name = format!("v{}", id);
198 self.stable.insert((self.block_id, slot), id);
199 self.vt.vars.push(VarInfo {
200 id,
201 slot,
202 name,
203 ty,
204 is_param: false,
205 range_start: 0,
206 range_end: u16::MAX,
207 synthetic_name: true,
208 });
209 while self.vt.by_slot.len() <= slot as usize {
210 self.vt.by_slot.push(Vec::new());
211 }
212 self.vt.by_slot[slot as usize].push((0, u16::MAX, id));
213 id
214 }
215
216 fn local_expr(&self, v: u32) -> Expr {
217 Expr::Local {
218 var: v,
219 ty: self.vt.var(v).ty.clone(),
220 }
221 }
222
223 #[track_caller]
227 fn read_nest(&mut self, r: u16) -> Expr {
228 match self.regs.get(r as usize).cloned().unwrap_or(Reg::Undef) {
229 Reg::Undef | Reg::WideHi => {
230 let ty = TypeRef::J(JavaType::Int);
234 let v = self.fresh_var(r, ty);
235 if (r as usize) < self.regs.len() {
236 self.regs[r as usize] = Reg::Live(v);
237 }
238 self.local_expr(v)
239 }
240 Reg::Live(v) => self.local_expr(v),
241 Reg::Pending(e) => e,
242 Reg::PendingCall(_) => {
243 let v = self.materialize(r);
244 self.local_expr(v)
245 }
246 }
247 }
248
249 fn read_term(&mut self, r: u16) -> Expr {
253 match self.regs.get(r as usize).cloned().unwrap_or(Reg::Undef) {
254 Reg::PendingCall(e) | Reg::Pending(e) => {
255 if (r as usize) < self.regs.len() {
256 self.regs[r as usize] = Reg::Undef;
257 }
258 self.pending_call = None;
259 e
260 }
261 _ => self.read_nest(r),
262 }
263 }
264
265 fn write(&mut self, r: u16, e: Expr, pc: u32, wide: bool) {
266 if r as usize >= self.regs.len() {
267 return;
268 }
269 self.write_pc[r as usize] = pc;
270 if expr_size(&e) > 32 {
275 self.write_pc[r as usize] = pc;
276 let v = self.materialize_value(r, e);
277 if (r as usize) < self.regs.len() {
278 self.regs[r as usize] = Reg::Live(v);
279 }
280 if wide {
281 self.mark_wide_hi(r + 1, pc);
282 }
283 return;
284 }
285 self.regs[r as usize] = Reg::Pending(e);
286 if wide {
287 self.mark_wide_hi(r + 1, pc);
288 }
289 }
290
291 fn materialize_value(&mut self, r: u16, e: Expr) -> u32 {
294 let e = value_of_cmp(&e);
295 let ty = e.type_ref();
296 let v = self.fresh_var(r, ty);
297 self.stmts.push(Stmt::LocalDef {
298 var: v,
299 init: Some(e),
300 is_final: false,
301 force_type: true,
302 });
303 v
304 }
305
306 fn write_call_result(&mut self, r: u16, e: Expr, pc: u32) {
309 if r as usize >= self.regs.len() {
310 return;
311 }
312 self.write_pc[r as usize] = pc;
313 self.regs[r as usize] = Reg::PendingCall(e);
314 }
315
316 fn mark_wide_hi(&mut self, r: u16, pc: u32) {
317 if (r as usize) < self.regs.len() {
318 self.regs[r as usize] = Reg::WideHi;
319 self.write_pc[r as usize] = pc;
320 }
321 }
322
323 fn materialize(&mut self, r: u16) -> u32 {
325 let cur = self.regs.get(r as usize).cloned().unwrap_or(Reg::Undef);
326 let e = match cur {
327 Reg::Pending(e) | Reg::PendingCall(e) => e,
328 Reg::Live(v) => return v,
329 _ => Expr::Const(ConstVal::Int(0)),
330 };
331 let e = value_of_cmp(&e);
333 let ty = e.type_ref();
334 let v = self.fresh_var(r, ty);
335 self.stmts.push(Stmt::LocalDef {
336 var: v,
337 init: Some(e),
338 is_final: false,
339 force_type: true,
340 });
341 if (r as usize) < self.regs.len() {
342 self.regs[r as usize] = Reg::Live(v);
343 }
344 v
345 }
346
347 fn drop_pending_call(&mut self) {
349 if let Some(e) = self.pending_call.take() {
350 let e = value_of_cmp(&e);
351 if has_side_effects(&e) {
352 self.stmts.push(Stmt::ExprStmt(e));
353 }
354 }
355 }
356
357 fn owner_expr(&self, obj: Expr, cls: &str) -> Option<Box<Expr>> {
360 if cls == self.env.class_name && matches!(obj, Expr::This) {
361 return None;
362 }
363 Some(Box::new(obj))
364 }
365
366 fn arith_op(op: ArithOp) -> BinOp {
367 match op {
368 ArithOp::Add => BinOp::Add,
369 ArithOp::Sub => BinOp::Sub,
370 ArithOp::Mul => BinOp::Mul,
371 ArithOp::Div => BinOp::Div,
372 ArithOp::Rem => BinOp::Rem,
373 ArithOp::And => BinOp::And,
374 ArithOp::Or => BinOp::Or,
375 ArithOp::Xor => BinOp::Xor,
376 ArithOp::Shl => BinOp::Shl,
377 ArithOp::Shr => BinOp::Shr,
378 ArithOp::Ushr => BinOp::Ushr,
379 }
380 }
381
382 fn cmp_op(op: CmpOp) -> BinOp {
383 match op {
384 CmpOp::Eq => BinOp::Eq,
385 CmpOp::Ne => BinOp::Ne,
386 CmpOp::Lt => BinOp::Lt,
387 CmpOp::Ge => BinOp::Ge,
388 CmpOp::Gt => BinOp::Gt,
389 CmpOp::Le => BinOp::Le,
390 }
391 }
392
393 fn cmp_sentinel(kind: CmpKind, l: Expr, r: Expr) -> Expr {
394 let name = match kind {
395 CmpKind::CmplF => "\0cmpl-float",
396 CmpKind::CmpgF => "\0cmpg-float",
397 CmpKind::CmplD => "\0cmpl-double",
398 CmpKind::CmpgD => "\0cmpg-double",
399 CmpKind::CmpJ => "\0cmp-long",
400 };
401 Expr::Invokedynamic {
402 name: name.into(),
403 desc: MethodDescriptor {
404 args: vec![],
405 ret: JavaType::Int,
406 },
407 args: vec![l, r],
408 bsm_text: String::new(),
409 bsm_static_args: vec![],
410 }
411 }
412
413 pub fn build_block(
418 mut self,
419 ins: &[Insn],
420 handler_types: &[Option<String>],
421 ) -> BResult<(BlockResult, OutState)> {
422 let payloads = &self.env.code.payloads;
423 for ins in ins {
424 match &ins.kind {
425 InsnKind::Nop => {}
426 InsnKind::Unknown => {
427 return Err(format!(
428 "unsupported opcode {:#04x} ({}) at pc {}",
429 ins.op,
430 ddc_dex::insn::op_name(ins.op),
431 ins.pc
432 ));
433 }
434 InsnKind::Move { dst, src } => {
435 let src_state = self.regs.get(*src as usize).cloned().unwrap_or(Reg::Undef);
438 if matches!(src_state, Reg::PendingCall(_))
439 || matches!(&src_state, Reg::Pending(e) if has_side_effects(e))
440 {
441 self.materialize(*src);
442 }
443 let st = self.regs.get(*src as usize).cloned().unwrap_or(Reg::Undef);
444 let wide = reg_is_wide(&st, self.vt);
445 if (*dst as usize) < self.regs.len() {
446 self.regs[*dst as usize] = st;
447 self.write_pc[*dst as usize] =
448 self.write_pc.get(*src as usize).copied().unwrap_or(0);
449 }
450 if wide {
451 self.mark_wide_hi(*dst + 1, ins.pc);
452 }
453 }
454 InsnKind::MoveResult { dst } => {
455 if let Some(e) = self.pending_call.take() {
456 let wide =
457 matches!(e.type_ref().erased(), JavaType::Long | JavaType::Double);
458 self.write_call_result(*dst, e, ins.pc);
459 if wide {
460 self.mark_wide_hi(*dst + 1, ins.pc);
461 }
462 } else {
463 let v = self.fresh_var(*dst, TypeRef::J(JavaType::Int));
465 if (*dst as usize) < self.regs.len() {
466 self.regs[*dst as usize] = Reg::Live(v);
467 }
468 }
469 }
470 InsnKind::MoveException { dst } => {
471 let ty = handler_types
472 .iter()
473 .flatten()
474 .next()
475 .map(|t| TypeRef::J(JavaType::Object(t.clone())))
476 .unwrap_or_else(|| {
477 TypeRef::J(JavaType::Object("java/lang/Throwable".into()))
478 });
479 let v = self.fresh_var(*dst, ty);
480 self.stmts.push(Stmt::LocalDef {
481 var: v,
482 init: None,
483 is_final: false,
484 force_type: true,
485 });
486 if (*dst as usize) < self.regs.len() {
487 self.regs[*dst as usize] = Reg::Live(v);
488 }
489 }
490 InsnKind::ReturnVoid
491 | InsnKind::Return { .. }
492 | InsnKind::Throw { .. }
493 | InsnKind::Goto { .. }
494 | InsnKind::PackedSwitch { .. }
495 | InsnKind::SparseSwitch { .. }
496 | InsnKind::If { .. } => {}
497 InsnKind::Const { dst, val, wide } => {
498 let e = if *wide {
499 Expr::Const(ConstVal::Long(*val))
500 } else {
501 Expr::Const(ConstVal::Int(*val as i32))
502 };
503 self.write(*dst, e, ins.pc, *wide);
504 }
505 InsnKind::ConstString { dst, str_idx } => {
506 let s = self.env.dex.string(*str_idx).to_string();
507 self.write(*dst, Expr::Const(ConstVal::Str(s)), ins.pc, false);
508 }
509 InsnKind::ConstClass { dst, type_idx } => {
510 let ty = TypeRef::J(self.env.java_type(*type_idx));
511 self.write(*dst, Expr::Const(ConstVal::ClassLit(ty)), ins.pc, false);
512 }
513 InsnKind::MonitorEnter { reg } => {
514 let e = self.read_nest(*reg);
515 *self.mflags = self.mflags.with_monitor();
516 self.stmts.push(Stmt::MonitorEnter(e));
517 }
518 InsnKind::MonitorExit { reg } => {
519 let e = self.read_nest(*reg);
520 *self.mflags = self.mflags.with_monitor();
521 self.stmts.push(Stmt::MonitorExit(e));
522 }
523 InsnKind::CheckCast { reg, type_idx } => {
524 let e = self.read_nest(*reg);
525 let ty = TypeRef::J(self.env.java_type(*type_idx));
526 self.write(*reg, Expr::Cast { ty, e: Box::new(e) }, ins.pc, false);
527 }
528 InsnKind::InstanceOf { dst, src, type_idx } => {
529 let e = self.read_nest(*src);
530 let ty = TypeRef::J(self.env.java_type(*type_idx));
531 self.write(*dst, Expr::InstanceOf { e: Box::new(e), ty }, ins.pc, false);
532 }
533 InsnKind::ArrayLength { dst, src } => {
534 let e = self.read_nest(*src);
535 let length = Expr::Field {
536 owner: Some(Box::new(e)),
537 cls: String::new(),
538 name: "length".into(),
539 ty: TypeRef::J(JavaType::Int),
540 is_static: false,
541 };
542 self.write(*dst, length, ins.pc, false);
543 }
544 InsnKind::NewInstance { dst, type_idx } => {
545 let cls = self.env.dex.class_name(*type_idx);
546 let ty = TypeRef::J(JavaType::Object(cls.clone()));
547 self.write(
548 *dst,
549 Expr::New {
550 cls,
551 ty,
552 args: vec![],
553 raw: true,
554 },
555 ins.pc,
556 false,
557 );
558 }
559 InsnKind::NewArray {
560 dst,
561 size,
562 type_idx,
563 } => {
564 let elem_desc = self.env.type_name(*type_idx);
565 let elem = crate::desc_type(elem_desc.trim_start_matches('['));
567 let dims = self.read_nest(*size);
568 self.write(
569 *dst,
570 Expr::NewArray {
571 elem: TypeRef::J(elem),
572 dims: vec![dims],
573 trailing_dims: 0,
574 init: None,
575 },
576 ins.pc,
577 false,
578 );
579 }
580 InsnKind::FilledNewArray { regs, type_idx } => {
581 self.drop_pending_call();
582 let elem_desc = self.env.type_name(*type_idx);
583 let elem = crate::desc_type(elem_desc.trim_start_matches('['));
584 let args: Vec<Expr> = regs.iter().map(|&r| self.read_nest(r)).collect();
585 self.pending_call = Some(Expr::NewArray {
586 elem: TypeRef::J(elem),
587 dims: vec![],
588 trailing_dims: 0,
589 init: Some(args),
590 });
591 }
592 InsnKind::FillArrayData { reg, payload_pc } => {
593 if let Some(Payload::ArrayData {
594 elem_width,
595 size,
596 data,
597 }) = payloads.get(payload_pc)
598 {
599 let elems = payload_consts(*elem_width, *size, data);
600 let cur = self.regs.get(*reg as usize).cloned().unwrap_or(Reg::Undef);
601 if let Reg::Pending(Expr::NewArray { elem, init, .. }) = cur {
602 if init.is_none() {
603 self.write(
604 *reg,
605 Expr::NewArray {
606 elem,
607 dims: vec![Expr::Const(ConstVal::Int(*size as i32))],
608 trailing_dims: 0,
609 init: Some(elems),
610 },
611 ins.pc,
612 false,
613 );
614 continue;
615 }
616 }
617 let arr = self.read_nest(*reg);
619 for (i, e) in elems.into_iter().enumerate() {
620 let idx = Expr::Const(ConstVal::Int(i as i32));
621 let target = Expr::ArrayIndex {
622 array: Box::new(arr.clone()),
623 index: Box::new(idx),
624 };
625 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
626 target: Box::new(target),
627 op: AssignOp::Plain,
628 value: Box::new(e),
629 }));
630 }
631 }
632 }
633 InsnKind::Cmp { dst, a, b, kind } => {
634 let l = self.read_nest(*a);
635 let r = self.read_nest(*b);
636 *self.mflags = self.mflags.with_cmp();
637 self.write(*dst, Self::cmp_sentinel(*kind, l, r), ins.pc, false);
638 }
639 InsnKind::AGet {
640 dst, array, index, ..
641 } => {
642 let a = self.read_nest(*array);
643 let i = self.read_nest(*index);
644 self.write(
645 *dst,
646 Expr::ArrayIndex {
647 array: Box::new(a),
648 index: Box::new(i),
649 },
650 ins.pc,
651 false,
652 );
653 }
654 InsnKind::APut {
655 value,
656 array,
657 index,
658 ..
659 } => {
660 self.drop_pending_call();
661 let v = self.read_nest(*value);
662 let a = self.read_nest(*array);
663 let i = self.read_nest(*index);
664 let target = Expr::ArrayIndex {
665 array: Box::new(a),
666 index: Box::new(i),
667 };
668 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
669 target: Box::new(target),
670 op: AssignOp::Plain,
671 value: Box::new(v),
672 }));
673 }
674 InsnKind::IGet {
675 dst,
676 obj,
677 field_idx,
678 } => {
679 self.drop_pending_call();
680 let (cls, name, ty) = self.env.field_ref(*field_idx);
681 let owner = self.read_nest(*obj);
682 let owner_opt = self.owner_expr(owner, &cls);
683 self.write(
684 *dst,
685 Expr::Field {
686 owner: owner_opt,
687 cls,
688 name,
689 ty: TypeRef::J(ty),
690 is_static: false,
691 },
692 ins.pc,
693 false,
694 );
695 }
696 InsnKind::IPut {
697 value,
698 obj,
699 field_idx,
700 } => {
701 self.drop_pending_call();
702 let (cls, name, ty) = self.env.field_ref(*field_idx);
703 let v = self.read_nest(*value);
704 let owner = self.read_nest(*obj);
705 let owner_opt = self.owner_expr(owner, &cls);
706 let target = Expr::Field {
707 owner: owner_opt,
708 cls,
709 name,
710 ty: TypeRef::J(ty),
711 is_static: false,
712 };
713 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
714 target: Box::new(target),
715 op: AssignOp::Plain,
716 value: Box::new(v),
717 }));
718 }
719 InsnKind::SGet { dst, field_idx } => {
720 self.drop_pending_call();
721 let (cls, name, ty) = self.env.field_ref(*field_idx);
722 self.write(
723 *dst,
724 Expr::Field {
725 owner: None,
726 cls,
727 name,
728 ty: TypeRef::J(ty),
729 is_static: true,
730 },
731 ins.pc,
732 false,
733 );
734 }
735 InsnKind::SPut { value, field_idx } => {
736 self.drop_pending_call();
737 let (cls, name, ty) = self.env.field_ref(*field_idx);
738 let v = self.read_nest(*value);
739 let target = Expr::Field {
740 owner: None,
741 cls,
742 name,
743 ty: TypeRef::J(ty),
744 is_static: true,
745 };
746 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
747 target: Box::new(target),
748 op: AssignOp::Plain,
749 value: Box::new(v),
750 }));
751 }
752 InsnKind::Invoke {
753 kind,
754 regs,
755 method_idx,
756 } => {
757 self.drop_pending_call();
758 self.do_invoke(*kind, regs, *method_idx, ins.pc)?;
759 }
760 InsnKind::InvokeCustom {
761 call_site_idx,
762 regs,
763 } => {
764 self.drop_pending_call();
765 let e = self.build_invoke_custom(*call_site_idx, regs, ins.pc);
766 self.pending_call = Some(e);
767 }
768 InsnKind::Un {
769 dst,
770 src,
771 op,
772 from: _,
773 to,
774 } => {
775 let e = self.read_nest(*src);
776 let ne = match op {
777 ddc_dex::insn::UnArith::Neg => Expr::Un {
778 op: UnOp::Neg,
779 e: Box::new(e),
780 },
781 ddc_dex::insn::UnArith::Not => Expr::Un {
782 op: UnOp::BitNot,
783 e: Box::new(e),
784 },
785 ddc_dex::insn::UnArith::Conv => Expr::Cast {
786 ty: TypeRef::J(primitive_type(*to)),
787 e: Box::new(e),
788 },
789 };
790 let wide = matches!(*to, 'J' | 'D');
791 self.write(*dst, ne, ins.pc, wide);
792 }
793 InsnKind::Bin { op, dst, a, b, ty } => {
794 let l = self.read_nest(*a);
795 let r = self.read_nest(*b);
796 let jt = primitive_type(*ty);
797 let e = Expr::Bin {
798 op: Self::arith_op(*op),
799 l: Box::new(l),
800 r: Box::new(r),
801 ty: Some(TypeRef::J(jt)),
802 };
803 let wide = matches!(*ty, 'J' | 'D');
804 self.write(*dst, e, ins.pc, wide);
805 }
806 InsnKind::BinLit {
807 op,
808 dst,
809 a,
810 lit,
811 rsub,
812 } => {
813 let av = self.read_nest(*a);
814 let e = if *rsub {
815 Expr::Bin {
816 op: BinOp::Sub,
817 l: Box::new(Expr::Const(ConstVal::Int(*lit))),
818 r: Box::new(av),
819 ty: Some(TypeRef::J(JavaType::Int)),
820 }
821 } else {
822 Expr::Bin {
823 op: Self::arith_op(*op),
824 l: Box::new(av),
825 r: Box::new(Expr::Const(ConstVal::Int(*lit))),
826 ty: Some(TypeRef::J(JavaType::Int)),
827 }
828 };
829 self.write(*dst, e, ins.pc, false);
830 }
831 InsnKind::ConstMethodHandle { dst, handle_idx } => {
832 self.drop_pending_call();
833 let e = self.method_handle_expr(*handle_idx);
834 self.write(*dst, e, ins.pc, false);
835 }
836 InsnKind::ConstMethodType { dst, proto_idx } => {
837 self.drop_pending_call();
838 let proto = self.env.dex.proto(*proto_idx);
839 let desc = format!(
840 "({}){}",
841 self.env
842 .dex
843 .proto_params(*proto_idx)
844 .into_iter()
845 .map(|t| self.env.dex.type_name(t))
846 .collect::<Vec<_>>()
847 .join(""),
848 self.env.dex.type_name(proto.return_type_idx)
849 );
850 self.write(*dst, Expr::Raw(desc), ins.pc, false);
851 }
852 }
853 }
854
855 let term = self.block_term(ins);
857
858 self.drop_pending_call();
862 self.materialize_impure_at_exit();
863
864 let out = OutState {
868 regs: std::mem::take(&mut self.regs),
869 write_pc: std::mem::take(&mut self.write_pc),
870 };
871 Ok((
872 BlockResult {
873 stmts: std::mem::take(&mut self.stmts),
874 out_stack: Vec::new(),
875 term,
876 },
877 out,
878 ))
879 }
880
881 fn do_invoke(
882 &mut self,
883 kind: InvokeKind,
884 regs: &[u16],
885 method_idx: u32,
886 pc: u32,
887 ) -> BResult<()> {
888 let (cls, name, md) = self.env.method_ref(method_idx);
889 if cls == "java/lang/StringBuilder" || cls == "java/lang/StringBuffer" {
890 *self.mflags = self.mflags.with_sb();
891 }
892 let is_static = matches!(kind, InvokeKind::Static);
893 let receiver_reg = if is_static {
894 None
895 } else {
896 regs.first().copied()
897 };
898 let arg_regs: Vec<u16> = if is_static {
899 regs.to_vec()
900 } else {
901 regs.iter().skip(1).copied().collect()
902 };
903 let mut args: Vec<Expr> = Vec::with_capacity(md.args.len());
904 for (i, _) in md.args.iter().enumerate() {
905 let r = arg_regs.get(i).copied().unwrap_or(0);
906 args.push(self.read_nest(r));
907 }
908 let recv_expr = match receiver_reg {
909 Some(r) => Some(self.read_nest(r)),
910 None => None,
911 };
912
913 if name == "<init>" && matches!(kind, InvokeKind::Direct) {
915 let recv_reg = receiver_reg.unwrap_or(0);
916 let recv_state = self
917 .regs
918 .get(recv_reg as usize)
919 .cloned()
920 .unwrap_or(Reg::Undef);
921 if let Reg::Pending(Expr::New {
922 raw: true, cls: nc, ..
923 }) = &recv_state
924 {
925 if nc == &cls {
926 let ty = TypeRef::J(JavaType::Object(cls.clone()));
927 self.write(
928 recv_reg,
929 Expr::New {
930 cls: cls.clone(),
931 ty,
932 args,
933 raw: false,
934 },
935 pc,
936 false,
937 );
938 return Ok(());
939 }
940 }
941 let owner_expr_v = recv_expr.unwrap_or(Expr::This);
942 let is_super = cls != self.env.class_name;
943 let owner = if matches!(owner_expr_v, Expr::This) && is_super {
944 None
945 } else {
946 Some(Box::new(owner_expr_v))
947 };
948 let call = Expr::Method {
949 owner,
950 cls,
951 name: "<init>".into(),
952 desc: md,
953 args,
954 is_static: false,
955 is_interface: false,
956 is_special: true,
957 is_super,
958 is_dynamic: false,
959 type_args: vec![],
960 };
961 self.stmts.push(Stmt::ExprStmt(call));
962 return Ok(());
963 }
964
965 let is_interface = matches!(kind, InvokeKind::Interface);
966 let is_super = match kind {
967 InvokeKind::Super => true,
968 InvokeKind::Direct if !is_static => cls != self.env.class_name,
969 _ => false,
970 };
971 let owner: Option<Box<Expr>> = match recv_expr {
972 None => None,
973 Some(o) => {
974 if is_super && matches!(o, Expr::This) {
975 None
976 } else {
977 self.owner_expr(o, &cls)
978 }
979 }
980 };
981 let is_dynamic = matches!(kind, InvokeKind::Polymorphic);
982 let call = Expr::Method {
983 owner,
984 cls,
985 name,
986 desc: md,
987 args,
988 is_static,
989 is_interface,
990 is_special: matches!(kind, InvokeKind::Direct),
991 is_super,
992 is_dynamic,
993 type_args: vec![],
994 };
995 self.pending_call = Some(call);
996 Ok(())
997 }
998
999 fn method_handle_expr(&mut self, handle_idx: u32) -> Expr {
1002 let Some(h) = self.env.dex.method_handle(handle_idx) else {
1003 return Expr::Raw(format!("methodHandle@{}", handle_idx));
1004 };
1005 if h.is_field {
1006 let f = self.env.dex.field(h.target_id);
1007 let cls = self.env.dex.class_name(f.class_idx);
1008 let name = self.env.dex.string(f.name_idx).to_string();
1009 return Expr::Raw(format!("{}::{}", cls.replace('/', "."), name));
1010 }
1011 let m = self.env.dex.method(h.target_id);
1012 let cls = self.env.dex.class_name(m.class_idx);
1013 let name = self.env.dex.string(m.name_idx).to_string();
1014 Expr::Raw(format!("{}::{}", cls.replace('/', "."), name))
1015 }
1016
1017 fn build_invoke_custom(&mut self, cs_idx: u32, regs: &[u16], pc: u32) -> Expr {
1019 let Some(cs) = self.env.dex.call_site(cs_idx).cloned() else {
1020 return Expr::Invokedynamic {
1021 name: format!("call-site#{}", cs_idx),
1022 desc: jdc_core::types::MethodDescriptor {
1023 args: vec![],
1024 ret: JavaType::Object("java/lang/Object".into()),
1025 },
1026 args: vec![],
1027 bsm_text: "unresolved".into(),
1028 bsm_static_args: vec![],
1029 };
1030 };
1031 let site_name = self.env.dex.string(cs.name_idx).to_string();
1032 let params: Vec<JavaType> = self
1033 .env
1034 .dex
1035 .proto_params(cs.proto_idx)
1036 .into_iter()
1037 .map(|t| self.env.java_type(t))
1038 .collect();
1039 let ret = self
1040 .env
1041 .java_type(self.env.dex.proto(cs.proto_idx).return_type_idx);
1042
1043 let (bs_cls, bs_name) = match self.env.dex.method_handle(cs.bootstrap_handle) {
1044 Some(h) if !h.is_field => {
1045 let m = self.env.dex.method(h.target_id);
1046 (
1047 self.env.dex.class_name(m.class_idx),
1048 self.env.dex.string(m.name_idx).to_string(),
1049 )
1050 }
1051 _ => (String::new(), String::new()),
1052 };
1053
1054 let mut args: Vec<Expr> = Vec::with_capacity(params.len().min(regs.len()));
1056 for (i, _) in params.iter().enumerate() {
1057 if let Some(&r) = regs.get(i) {
1058 args.push(self.read_nest(r));
1059 }
1060 }
1061
1062 if bs_name == "makeConcatWithConstants" {
1064 let recipe = cs
1065 .linker_args
1066 .iter()
1067 .find_map(|v| match v {
1068 ddc_dex::annotations::EncodedValue::String(s) => {
1069 Some(self.env.dex.string(*s).to_string())
1070 }
1071 _ => None,
1072 })
1073 .unwrap_or_default();
1074 return concat_from_recipe(&recipe, args);
1075 }
1076
1077 let mut impl_m = None;
1081 let mut instantiated_proto: Option<u32> = None;
1082 if bs_name == "metafactory" || bs_name == "altMetafactory" {
1083 let mut seen_handle = false;
1084 for lv in &cs.linker_args {
1085 match lv {
1086 ddc_dex::annotations::EncodedValue::MethodHandle(hi) => {
1087 if !seen_handle {
1088 seen_handle = true;
1089 if let Some(h) = self.env.dex.method_handle(*hi) {
1090 if !h.is_field {
1091 let m = self.env.dex.method(h.target_id);
1092 impl_m = Some((
1093 self.env.dex.class_name(m.class_idx).replace('/', "."),
1094 self.env.dex.string(m.name_idx).to_string(),
1095 ));
1096 }
1097 }
1098 }
1099 }
1100 ddc_dex::annotations::EncodedValue::MethodType(p) => {
1101 if seen_handle {
1102 instantiated_proto = Some(*p);
1103 }
1104 }
1105 _ => {}
1106 }
1107 }
1108 }
1109 let _ = pc;
1110
1111 let mut captures = Vec::new();
1113 for &r in regs.iter().skip(params.len().min(regs.len())) {
1114 captures.push(self.read_nest(r));
1115 }
1116
1117 if let Some((impl_cls, impl_name)) = impl_m {
1118 if captures.is_empty() {
1119 if impl_name.starts_with("lambda$") {
1123 let n_params = match instantiated_proto {
1127 Some(p) => self.env.dex.proto_params(p).len(),
1128 None => params.len(),
1129 };
1130 let pnames: Vec<String> = (0..n_params).map(|i| format!("a{}", i)).collect();
1131 return Expr::Raw(format!(
1132 "({}) -> {}.{}({})",
1133 pnames.join(", "),
1134 impl_cls,
1135 impl_name,
1136 pnames.join(", ")
1137 ));
1138 }
1139 return Expr::Raw(format!("{}::{}", impl_cls, impl_name));
1140 }
1141 let mut call_args = args;
1144 call_args.extend(captures);
1145 return Expr::Invokedynamic {
1146 name: format!("{} -> {}.{}", site_name, impl_cls, impl_name),
1147 desc: jdc_core::types::MethodDescriptor { args: params, ret },
1148 args: call_args,
1149 bsm_text: format!("{}::{}", bs_cls, bs_name),
1150 bsm_static_args: vec![],
1151 };
1152 }
1153
1154 Expr::Invokedynamic {
1155 name: site_name,
1156 desc: jdc_core::types::MethodDescriptor { args: params, ret },
1157 args: captures,
1158 bsm_text: format!("{}::{}", bs_cls, bs_name),
1159 bsm_static_args: vec![],
1160 }
1161 }
1162
1163 fn materialize_impure_at_exit(&mut self) {
1164 let n = self.regs.len();
1165 for r in 0..n {
1166 let st = self.regs[r].clone();
1167 let impure = match &st {
1168 Reg::PendingCall(_) => true,
1169 Reg::Pending(e) => {
1170 has_side_effects(e)
1171 && !matches!(e, Expr::New { raw: true, .. })
1172 && !matches!(e, Expr::NewArray { init: None, .. })
1173 }
1174 _ => false,
1175 };
1176 if impure {
1177 self.materialize(r as u16);
1178 }
1179 }
1180 }
1181
1182 fn block_term(&mut self, ins: &[Insn]) -> Term {
1184 let Some(last) = ins.last() else {
1185 return Term::Goto;
1186 };
1187 match &last.kind {
1188 InsnKind::Goto { .. } => Term::Goto,
1189 InsnKind::ReturnVoid => Term::Return(None),
1190 InsnKind::Return { src } => Term::Return(Some(self.read_term(*src))),
1191 InsnKind::Throw { reg } => Term::Throw(self.read_term(*reg)),
1192 InsnKind::If { op, a, b, z, .. } => {
1193 let cond = if *z {
1194 let v = self.read_nest(*a);
1195 let (l, r, realop) = jdc_core::ir::build::unfold_cmp(v, Self::cmp_op(*op));
1196 Expr::Bin {
1197 op: realop,
1198 l: Box::new(l),
1199 r: Box::new(r),
1200 ty: Some(TypeRef::J(JavaType::Boolean)),
1201 }
1202 } else {
1203 let l = self.read_nest(*a);
1204 let r = self.read_nest(*b);
1205 Expr::Bin {
1206 op: Self::cmp_op(*op),
1207 l: Box::new(l),
1208 r: Box::new(r),
1209 ty: Some(TypeRef::J(JavaType::Boolean)),
1210 }
1211 };
1212 Term::Cond { cond }
1213 }
1214 InsnKind::PackedSwitch { reg, payload_pc }
1215 | InsnKind::SparseSwitch { reg, payload_pc } => {
1216 let selector = self.read_term(*reg);
1217 let default = Some(last.pc + last.size);
1218 let targets = match self.env.code.payloads.get(payload_pc) {
1219 Some(Payload::Packed { first_key, targets }) => SwitchTargets::Table {
1220 low: *first_key,
1221 targets: targets
1222 .iter()
1223 .map(|t| (last.pc as i64 + *t as i64) as u32)
1224 .collect(),
1225 },
1226 Some(Payload::Sparse { pairs }) => SwitchTargets::Lookup {
1227 pairs: pairs
1228 .iter()
1229 .map(|(k, t)| (*k, (last.pc as i64 + *t as i64) as u32))
1230 .collect(),
1231 },
1232 _ => SwitchTargets::Lookup { pairs: vec![] },
1233 };
1234 Term::Switch {
1235 selector,
1236 targets,
1237 default,
1238 }
1239 }
1240 _ => {
1242 let next = last.pc + last.size;
1243 if next < self.code_units {
1244 Term::Fallthrough
1245 } else {
1246 Term::Goto
1247 }
1248 }
1249 }
1250 }
1251}
1252
1253fn concat_from_recipe(recipe: &str, args: Vec<Expr>) -> Expr {
1257 use jdc_core::ir::expr::ConcatPart;
1258 let mut parts: Vec<ConcatPart> = Vec::new();
1259 let mut arg_i = 0usize;
1260 let mut const_buf = String::new();
1261 for c in recipe.chars() {
1262 match c {
1263 '\u{1}' => {
1264 if !const_buf.is_empty() {
1265 parts.push(ConcatPart::Const(std::mem::take(&mut const_buf)));
1266 }
1267 if arg_i < args.len() {
1268 parts.push(ConcatPart::Str(args[arg_i].clone()));
1269 arg_i += 1;
1270 }
1271 }
1272 '\u{2}' => {
1273 }
1276 c => const_buf.push(c),
1277 }
1278 }
1279 if !const_buf.is_empty() {
1280 parts.push(ConcatPart::Const(const_buf));
1281 }
1282 Expr::StringConcat(parts)
1283}
1284
1285fn expr_size(e: &Expr) -> usize {
1289 let mut n = 0usize;
1290 count_nodes(e, &mut n);
1291 n
1292}
1293
1294fn count_nodes(e: &Expr, n: &mut usize) {
1295 *n += 1;
1296 if *n > 512 {
1297 return; }
1299 match e {
1300 Expr::Un { e, .. } | Expr::Cast { e, .. } | Expr::InstanceOf { e, .. } => count_nodes(e, n),
1301 Expr::Bin { l, r, .. } => {
1302 count_nodes(l, n);
1303 count_nodes(r, n);
1304 }
1305 Expr::Cond { c, t, f } => {
1306 count_nodes(c, n);
1307 count_nodes(t, n);
1308 count_nodes(f, n);
1309 }
1310 Expr::Assign { target, value, .. } => {
1311 count_nodes(target, n);
1312 count_nodes(value, n);
1313 }
1314 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => count_nodes(e, n),
1315 Expr::Field { owner: Some(o), .. } => count_nodes(o, n),
1316 Expr::Method {
1317 owner: Some(o),
1318 args,
1319 ..
1320 } => {
1321 count_nodes(o, n);
1322 for a in args {
1323 count_nodes(a, n);
1324 }
1325 }
1326 Expr::ArrayIndex { array, index } => {
1327 count_nodes(array, n);
1328 count_nodes(index, n);
1329 }
1330 Expr::New { args, .. } => {
1331 for a in args {
1332 count_nodes(a, n);
1333 }
1334 }
1335 Expr::NewArray { dims, init, .. } => {
1336 for d in dims {
1337 count_nodes(d, n);
1338 }
1339 if let Some(v) = init {
1340 for x in v {
1341 count_nodes(x, n);
1342 }
1343 }
1344 }
1345 Expr::NewMultiArray { dims, .. } => {
1346 for d in dims {
1347 count_nodes(d, n);
1348 }
1349 }
1350 Expr::StringConcat(parts) => {
1351 for p in parts {
1352 if let ConcatPart::Str(x) = p {
1353 count_nodes(x, n);
1354 }
1355 }
1356 }
1357 _ => {}
1358 }
1359}
1360
1361fn reg_is_wide(st: &Reg, vt: &VarTable) -> bool {
1362 match st {
1363 Reg::Pending(e) | Reg::PendingCall(e) => e.type_ref().erased().is_wide(),
1364 Reg::Live(v) => vt.var(*v).ty.erased().is_wide(),
1365 Reg::WideHi => true,
1366 Reg::Undef => false,
1367 }
1368}
1369
1370fn value_of_cmp(e: &Expr) -> Expr {
1372 if let Expr::Invokedynamic { name, args, .. } = e {
1373 if name.starts_with('\0') && args.len() == 2 {
1374 let (cls, ty) = match name.as_str() {
1375 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
1376 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
1377 _ => ("java/lang/Double", JavaType::Double),
1378 };
1379 let desc = MethodDescriptor {
1380 args: vec![ty.clone(), ty],
1381 ret: JavaType::Int,
1382 };
1383 return Expr::Method {
1384 owner: None,
1385 cls: cls.into(),
1386 name: "compare".into(),
1387 desc,
1388 args: args.clone(),
1389 is_static: true,
1390 is_interface: false,
1391 is_special: false,
1392 is_super: false,
1393 is_dynamic: false,
1394 type_args: vec![],
1395 };
1396 }
1397 }
1398 e.clone()
1399}
1400
1401pub fn primitive_type(c: char) -> JavaType {
1402 match c {
1403 'Z' => JavaType::Boolean,
1404 'B' => JavaType::Byte,
1405 'C' => JavaType::Char,
1406 'S' => JavaType::Short,
1407 'F' => JavaType::Float,
1408 'J' => JavaType::Long,
1409 'D' => JavaType::Double,
1410 _ => JavaType::Int,
1411 }
1412}
1413
1414fn payload_consts(elem_width: u16, size: u32, data: &[u8]) -> Vec<Expr> {
1417 let n = (size as usize).min(data.len() / elem_width.max(1) as usize);
1418 let mut out = Vec::with_capacity(n);
1419 for i in 0..n {
1420 let off = i * elem_width as usize;
1421 let e = match elem_width {
1422 1 => Expr::Const(ConstVal::Int(data[off] as i8 as i32)),
1423 2 => {
1424 let v = i16::from_le_bytes([data[off], data[off + 1]]);
1425 Expr::Const(ConstVal::Int(v as i32))
1426 }
1427 4 => {
1428 let mut b = [0u8; 4];
1429 b.copy_from_slice(&data[off..off + 4]);
1430 Expr::Const(ConstVal::Int(i32::from_le_bytes(b)))
1431 }
1432 8 => {
1433 let mut b = [0u8; 8];
1434 b.copy_from_slice(&data[off..off + 8]);
1435 Expr::Const(ConstVal::Long(i64::from_le_bytes(b)))
1436 }
1437 _ => Expr::Const(ConstVal::Int(0)),
1438 };
1439 out.push(e);
1440 }
1441 out
1442}
1443
1444pub fn entry_regs(vt: &mut VarTable, env: &MethodEnv, param_names: &[Option<String>]) -> Vec<Reg> {
1446 let code = env.code;
1447 let n = code.registers_size as usize;
1448 let mut regs = vec![Reg::Undef; n.max(1)];
1449 let ins = code.ins_size.min(code.registers_size) as usize;
1450 let mut reg = n.saturating_sub(ins);
1451 let mut pidx = 0;
1452 if !env.is_static && reg < n {
1453 let id = vt.vars.len() as u32;
1455 vt.vars.push(VarInfo {
1456 id,
1457 slot: reg as u16,
1458 name: "this".into(),
1459 ty: TypeRef::J(JavaType::Object(env.class_name.clone())),
1460 is_param: true,
1461 range_start: 0,
1462 range_end: u16::MAX,
1463 synthetic_name: false,
1464 });
1465 while vt.by_slot.len() <= reg {
1466 vt.by_slot.push(Vec::new());
1467 }
1468 vt.by_slot[reg].push((0, u16::MAX, id));
1469 regs[reg] = Reg::Live(id);
1470 reg += 1;
1471 pidx += 1;
1472 }
1473 for (i, arg) in env.desc.args.iter().enumerate() {
1474 if reg >= n {
1475 break;
1476 }
1477 let dbg = param_names.get(i).and_then(|o| o.clone());
1478 let named = dbg.is_some();
1479 let name = dbg.unwrap_or_else(|| format!("p{}", pidx));
1480 let id = vt.vars.len() as u32;
1481 vt.vars.push(VarInfo {
1482 id,
1483 slot: reg as u16,
1484 name,
1485 ty: TypeRef::J(arg.clone()),
1486 is_param: true,
1487 range_start: 0,
1488 range_end: u16::MAX,
1489 synthetic_name: !named,
1490 });
1491 while vt.by_slot.len() <= reg {
1492 vt.by_slot.push(Vec::new());
1493 }
1494 vt.by_slot[reg].push((0, u16::MAX, id));
1495 regs[reg] = Reg::Live(id);
1496 reg += if arg.is_wide() { 2 } else { 1 };
1497 pidx += 1;
1498 }
1499 regs
1500}