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: std::sync::Arc<str>,
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 .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
140fn src_regs(kind: &InsnKind) -> Vec<u16> {
144 let mut out: Vec<u16> = Vec::with_capacity(4);
145 let mut push = |r: u16| out.push(r);
146 match kind {
147 InsnKind::Nop
148 | InsnKind::ReturnVoid
149 | InsnKind::Unknown
150 | InsnKind::NewInstance { .. }
151 | InsnKind::SGet { .. } => {}
152 InsnKind::Move { src, .. }
153 | InsnKind::Un { src, .. }
154 | InsnKind::Return { src }
155 | InsnKind::ArrayLength { src, .. } => push(*src),
156 InsnKind::MonitorEnter { reg }
157 | InsnKind::MonitorExit { reg }
158 | InsnKind::Throw { reg }
159 | InsnKind::CheckCast { reg, .. }
160 | InsnKind::FillArrayData { reg, .. }
161 | InsnKind::PackedSwitch { reg, .. }
162 | InsnKind::SparseSwitch { reg, .. } => push(*reg),
163 InsnKind::MoveResult { .. } | InsnKind::MoveException { .. } => {}
164 InsnKind::Const { .. }
165 | InsnKind::ConstClass { .. }
166 | InsnKind::ConstString { .. }
167 | InsnKind::ConstMethodHandle { .. }
168 | InsnKind::ConstMethodType { .. } => {}
169 InsnKind::InstanceOf { src, .. } => {
170 push(*src);
171 }
172 InsnKind::NewArray { size, .. } => {
173 push(*size);
174 }
175 InsnKind::FilledNewArray { regs, .. } | InsnKind::InvokeCustom { regs, .. } => {
176 for r in regs {
177 push(*r);
178 }
179 }
180 InsnKind::Goto { .. } => {}
181 InsnKind::Cmp { a, b, .. } | InsnKind::Bin { a, b, .. } => {
182 push(*a);
183 push(*b);
184 }
185 InsnKind::BinLit { a, .. } => push(*a),
186 InsnKind::If { a, b, z, .. } => {
187 push(*a);
188 if !*z {
189 push(*b);
190 }
191 }
192 InsnKind::AGet { array, index, .. } => {
193 push(*array);
194 push(*index);
195 }
196 InsnKind::APut {
197 value,
198 array,
199 index,
200 ..
201 } => {
202 push(*value);
203 push(*array);
204 push(*index);
205 }
206 InsnKind::IGet { obj, .. } => {
207 push(*obj);
208 }
209 InsnKind::IPut { value, obj, .. } => {
210 push(*value);
211 push(*obj);
212 }
213 InsnKind::SPut { value, .. } => {
214 push(*value);
215 }
216 InsnKind::Invoke { regs, .. } => {
217 for r in regs {
218 push(*r);
219 }
220 }
221 }
222 out
223}
224
225fn dst_regs(kind: &InsnKind) -> Vec<u16> {
228 let mut out: Vec<u16> = Vec::with_capacity(2);
229 let mut push = |r: u16| out.push(r);
230 match kind {
231 InsnKind::Move { dst, .. }
232 | InsnKind::MoveResult { dst }
233 | InsnKind::MoveException { dst }
234 | InsnKind::Const { dst, .. }
235 | InsnKind::ConstClass { dst, .. }
236 | InsnKind::ConstString { dst, .. }
237 | InsnKind::ConstMethodHandle { dst, .. }
238 | InsnKind::ConstMethodType { dst, .. }
239 | InsnKind::NewInstance { dst, .. }
240 | InsnKind::NewArray { dst, .. }
241 | InsnKind::InstanceOf { dst, .. }
242 | InsnKind::ArrayLength { dst, .. }
243 | InsnKind::Cmp { dst, .. }
244 | InsnKind::AGet { dst, .. }
245 | InsnKind::IGet { dst, .. }
246 | InsnKind::SGet { dst, .. }
247 | InsnKind::Un { dst, .. }
248 | InsnKind::Bin { dst, .. }
249 | InsnKind::BinLit { dst, .. } => push(*dst),
250 _ => {}
251 }
252 out
253}
254
255fn compute_final_reads(ins: &[Insn]) -> std::collections::HashSet<(u32, u16)> {
260 use std::collections::HashMap;
261 let mut events: HashMap<u16, Vec<(u32, bool)>> = HashMap::new();
263 for i in ins {
264 for r in src_regs(&i.kind) {
265 events.entry(r).or_default().push((i.pc, true));
266 }
267 for r in dst_regs(&i.kind) {
268 events.entry(r).or_default().push((i.pc, false));
269 }
270 }
271 let mut out = std::collections::HashSet::new();
272 for (reg, evs) in events {
273 let mut dedup: Vec<(u32, bool)> = Vec::with_capacity(evs.len());
274 for (pc, is_read) in evs {
275 if dedup.last() != Some(&(pc, is_read)) {
276 dedup.push((pc, is_read));
277 }
278 }
279 for (i, &(pc, is_read)) in dedup.iter().enumerate() {
280 if !is_read {
281 continue;
282 }
283 let mut final_ = true;
286 if let Some(&(_, is_read2)) = dedup.get(i + 1) {
287 if is_read2 {
288 final_ = false;
289 }
290 }
291 if final_ {
292 out.insert((pc, reg));
293 }
294 }
295 }
296 out
297}
298
299pub struct Lifter<'a> {
300 pub env: &'a MethodEnv<'a>,
301 pub vt: &'a mut VarTable,
302 block_id: usize,
304 stable: &'a mut std::collections::HashMap<(usize, u16), u32>,
311 regs: Vec<Reg>,
312 write_pc: Vec<u32>,
313 stmts: Vec<Stmt>,
314 pending_call: Option<Expr>,
315 code_units: u32,
316 cur_pc: u32,
319 final_read: std::collections::HashSet<(u32, u16)>,
326 mflags: &'a mut MethodFlags,
330}
331
332impl<'a> Lifter<'a> {
333 pub fn new(
334 env: &'a MethodEnv<'a>,
335 vt: &'a mut VarTable,
336 in_regs: Vec<Reg>,
337 block_id: usize,
338 stable: &'a mut std::collections::HashMap<(usize, u16), u32>,
339 mflags: &'a mut MethodFlags,
340 ) -> Lifter<'a> {
341 let n = env.code.registers_size as usize;
342 let n = n.max(in_regs.len()).max(1);
343 let mut regs = in_regs;
344 regs.resize(n, Reg::Undef);
345 Lifter {
346 env,
347 vt,
348 regs,
349 write_pc: vec![0; n],
350 stmts: Vec::new(),
351 block_id,
352 stable,
353 pending_call: None,
354 code_units: env.code_units,
355 cur_pc: 0,
356 final_read: std::collections::HashSet::new(),
357 mflags,
358 }
359 }
360
361 fn fresh_var(&mut self, slot: u16, ty: TypeRef) -> u32 {
364 if let Some(&id) = self.stable.get(&(self.block_id, slot)) {
365 return id;
366 }
367 let id = self.vt.vars.len() as u32;
368 let name = format!("v{}", id);
369 self.stable.insert((self.block_id, slot), id);
370 self.vt.vars.push(VarInfo {
371 id,
372 slot,
373 name,
374 ty,
375 is_param: false,
376 range_start: 0,
377 range_end: u16::MAX,
378 synthetic_name: true,
379 });
380 while self.vt.by_slot.len() <= slot as usize {
381 self.vt.by_slot.push(Vec::new());
382 }
383 self.vt.by_slot[slot as usize].push((0, u16::MAX, id));
384 id
385 }
386
387 fn local_expr(&self, v: u32) -> Expr {
388 Expr::Local {
389 var: v,
390 ty: self.vt.var(v).ty.clone(),
391 }
392 }
393
394 #[track_caller]
398 fn read_nest(&mut self, r: u16) -> Expr {
399 match self.regs.get(r as usize).cloned().unwrap_or(Reg::Undef) {
400 Reg::Undef | Reg::WideHi => {
401 let ty = TypeRef::J(JavaType::Int);
405 let v = self.fresh_var(r, ty);
406 if (r as usize) < self.regs.len() {
407 self.regs[r as usize] = Reg::Live(v);
408 }
409 self.local_expr(v)
410 }
411 Reg::Live(v) => self.local_expr(v),
412 Reg::Pending(e) => {
413 let alloc = matches!(&e, Expr::NewArray { .. })
425 || (matches!(&e, Expr::New { raw: false, .. })
426 && !self.final_read.contains(&(self.cur_pc, r)));
427 if alloc {
428 let v = self.materialize(r);
429 self.local_expr(v)
430 } else {
431 e
432 }
433 }
434 Reg::PendingCall(_) => {
435 let v = self.materialize(r);
436 self.local_expr(v)
437 }
438 }
439 }
440
441 fn read_term(&mut self, r: u16) -> Expr {
445 match self.regs.get(r as usize).cloned().unwrap_or(Reg::Undef) {
446 Reg::PendingCall(e) | Reg::Pending(e) => {
447 if (r as usize) < self.regs.len() {
448 self.regs[r as usize] = Reg::Undef;
449 }
450 self.pending_call = None;
451 e
452 }
453 _ => self.read_nest(r),
454 }
455 }
456
457 fn write(&mut self, r: u16, e: Expr, pc: u32, wide: bool) {
458 if r as usize >= self.regs.len() {
459 return;
460 }
461 self.write_pc[r as usize] = pc;
462 if expr_size(&e) > 32 {
467 self.write_pc[r as usize] = pc;
468 let v = self.materialize_value(r, e);
469 if (r as usize) < self.regs.len() {
470 self.regs[r as usize] = Reg::Live(v);
471 }
472 if wide {
473 self.mark_wide_hi(r + 1, pc);
474 }
475 return;
476 }
477 self.regs[r as usize] = Reg::Pending(e);
478 if wide {
479 self.mark_wide_hi(r + 1, pc);
480 }
481 }
482
483 fn materialize_value(&mut self, r: u16, e: Expr) -> u32 {
486 let e = value_of_cmp(&e);
487 let ty = e.type_ref();
488 let v = self.fresh_var(r, ty.clone());
489 if let Some(info) = self.vt.vars.get_mut(v as usize) {
494 info.ty = ty;
495 }
496 self.stmts.push(Stmt::LocalDef {
497 var: v,
498 init: Some(e),
499 is_final: false,
500 force_type: true,
501 });
502 v
503 }
504
505 fn write_call_result(&mut self, r: u16, e: Expr, pc: u32) {
508 if r as usize >= self.regs.len() {
509 return;
510 }
511 self.write_pc[r as usize] = pc;
512 self.regs[r as usize] = Reg::PendingCall(e);
513 }
514
515 fn mark_wide_hi(&mut self, r: u16, pc: u32) {
516 if (r as usize) < self.regs.len() {
517 self.regs[r as usize] = Reg::WideHi;
518 self.write_pc[r as usize] = pc;
519 }
520 }
521
522 fn materialize(&mut self, r: u16) -> u32 {
524 let cur = self.regs.get(r as usize).cloned().unwrap_or(Reg::Undef);
525 let e = match cur {
526 Reg::Pending(e) | Reg::PendingCall(e) => e,
527 Reg::Live(v) => return v,
528 _ => {
531 let v = self.fresh_var(r, TypeRef::J(JavaType::Object("java/lang/Object".into())));
532 self.stmts.push(Stmt::LocalDef {
533 var: v,
534 init: None,
535 is_final: false,
536 force_type: false,
537 });
538 if (r as usize) < self.regs.len() {
539 self.regs[r as usize] = Reg::Live(v);
540 }
541 return v;
542 }
543 };
544 let e = value_of_cmp(&e);
546 let ty = e.type_ref();
547 let v = self.fresh_var(r, ty.clone());
548 if let Some(info) = self.vt.vars.get_mut(v as usize) {
553 info.ty = ty;
554 }
555 self.stmts.push(Stmt::LocalDef {
556 var: v,
557 init: Some(e),
558 is_final: false,
559 force_type: true,
560 });
561 if (r as usize) < self.regs.len() {
562 self.regs[r as usize] = Reg::Live(v);
563 }
564 v
565 }
566
567 fn drop_pending_call(&mut self) {
569 if let Some(e) = self.pending_call.take() {
570 let e = value_of_cmp(&e);
571 if has_side_effects(&e) {
572 self.stmts.push(Stmt::ExprStmt(e));
573 }
574 }
575 }
576
577 fn owner_expr(&self, obj: Expr, cls: &str) -> Option<Box<Expr>> {
580 if cls == self.env.class_name && matches!(obj, Expr::This) {
581 return None;
582 }
583 Some(Box::new(obj))
584 }
585
586 fn arith_op(op: ArithOp) -> BinOp {
587 match op {
588 ArithOp::Add => BinOp::Add,
589 ArithOp::Sub => BinOp::Sub,
590 ArithOp::Mul => BinOp::Mul,
591 ArithOp::Div => BinOp::Div,
592 ArithOp::Rem => BinOp::Rem,
593 ArithOp::And => BinOp::And,
594 ArithOp::Or => BinOp::Or,
595 ArithOp::Xor => BinOp::Xor,
596 ArithOp::Shl => BinOp::Shl,
597 ArithOp::Shr => BinOp::Shr,
598 ArithOp::Ushr => BinOp::Ushr,
599 }
600 }
601
602 fn cmp_op(op: CmpOp) -> BinOp {
603 match op {
604 CmpOp::Eq => BinOp::Eq,
605 CmpOp::Ne => BinOp::Ne,
606 CmpOp::Lt => BinOp::Lt,
607 CmpOp::Ge => BinOp::Ge,
608 CmpOp::Gt => BinOp::Gt,
609 CmpOp::Le => BinOp::Le,
610 }
611 }
612
613 fn cmp_sentinel(kind: CmpKind, l: Expr, r: Expr) -> Expr {
614 let name = match kind {
615 CmpKind::CmplF => "\0cmpl-float",
616 CmpKind::CmpgF => "\0cmpg-float",
617 CmpKind::CmplD => "\0cmpl-double",
618 CmpKind::CmpgD => "\0cmpg-double",
619 CmpKind::CmpJ => "\0cmp-long",
620 };
621 Expr::Invokedynamic {
622 name: name.into(),
623 desc: MethodDescriptor {
624 args: vec![],
625 ret: JavaType::Int,
626 },
627 args: vec![l, r],
628 bsm_text: String::new(),
629 bsm_static_args: vec![],
630 }
631 }
632
633 pub fn build_block(
638 mut self,
639 ins: &[Insn],
640 handler_types: &[Option<String>],
641 ) -> BResult<(BlockResult, OutState)> {
642 let payloads = &self.env.code.payloads;
643 self.final_read = compute_final_reads(ins);
653 for ins in ins {
654 self.cur_pc = ins.pc;
655 match &ins.kind {
656 InsnKind::Nop => {}
657 InsnKind::Unknown => {
658 return Err(format!(
659 "unsupported opcode {:#04x} ({}) at pc {}",
660 ins.op,
661 ddc_dex::insn::op_name(ins.op),
662 ins.pc
663 ));
664 }
665 InsnKind::Move { dst, src } => {
666 let src_state = self.regs.get(*src as usize).cloned().unwrap_or(Reg::Undef);
669 if matches!(src_state, Reg::PendingCall(_))
670 || matches!(&src_state, Reg::Pending(e) if has_side_effects(e))
671 {
672 self.materialize(*src);
673 }
674 let st = self.regs.get(*src as usize).cloned().unwrap_or(Reg::Undef);
675 let wide = reg_is_wide(&st, self.vt);
676 if (*dst as usize) < self.regs.len() {
677 self.regs[*dst as usize] = st;
678 self.write_pc[*dst as usize] =
679 self.write_pc.get(*src as usize).copied().unwrap_or(0);
680 }
681 if wide {
682 self.mark_wide_hi(*dst + 1, ins.pc);
683 }
684 }
685 InsnKind::MoveResult { dst } => {
686 if let Some(e) = self.pending_call.take() {
687 let wide =
688 matches!(e.type_ref().erased(), JavaType::Long | JavaType::Double);
689 self.write_call_result(*dst, e, ins.pc);
690 if wide {
691 self.mark_wide_hi(*dst + 1, ins.pc);
692 }
693 } else {
694 let v = self.fresh_var(*dst, TypeRef::J(JavaType::Int));
696 if (*dst as usize) < self.regs.len() {
697 self.regs[*dst as usize] = Reg::Live(v);
698 }
699 }
700 }
701 InsnKind::MoveException { dst } => {
702 let ty = handler_types
703 .iter()
704 .flatten()
705 .next()
706 .map(|t| TypeRef::J(JavaType::Object(t.clone())))
707 .unwrap_or_else(|| {
708 TypeRef::J(JavaType::Object("java/lang/Throwable".into()))
709 });
710 let v = self.fresh_var(*dst, ty);
711 self.stmts.push(Stmt::LocalDef {
712 var: v,
713 init: None,
714 is_final: false,
715 force_type: true,
716 });
717 if (*dst as usize) < self.regs.len() {
718 self.regs[*dst as usize] = Reg::Live(v);
719 }
720 }
721 InsnKind::ReturnVoid
722 | InsnKind::Return { .. }
723 | InsnKind::Throw { .. }
724 | InsnKind::Goto { .. }
725 | InsnKind::PackedSwitch { .. }
726 | InsnKind::SparseSwitch { .. }
727 | InsnKind::If { .. } => {}
728 InsnKind::Const { dst, val, wide } => {
729 let e = if *wide {
730 Expr::Const(ConstVal::Long(*val))
731 } else {
732 Expr::Const(ConstVal::Int(*val as i32))
733 };
734 self.write(*dst, e, ins.pc, *wide);
735 }
736 InsnKind::ConstString { dst, str_idx } => {
737 let s = self.env.dex.string(*str_idx).to_string();
738 self.write(*dst, Expr::Const(ConstVal::Str(s)), ins.pc, false);
739 }
740 InsnKind::ConstClass { dst, type_idx } => {
741 let ty = TypeRef::J(self.env.java_type(*type_idx));
742 self.write(*dst, Expr::Const(ConstVal::ClassLit(ty)), ins.pc, false);
743 }
744 InsnKind::MonitorEnter { reg } => {
745 let e = self.read_nest(*reg);
746 *self.mflags = self.mflags.with_monitor();
747 self.stmts.push(Stmt::MonitorEnter(e));
748 }
749 InsnKind::MonitorExit { reg } => {
750 let e = self.read_nest(*reg);
751 *self.mflags = self.mflags.with_monitor();
752 self.stmts.push(Stmt::MonitorExit(e));
753 }
754 InsnKind::CheckCast { reg, type_idx } => {
755 let e = self.read_nest(*reg);
756 let ty = TypeRef::J(self.env.java_type(*type_idx));
757 self.write(*reg, Expr::Cast { ty, e: Box::new(e) }, ins.pc, false);
758 }
759 InsnKind::InstanceOf { dst, src, type_idx } => {
760 let e = self.read_nest(*src);
761 let ty = TypeRef::J(self.env.java_type(*type_idx));
762 self.write(*dst, Expr::InstanceOf { e: Box::new(e), ty }, ins.pc, false);
763 }
764 InsnKind::ArrayLength { dst, src } => {
765 let e = self.read_nest(*src);
766 let length = Expr::Field {
767 owner: Some(Box::new(e)),
768 cls: String::new(),
769 name: "length".into(),
770 ty: TypeRef::J(JavaType::Int),
771 is_static: false,
772 };
773 self.write(*dst, length, ins.pc, false);
774 }
775 InsnKind::NewInstance { dst, type_idx } => {
776 let cls = self.env.dex.class_name(*type_idx);
777 let ty = TypeRef::J(JavaType::Object(cls.clone()));
778 self.write(
779 *dst,
780 Expr::New {
781 cls,
782 ty,
783 args: vec![],
784 raw: true,
785 },
786 ins.pc,
787 false,
788 );
789 }
790 InsnKind::NewArray {
791 dst,
792 size,
793 type_idx,
794 } => {
795 let elem_desc = self.env.type_name(*type_idx);
796 let elem = crate::desc_type(elem_desc.trim_start_matches('['));
798 let dims = self.read_nest(*size);
799 self.write(
800 *dst,
801 Expr::NewArray {
802 elem: TypeRef::J(elem),
803 dims: vec![dims],
804 trailing_dims: 0,
805 init: None,
806 },
807 ins.pc,
808 false,
809 );
810 }
811 InsnKind::FilledNewArray { regs, type_idx } => {
812 self.drop_pending_call();
813 let elem_desc = self.env.type_name(*type_idx);
814 let elem = crate::desc_type(elem_desc.trim_start_matches('['));
815 let args: Vec<Expr> = regs.iter().map(|&r| self.read_nest(r)).collect();
816 self.pending_call = Some(Expr::NewArray {
817 elem: TypeRef::J(elem),
818 dims: vec![],
819 trailing_dims: 0,
820 init: Some(args),
821 });
822 }
823 InsnKind::FillArrayData { reg, payload_pc } => {
824 if let Some(Payload::ArrayData {
825 elem_width,
826 size,
827 data,
828 }) = payloads.get(payload_pc)
829 {
830 let elems = payload_consts(*elem_width, *size, data);
831 let cur = self.regs.get(*reg as usize).cloned().unwrap_or(Reg::Undef);
832 if let Reg::Pending(Expr::NewArray { elem, init, .. }) = cur {
833 if init.is_none() {
834 self.write(
835 *reg,
836 Expr::NewArray {
837 elem,
838 dims: vec![Expr::Const(ConstVal::Int(*size as i32))],
839 trailing_dims: 0,
840 init: Some(elems),
841 },
842 ins.pc,
843 false,
844 );
845 continue;
846 }
847 }
848 let arr = self.read_nest(*reg);
850 for (i, e) in elems.into_iter().enumerate() {
851 let idx = Expr::Const(ConstVal::Int(i as i32));
852 let target = Expr::ArrayIndex {
853 array: Box::new(arr.clone()),
854 index: Box::new(idx),
855 };
856 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
857 target: Box::new(target),
858 op: AssignOp::Plain,
859 value: Box::new(e),
860 }));
861 }
862 }
863 }
864 InsnKind::Cmp { dst, a, b, kind } => {
865 let l = self.read_nest(*a);
866 let r = self.read_nest(*b);
867 *self.mflags = self.mflags.with_cmp();
868 self.write(*dst, Self::cmp_sentinel(*kind, l, r), ins.pc, false);
869 }
870 InsnKind::AGet {
871 dst, array, index, ..
872 } => {
873 let a = self.read_nest(*array);
874 let i = self.read_nest(*index);
875 self.write(
876 *dst,
877 Expr::ArrayIndex {
878 array: Box::new(a),
879 index: Box::new(i),
880 },
881 ins.pc,
882 false,
883 );
884 }
885 InsnKind::APut {
886 value,
887 array,
888 index,
889 ..
890 } => {
891 self.drop_pending_call();
892 let v = self.read_nest(*value);
893 let a = self.read_nest(*array);
894 let i = self.read_nest(*index);
895 let target = Expr::ArrayIndex {
896 array: Box::new(a),
897 index: Box::new(i),
898 };
899 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
900 target: Box::new(target),
901 op: AssignOp::Plain,
902 value: Box::new(v),
903 }));
904 }
905 InsnKind::IGet {
906 dst,
907 obj,
908 field_idx,
909 } => {
910 self.drop_pending_call();
911 let (cls, name, ty) = self.env.field_ref(*field_idx);
912 let owner = self.read_nest(*obj);
913 let owner_opt = self.owner_expr(owner, &cls);
914 self.write(
915 *dst,
916 Expr::Field {
917 owner: owner_opt,
918 cls,
919 name,
920 ty: TypeRef::J(ty),
921 is_static: false,
922 },
923 ins.pc,
924 false,
925 );
926 }
927 InsnKind::IPut {
928 value,
929 obj,
930 field_idx,
931 } => {
932 self.drop_pending_call();
933 let (cls, name, ty) = self.env.field_ref(*field_idx);
934 let v = self.read_nest(*value);
935 let owner = self.read_nest(*obj);
936 let owner_opt = self.owner_expr(owner, &cls);
937 let target = Expr::Field {
938 owner: owner_opt,
939 cls,
940 name,
941 ty: TypeRef::J(ty),
942 is_static: false,
943 };
944 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
945 target: Box::new(target),
946 op: AssignOp::Plain,
947 value: Box::new(v),
948 }));
949 }
950 InsnKind::SGet { dst, field_idx } => {
951 self.drop_pending_call();
952 let (cls, name, ty) = self.env.field_ref(*field_idx);
953 self.write(
954 *dst,
955 Expr::Field {
956 owner: None,
957 cls,
958 name,
959 ty: TypeRef::J(ty),
960 is_static: true,
961 },
962 ins.pc,
963 false,
964 );
965 }
966 InsnKind::SPut { value, field_idx } => {
967 self.drop_pending_call();
968 let (cls, name, ty) = self.env.field_ref(*field_idx);
969 let v = self.read_nest(*value);
970 let target = Expr::Field {
971 owner: None,
972 cls,
973 name,
974 ty: TypeRef::J(ty),
975 is_static: true,
976 };
977 self.stmts.push(Stmt::ExprStmt(Expr::Assign {
978 target: Box::new(target),
979 op: AssignOp::Plain,
980 value: Box::new(v),
981 }));
982 }
983 InsnKind::Invoke {
984 kind,
985 regs,
986 method_idx,
987 } => {
988 self.drop_pending_call();
989 self.do_invoke(*kind, regs, *method_idx, ins.pc)?;
990 }
991 InsnKind::InvokeCustom {
992 call_site_idx,
993 regs,
994 } => {
995 self.drop_pending_call();
996 let e = self.build_invoke_custom(*call_site_idx, regs, ins.pc);
997 self.pending_call = Some(e);
998 }
999 InsnKind::Un {
1000 dst,
1001 src,
1002 op,
1003 from: _,
1004 to,
1005 } => {
1006 let e = self.read_nest(*src);
1007 let ne = match op {
1008 ddc_dex::insn::UnArith::Neg => Expr::Un {
1009 op: UnOp::Neg,
1010 e: Box::new(e),
1011 },
1012 ddc_dex::insn::UnArith::Not => Expr::Un {
1013 op: UnOp::BitNot,
1014 e: Box::new(e),
1015 },
1016 ddc_dex::insn::UnArith::Conv => Expr::Cast {
1017 ty: TypeRef::J(primitive_type(*to)),
1018 e: Box::new(e),
1019 },
1020 };
1021 let wide = matches!(*to, 'J' | 'D');
1022 self.write(*dst, ne, ins.pc, wide);
1023 }
1024 InsnKind::Bin { op, dst, a, b, ty } => {
1025 let l = self.read_nest(*a);
1026 let r = self.read_nest(*b);
1027 let jt = primitive_type(*ty);
1028 let e = Expr::Bin {
1029 op: Self::arith_op(*op),
1030 l: Box::new(l),
1031 r: Box::new(r),
1032 ty: Some(TypeRef::J(jt)),
1033 };
1034 let wide = matches!(*ty, 'J' | 'D');
1035 self.write(*dst, e, ins.pc, wide);
1036 }
1037 InsnKind::BinLit {
1038 op,
1039 dst,
1040 a,
1041 lit,
1042 rsub,
1043 } => {
1044 let av = self.read_nest(*a);
1045 let e = if *rsub {
1046 Expr::Bin {
1047 op: BinOp::Sub,
1048 l: Box::new(Expr::Const(ConstVal::Int(*lit))),
1049 r: Box::new(av),
1050 ty: Some(TypeRef::J(JavaType::Int)),
1051 }
1052 } else {
1053 Expr::Bin {
1054 op: Self::arith_op(*op),
1055 l: Box::new(av),
1056 r: Box::new(Expr::Const(ConstVal::Int(*lit))),
1057 ty: Some(TypeRef::J(JavaType::Int)),
1058 }
1059 };
1060 self.write(*dst, e, ins.pc, false);
1061 }
1062 InsnKind::ConstMethodHandle { dst, handle_idx } => {
1063 self.drop_pending_call();
1064 let e = self.method_handle_expr(*handle_idx);
1065 self.write(*dst, e, ins.pc, false);
1066 }
1067 InsnKind::ConstMethodType { dst, proto_idx } => {
1068 self.drop_pending_call();
1069 let proto = self.env.dex.proto(*proto_idx);
1070 let desc = format!(
1071 "({}){}",
1072 self.env
1073 .dex
1074 .proto_params(*proto_idx)
1075 .iter()
1076 .map(|&t| self.env.dex.type_name(t))
1077 .collect::<Vec<_>>()
1078 .join(""),
1079 self.env.dex.type_name(proto.return_type_idx)
1080 );
1081 self.write(*dst, Expr::Raw(desc), ins.pc, false);
1082 }
1083 }
1084 }
1085
1086 let term = self.block_term(ins);
1088
1089 self.drop_pending_call();
1093 self.materialize_impure_at_exit();
1094
1095 let out = OutState {
1099 regs: std::mem::take(&mut self.regs),
1100 write_pc: std::mem::take(&mut self.write_pc),
1101 };
1102 Ok((
1103 BlockResult {
1104 stmts: std::mem::take(&mut self.stmts),
1105 out_stack: Vec::new(),
1106 term,
1107 },
1108 out,
1109 ))
1110 }
1111
1112 fn do_invoke(
1113 &mut self,
1114 kind: InvokeKind,
1115 regs: &[u16],
1116 method_idx: u32,
1117 pc: u32,
1118 ) -> BResult<()> {
1119 let (cls, name, md) = self.env.method_ref(method_idx);
1120 if cls == "java/lang/StringBuilder" || cls == "java/lang/StringBuffer" {
1121 *self.mflags = self.mflags.with_sb();
1122 }
1123 let is_static = matches!(kind, InvokeKind::Static);
1124 let receiver_reg = if is_static {
1125 None
1126 } else {
1127 regs.first().copied()
1128 };
1129 let arg_regs: Vec<u16> = if is_static {
1130 regs.to_vec()
1131 } else {
1132 regs.iter().skip(1).copied().collect()
1133 };
1134 let mut args: Vec<Expr> = Vec::with_capacity(md.args.len());
1135 for (i, _) in md.args.iter().enumerate() {
1136 let r = arg_regs.get(i).copied().unwrap_or(0);
1137 args.push(self.read_nest(r));
1138 }
1139 let recv_expr = receiver_reg.map(|r| self.read_nest(r));
1140
1141 if name == "<init>" && matches!(kind, InvokeKind::Direct) {
1143 let recv_reg = receiver_reg.unwrap_or(0);
1144 let recv_state = self
1145 .regs
1146 .get(recv_reg as usize)
1147 .cloned()
1148 .unwrap_or(Reg::Undef);
1149 if let Reg::Pending(Expr::New {
1150 raw: true, cls: nc, ..
1151 }) = &recv_state
1152 {
1153 if nc == &cls {
1154 let ty = TypeRef::J(JavaType::Object(cls.clone()));
1155 self.write(
1156 recv_reg,
1157 Expr::New {
1158 cls: cls.clone(),
1159 ty,
1160 args,
1161 raw: false,
1162 },
1163 pc,
1164 false,
1165 );
1166 return Ok(());
1167 }
1168 }
1169 let owner_expr_v = recv_expr.unwrap_or(Expr::This);
1170 let is_super = cls != self.env.class_name;
1171 let owner = if matches!(owner_expr_v, Expr::This) && is_super {
1172 None
1173 } else {
1174 Some(Box::new(owner_expr_v))
1175 };
1176 let call = Expr::Method {
1177 owner,
1178 cls,
1179 name: "<init>".into(),
1180 desc: md,
1181 args,
1182 is_static: false,
1183 is_interface: false,
1184 is_special: true,
1185 is_super,
1186 is_dynamic: false,
1187 type_args: vec![],
1188 };
1189 self.stmts.push(Stmt::ExprStmt(call));
1190 return Ok(());
1191 }
1192
1193 let is_interface = matches!(kind, InvokeKind::Interface);
1194 let is_super = match kind {
1195 InvokeKind::Super => true,
1196 InvokeKind::Direct if !is_static => cls != self.env.class_name,
1197 _ => false,
1198 };
1199 let owner: Option<Box<Expr>> = match recv_expr {
1200 None => None,
1201 Some(o) => {
1202 if is_super && matches!(o, Expr::This) {
1203 None
1204 } else {
1205 self.owner_expr(o, &cls)
1206 }
1207 }
1208 };
1209 let is_dynamic = matches!(kind, InvokeKind::Polymorphic);
1210 let call = Expr::Method {
1211 owner,
1212 cls,
1213 name,
1214 desc: md,
1215 args,
1216 is_static,
1217 is_interface,
1218 is_special: matches!(kind, InvokeKind::Direct),
1219 is_super,
1220 is_dynamic,
1221 type_args: vec![],
1222 };
1223 self.pending_call = Some(call);
1224 Ok(())
1225 }
1226
1227 fn method_handle_expr(&mut self, handle_idx: u32) -> Expr {
1230 let Some(h) = self.env.dex.method_handle(handle_idx) else {
1231 return Expr::Raw(format!("methodHandle@{}", handle_idx));
1232 };
1233 if h.is_field {
1234 let f = self.env.dex.field(h.target_id);
1235 let cls = self.env.dex.class_name(f.class_idx);
1236 let name = self.env.dex.string(f.name_idx).to_string();
1237 return Expr::Raw(format!("{}::{}", cls.replace('/', "."), name));
1238 }
1239 let m = self.env.dex.method(h.target_id);
1240 let cls = self.env.dex.class_name(m.class_idx);
1241 let name = self.env.dex.string(m.name_idx).to_string();
1242 Expr::Raw(format!("{}::{}", cls.replace('/', "."), name))
1243 }
1244
1245 fn build_invoke_custom(&mut self, cs_idx: u32, regs: &[u16], pc: u32) -> Expr {
1247 let Some(cs) = self.env.dex.call_site(cs_idx).cloned() else {
1248 return Expr::Invokedynamic {
1249 name: format!("call-site#{}", cs_idx),
1250 desc: jdc_core::types::MethodDescriptor {
1251 args: vec![],
1252 ret: JavaType::Object("java/lang/Object".into()),
1253 },
1254 args: vec![],
1255 bsm_text: "unresolved".into(),
1256 bsm_static_args: vec![],
1257 };
1258 };
1259 let site_name = self.env.dex.string(cs.name_idx).to_string();
1260 let params: Vec<JavaType> = self
1261 .env
1262 .dex
1263 .proto_params(cs.proto_idx)
1264 .iter()
1265 .map(|&t| self.env.java_type(t))
1266 .collect();
1267 let ret = self
1268 .env
1269 .java_type(self.env.dex.proto(cs.proto_idx).return_type_idx);
1270
1271 let (bs_cls, bs_name) = match self.env.dex.method_handle(cs.bootstrap_handle) {
1272 Some(h) if !h.is_field => {
1273 let m = self.env.dex.method(h.target_id);
1274 (
1275 self.env.dex.class_name(m.class_idx),
1276 self.env.dex.string(m.name_idx).to_string(),
1277 )
1278 }
1279 _ => (String::new(), String::new()),
1280 };
1281
1282 let mut args: Vec<Expr> = Vec::with_capacity(params.len().min(regs.len()));
1284 for (i, _) in params.iter().enumerate() {
1285 if let Some(&r) = regs.get(i) {
1286 args.push(self.read_nest(r));
1287 }
1288 }
1289
1290 if bs_name == "makeConcatWithConstants" {
1292 let recipe = cs
1293 .linker_args
1294 .iter()
1295 .find_map(|v| match v {
1296 ddc_dex::annotations::EncodedValue::String(s) => {
1297 Some(self.env.dex.string(*s).to_string())
1298 }
1299 _ => None,
1300 })
1301 .unwrap_or_default();
1302 return concat_from_recipe(&recipe, args);
1303 }
1304
1305 let mut impl_m = None;
1309 let mut instantiated_proto: Option<u32> = None;
1310 if bs_name == "metafactory" || bs_name == "altMetafactory" {
1311 let mut seen_handle = false;
1312 for lv in &cs.linker_args {
1313 match lv {
1314 ddc_dex::annotations::EncodedValue::MethodHandle(hi) => {
1315 if !seen_handle {
1316 seen_handle = true;
1317 if let Some(h) = self.env.dex.method_handle(*hi) {
1318 if !h.is_field {
1319 let m = self.env.dex.method(h.target_id);
1320 impl_m = Some((
1321 self.env.dex.class_name(m.class_idx).replace('/', "."),
1322 self.env.dex.string(m.name_idx).to_string(),
1323 ));
1324 }
1325 }
1326 }
1327 }
1328 ddc_dex::annotations::EncodedValue::MethodType(p) if seen_handle => {
1329 instantiated_proto = Some(*p);
1330 }
1331 _ => {}
1332 }
1333 }
1334 }
1335 let _ = pc;
1336
1337 let mut captures = Vec::new();
1339 for &r in regs.iter().skip(params.len().min(regs.len())) {
1340 captures.push(self.read_nest(r));
1341 }
1342
1343 if let Some((impl_cls, impl_name)) = impl_m {
1344 if captures.is_empty() {
1345 if impl_name.starts_with("lambda$") {
1349 let n_params = match instantiated_proto {
1353 Some(p) => self.env.dex.proto_params(p).len(),
1354 None => params.len(),
1355 };
1356 let pnames: Vec<String> = (0..n_params).map(|i| format!("a{}", i)).collect();
1357 return Expr::Raw(format!(
1358 "({}) -> {}.{}({})",
1359 pnames.join(", "),
1360 impl_cls,
1361 impl_name,
1362 pnames.join(", ")
1363 ));
1364 }
1365 return Expr::Raw(format!("{}::{}", impl_cls, impl_name));
1366 }
1367 let mut call_args = args;
1370 call_args.extend(captures);
1371 return Expr::Invokedynamic {
1372 name: format!("{} -> {}.{}", site_name, impl_cls, impl_name),
1373 desc: jdc_core::types::MethodDescriptor { args: params, ret },
1374 args: call_args,
1375 bsm_text: format!("{}::{}", bs_cls, bs_name),
1376 bsm_static_args: vec![],
1377 };
1378 }
1379
1380 Expr::Invokedynamic {
1381 name: site_name,
1382 desc: jdc_core::types::MethodDescriptor { args: params, ret },
1383 args: captures,
1384 bsm_text: format!("{}::{}", bs_cls, bs_name),
1385 bsm_static_args: vec![],
1386 }
1387 }
1388
1389 fn materialize_impure_at_exit(&mut self) {
1390 let n = self.regs.len();
1391 for r in 0..n {
1392 let st = self.regs[r].clone();
1393 let impure = match &st {
1394 Reg::PendingCall(_) => true,
1395 Reg::Pending(e) => {
1396 has_side_effects(e)
1397 && !matches!(e, Expr::New { raw: true, .. })
1398 && !matches!(e, Expr::NewArray { init: None, .. })
1399 }
1400 _ => false,
1401 };
1402 if impure {
1403 self.materialize(r as u16);
1404 }
1405 }
1406 }
1407
1408 fn block_term(&mut self, ins: &[Insn]) -> Term {
1410 let Some(last) = ins.last() else {
1411 return Term::Goto;
1412 };
1413 match &last.kind {
1414 InsnKind::Goto { .. } => Term::Goto,
1415 InsnKind::ReturnVoid => Term::Return(None),
1416 InsnKind::Return { src } => Term::Return(Some(self.read_term(*src))),
1417 InsnKind::Throw { reg } => Term::Throw(self.read_term(*reg)),
1418 InsnKind::If { op, a, b, z, .. } => {
1419 let cond = if *z {
1420 let v = self.read_nest(*a);
1421 let (l, r, realop) = jdc_core::ir::build::unfold_cmp(v, Self::cmp_op(*op));
1422 Expr::Bin {
1423 op: realop,
1424 l: Box::new(l),
1425 r: Box::new(r),
1426 ty: Some(TypeRef::J(JavaType::Boolean)),
1427 }
1428 } else {
1429 let l = self.read_nest(*a);
1430 let r = self.read_nest(*b);
1431 Expr::Bin {
1432 op: Self::cmp_op(*op),
1433 l: Box::new(l),
1434 r: Box::new(r),
1435 ty: Some(TypeRef::J(JavaType::Boolean)),
1436 }
1437 };
1438 Term::Cond { cond }
1439 }
1440 InsnKind::PackedSwitch { reg, payload_pc }
1441 | InsnKind::SparseSwitch { reg, payload_pc } => {
1442 let selector = self.read_term(*reg);
1443 let default = Some(last.pc + last.size);
1444 let targets = match self.env.code.payloads.get(payload_pc) {
1445 Some(Payload::Packed { first_key, targets }) => SwitchTargets::Table {
1446 low: *first_key,
1447 targets: targets
1448 .iter()
1449 .map(|t| (last.pc as i64 + *t as i64) as u32)
1450 .collect(),
1451 },
1452 Some(Payload::Sparse { pairs }) => SwitchTargets::Lookup {
1453 pairs: pairs
1454 .iter()
1455 .map(|(k, t)| (*k, (last.pc as i64 + *t as i64) as u32))
1456 .collect(),
1457 },
1458 _ => SwitchTargets::Lookup { pairs: vec![] },
1459 };
1460 Term::Switch {
1461 selector,
1462 targets,
1463 default,
1464 }
1465 }
1466 _ => {
1468 let next = last.pc + last.size;
1469 if next < self.code_units {
1470 Term::Fallthrough
1471 } else {
1472 Term::Goto
1473 }
1474 }
1475 }
1476 }
1477}
1478
1479fn concat_from_recipe(recipe: &str, args: Vec<Expr>) -> Expr {
1483 use jdc_core::ir::expr::ConcatPart;
1484 let mut parts: Vec<ConcatPart> = Vec::new();
1485 let mut arg_i = 0usize;
1486 let mut const_buf = String::new();
1487 for c in recipe.chars() {
1488 match c {
1489 '\u{1}' => {
1490 if !const_buf.is_empty() {
1491 parts.push(ConcatPart::Const(std::mem::take(&mut const_buf)));
1492 }
1493 if arg_i < args.len() {
1494 parts.push(ConcatPart::Str(args[arg_i].clone()));
1495 arg_i += 1;
1496 }
1497 }
1498 '\u{2}' => {
1499 }
1502 c => const_buf.push(c),
1503 }
1504 }
1505 if !const_buf.is_empty() {
1506 parts.push(ConcatPart::Const(const_buf));
1507 }
1508 Expr::StringConcat(parts)
1509}
1510
1511fn expr_size(e: &Expr) -> usize {
1515 let mut n = 0usize;
1516 count_nodes(e, &mut n);
1517 n
1518}
1519
1520fn count_nodes(e: &Expr, n: &mut usize) {
1521 *n += 1;
1522 if *n > 512 {
1523 return; }
1525 match e {
1526 Expr::Un { e, .. } | Expr::Cast { e, .. } | Expr::InstanceOf { e, .. } => count_nodes(e, n),
1527 Expr::Bin { l, r, .. } => {
1528 count_nodes(l, n);
1529 count_nodes(r, n);
1530 }
1531 Expr::Cond { c, t, f } => {
1532 count_nodes(c, n);
1533 count_nodes(t, n);
1534 count_nodes(f, n);
1535 }
1536 Expr::Assign { target, value, .. } => {
1537 count_nodes(target, n);
1538 count_nodes(value, n);
1539 }
1540 Expr::PreIncDec { e, .. } | Expr::PostIncDec { e, .. } => count_nodes(e, n),
1541 Expr::Field { owner: Some(o), .. } => count_nodes(o, n),
1542 Expr::Method {
1543 owner: Some(o),
1544 args,
1545 ..
1546 } => {
1547 count_nodes(o, n);
1548 for a in args {
1549 count_nodes(a, n);
1550 }
1551 }
1552 Expr::ArrayIndex { array, index } => {
1553 count_nodes(array, n);
1554 count_nodes(index, n);
1555 }
1556 Expr::New { args, .. } => {
1557 for a in args {
1558 count_nodes(a, n);
1559 }
1560 }
1561 Expr::NewArray { dims, init, .. } => {
1562 for d in dims {
1563 count_nodes(d, n);
1564 }
1565 if let Some(v) = init {
1566 for x in v {
1567 count_nodes(x, n);
1568 }
1569 }
1570 }
1571 Expr::NewMultiArray { dims, .. } => {
1572 for d in dims {
1573 count_nodes(d, n);
1574 }
1575 }
1576 Expr::StringConcat(parts) => {
1577 for p in parts {
1578 if let ConcatPart::Str(x) = p {
1579 count_nodes(x, n);
1580 }
1581 }
1582 }
1583 _ => {}
1584 }
1585}
1586
1587fn reg_is_wide(st: &Reg, vt: &VarTable) -> bool {
1588 match st {
1589 Reg::Pending(e) | Reg::PendingCall(e) => e.type_ref().erased().is_wide(),
1590 Reg::Live(v) => vt.var(*v).ty.erased().is_wide(),
1591 Reg::WideHi => true,
1592 Reg::Undef => false,
1593 }
1594}
1595
1596fn value_of_cmp(e: &Expr) -> Expr {
1598 if let Expr::Invokedynamic { name, args, .. } = e {
1599 if name.starts_with('\0') && args.len() == 2 {
1600 let (cls, ty) = match name.as_str() {
1601 "\0cmp-long" => ("java/lang/Long", JavaType::Long),
1602 "\0cmpl-float" | "\0cmpg-float" => ("java/lang/Float", JavaType::Float),
1603 _ => ("java/lang/Double", JavaType::Double),
1604 };
1605 let desc = MethodDescriptor {
1606 args: vec![ty.clone(), ty],
1607 ret: JavaType::Int,
1608 };
1609 return Expr::Method {
1610 owner: None,
1611 cls: cls.into(),
1612 name: "compare".into(),
1613 desc,
1614 args: args.clone(),
1615 is_static: true,
1616 is_interface: false,
1617 is_special: false,
1618 is_super: false,
1619 is_dynamic: false,
1620 type_args: vec![],
1621 };
1622 }
1623 }
1624 e.clone()
1625}
1626
1627pub fn primitive_type(c: char) -> JavaType {
1628 match c {
1629 'Z' => JavaType::Boolean,
1630 'B' => JavaType::Byte,
1631 'C' => JavaType::Char,
1632 'S' => JavaType::Short,
1633 'F' => JavaType::Float,
1634 'J' => JavaType::Long,
1635 'D' => JavaType::Double,
1636 _ => JavaType::Int,
1637 }
1638}
1639
1640fn payload_consts(elem_width: u16, size: u32, data: &[u8]) -> Vec<Expr> {
1643 let n = (size as usize).min(data.len() / elem_width.max(1) as usize);
1644 let mut out = Vec::with_capacity(n);
1645 for i in 0..n {
1646 let off = i * elem_width as usize;
1647 let e = match elem_width {
1648 1 => Expr::Const(ConstVal::Int(data[off] as i8 as i32)),
1649 2 => {
1650 let v = i16::from_le_bytes([data[off], data[off + 1]]);
1651 Expr::Const(ConstVal::Int(v as i32))
1652 }
1653 4 => {
1654 let mut b = [0u8; 4];
1655 b.copy_from_slice(&data[off..off + 4]);
1656 Expr::Const(ConstVal::Int(i32::from_le_bytes(b)))
1657 }
1658 8 => {
1659 let mut b = [0u8; 8];
1660 b.copy_from_slice(&data[off..off + 8]);
1661 Expr::Const(ConstVal::Long(i64::from_le_bytes(b)))
1662 }
1663 _ => Expr::Const(ConstVal::Int(0)),
1664 };
1665 out.push(e);
1666 }
1667 out
1668}
1669
1670pub fn entry_regs(vt: &mut VarTable, env: &MethodEnv, param_names: &[Option<String>]) -> Vec<Reg> {
1672 let code = env.code;
1673 let n = code.registers_size as usize;
1674 let mut regs = vec![Reg::Undef; n.max(1)];
1675 let ins = code.ins_size.min(code.registers_size) as usize;
1676 let mut reg = n.saturating_sub(ins);
1677 let mut pidx = 0;
1678 if !env.is_static && reg < n {
1679 let id = vt.vars.len() as u32;
1681 vt.vars.push(VarInfo {
1682 id,
1683 slot: reg as u16,
1684 name: "this".into(),
1685 ty: TypeRef::J(JavaType::Object(env.class_name.clone())),
1686 is_param: true,
1687 range_start: 0,
1688 range_end: u16::MAX,
1689 synthetic_name: false,
1690 });
1691 while vt.by_slot.len() <= reg {
1692 vt.by_slot.push(Vec::new());
1693 }
1694 vt.by_slot[reg].push((0, u16::MAX, id));
1695 regs[reg] = Reg::Live(id);
1696 reg += 1;
1697 pidx += 1;
1698 }
1699 for (i, arg) in env.desc.args.iter().enumerate() {
1700 if reg >= n {
1701 break;
1702 }
1703 let dbg = param_names.get(i).and_then(|o| o.clone());
1704 let named = dbg.is_some();
1705 let name = dbg.unwrap_or_else(|| format!("p{}", pidx));
1706 let id = vt.vars.len() as u32;
1707 vt.vars.push(VarInfo {
1708 id,
1709 slot: reg as u16,
1710 name,
1711 ty: TypeRef::J(arg.clone()),
1712 is_param: true,
1713 range_start: 0,
1714 range_end: u16::MAX,
1715 synthetic_name: !named,
1716 });
1717 while vt.by_slot.len() <= reg {
1718 vt.by_slot.push(Vec::new());
1719 }
1720 vt.by_slot[reg].push((0, u16::MAX, id));
1721 regs[reg] = Reg::Live(id);
1722 reg += if arg.is_wide() { 2 } else { 1 };
1723 pidx += 1;
1724 }
1725 regs
1726}